Androidでfacebookクライアントアプリを開発する(準備編)の続き
今日は開発のところ
以下作成の手順
- 下準備1(Android版facebook sdkのダウンロード)
- 下準備2(facebookへのdeveloper登録)
- プロジェクト作成
- Facebook SDKから必要なファイルをコピー
- AndroidManifest.xmlの編集
- layoutの作成
- 定義ファイルの作成
- 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クライアントのほうもリファクタリングしておくかな。