2013 年 5 月 6 日

アンドロイドアプリからFacebookへログインする方法(後編)

アンドロイドアプリからFacebookへログインする方法(後編)

前回はコーディングの前の準備として、Facebook SDK3.0のダウンロード、Facebook App作成などについて説明しました。
今回は実際にアンドロイドアプリからFacebookログインする部分をコーディングしていきます。
前編の「アンドロイドアプリからFacebookeへログインする方法(前編)」も参照しながら、読んでください。

一番簡単な実装は、Facebook SDKに含まれているButtonウィジェット(com.facebook.widget.LoginButton)や認証ダイアログ(com.facebook.widget.UserSettingsFragment)を利用することだと思います。

しかし、Buttonウィジェットを使うと、ボタンのデザインに自由度が欠けるということ、また、このボタンを使わないサンプルのコードでは、パーミッションの設定方法がよくわからなかったので、独自に実装してみました。

ここではemail取得のパーミッションを付加して、ログインしたユーザーのユーザー情報をログに出力することにします。アプリにログインするときはemailアドレスがよく使われるので、emailが取得できるサンプルになっています。

変数の宣言

メインのアクティビティに必要なメンバ変数を宣言します。

  // ログインボタン
  private Button mFbloginBtn;
  // ライフサイクルヘルパー
  private UiLifecycleHelper mUiHelper;
  // セッションステートコールバックメソッド
  private Session.StatusCallback mFacebookCallback = new Session.StatusCallback() {
    @Override
    public void call(Session session, SessionState state, Exception exception) {
      onSessionStateChange(session, state, exception);
    }
  };

ライフサイクルヘルパーとはアクティビティのライフサイクルにあわせて、必要なセッション処理を実行します。アクティビティのライフサイクルハンドラーにあわせてコールする必要があります。セッションコールバックメソッドは、セッションの状態が変化したときに呼び出しされるメソッドです。

初期処理

このメソッドをonCreateでコールしておきます。

  private void initFacebook(Bundle savedInstanceState) {
    // ライフサイクルヘルパーの初期化
    mUiHelper = new UiLifecycleHelper(this, mFacebookCallback);
    mUiHelper.onCreate(savedInstanceState);
    // ログインボタンの初期化
    mFbloginBtn = (Button)findViewById(R.id.buttonAuth);
    mFbloginBtn.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        loginFacebook();
      }
    });    
  }

ログイン・ログアウト

今回は、ログアウトは使用していませんが、ログイン済みならログアウトボタンを表示するようにします。

ログイン処理では、リクエストを生成し、パーミッションを付加して、セッションとともに認証要求します。

private void loginFacebook() {
  // リクエストの生成
  OpenRequest openRequest = new OpenRequest(this).setCallback(mFacebookCallback);
  // emailを要求するパーミッションを設定
  openRequest.setPermissions(Arrays.asList("email"));
  // セッションを生成
  Session session = new Builder(this).build();
  // アクティブセッションとする。
  Session.setActiveSession(session);
  // 認証を要求する。
  session.openForRead(openRequest);
  return session;
}

private void logoutFacebook() {
  Session session = Session.getActiveSession();
  if (!session.isClosed()) {
    // セッションとトークン情報を廃棄する。
    session.closeAndClearTokenInformation();
  }
}

セッションステートコールバック

認証から戻ってきたときの処理を記述します。ここでは認証が成功すれば、さらにログインした人の情報を取得するためにGraphAPIを呼び出しています。HTTP通信が必要なので非同期処理になっています。

SDK3.5では、executeMeRequestAsyncがdeprecatedされましたので、3.5.2でのコーディングを追記します(2013/11/10)。

SDK3.0

private void onSessionStateChange(Session session, SessionState state, Exception exception) {
  if (session.isOpened()) {
    // GraphAPIのmeリクエストを呼び出す。
    Request.executeMeRequestAsync(session, new FacebookGraphUserCallback("wait...") {
      @Override
      public void onCompleted(GraphUser user, Response response) {
        super.onCompleted(user, response);
  
        Log.d(TAG, "user = " + user.getInnerJSONObject());
      }
    });
  }
}

SDK3.5.2

private void onSessionStateChange(Session session, SessionState state, Exception exception) {
    if (session.isOpened()) {
    // GraphAPIのmeリクエストを呼び出す。
      Request.newMeRequest(session, new FacebookGraphUserCallback("wait...") {
        @Override
        public void onCompleted(GraphUser user, Response response) {
          super.onCompleted(user, response);
          
          Log.d(TAG, "user = " + user.getInnerJSONObject());
        }
      }).executeAsync();
    }
  }

ライフサイクルメソッド

以下のライフサイクルメソッドをOverrideして、それぞれFacebookのライフサイクルヘルパーのメソッドを呼び出しておきます。

 @Override
  public void onPause() {
      super.onPause();
      mUiHelper.onPause();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    mUiHelper.onDestroy();
  }

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    mUiHelper.onActivityResult(requestCode, resultCode, data);
  }
  
  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mUiHelper.onSaveInstanceState(outState);
  }

グラフユーザーコールバック

認証が得られたログインユーザーの情報を取得するときにプログレスを表示するためのコールバッククラスです。

class FacebookGraphUserCallback implements Request.GraphUserCallback {

  private ProgressDialog mProgress = null;

  public FacebookGraphUserCallback(String message) {
    mProgress = new ProgressDialog(MainActivity.this);
    mProgress.setMessage(message);
    mProgress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    mProgress.show();     
  
  }
  @Override
  public void onCompleted(GraphUser user, Response response) {
    mProgress.dismiss();
  }
}

サンプルの実行

このプログラムは以下のように動作します。まず、ログイン画面を表示します。

ログインボタンをタップすると、Facebookの認証画面へ遷移し、Facebookアカウントの入力が求められます。アカウントを入力してログインボタンをタップします。

最後にどのような情報を要求しているかの説明と、許可を求めます。よければOKボタンをタップすると、元の画面へ戻ります。

このとき、ログにJSON形式のユーザー情報が表示されます。個人情報は伏せさせていただいています。メールアドレスも取得できています。

{
  "location":{
    "id":"xxx",
    "name":"Osaka-shi, Osaka, Japan"
  },
  "locale":"ja_JP",
  "link":"http:\/\/www.facebook.com\/xxx",
  "updated_time":"2013-0407T08:37:27+0000",
  "id":"xx53xx04xx",
  "first_name":"xxx",
  "timezone":9,
  "username":"xxx",
  "quotes":"xxx",
  "email":"hoge@hoge.com",
  "verified":true,
  "name":"xxx",
  "last_name":"xxx",
  "gender":"male"
}

これでFacebook SDK3.0でアンドロイドアプリからFacebookログインにできました。
SDK2.0でFacebookクラスに集約されていた機能が、SDK3.0では、Session,Request,UiLifecycleHelperの各クラスに機能分割されたようです。

認証アクティビティを行き来するアプリでは、アクティビティで遷移するより、ひとつのアクティビティで複数のFragmentを制御する方が実装が簡単でしょう。特に戻るボタンを押されたときの処理を考えると、このような実装がよいのではないでしょうか。実際、Facebookのサンプルでも、そのような実装があります。

蛇足ですが、com.facebook.widget.LoginButtonでは、com.facebook.internal下のAPIが結構利用されています。
これらを参考にしようと思いましたが、com.facebook.internal下のクラスは、いつ変更が加えられたり、deprecatedされるかわからないらしいので、あまり使わない方がよさそうです。