[Qiita] Androidの新しいGoogleサインインAPIについて

※この記事は以前Qiitaに投稿されていた古い記事です

はじめに

GoogleがGoogle Play services 8.3をリリースした際に、その新機能の1つとして新しいGoogleサインインAPIを公開しました。
GoogleサインインをAndroidで実装するためには、以前は実装がとても面倒かつ分かりづらいものでしたが、これが簡単に実装できるようになったのが、新しいGoogleサインインAPIです。これにより、ユーザーエクスペリエンスも向上します。
この内容については以前、勉強会で発表したのですが、今回はQiita記事として書こうと思います。
基本的に以下のGoogle Play Developersの記事を参考に書いているので、より正確な情報を見たい場合にはそちらを参照してください。
Integrating Google Sign-In into Your Android App
https://developers.google.com/identity/sign-in/android/sign-in

Googleサインインとはどういうものか

だいたいこんな感じのやつです。
  1. ログインボタンを押す
    device-2015-11-16-012403.png
  2. Android端末にログインしているアカウントを選択する
    device-2015-11-16-020408.png
  3. 許可する
    device-2015-11-16-012426.png
  4. 許可されたAPIが使えたりアカウント情報が取得できる(∩´∀`)∩
    device-2015-11-16-020451.png
  5. 許可すると、ユーザーのGoogleアカウントの「アカウントに接続されてるアプリ」から確認できるようになる
    スクリーンショット 2015-11-16 4.15.31.png

Googleサインインを使うための前提条件

  • Google Play Storeを含むAndroid 2.3以上
  • Google APIプラットフォームベースのAndroid 4.2.2以上を実行するAVD

設定ファイルを取得する

設定ファイルには、サービス固有の情報が記述されています。それを取得するためには、Google Developers Consoleから既存のプロジェクトを選択するか、新しいプロジェクトを作成する必要があります。また、アプリのパッケージ名と署名証明書のSHA-1ハッシュも必要となります。
プロジェクトが既にある場合は、以下のページから必要な項目を入力し、「google-services.json」をダウンロードします。
Enable Google services for your app
スクリーンショット 2015-11-15 2.43.09.png
ダウンロードしたら、「google-services.json」をアプリケーションのプロジェクトのappディレクトリ配下にコピーします。

Google Services pluginを追加する

  1. プロジェクトのbuild.gradleのdependenciesに以下を追記します。
    classpath 'com.google.gms:google-services:1.5.0-beta2'
    
  2. appのbuild.gradleに以下を追加します。
    apply plugin: 'com.google.gms.google-services'
    

Google Play Servicesを追加する

appのbuild.gradleのdependenciesにGoogle Play Servicesを宣言します。
compile 'com.google.android.gms:play-services-auth:8.3.0'

OAuth 2.0 client ID を作成する

アプリがバックエンドサーバで認証したり、バックエンドサーバからGoogleのAPIにアクセスする場合は、サーバー用のOAuth 2.0 client IDを作成する必要があります。
  1. 資格情報ページを開きます
  2. 資格情報を追加 > OAuth 2.0 クライアント ID
  3. ウェブアプリケーションを選択
  4. 作成
GoogleSignInOptionsオブジェクトを作成するときに、requestIdTokenまたはrequestServerAuthCodeメソッドにこのクライアントIDを渡します。

Googleサインインの実装

GoogleSignInOptionsオブジェクトを作成する

Googleサインインを実装するActivityのonCreateメソッドの中で、アプリで必要とされるユーザデータを要求するように設定します。
たとえば、ユーザーのIDと基本的なプロフィール情報を要求するためにGoogleサインインを設定するには、DEFAULT_SIGN_INパラメータでGoogleSignInOptionsオブジェクトを作成します。
同様に、ユーザーのメールアドレスを要求するには、GoogleSignInOptionsがrequestEmailオプションを指定してオブジェクトを作成します。
GoogleSignInOptions gso = new GoogleSignInOptions
    .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestEmail()
    .build();
GoogleのAPIにアクセスするための追加のスコープを要求する必要がある場合には、requestScopeでそれらを設定します。
例えば、Google Url Shortener APIへのリクエストを要求するには以下のようになります。
mScope = new Scope("https://www.googleapis.com/auth/urlshortener");
GoogleSignInOptions gso = new GoogleSignInOptions
    .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestScopes(mScope)
    .requestEmail()
    .build();

GoogleApiClientオブジェクトを作成する

GoogleSignInOptionsオブジェクトを作成した後、またGoogleサインインを実装するActivityのonCreateメソッドの中で、GoogleサインインAPIへのアクセスとオプションを使用してGoogleApiClientオブジェクトを作成します。
mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
    .addScope(mScope)
    .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
    .build();
ここで使用しているenableAutoManageメソッドの第一引数にはFragmentActivityを、第二引数にはGoogleApiClient#OnConnectionFailedListenerをActivityに実装しておく必要があります。
また、addScopeについてですが、GoogleSignInOptionsでrequestScopeせずにaddScopeするとIllegalStateExceptionで落ちますが、requestScopeが設定されていてaddScopeしなくてもrequestScopeしたAPIに問題無くアクセス許可できたので、今のところいつ設定すべきなのかがまだ分かっていません。

Googleサインインボタンを追加する

Googleサインインボタンを追加したいレイアウトにSignInButtonを追加します。
<com.google.android.gms.common.SignInButton
 android:id="@+id/sign_in_button"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />
ActivityにView#OnClickListenerを実装し、
サインインボタンにクリックイベントをセットします。
SignInButton signInButton = (SignInButton) findViewById(R.id.sign_in_button);
signInButton.setOnClickListener(this);
このサインインボタンを使用している場合、setSizeやsetScopesメソッドによってボタンのサイズやカラースキームを変更することができます。
signInButton.setSize(SignInButton.SIZE_WIDE);
signInButton.setScopes(gso.getScopeArray());
例えば、Google+のスコープを設定している場合には、サインインボタンが赤のGoogle+ブランディングでレンダリングされます。
device-2015-11-16-020541.png

サインイン処理の実装

サインイン処理を開始するには、getSignInIntentで取得したIntentをそのままstartActivityForResultに渡してActivityを起動するだけです。
@Override
public void onClick(View v) {
    if(v.getId() == R.id.sign_in_button) {
        signIn();
    }
}

private void signIn() {
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_SIGN_IN);
}
サインイン処理の開始ができたら、ActivityのonActivityResultメソッドで結果を受け取る処理を書いていきます。
結果の取得はgetSignInResultFromIntentメソッドで行い、返ってきたGoogleSignInResultから各情報を取得します。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        handleSignInResult(result);
    }
}
サインインが成功したかどうかを取得するにはisSuccessメソッドを使用します。また、サインインが成功していた場合にはgetSignInAccountメソッドでGoogleSignInAccountオブジェクトを取得し、そこからサインインしたユーザーの電子メールアドレスやユーザー名などを取得することができます。
private void handleSignInResult(GoogleSignInResult result) {
    Log.d(TAG, "handleSignInResult:" + result.isSuccess());
    if (result.isSuccess()) {
        // サインインが成功したら、サインインボタンを消して、ユーザー名を表示する
        mGoogleSignInAccount = result.getSignInAccount();
        mStatusTextView.setText("DisplayName: " + mGoogleSignInAccount.getDisplayName());
        updateUI(true);
    } else {
        // Signed out, show unauthenticated UI.
        updateUI(false);
    }
}

クロスプラットフォームシングルサインオン

ユーザーが以前に別のプラットフォーム上のアプリに許可を付与している場合、silentSignInを使用することができます。
これは、プロジェクトのクライアントが次の要件を満たすように構成されている場合、ユーザーはすぐにアプリケーションにサインインすることができます。
  • OAuth 2.0クライアントIDは、同じGoogle Developer Consoleプロジェクトでなければならない
  • OAuthのスコープは、両方のクライアントで同じでなければならない
ユーザーが以前にウェブアプリケーションでサインインしていた場合は、silentサインインが成功します。
そして、ユーザーの情報を取得し、Google APIへのアクセスに進むことができ、ユーザーが再びアプリにサインインする必要性を回避できます。

多言語対応

com.google.android.gms.common.SignInButtonは、ローカライズされた文字列を提供しており、これらは自動的に使用可能となります。どの言語に対応しているかなどを確認するためにはSDKの以下のディレクトリから確認することができます。
<android-sdk-folder>/extras/google/google_play_services/libproject/google-play-services_lib/res/. In that location, you will find directories named values-<langcode>

AuthTokenはどうやって取得するのか

AccountManagerでOAuth2認証していたときにはAccountManagerのgetAuthTokenメソッドを使用してAuthTokenを取得していました。
しかし、GoogleSignInAccountクラスにはそのようなメソッドは用意されていないので、GoogleAuthUtilクラスのgetTokenメソッドを使ってAuthTokenを取得します。具体的には以下のように取得します。
String accessToken;
try {
    accessToken = GoogleAuthUtil.getToken(
            MainActivity.this,
            new Account(mGoogleSignInAccount.getEmail(), ACCOUNT_TYPE),
            "oauth2:" + API_SCOPE
    );
    Log.d(TAG, "accessToken: " + accessToken);
} catch (IOException | GoogleAuthException e) {
    e.printStackTrace();
}
GoogleAuthUtilにはgetToken (Context context, String accountName, String scope)というメソッドも存在するのですが、リファレンスを見ると[このメソッドは非推奨となっていた](https://developers.google.com/android/reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context, java.lang.String, java.lang.String))ので、getToken (Context context, Account account, String scope)を使うようにしましょう。
36b8e1c1-f45e-06fb-f4b0-a8dbff77cbeb.png
AuthTokenが取得できたら、あとは以前と同じようにAPIにリクエストを投げるときにこのAuthTokenをaccess_tokenパラメータとして渡してあげることで、APIを使うことができます。

おわりに

僕が以前にGoogle+ログインを使ってGoogle Url Shortenerを使おうとした時にどうやるのか調べたら、認証方法がいくつかあって、複雑すぎてとても苦労したのでOAuthHelperなんていうものを作って簡単に実装できるようにしたのですが、このGoogleサインインAPIを使えばサインインが簡単に実装できるようになるので開発者としては実装の手間が省けて幸せになれるのかなと思いました。
以上、お疲れさまでした。

参考



コメント

人気の投稿

[Qiita] Google Playのクローズドベータ版テストでメールアドレスを指定して公開する

[Qiita] Androidの実機でPCのlocalhostに接続したり、Webページの要素を検証する

[Qiita] AppCompatのAlertDialogのテーマカラーを変える