Mempelajari cara mengimplementasikan Login dengan Google di aplikasi Android Anda

1. Sebelum memulai

Pelajari cara menerapkan Login dengan Google di Android menggunakan Credential Manager.

Prasyarat

Yang akan Anda pelajari

  • Buat project Google Cloud dan klien OAuth.
  • Terapkan alur login Sheet Bawah.
  • Terapkan alur login Tombol eksplisit.

Yang Anda perlukan

2. Membuat project Android Studio

Untuk memulai, buat project baru di Android Studio:

  1. Buka Android Studio, lalu klik New Project. Selamat Datang di Android Studio
  2. Pilih Phone and Tablet > Empty Activity, lalu klik Next. Project Android Studio
  3. Konfigurasi setelan project:
    • Nama: Pilih nama project.
    • Nama paket: Gunakan nama default atau pilih nama Anda sendiri.
    • SDK Minimum: Pilih versi stabil terbaru atau > Android 14.

Project Penyiapan Android Studio

  1. Klik Finish dan tunggu hingga build project awal selesai. Project Android Studio yang Dibuat

3. Menyiapkan project Google Cloud

Membuat project Google Cloud

  1. Buka Konsol Google Cloud, lalu pilih atau buat project. GCP membuat project baru
  2. Buka APIs & Services > layar izin OAuth. Layar izin OAuth GCP
  3. Klik Mulai dan isi kolom yang wajib diisi:
    • Nama Aplikasi: Gunakan nama aplikasi Android Anda.
    • Email Dukungan Pengguna: Pilih Akun Google Anda.
    • Audiens: Pilih Eksternal.
    • Info Kontak: Masukkan alamat email Anda. Info Aplikasi GCP
  4. Tinjau Google API Services: Kebijakan Data Pengguna, lalu klik Create. Pembuatan GCP

Menyiapkan klien OAuth

Anda perlu membuat klien Web dan klien Android di Konsol Google Cloud untuk mendapatkan client ID untuk autentikasi.

  • Klien Android: Mengamankan permintaan dengan memverifikasi nama paket dan tanda tangan SHA-1 aplikasi Anda.
  • Klien web: Bertindak sebagai klien backend untuk layanan Login dengan Google.

Buat klien OAuth 2.0 Android

  1. Di halaman Clients, klik Create Client, lalu pilih Android sebagai Application type. Membuat Klien GCP
  2. Masukkan nama paket aplikasi Anda (cocok dengan baris 1 MainActivity.kt).
  3. Buat tanda tangan SHA-1 Anda. Buka terminal Android Studio dan jalankan:macOS/Linux:
    keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
    
    Windows:
    keytool -list -v -keystore "C:\Users\USERNAME\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
    
  4. Salin sidik jari SHA-1 dari output perintah, tempelkan ke kolom SHA-1 fingerprint di konsol, lalu klik Create. Detail Klien Android

Buat klien OAuth 2.0 Web

  1. Klik Create Client lagi, lalu pilih Web Application sebagai Application type.
  2. Beri nama klien web Anda, biarkan kolom URL/Asal kosong, lalu klik Buat. Detail Klien Web
  3. Salin Client ID yang dibuat dari dialog konfirmasi. Anda akan menggunakannya dalam kode Kotlin. Salin Client ID

4. Menyiapkan Perangkat Virtual Android

Untuk menguji aplikasi, Anda dapat menggunakan perangkat Android fisik atau Perangkat Virtual Android (AVD).

Membuat dan menjalankan AVD

  1. Di Android Studio, buka Pengelola Perangkat, klik Create Virtual Device (atau ikon +), lalu pilih Medium Phone.
  2. Pilih versi stabil terbaru sebagai image sistem, lalu klik Finish.
  3. Luncurkan emulator dengan mengklik ikon Play/Run di samping perangkat. Menjalankan Perangkat

Login ke Akun Google di perangkat

  1. Di emulator, buka aplikasi Setelan, lalu buka Google.
  2. Klik Login ke Akun Google Anda dan ikuti petunjuknya. Perangkat Login

5. Menambahkan dependensi

Tambahkan library yang diperlukan untuk autentikasi dan integrasi ID Google ke project Anda:

  1. Buka File > Project Structure > Dependencies > app.
  2. Klik + > Library Dependency, telusuri com.google.android.libraries.identity.googleid:googleid, lalu pilih versi terbaru (misalnya, 1.1.1).
  3. Klik + > Library Dependency lagi, telusuri play-services-auth, lalu pilih pustaka dengan Group ID com.google.android.gms.
  4. Klik OK untuk menerapkan perubahan dan menyinkronkan project Anda. Dependensi Selesai

6. Menerapkan alur Sheet Bawah

Alur Sheet Bawah

Alur Sheet Bawah memanfaatkan Credential Manager API untuk menyediakan cara yang lebih mudah bagi pengguna untuk login ke aplikasi Anda menggunakan Akun Google mereka di Android. Didesain untuk kecepatan dan kenyamanan, terutama bagi pengguna yang kembali, alur ini harus dipicu saat peluncuran aplikasi.

Buat permintaan login

  1. Untuk memulai, buka MainActivity.kt dan hapus fungsi Greeting() dan GreetingPreview() default.
  2. Tambahkan pernyataan impor berikut setelah pernyataan yang ada, dimulai dari baris 3:
    import android.content.Context
    import android.os.Build
    import android.util.Log
    import android.widget.Toast
    import androidx.annotation.RequiresApi
    import androidx.compose.foundation.Image
    import androidx.compose.foundation.clickable
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Column
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Surface
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.LaunchedEffect
    import androidx.compose.runtime.rememberCoroutineScope
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.platform.LocalContext
    import androidx.compose.ui.res.painterResource
    import androidx.credentials.CredentialManager
    import androidx.credentials.CustomCredential
    import androidx.credentials.GetCredentialRequest
    import androidx.credentials.exceptions.GetCredentialCancellationException
    import androidx.credentials.exceptions.GetCredentialCustomException
    import androidx.credentials.exceptions.GetCredentialException
    import androidx.credentials.exceptions.NoCredentialException
    import com.google.android.libraries.identity.googleid.GetGoogleIdOption
    import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
    import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
    import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
    import java.security.SecureRandom
    import java.util.Base64
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    
    const val TAG = "MainActivity"
    
  3. Tambahkan fungsi Composable ini di bawah class MainActivity dalam file MainActivity.kt Anda:
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     @Composable
     fun BottomSheet(webClientId: String) {
         val context = LocalContext.current
    
         // LaunchedEffect is used to run a suspend function when the composable is first launched.
         LaunchedEffect(Unit) {
             // Create a Google ID option with filtering by authorized accounts enabled.
             val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
                 .setFilterByAuthorizedAccounts(true)
                 .setServerClientId(webClientId)
                 .setNonce(generateSecureRandomNonce())
                 .build()
    
             // Create a credential request with the Google ID option.
             val request: GetCredentialRequest = GetCredentialRequest.Builder()
                 .addCredentialOption(googleIdOption)
                 .build()
    
             // Attempt to sign in with the created request using an authorized account
             val e = signIn(request, context)
             // If the sign-in fails with NoCredentialException,  there are no authorized accounts.
             // In this case, we attempt to sign in again with filtering disabled.
             if (e is NoCredentialException) {
                 val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder()
                     .setFilterByAuthorizedAccounts(false)
                     .setServerClientId(webClientId)
                     .setNonce(generateSecureRandomNonce())
                     .build()
    
                 val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder()
                     .addCredentialOption(googleIdOptionFalse)
                     .build()
    
                 //We will build out this function in a moment
                 signIn(requestFalse, context)
             }
         }
     }
    
     //This function is used to generate a secure nonce to pass in with our request
     fun generateSecureRandomNonce(byteLength: Int = 32): String {
         val randomBytes = ByteArray(byteLength)
         SecureRandom.getInstanceStrong().nextBytes(randomBytes)
         return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes)
     }
    

Perincian Kode

  • LaunchedEffect(Unit): Memicu alur login segera saat Composable pertama kali ditampilkan.
  • GetGoogleIdOption.Builder(): Mengonfigurasi permintaan token ID Google.
    • setFilterByAuthorizedAccounts(true): Pertama-tama mencoba login diam-diam dengan memfilter akun yang telah diizinkan pengguna untuk aplikasi ini, sehingga meminimalkan hambatan bagi pengguna yang kembali.
    • setNonce(...): Meneruskan nonce acak aman yang dibuat untuk setiap permintaan oleh generateSecureRandomNonce() untuk mencegah serangan replay.
  • signIn(request, context): Mengeksekusi permintaan. Jika gagal dengan NoCredentialException (artinya tidak ada akun yang sebelumnya diizinkan), alurnya akan kembali ke setFilterByAuthorizedAccounts(false) agar pengguna dapat memilih dari akun Google yang login di perangkat.

Buat permintaan login

Setelah permintaan login dibuat, Anda dapat menggunakan Credential Manager untuk menyelesaikan proses login. Buat fungsi bernama signIn yang menjalankan permintaan dan menangani pengecualian umum yang mungkin terjadi.

Tambahkan fungsi ini di bawah fungsi BottomSheet dalam file MainActivity.kt Anda:

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
    val credentialManager = CredentialManager.create(context)
    val failureMessage = "Sign in failed!"
    //using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
    //on the initial running of our app
    delay(250)
    return try {
        // The getCredential is called to request a credential from Credential Manager.
        val result = credentialManager.getCredential(
            request = request,
            context = context,
        )
        Log.i(TAG, result.toString())

        val credential = result.credential
        if (credential is CustomCredential &&
            credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
            val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
            Log.i(TAG, "Signed in as: ${googleIdTokenCredential.id}")
        }

        Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
        Log.i(TAG, "(☞゚ヮ゚)☞  Sign in Successful!  ☜(゚ヮ゚☜)")
        null
    } catch (e: GoogleIdTokenParsingException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)
        e
    } catch (e: NoCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": No credentials found", e)
        e
    } catch (e: GetCredentialCancellationException) {
        Toast.makeText(context, "Sign-in cancelled", Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
        e
    } catch (e: GetCredentialCustomException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with custom credential request", e)
        e
    } catch (e: GetCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Failure getting credentials", e)
        e
    }
}

Perincian Kode

  • credentialManager.getCredential(...): Memanggil Credential Manager API untuk menampilkan dialog atau sheet bawah pemilih akun sistem.
  • delay(250): Dijeda sebentar untuk mencegah kondisi race saat panel bawah dipicu segera saat aplikasi dimulai sebelum layanan Credential Manager selesai melakukan inisialisasi.
  • Penanganan Pengecualian: Menangkap dan mencatat error kredensial umum (misalnya, pembatalan, kredensial yang tidak ada, atau masalah parsing token) dan memberikan masukan pengguna menggunakan toast.

Memicu alur Panel Bawah

Perbarui class MainActivity untuk memanggil BottomSheet() saat startup. Ganti YOUR_CLIENT_ID_HERE dengan ID klien Aplikasi web Anda:

class MainActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //replace with your own web client ID from Google Cloud Console
        val webClientId = "YOUR_CLIENT_ID_HERE"

        setContent {
            //ExampleTheme - this is derived from the name of the project not any added library
            //e.g. if this project was named "Testing" it would be generated as TestingTheme
            ExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
                ) {
                    //This will trigger on launch
                    BottomSheet(webClientId)
                }
            }
        }
    }
}

Simpan project Anda (File > Save) dan jalankan aplikasi:

  1. Tekan tombol run:Menjalankan Project
  2. Saat aplikasi diluncurkan di emulator, Bottom Sheet login akan muncul. Klik Lanjutkan untuk menguji alur.Sheet Bawah
  3. Notifikasi toast akan muncul, yang mengonfirmasi bahwa login berhasil.Keberhasilan Sheet Bawah

7. Menerapkan alur Tombol

GIF Alur Tombol

Alur Tombol memberikan opsi eksplisit bagi pengguna untuk login atau mendaftar. Penggunaan branding standar memastikan pengalaman yang konsisten. Gunakan aset yang telah disetujui sebelumnya dan sesuai dengan Pedoman Branding Login dengan Google.

Menambahkan ikon merek

  1. Download aset merek di sini dan ekstrak ZIP.
  2. Salin signin-assets/Android/png@2x/neutral/android_neutral_sq_SI@2x.png.
  3. Di Android Studio, tempelkan file ke folder res > drawable, ganti namanya menjadi siwg_button.png, lalu klik OK. Menambahkan Tombol

Kode alur tombol

Alur ini menggunakan kembali fungsi bantuan signIn yang sama, tetapi meneruskan GetSignInWithGoogleOption, bukan GetGoogleIdOption. Tidak seperti alur Sheet Bawah, alur tombol eksplisit tidak melakukan pra-pemfilteran atau otomatis meminta kredensial tersimpan atau kunci sandi. Tempel fungsi Composable ini di bawah fungsi BottomSheet:

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    val onClick: () -> Unit = {
        val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
            .Builder(serverClientId = webClientId)
            .setNonce(generateSecureRandomNonce())
            .build()

        val request: GetCredentialRequest = GetCredentialRequest.Builder()
            .addCredentialOption(signInWithGoogleOption)
            .build()

        coroutineScope.launch {
            signIn(request, context)
        }
    }
    Image(
        painter = painterResource(id = R.drawable.siwg_button),
        contentDescription = "",
        modifier = Modifier
            .fillMaxSize()
            .clickable(enabled = true, onClick = onClick)
    )
}

Perincian Kode

  • GetSignInWithGoogleOption: Tidak seperti alur Lembar Bawah, alur tombol eksplisit menggunakan opsi ini untuk meminta pengguna memilih Akun Google mereka tanpa pemfilteran otomatis.
  • coroutineScope.launch: Meluncurkan coroutine untuk menjalankan fungsi signIn penangguhan secara asinkron saat tombol diklik.
  • Image: Menampilkan drawable siwg_button bermerek dan melampirkan pemroses klik untuk memicu alur.

Menambahkan Tombol ke tata letak UI

Perbarui tata letak MainActivity untuk menampilkan BottomSheet otomatis dan ButtonUI eksplisit yang disusun secara vertikal:

class MainActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //replace with your own web client ID from Google Cloud Console
        val webClientId = "YOUR_CLIENT_ID_HERE"

        setContent {
            //ExampleTheme - this is derived from the name of the project not any added library
            //e.g. if this project was named "Testing" it would be generated as TestingTheme
            ExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
                ) {
                    Column(
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally

                    ) {
                        //This will trigger on launch
                        BottomSheet(webClientId)

                        //This requires the user to press the button
                        ButtonUI(webClientId)
                    }
                }
            }
        }
    }
}

Menguji alur Tombol

  1. Jalankan aplikasi.
  2. Tutup Sheet Bawah awal dengan mengklik di luar area sheet.
  3. Klik tombol Login dengan Google untuk meluncurkan dialog login, lalu pilih akun Anda. Dialog Login
  4. Verifikasi hasilnya: periksa Logcat untuk mengonfirmasi cetakan nama pengguna/alamat email Anda.

8. Kesimpulan

Selamat! Anda telah berhasil menerapkan Login dengan Google menggunakan Android Credential Manager.

Referensi lainnya

Kode MainActivity.kt lengkap

Berikut adalah kode lengkap untuk MainActivity.kt sebagai referensi:

package com.example.example

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.example.example.ui.theme.ExampleTheme
import android.content.ContentValues.TAG
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.credentials.exceptions.GetCredentialException
import androidx.compose.foundation.clickable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.credentials.CredentialManager
import androidx.credentials.exceptions.GetCredentialCancellationException
import androidx.credentials.exceptions.GetCredentialCustomException
import androidx.credentials.exceptions.NoCredentialException
import androidx.credentials.GetCredentialRequest
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
import java.security.SecureRandom
import java.util.Base64
import kotlinx.coroutines.CoroutineScope
import androidx.compose.runtime.LaunchedEffect
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //replace with your own web client ID from Google Cloud Console
        val webClientId = "YOUR_CLIENT_ID_HERE"

        setContent {
            //ExampleTheme - this is derived from the name of the project not any added library
            //e.g. if this project was named "Testing" it would be generated as TestingTheme
            ExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
                ) {
                    Column(
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally

                    ) {
                        //This will trigger on launch
                        BottomSheet(webClientId)

                        //This requires the user to press the button
                        ButtonUI(webClientId)
                    }
                }
            }
        }
    }
}

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun BottomSheet(webClientId: String) {
    val context = LocalContext.current

    // LaunchedEffect is used to run a suspend function when the composable is first launched.
    LaunchedEffect(Unit) {
        // Create a Google ID option with filtering by authorized accounts enabled.
        val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
            .setFilterByAuthorizedAccounts(true)
            .setServerClientId(webClientId)
            .setNonce(generateSecureRandomNonce())
            .build()

        // Create a credential request with the Google ID option.
        val request: GetCredentialRequest = GetCredentialRequest.Builder()
            .addCredentialOption(googleIdOption)
            .build()

        // Attempt to sign in with the created request using an authorized account
        val e = signIn(request, context)
        // If the sign-in fails with NoCredentialException,  there are no authorized accounts.
        // In this case, we attempt to sign in again with filtering disabled.
        if (e is NoCredentialException) {
            val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder()
                .setFilterByAuthorizedAccounts(false)
                .setServerClientId(webClientId)
                .setNonce(generateSecureRandomNonce())
                .build()

            val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOptionFalse)
                .build()

            signIn(requestFalse, context)
        }
    }
}

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    val onClick: () -> Unit = {
        val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
            .Builder(serverClientId = webClientId)
            .setNonce(generateSecureRandomNonce())
            .build()

        val request: GetCredentialRequest = GetCredentialRequest.Builder()
            .addCredentialOption(signInWithGoogleOption)
            .build()

        coroutineScope.launch {
            signIn(request, context)
        }
    }
    Image(
        painter = painterResource(id = R.drawable.siwg_button),
        contentDescription = "",
        modifier = Modifier
            .fillMaxSize()
            .clickable(onClick = onClick)
    )
}

fun generateSecureRandomNonce(byteLength: Int = 32): String {
    val randomBytes = ByteArray(byteLength)
    SecureRandom.getInstanceStrong().nextBytes(randomBytes)
    return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes)
}

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
    val credentialManager = CredentialManager.create(context)
    val failureMessage = "Sign in failed!"
    //using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
    //on the initial running of our app
    delay(250)
    return try {
        // The getCredential is called to request a credential from Credential Manager.
        val result = credentialManager.getCredential(
            request = request,
            context = context,
        )
        Log.i(TAG, result.toString())

        val credential = result.credential
        if (credential is CustomCredential &&
            credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
            val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
            Log.i(TAG, "Signed in as: ${googleIdTokenCredential.id}")
        }

        Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
        Log.i(TAG, "(☞゚ヮ゚)☞  Sign in Successful!  ☜(゚ヮ゚☜)")
        null
    } catch (e: GoogleIdTokenParsingException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)
        e
    } catch (e: NoCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": No credentials found", e)
        e
    } catch (e: GetCredentialCancellationException) {
        Toast.makeText(context, "Sign-in cancelled", Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
        e
    } catch (e: GetCredentialCustomException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with custom credential request", e)
        e
    } catch (e: GetCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Failure getting credentials", e)
        e
    }
}