Androidでfacebookクライアントアプリを開発する(開発編)

Androidでfacebookクライアントアプリを開発する(準備編)の続き
今日は開発のところ

以下作成の手順

  1. 下準備1(Android版facebook sdkのダウンロード)
  2. 下準備2(facebookへのdeveloper登録)
  3. プロジェクト作成
  4. Facebook SDKから必要なファイルをコピー
  5. AndroidManifest.xmlの編集
  6. layoutの作成
  7. 定義ファイルの作成
  8. Activityの設定

5.AndroidManifest.xmlの編集

今回開発するアプリもTwitterのクライアントアプリの時と同様に、トリガとなるページとFacebookの認証画面を表示するページの2枚のアクティビティが必要なので、それらを追加。インターンネットへの接続も許可しなくてはならないので、uses-permissionにINTERNETも追加。
※TwitterのSDKを使う場合、認証画面はポップアップのような形で表示されるので、別画面にする必要は特にないのだが、今後のため(?)にtwitterと同じアクティビティ遷移にしてみた

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.miraoto.testfacebook"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <application android:icon="@drawable/ic_launcher"
           android:label="@string/app_name"
              android:debuggable="true">
              <activity android:name=".IndexActivity"
                  android:label="@string/app_name"
                     android:configChanges="keyboardHidden">
                     <intent-filter>
                           <action android:name="android.intent.action.MAIN" />
                           <category android:name="android.intent.category.LAUNCHER" />
                     </intent-filter>
              </activity>
              <activity android:name=".LoginFacebookActivity" ></activity>
       </application>
       <uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

6.layoutの作成

Twitterクライアントアプリの時と同様に、ボタンのページ(main.xml)、Facebookの認証画面(login.xml)を表示するページを作成
例)
main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:gravity="center" >

    <Button
        android:id="@+id/tweet"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="うぉーるに投稿する" />
</LinearLayout>

login.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <WebView
        android:id="@+id/login"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

7.定義ファイルの作成

app idとかを設定。

public class FacebookConst {

       // Application ID
       public static final String APP_ID = "XXXXXXXXXXXXXXXX";

       public static final String PERM_PUBLIC_STREAM = "publish_stream";
       public static final String PERM_OFFLINE_ACCESS = "offline_access";

       public static final String PREF_KEY = "facebook_setting";
       public static final String SUB_KEY_ACCESS_TOKEN = "access_token";
       public static final String SUB_KEY_ACCESS_TOKEN_EXPIRES = "access_token_expires";

       public static final String PARAM_MESSAGE = "message";
       public static final String PARAM_ACCESS_TOKEN = "access_token";
}

8.Activityの設定

Twitterの時と同じようなアクティビティの構成(ボタンのページ(IndexActivity)、Twitterの認証画面(LoginTwitterActivity))でもよかったんだけど、複数のサービスを利用する場合には、この構成だと色々と煩雑になってしまうので、若干構成を変更。
ボタンのページ(IndexActivity)、認証画面(LoginFacebookActivity)、認証以外のfacebookの処理(ServiceFacebook)の3画面に変更した。(ちなみにServiceFacebookはActivityではないです)

例)
IndexActivity.java

public class IndexActivity extends Activity {

       private ServiceFacebook serviceFacebook;



    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        serviceFacebook = new ServiceFacebook(this);

        Button tweetBotton = (Button) findViewById(R.id.wall);
        tweetBotton.setOnClickListener(new OnClickListener() {
                     @Override
                     public void onClick(View v) {
                           serviceFacebook.setMessage("ついーとついーと");
                           serviceFacebook.dispatch();
                     }
              });
    }
}

LoginFacebookActivity.java

public class LoginFacebookActivity extends Activity {

       private Facebook facebook = new Facebook(FacebookConst.APP_ID);
       private String[] permissions = { FacebookConst.PERM_PUBLIC_STREAM, FacebookConst.PERM_OFFLINE_ACCESS };



    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);

       this.facebook.authorize(this, this.permissions, new DialogListener() {
                     @Override
                     public void onComplete(Bundle values) {
                           SharedPreferences pref = getSharedPreferences(FacebookConst.PREF_KEY, MODE_PRIVATE);
                           SharedPreferences.Editor editor = pref.edit();
                            editor.putLong(FacebookConst.SUB_KEY_ACCESS_TOKEN_EXPIRES, facebook.getAccessExpires());
                           editor.putString(FacebookConst.SUB_KEY_ACCESS_TOKEN, facebook.getAccessToken());
                           editor.commit();
                           finish();
                     }

                     @Override
                     public void onFacebookError(FacebookError e) {
                           Log.e("DEBUG_LOG", "FacebookError::" + e.getMessage());
                           finish();
                     }

                     @Override
                     public void onError(DialogError e) {
                           Log.e("DEBUG_LOG", "FacebookError::" + e.getMessage());
                           finish();
                     }

                     @Override
                     public void onCancel() {
                           Log.d("DEBUG_LOG", "Facebook::authorize::onCancel");
                           finish();
                     }
              });
    }

    @Override
       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
              super.onActivityResult(requestCode, resultCode, data);
              this.facebook.authorizeCallback(requestCode, resultCode, data);
       }
}

ServiceFacebook.java

public class ServiceFacebook {

       private Context context;
       private Facebook facebook = new Facebook(FacebookConst.APP_ID);
       private ProgressDialog dialog;
       private String message = null;



       public ServiceFacebook(Context context) {
              this.context = context;
       }

       /**
        * 処理分岐制御
        *
        */
       public void dispatch() {

              SharedPreferences pref = pref  = context.getSharedPreferences(FacebookConst.PREF_KEY,context.MODE_PRIVATE);
              facebook.setAccessToken(pref.getString(FacebookConst.SUB_KEY_ACCESS_TOKEN, null));
              facebook.setAccessExpires(pref.getLong(FacebookConst.SUB_KEY_ACCESS_TOKEN_EXPIRES, 0));

              if (isConnected(facebook)) {
                     post();
              }
              else {
               Intent intent = new Intent(this.context, LoginFacebookActivity.class);
               this.context.startActivity(intent);
              }
       }

       /**
        * 接続判定
        *
        * @param token
        * @param tokenSecret
        * @return boolean true or false
        */
       public boolean isConnected(Facebook facebook) {
              try {
                     if (!facebook.isSessionValid()) {
                           return false;
                     }
                     String res = facebook.request("/me");
                     if (res.startsWith("{\"error\"")) {
                           return false;
                     }
                     return true;
              } catch (MalformedURLException e) {
                     return false;
              } catch (IOException e) {
                     return false;
              }
       }

       /**
        * ツイート処理
        *
        */
       public void post() {
              SharedPreferences pref = context.getSharedPreferences(FacebookConst.PREF_KEY, Service.MODE_PRIVATE);
              facebook.setAccessToken(pref.getString(FacebookConst.SUB_KEY_ACCESS_TOKEN, null));
              facebook.setAccessExpires(pref.getLong(FacebookConst.SUB_KEY_ACCESS_TOKEN_EXPIRES, 0));

           dialog = new ProgressDialog(context);
        dialog.setTitle("共有");
        dialog.setMessage("ウォールに投稿しています...");
        dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        dialog.show();

        Thread thread = new Thread(new Progress());
        thread.start();
       }

       private class Progress implements Runnable {

        public void run() {
              try {
                           Bundle param = new Bundle();
                           param.putString(FacebookConst.PARAM_MESSAGE, getMessage());
                           param.putString(FacebookConst.PARAM_ACCESS_TOKEN, facebook.getAccessToken());

                           facebook.request("/me/feed", param, "POST");
                     } catch (MalformedURLException e) {
                           Log.e("facebookRequest::wall : ", e.getMessage());
                     } catch (IOException e) {
                           Log.e("facebookRequest::wall : ", e.getMessage());
                     }
            handler.sendEmptyMessage(0);
        }
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            dialog.dismiss();
        }
    };

    /**
    *
    */
   public void setMessage(String message) {
       this.message = message;
   }

   /**
    *
    */
   private String getMessage() {
       return message;
   }
}

これで完了。
Twitterクライアントのほうもリファクタリングしておくかな。