FirebaseUI による Android ログイン

この Codelab は、Kotlin での高度な Android 開発コースの一部です。Codelab を順番に進めていくと、このコースを最大限に活用できますが、これは必須ではありません。すべてのコース Codelab は Kotlin での Codelab の高度な Codelab のランディング ページに掲載されています。

はじめに

Android アプリを作成する場合、ユーザーのログインをサポートすることには多くの利点があります。アプリ内でユーザー自身に ID の作成を許可することで、アプリ内でのエンゲージメントをさらに促進できます。

カスタマイズ アカウントを使用すると、ユーザーは別のデバイス(ウェブや新しいスマートフォンなど)でアプリを使用している場合に、アプリ内エクスペリエンスのカスタマイズ、他のユーザーとの交流、データの保持と転送を行うことができます。

この Codelab では、FirebaseUI ライブラリを使用してアプリのログインをサポートする基本的な方法について学習します。とりわけ、FirebaseUI ライブラリにより、ログインフローを構築し、ユーザー アカウントを管理する作業を簡単に行えるようになります。

前提となる知識

  • Android アプリの作成方法の基礎
  • LiveData と ViewModel

学習内容

  • プロジェクトに Firebase を追加する方法
  • Android アプリのログインをサポートする方法
  • アプリの現在の認証ステータスを確認する方法
  • ユーザーのログアウト方法

演習内容

  • Firebase コンソールを使用して、アプリに Firebase を統合します。
  • ログイン機能を実装します。
  • ログイン ユーザー向けにアプリをカスタマイズします。
  • ユーザーのログアウトを実装します。

LiveData と ViewModel の詳細

この Codelab のアプリについては、LiveData と ViewModel に関する基本的な知識が必要です。これらのコンセプトの簡単な概要が必要な場合は、LiveDataViewModel の概要を確認してください。

また、この Codelab の一環として学習する、Android アプリの基本的なトピックについて学ぶ「Kotlin による Android アプリの開発」コースの受講も可能です。このコースは、Udacity コースCodelab コースの両方として利用できます。

この Codelab では、Android のおもしろい情報を表示するアプリを作成します。さらに重要なのは、アプリにログイン/ログアウト ボタンが表示されることです。ユーザーがアプリにログインすると、表示される Android データには、カスタマイズの手段としてユーザーに挨拶する内容が含まれています。

次のいずれかの方法でサンプルアプリをダウンロードします。

ZIP をダウンロード

または、次のコマンドを使用して GitHub リポジトリのクローンを作成し、リポジトリの start ブランチに切り替えます。

$  git clone https://github.com/googlecodelabs/android-kotlin-login

重要: Firebase を使用するためにアプリを統合するため、スターター アプリのビルドと実行にはセットアップが必要です。これは Codelab の次のステップで行います。

ステップ 1: Firebase プロジェクトを作成する

Android アプリに Firebase を追加する前に、Android アプリに接続するための Firebase プロジェクトを作成します。

  1. Firebase コンソールで [プロジェクトを追加] をクリックします。
  2. [Project name] を選択または入力します。プロジェクトには任意の名前を付けることができますが、作成中のアプリに関連する名前を選んでください。
  3. [続行] をクリックします。
  4. Google アナリティクスの設定をスキップし、[後で] オプションを選択することもできます。
  5. [プロジェクトを作成] をクリックして、Firebase プロジェクトの設定を完了します。

ステップ 2: アプリを Firebase に登録する

Firebase プロジェクトが作成されたので、Android アプリを追加できます。

  1. Firebase コンソールの [プロジェクトの概要] ページの中央にある Android アイコンをクリックして設定ワークフローを起動します。
  2. アプリのアプリケーション ID を [Android パッケージ名] に入力します。アプリが使用している ID を入力してください。この値は、アプリを Firebase プロジェクトに登録した後で追加、変更することはできません。
  1. アプリケーション ID はパッケージ名とも呼ばれます。
  2. モジュール(アプリレベル)の Gradle ファイルでこのアプリケーション ID を見つけます。通常は app/build.gradle です(例: com.yourcompany.yourproject)。
  3. デバッグ用の署名証明書 SHA-1 を入力します。この鍵を生成するには、コマンドライン ターミナルで次のコマンドを入力します。
keytool -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android
  1. [アプリの登録] をクリックします。

ステップ 3: プロジェクトに Firebase 構成ファイルを追加する

Firebase Android 構成ファイルをアプリに追加します。

  1. [google-services.json をダウンロード] をクリックして、Firebase Android 構成ファイル(google-services.json)を取得します。
  • Firebase Android 構成ファイルはいつでも再ダウンロードできます。
  • 構成ファイルに文字が追加されていないこと、名前が google-services.json であることをご確認ください。
  1. 構成ファイルをアプリのモジュール(アプリレベル)ディレクトリに移動します。

ステップ 4: Firebase プロダクトを有効にするように Android プロジェクトを構成する

  1. アプリで Firebase プロダクトを有効にするには、Gradle ファイルに google-services プラグインを追加します。
  1. ルートレベル(プロジェクト レベル)の Gradle ファイル(build.gradle)に、Google サービスのプラグインを含めるためのルールを追加します。Google の Maven リポジトリがあることも確認してください。

build.gradle

buildscript {

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }

  dependencies {
    // ...

    // Add the following line:
    classpath 'com.google.gms:google-services:4.3.0'  // Google Services plugin
  }
}

allprojects {
  // ...

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    // ...
  }
}
  1. モジュール(アプリレベル)の Gradle ファイル(通常は app/build.gradle)で、ファイルの下部に行を追加します。

app/build.gradle

apply plugin: 'com.android.application'

android {
  // ...
}

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

ステップ 4: Firebase の依存関係を追加する

この Codelab で Firebase を統合する主な理由は、ユーザーを作成して管理する方法があることです。そのためには、ログインを実装できる Firebase ライブラリを追加する必要があります。

  1. build.gradle (Module:app) ファイルに次の依存関係を追加して、アプリで SDK を使用できるようにします。firebase-auth SDK を使用すると、アプリケーションの認証済みユーザーを管理できます。

app/build.gradle:

implementation 'com.firebaseui:firebase-ui-auth:5.0.0'
  1. プロジェクトを Gradle ファイルと同期して、すべての依存関係がアプリで利用できることを確認します。プロンプトが表示されない場合は、Android Studio またはツールバーで [File > Sync Project with Gradle Files] を選択します。

ステップ 5: アプリを実行してコードを調べる

  1. エミュレータまたは実機でアプリを実行して、開発環境で環境の設定を開始できることを確認します。

正常に完了すると、ホーム画面に Android に関する面白い情報が表示され、左上にログインボタンが表示されます。ログインボタンをタップしても、まだ何も起こりません。

大まかに言うと、これは複数のフラグメントを含む単一のアクティビティ アプリです。MainFragment には、以下の画面に表示されるすべての UI が含まれます。(受講者は、フォローアップ Codelab で LoginFragmentSettingsFragment を使用します)。

  1. コードについてよく理解します。特に、以下の点に注意してください。
  • FirebaseUserLiveData は、アプリに関連付けられている現在の Firebase ユーザーを監視するために実装するクラスです。FirebaseAuth インスタンスをエントリ ポイントとして使用し、後のステップでこのユーザー情報を取得します。
  • MainFragmentLoginViewModel に関連付けられています。LoginViewModel は、FirebaseUserLiveData を利用して authenticationState 変数を作成するために実装するクラスです。MainFragment では、この authenticationState 変数を使用して値をモニタリングすることで、それに応じて UI を更新できます。

このステップでは、Firebase コンソールを使用して、アプリがサポートする認証方法を設定します。この Codelab では、ユーザーが自分のメールアドレスまたは Google アカウントでログインできるようにします。

  1. Firebase コンソールに移動します。(注: [Firebase を追加] ワークフローを開いている場合は、左上の [X] をクリックしてコンソールに戻ります。
  2. まだプロジェクトに参加していない場合は、プロジェクトを選択します。
  3. 左側のナビゲーションを開き、[Develop & & Authentication] を選択します。

  1. 上部のナビゲーション バーにある [Sign-in method] タブを選択します。

  1. [Email/Password] 行をクリックします。
  2. ポップアップで [有効] スイッチを切り替え、[保存] をクリックします。
  3. 同様に、[Google] 行をクリックします。
  4. [有効] スイッチを切り替えて、[プロジェクトのサポートメール] を入力し、[保存] をクリックします。

このタスクでは、ユーザー向けにログイン機能を実装します。

  1. MainFragment.kt を開きます。
  2. MainFragment のレイアウトに auth_button があります。現在、ユーザー入力を処理するように設定されていません。
  3. onViewCreated(), で、onClickListenerauth_button に追加して launchSignInFlow() を呼び出します。

MainFragment.kt

binding.authButton.setOnClickListener { launchSignInFlow() }
  1. MainFragment.ktlaunchSignInFlow() メソッドを探します。現在、TODO が含まれています。
  2. 次のように launchSignInFlow() 関数を完了します。

MainFragment.kt

private fun launchSignInFlow() {
   // Give users the option to sign in / register with their email or Google account.
   // If users choose to register with their email,
   // they will need to create a password as well.
   val providers = arrayListOf(
       AuthUI.IdpConfig.EmailBuilder().build(), AuthUI.IdpConfig.GoogleBuilder().build()

       // This is where you can provide more ways for users to register and 
       // sign in.
   )

   // Create and launch sign-in intent.
   // We listen to the response of this activity with the
   // SIGN_IN_REQUEST_CODE 
   startActivityForResult(
       AuthUI.getInstance()
           .createSignInIntentBuilder()
           .setAvailableProviders(providers)
           .build(),
       MainFragment.SIGN_IN_REQUEST_CODE
   )
}

これにより、ユーザーは自分のメールアドレスまたは Google アカウントで登録とログインを行うことができます。ユーザーがメールアドレスで登録する場合、作成するメールアドレスとパスワードの組み合わせは、アプリに固有のものとなります。つまり、メールアドレスとパスワードの組み合わせを使用してアプリにログインすることはできますが、同じ認証情報で他の Firebase 対応アプリにログインできるわけではありません。

  1. MainFragment.ktonActivityResult() メソッドを実装することで、ログイン プロセスの結果をリッスンできます。以下をご覧ください。SIGN_IN_REQUEST_CODE でログイン プロセスを開始したため、SIGN_IN_REQUEST_CODEonActivityResult() に返されるタイミングをフィルタして、ログイン プロセスの結果をリッスンすることもできます。まず、ログ ステートメントをいくつか作成して、ユーザーが正常にログインしたかどうかを確認します。

MainFragment.kt

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   super.onActivityResult(requestCode, resultCode, data)
   if (requestCode == SIGN_IN_REQUEST_CODE) {
       val response = IdpResponse.fromResultIntent(data)
       if (resultCode == Activity.RESULT_OK) {
           // User successfully signed in
           Log.i(TAG, "Successfully signed in user ${FirebaseAuth.getInstance().currentUser?.displayName}!")
       } else {
           // Sign in failed. If response is null the user canceled the
           // sign-in flow using the back button. Otherwise check
           // response.getError().getErrorCode() and handle the error.
           Log.i(TAG, "Sign in unsuccessful ${response?.error?.errorCode}")
       }
   }
}

これで、ユーザーの登録とログインをアプリで処理できるようになりました。

  1. アプリを実行し、[Login] ボタンをタップしてログイン画面が表示されることを確認します。
  2. メールアドレスとパスワード、または Google アカウントを使用してログインできるようになりました。
  3. ログイン後に UI が変更されることはありません(次のステップで UI の更新を実装します)。ただし、すべてが正常に機能している場合、登録フローを実行するとログメッセージ Successfully signed in user ${your name}! が表示されます。
  4. Firebase コンソールに移動して [開発&認証; ユーザー] に移動し、アプリに登録済みユーザーが 1 人いるかどうかを確認することもできます。
  5. ユーザーがアプリのアカウントを作成すると、このアカウントはアプリにのみ関連付けられ、ログイン機能に Firebase を使用するアプリには関連付けられません。

このタスクでは、認証状態に基づいて UI の更新を実装します。ユーザーがログインしているときに、ユーザーの名前を表示することで、ホーム画面をカスタマイズできます。また、ユーザーがログインしたときの [Login] ボタンを [Logout] ボタンに変更します。

  1. すでに作成済みの FirebaseUserLiveData.kt クラスを開きます。まず、アプリ内の他のクラスが、ユーザーがいつログインまたはログアウトしたかを把握できるようにする必要があります。ただし、LiveData の値は更新されていないため、クラスはまだ何も行いません。
  2. FirebaseAuth ライブラリを使用しているため、FirebaseUI ライブラリの一部として実装されている FirebaseUser.AuthStateListener コールバックを使用して、ログイン ユーザーの変更をリッスンできます。このコールバックは、ユーザーがアプリにログインまたはログアウトするたびにトリガーされます。
  3. FirebaseUserLiveData.ktauthStateListener 変数を定義します。この変数を使用して、LiveData の値を格納します。authStateListener 変数は、アプリケーションの状態に基づいて認証状態の変更のリッスンを適切に開始および停止できるように作成されました。たとえば、ユーザーがアプリをバックグラウンドに移行した場合、メモリリークを防ぐため、アプリで認証状態の変化をリッスンできるようにする必要があります。
  4. FirebaseUserLiveData の値が現在の Firebase ユーザーに対応するように authStateListener を更新します。

FirebaseUserLiveData.kt

private val authStateListener = FirebaseAuth.AuthStateListener { firebaseAuth ->
   value = firebaseAuth.currentUser
}
  1. LoginViewModel.kt を開きます。
  2. LoginViewModel.kt で、実装した FirebaseUserLiveData オブジェクトに基づいて authenticationState 変数を作成します。この authenticationState 変数を作成することで、ユーザーが LoginViewModel でログインしているかどうかを他のクラスでクエリできるようになります。

LoginViewModel.kt

val authenticationState = FirebaseUserLiveData().map { user ->
   if (user != null) {
       AuthenticationState.AUTHENTICATED
   } else {
       AuthenticationState.UNAUTHENTICATED
   }
}
  1. MainFragment.kt. を開きます。
  2. MainFragment.ktobserveAuthenticationState() では、LoginViewModel に追加した authenticationState 変数を使用し、それに応じて UI を変更できます。ログイン中のユーザーの場合、authButton に [Logout] と表示されます。

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
       when (authenticationState) {
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   // TODO implement logging out user in next step
               }

                // TODO 2. If the user is logged in, 
                 // you can customize the welcome message they see by
                 // utilizing the getFactWithPersonalization() function provided

           }
           else -> {
               // TODO 3. Lastly, if there is no logged-in user, 
                // auth_button should display Login and
                //  launch the sign in screen when clicked.
           }
       }
   })
}
  1. ユーザーがログインしている場合は、MainFragment にある getFactWithPersonalization() 関数を使用して、ウェルカム メッセージをカスタマイズすることもできます。

MainFragment.kt

binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
  1. 最後に、ログイン ユーザーがいない場合(authenticationStateLoginViewModel.AuthenticationState.AUTHENTICATED 以外)、auth_button は [Login] を表示し、クリック時にログイン画面を起動します。また、メッセージをカスタマイズしないようにする必要があります。

MainFragment.kt

binding.authButton.text = getString(R.string.login_button_text)
binding.authButton.setOnClickListener { launchSignInFlow() }
binding.welcomeText.text = factToDisplay

すべてのステップが完了すると、最終的な observeAuthenticationState() メソッドは次のコードのようになります。

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
        // TODO 1. Use the authenticationState variable you just added 
         // in LoginViewModel and change the UI accordingly.
       when (authenticationState) {
            // TODO 2.  If the user is logged in, 
             // you can customize the welcome message they see by
             // utilizing the getFactWithPersonalization() function provided
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   // TODO implement logging out user in next step
               }
           }
           else -> {
                // TODO 3. Lastly, if there is no logged-in user, 
                 // auth_button should display Login and
                 // launch the sign in screen when clicked.
               binding.welcomeText.text = factToDisplay

               binding.authButton.text = getString(R.string.login_button_text)
               binding.authButton.setOnClickListener {
                   launchSignInFlow()
               }
           }
       }
   })
}
  1. アプリを実行します。ユーザーがログインしているかどうかに応じて UI が更新されます。すべてが正常に機能していて、ログインしていれば、ホーム画面に Android に関するファクトと名前が表示されるはずです。[Login] ボタンにも [Logout] と表示されます。

このタスクでは、ログアウト機能を実装します。

このアプリではユーザーがログインできるため、ログアウトする方法も提示する必要があります。ユーザーを 1 行だけログアウトさせる方法の例を以下に示します。

AuthUI.getInstance().signOut(requireContext())
  1. MainFragment.kt を開きます。
  2. MainFragment.ktobserveAuthenticationState() に、ログアウト ロジックを追加します。これにより、ログイン ユーザーが存在する場合に auth_button が正しく機能するようになります。このメソッドの最終結果は下のコードのようになります。

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
       when (authenticationState) {
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.welcomeText.text = getFactWithPersonalization(factToDisplay)

               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   AuthUI.getInstance().signOut(requireContext())
               }
           }
           else -> {
               binding.welcomeText.text = factToDisplay

               binding.authButton.text = getString(R.string.login_button_text)
               binding.authButton.setOnClickListener {
                   launchSignInFlow()
               }
           }
       }
   })
}
  1. アプリを実行します。
  2. [ログアウト] ボタンをタップして、ユーザーがログアウトされ、ボタンのステータスが [ログイン] に変わります。

完成したアプリの最終版は、https://github.com/googlecodelabs/android-kotlin-login にあります。

この Codelab では、以下のことを学びました。

  • Gradle ファイルに適切な依存関係を追加し、Firebase コンソールでプロジェクトを設定して、Firebase をプロジェクトに追加する方法。
  • FirebaseUI ライブラリを使用してアプリにログインを実装する方法と、ユーザーにログインを許可する方法を指定する方法。ユーザーがアプリで作成したアカウントはアプリ専用であり、ログイン機能のために Firebase を利用するすべてのアプリと共有されることはありません。
  • LiveData を使用してアプリの現在の認証ステータスを監視する方法
  • ユーザーをログアウトする方法。

この Codelab では、Android アプリのログインをサポートする基本的な方法について学習しました。

この Codelab では、ユーザーがメールアドレスを登録してログインできるようにしました。ただし、FirebaseUI ライブラリでは、電話番号によるログインなどの他の認証方法もサポートしています。FirebaseUI ライブラリの機能と、このライブラリが提供するその他の機能の使用方法について詳しくは、以下のリソースをご覧ください。

ログインに関するその他のベスト プラクティスについて詳しくは、以下のリソースをご覧ください。

Codelab:

Android デベロッパー ドキュメント:

動画:

このコースの他の Codelab へのリンクについては、Kotlin Codelab の高度な Codelab のランディング ページをご覧ください。