Android : Firebase Authenticationを用いたGoogleアカウント認証をKotlinで実装

#この記事で紹介すること
FirebaseのAuthenticationを用いてGoogleログイン/ユーザー登録を実現する方法を紹介します。

プロジェクト(アプリ)をFirebaseに登録していることが前提です。
まだできていない方は[公式ドキュメント|AndroidプロジェクトにFirebaseを追加する](https://firebase.google.com/docs/android/setup?hl=ja)を参考に、まずはアプリの登録から行ってください。

またこの記事は私がアプリを作る中でドキュメントを参考にしてコードを書き、ドキュメント通りにいかなかった部分もあったため、あえて記事としてまとめています。参考にした公式ドキュメントは以下です。[Android で Firebase Authentication を使ってみる](https://firebase.google.com/docs/auth/android/start?hl=ja)


#Firebase Authenticationを使ってGoogleアカウントでログイン/ユーザー登録ができるようにしよう！
まずは、Firebase Authenticationがあなたのアプリで使えるように、`app/build.gradle`に以下を追加します。

```app/builg.gradle
dependencies {
    implementation platform('com.google.firebase:firebase-bom:26.2.0')
    implementation 'com.google.firebase:firebase-auth-ktx'
}
```
`firebase-bom:`の後ろは最新のバージョンを公式ドキュメントで確認して入力してください([Android で Firebase Authentication を使ってみる](https://firebase.google.com/docs/auth/android/start?hl=ja)の「アプリにFirebase Authentication を追加する」のところのコード)。上記はこの記事をかいた時点での最新です。


次に、AuthenticationActivityを作成します。
だいたいこんなかんじです。
※FirebaseAuthと関係ないコードも入ってます
※以下のコードはViewBindingを適用しています
(ViewBindingに関しては別の記事で紹介しています)
※以下のコードはRepositoryを利用してFirestoreへアクセスしています
(Repositoryに関しては別で書いているMVVMの記事を参考にして下さい)

```AuthenticationActivity.kt
class AuthenticationActivity : AppCompatActivity() {

    private var googleSignInClient: GoogleSignInClient? = null

    private val repository = UserInfoRepository()
    private val RC_GOOGLE_SIGN_IN_CODE = 9001

    // ViewBindingの設定
    private lateinit var binding: ActivityAuthenticationBinding

    private lateinit var auth: FirebaseAuth

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // ViewBindingの設定
        binding = ActivityAuthenticationBinding.inflate(layoutInflater)
            .apply { setContentView(this.root) }

        var gso = GoogleSignInOptions
            .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.default_web_client_id))
            .requestEmail()
            .build()

        googleSignInClient = GoogleSignIn.getClient(this, gso)

        // lateinit var authの初期化
        auth = Firebase.auth

        binding.googleAccountLinkButton.setOnClickListener {
            googleSignIn()
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == RC_GOOGLE_SIGN_IN_CODE) {
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            try {
                val account = task.getResult(ApiException::class.java)!!
                firebaseAuthWithGoogle(account.idToken!!)
            } catch (e: Exception) {
                Log.w("TAG", "Google sign in failed", e)
            }
        }
    }

    private fun googleSignIn() {
        var googleSignInIntent = googleSignInClient?.signInIntent
        startActivityForResult(googleSignInIntent, RC_GOOGLE_SIGN_IN_CODE)
    }

    // Google認証(Googleアカウントを選択する画面)から戻ってきた後のプログラムはここに書く
    // 以下は、ログインなのか新規ユーザー登録をすべきなのか判定して、それぞれにあった画面遷移をするようなプログラムになっている
    private fun firebaseAuthWithGoogle(idToken: String) {
        val credential = GoogleAuthProvider.getCredential(idToken, null)
        auth.signInWithCredential(credential)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    // 現在このアプリを開いているユーザのuidを取得
                    val uid = auth.currentUser!!.uid
                    Log.d("TAG", uid)

                    // 取得したuidと一致するuidがFirestoreのユーザー情報を登録しているコレクションに存在するか検索する
                    var result: List<SaveUserInfo>
                    GlobalScope.launch {
                        // 引数のuidと一致するuidのデータを取得
                        result = repository.getUser(uid)
                        Log.d("TAG", result.toString())
                        if (result.isNullOrEmpty()) {
                            // データを取得できなかった場合(初回ログイン)
                            // 新規ユーザー登録画面へ移動
                            val intent = Intent(
                                this@AuthenticationActivity,
                                RegisterUserInfoActivity::class.java
                            )
                            intent.putExtra("UID", uid)
                            startActivity(intent)
                        } else {
                            // データを取得できた場合(２回目以降ログイン)
                            // ログインした後一番最初に表示したい画面に移動
                            val intent =
                                Intent(this@AuthenticationActivity, CountPageActivity::class.java)
                            startActivity(intent)
                        }

                    }
                }
            }
    }
}
```

ちなみに、Repositoryは以下のようになってます。
#Repository

```UserInfoRepository.kt
class UserInfoRepository {
    // 引数のuidと一致するユーザー情報を取得
    suspend fun getUser(uid: String?): List<SaveUserInfo> {
        return suspendCoroutine { cont ->
            val db = FirebaseFirestore.getInstance()
            val task = db.collection("userInfo")
                .whereEqualTo("uid", uid)
                .get()
            task.addOnCompleteListener {
                val resultList = task.result!!.toObjects(SaveUserInfo::class.java)
                cont.resume(resultList)
            }
        }
    }
}
```
上記では、Firestoreの`userInfo`というコレクションの中から引数のuidと一致するデータ(uidやユーザー名等を含んだユーザー情報)を取得しています。

#さいごに
(一番最初に見せるべきだったのかもしれませんが...)
やっていることを漫画チックに表現するとこんな感じです。
逆に分かりにくいかもですが。以上です！！！！
![スクリーンショット 2021-02-04 184236.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/6c515af3-8dd4-6f9b-41d5-2f91c2beef0c.jpeg)