【Android】google+投稿取得

[Android]google+投稿取得

https://www.android.com/

こんにちは。良昌です。

最近、Twitter、facebook、google+の投稿を取得するプログラムを作る機会がありました。各種ライブラリや、SDKが用意されているので、誰でも簡単に作ることができますね。

今回は、google+の投稿取得方法について書きたいと思います。残念ながら現時点ではAPIの制限によりpublicの投稿しか取得できませんが、天下のgoogle様のSNSですので、そのうちprivateの投稿も取得できるようになると思います。たぶん。。。。

今回はstaticメソッドのみを保持するユーティリティクラスを作成しました。publicなインターフェースは以下4点となります。下記機能を実現するため、google-play-services_libを利用しています。

  • ログイン
  • ログアウト
  • google+ログインActivityの終了通知を受ける
  • 投稿を取得する

※google+と連携するためには、Google Developers Consoleにて、各種設定を行う必要がありますが、今回は割愛させて頂きます。

(1)google+にログインする

ログインにはOAuth2.0を利用しています。google-play-services_libにはGoogleApiClientオブジェクトが用意されており、このクライアントを生成しconnect()メソッドを呼ぶことで、コネクション取得時のコールバック、及び認証失敗時のリスナを呼出す仕組みになっています。

まずは、クラス変数としてContext、GoogleApiClient、ステータスを判定するための列挙型を用意します。

private static GoogleApiClient mClient;
private static Context mContext;
private enum State {
    LOGIN,
    LOGOUT,
    CANCEL,
    RECEIVE_POST_DATA,
    ERROR,
}
private static State mState;

次に、認証失敗時のリスナを定義します。未ログイン状態の場合は初回必ずOnConncetionFailedListenerインターフェースのonConnectionFailed()メソッドが呼出されますので、この中で、ログイン画面を呼出す仕組みを定義します。今回はstaticメソッドとなりますので、匿名クラスを返却する形にしました。

private static OnConnectionFailedListener getConnectionFailedListener() {
    return new OnConnectionFailedListener() {
        @Override
        public void onConnctionFailed(final ConnectionResult result) {
            switch (result.getErrorCode()) {
                case ConnectionResult.API_UNAVAILABLE:
                case ConnectionResult.INTERNAL_ERROR:
                    // ①ここでエラー処理を行う。
                    mState = State.ERROR;
                    break;
                default:
                    try {
                        // ②ログイン画面の呼出し
                        ((Activity)mContext).startIntentSenderForResult(
                            result.getResolution().getIntentSender(), 0, null, 0, 0, 0);
                    } catch (IntentSender.SendException e) {
                        // ③ログイン画面の呼出しに失敗した場合は再接続
                        mClient.connect();
                    }
                    break;
            }
        }
    };
}

①はGoogle APIが利用できないアカウントだったり、ネットワークがつながっていない場合のケースですので、好きなようにエラー処理を実装してください。今回はステータスにErrorをセットしているだけです。このケース以外は、未ログイン状態ですので、②でリスナに送信されたIntentを利用して、ログイン画面のActivityを呼出します。呼出しが失敗した場合はIntentSender.SendException()がスローされますので、③で再度接続を行います。


認証失敗時のリスナからログイン画面が呼び出されると、ユーザ操作に入ります。操作完了後に、このユーティリティのログイン処理を呼出したActivity#onActivityResult()メソッドに処理が移りますので、その中で呼出す処理を実装します。

public static void onActivityResult(final int resultCode) {
    switch (resultCode) {
        case Activity.RESULT_CANCELED:
            // ①ここでキャンセル時の処理を行う。
            mState = State.CANCEL;
            break;
        case Activity.RESULT_OK:
            // ②ログインボタンが押下された場合は認証処理を行う。
            mClient.connect();
            break;
        default:
            break;
    }
}

①はバックキー、またはキャンセルボタンが押下されたケースですので、その場合の処理を実装してください。今回はステータスにCANCELをセットしているだけです。ログインボタンを押下された場合は、入力されたアカウントにて認証処理を実行します。


続いて、コネクション取得時のコールバックです。既にログイン済みの場合や、ログイン画面からのアカウント送信時には状態により、ConnectionCallbacksインターフェースのonConnectionSuspended()、またはonConnected()メソッドが呼出されます。

private static ConnectionCallbacks getConnectionCallbacks() {
    return new ConnectionCallbacks() {
        @Override
        public void onConnectionSuspended(final int cause) {
            // ①サスペンド時は再接続
            mClient.connect();
        }
        @Override
        public void onConnected(final Bundle connectionHint) {
            switch (mState) {
                case LOGIN:
                    // ②ここでログイン時の処理を行う。
                    break;
                case LOGOUT:
                    // ③ここでログアウト処理を行う。
                    break;
                case RECEIVE_POST_DATA:
                    // ④ここで投稿取得の処理を行う。
                    break;
                default:
                    break;
            }
        }
    };
}

①は通信状態が悪い場合などのサスペンド時ですので、再接続を行います。認証に成功した場合は、onConnected()メソッドが呼び出されますので、ステータスに応じて処理を行います。③ログアウト、④投稿データ取得の処理については、後程説明します。


クライアントの生成処理は以下の様に行います。

private static void createClient() {
    mClient = new Builder(mContext)
              .addApi(Plus.API, Plus.PlusOptions.builder().build()) // ①
              .addScope(Plus.SCOPE_PLUS_LOGIN) // ②
              .addConnectionCallbacks(getConnectionCallbacks()) // ③
              .addOnConnectionFailedListener(getConnectionFailedListener()) // ④
              .build();
}

①では利用するAPIを設定します。今回はgoogle+を利用しますので、Plus.APIと、Plus.PlusOptionsをセットしています。②はクライアントのスコープです。今回はログインと、投稿データの取得のみですので、ログインスコープをセットしています。③④については、上記のコールバック、リスナを設定します。


最後にログイン時のインターフェースです。

public static void login(final Context context) {
    mContext = context;
    mState = State.LOGIN;
    createClient();
    mClient.connect();
}

また、呼出し側のActivityは以下の様になります。作成したユーティリティはGooglePlusUtilsとしています。

@Override
protected void onCreate(final Bundle savedInstanceState) {
    GooglePlusUtils.login(this.getApplicationContext());
}

@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    GooglePlusUtils.onActivityResult(resultCode);
}

ログイン処理時のフローは以下の様になります。

  • ①Activity#onCreate()
  • ②GooglePlusUtils#login()
  • ③GooglePlusUtils#createClient()
  • ④OnConnectionFailedListener#onConnectionFailed()
  • ⑤ログイン画面表示、ユーザ操作
  • ⑥Activity#onActivityResult()
  • ⑦GooglePlusUtils#onActivityResult()
  • ⑧ConnectionCallbacks#onConnected()

(2)google+からログアウトする

ログインの処理ができていれば、ログアウトの処理は比較的簡単です。まずはインターフェースです。

public static void logout(final Context context) {
    mContext = context;
    mState = State.LOGOUT;
    createClient();
    mClient.connect();
}

気付いているかもしれませんが、login()メソッドのステータス設定箇所をLOGOUTにしただけです。GoogleAPIではログアウト時も認証処理を行う必要がありますので、同様の処理となります。


既にログイン済みですので、認証処理を行うと、ConncetionCallbacks#onConnected()が呼ばれます。ですので、先ほど作成したonConnected()メソッド内に処理を追記すれば完了です。③の部分に以下を追記します。

case LOGOUT:
    // ③ここでログアウト処理を行う。
    Plus.AccountApi.revokeAccessAndDisconnect(mClient).setResultCallback(new ResultCallback<Status>() {
        @Override
        public void onResult(final Status status) {
            if (status.isSuccess()) {
                // ③-a.ここで、ログアウト成功時の処理を行う。
            } else {
                // ③-b.ここで、ログアウト失敗時の処理を行う。
                mState = State.ERROR;
            }
        }
    });
    break;

ログアウト処理にはコールバックを設定することができますので、そこで成功/失敗の判定を行います。

また、呼出し側のActivityは以下の様になります。

@Override
protected void onCreate(final Bundle savedInstanceState) {
    GooglePlusUtils.logout(this.getApplicationContext());
}

ログアウト処理時のフローは以下の様になります。

  • ①Activity#onCreate()
  • ②GooglePlusUtils#logout()
  • ③GooglePlusUtils#createClient()
  • ④ConnectionCallbacks#onConnected()

(3)google+から投稿を取得する

取得した投稿データはJsonReaderオブジェクトで返却しますので、まずはこれを受けるインターフェースを用意しておきます。

public interface SNSRequestListener {
    void onComplete(final JsonReader reader);
    void onError();
}

続いて、ユーティリティ側のインターフェースです。まずは先ほど作成したリスナをクラス変数として定義します。

private static SNSRequestListener mListener;

インターフェースは以下の様に定義します。

public static void receivePostData(final Context context, final SNSRequestListener listener) {
    mContext = context;
    mState = State.RECEIVE_POST_DATA;
    mListener = listener;
    createClient();
    mClient.connect();
}

ログイン、ログアウトと流れは同様で、引数をクラス変数にセットし、ステータスを変更後、認証処理を行います。


次は認証成功時に投稿データ取得APIを叩くための非同期タスクの実装です。

投稿データを取得する場合はhttps://www.googleapis.com/plus/v1/people/userId/activities/collectionをリクエストします。userIdはme、collectionにはpublic(*1)を指定します。
(*1)冒頭で記載した通り、現状ではpublicのみしか指定できません。

private static class ActivitiesListGetTask extends AsyncTask {
    @Override
    protected String doInBackground(final Void... params) {
        HttpURLConnection conn = null;
        BufferedReader br = null;
        JsonReader reader = null;
        try {
            // ①リクエストするAPIの生成
            final String url =
                Uri.parse("https://www.googleapis.com/plus/v1/people/me/activities/public")
                .buildUpon()
                .appendQueryParameter("maxResults", "10") // ①-a.パラメータの設定
                .build().toString();

            // ②アクセストークンの生成
            final String accessToken = GoogleAuthUtil.getToken(mContext,
                Plus.AccountApi.getAccountName(mClient), "oauth2:" + Scopes.PLUS_LOGIN);

            // ③コネクションの生成
            conn = (HttpURLConnection)new URL(url).openConnection();
            
            // ④リクエストヘッダの設定
            conn.setRequestProperty("Authorization", "OAuth " + accessToken);

            // ⑤ストリームの処理
            br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while((line = br.readLine()) != null) {
                sb.append(line);
            }
            reader = new JsonReader(new InputStreamReader(
                new ByteArrayInputStream(sb.toString().getBytes())));
        } catch (Exception e) {
            // エラー処理
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    // エラー処理
                }
            }
            if (conn != null) {
                conn.disconnect();
            }
        }
        return reader;
    }
    @Override
    protected void onPostExecute(final JsonReader result) {
        if (result == null) {
            // ⑥エラー時処理
            mListener.onError();
            return;
        }
        // 成功時処理
        mListener.onComplete(result);
    }
}

①ではリクエストするAPIのURLを生成しています。①-a.のようにクエリパラメータを設定することで、取得データにフィルタをかけることが可能です。今回は取得データ数が10件となるようパラメータを設定しています。パラメータについてはこちらを参照してください。

②ではリクエストヘッダに設定するための、アクセストークンを生成しています。今回は認証にOAuth2.0を利用しますので、GoogleAuthUtil#getToken()の第3引数にoauth2スコープとログインスコープを設定しています。

③でコネクションを開き、④でリクエストヘッダの設定、⑤でストリームの取得、及びJsonReaderへの変換を行っています。

onPostExecute()に処理が移ったら、引数の状態に応じて、リスナのメソッドを呼出します。


最後に、上記で作成した非同期タスクを呼出す処理を実装します。ログアウト処理と同様、ログイン処理時に作成したonConnected()メソッド内の④の部分に以下を追記します。

case RECEIVE_POST_DATA:
    // ④ここで投稿取得の処理を行う。
    new ActivitiesListGetTask().execute();
    break;

投稿データ取得処理時のフローは以下の様になります。

  • ①GooglePlusUtils#receivePostData()
  • ②GooglePlusUtils#createClient()
  • ③ConnectionCallbacks#onConnected()
  • ④ActivitiesListGetTask#doInBackground()
  • ⑤ActivitiesListGetTask#onPostExecute()
  • ⑥SNSRequestListener#onComplete()

Twitter、facebook、google+と同じことをやってみて、googleの公式サイトでのデモコードが最新のライブラリに対応していなかったのと、日本人の方で同様の記事を書いている方があまりいらっしゃらなかったので、チョイスしてみました。privateな投稿が取得できるようになった際に、この記事を見てくださった方の手助けになれば幸いです。

では、また。

コメントを残す

メールアドレスが公開されることはありません。