Informationen zum Implementieren von „Über Google anmelden“ in Ihre Android-App

1. Hinweis

Informationen zum Implementieren von „Über Google anmelden“ auf Android-Geräten mit dem Credential Manager

Vorbereitung

Lerninhalte

  • Google Cloud-Projekt und OAuth-Clients erstellen
  • Anmeldevorgang für die Ansicht am unteren Rand implementieren
  • Anmeldevorgang für die explizite Schaltfläche implementieren

Voraussetzungen

2. Android Studio-Projekt erstellen

Erstellen Sie zuerst ein neues Projekt in Android Studio:

  1. Öffnen Sie Android Studio und klicken Sie auf New Project (Neues Projekt). Willkommen bei Android Studio
  2. Wählen Sie Phone and Tablet > Empty Activity (Smartphone und Tablet > Leere Aktivität) aus und klicken Sie auf Next (Weiter). Android Studio-Projekt
  3. Konfigurieren Sie die Projekteinstellungen:
    • Name: Wählen Sie einen Projektnamen aus.
    • Package name (Paketname): Verwenden Sie den Standardnamen oder wählen Sie einen eigenen aus.
    • Minimum SDK (Mindest-SDK): Wählen Sie die aktuelle stabile Version oder > Android 14 aus.

Android Studio-Einrichtungsprojekt

  1. Klicken Sie auf Finish (Fertig) und warten Sie, bis die erste Projekterstellung abgeschlossen ist. Android Studio-Projekt erstellt

3. Google Cloud-Projekt einrichten

Google Cloud-Projekt erstellen

  1. Rufen Sie die Google Cloud Console auf und wählen Sie ein Projekt aus oder erstellen Sie ein Projekt. GCP-Projekt erstellen
  2. Gehen Sie zu APIs & Services > OAuth-Zustimmungsbildschirm. GCP-OAuth-Zustimmungsbildschirm
  3. Klicken Sie auf Get started (Jetzt starten) und füllen Sie die Pflichtfelder aus:
    • App Name (App-Name): Verwenden Sie den Namen Ihrer Android-App.
    • User Support Email (E-Mail-Adresse für den Nutzersupport): Wählen Sie Ihr Google-Konto aus.
    • Audience (Zielgruppe): Wählen Sie External (Extern) aus.
    • Contact Info (Kontaktinformationen): Geben Sie Ihre E-Mail-Adresse ein. GCP-App-Informationen
  4. Lesen Sie die Richtlinie zu Nutzerdaten für Google API-Dienste und klicken Sie auf Create (Erstellen). GCP Create

OAuth-Clients einrichten

Sie müssen in der Google Cloud Console sowohl einen Web-Client als auch einen Android-Client erstellen, um die Client-IDs für die Authentifizierung zu erhalten.

  • Android-Client: Schützt Anfragen durch Überprüfung des Paketnamens und der SHA-1-Signatur Ihrer App.
  • Web-Client: Fungiert als Backend-Client für den Google-Anmeldedienst.

Android-OAuth 2.0-Client erstellen

  1. Klicken Sie auf der Seite Clients (Clients) auf Create Client (Client erstellen) und wählen Sie Android als Application type (Anwendungstyp) aus. GCP-Clients erstellen
  2. Geben Sie den Paketnamen Ihrer App ein (entspricht Zeile 1 von MainActivity.kt).
  3. Generieren Sie Ihre SHA-1-Signatur. Öffnen Sie das Android Studio-Terminal und führen Sie den folgenden Befehl aus: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. Kopieren Sie den SHA-1-Fingerabdruck aus der Befehlsausgabe, fügen Sie ihn in der Console in das Feld SHA-1 fingerprint ein und klicken Sie auf Create. Android-Clientdetails

Web-OAuth 2.0-Client erstellen

  1. Klicken Sie noch einmal auf Create Client (Client erstellen) und wählen Sie Web Application (Webanwendung) als Application type (Anwendungstyp) aus.
  2. Geben Sie Ihrem Web-Client einen Namen, lassen Sie die Felder für URL/Origins leer und klicken Sie auf Create. Webclient-Details
  3. Kopieren Sie die generierte Client ID (Client-ID) aus dem Bestätigungsdialogfeld. Sie verwenden sie in Ihrem Kotlin-Code. Client-ID kopieren

4. Virtuelles Android-Gerät einrichten

Zum Testen der App können Sie ein physisches Android-Gerät oder ein virtuelles Android-Gerät (Android Virtual Device, AVD) verwenden.

AVD erstellen und ausführen

  1. Öffnen Sie in Android Studio den Device Manager (Geräteverwaltung), klicken Sie auf Create Virtual Device (Virtuelles Gerät erstellen) oder das Symbol + und wählen Sie Medium Phone (Mittelgroßes Smartphone) aus.
  2. Wählen Sie die aktuelle stabile Version als System-Image aus und klicken Sie auf Finish (Fertig).
  3. Starten Sie den Emulator, indem Sie neben dem Gerät auf das Symbol Play/Run (Wiedergabe/Ausführen) klicken. Laufgerät

Auf dem Gerät in einem Google-Konto anmelden

  1. Öffnen Sie im Emulator die App Einstellungen und gehen Sie zu Google.
  2. Klicken Sie auf In Ihrem Google-Konto anmelden und folgen Sie der Anleitung. Auf dem Gerät angemeldet

5. Abhängigkeiten hinzufügen

Fügen Sie Ihrem Projekt die für die Authentifizierung und die Google ID-Integration erforderlichen Bibliotheken hinzu:

  1. Gehen Sie zu File > Project Structure > Dependencies > app (Datei > Projektstruktur > Abhängigkeiten > App).
  2. Klicken Sie auf + > Library Dependency (+ > Bibliotheksabhängigkeit), suchen Sie nach com.google.android.libraries.identity.googleid:googleid und wählen Sie die aktuelle Version aus (z.B. 1.1.1).
  3. Klicken Sie noch einmal auf + > Library Dependency (+ > Bibliotheksabhängigkeit), suchen Sie nach play-services-auth und wählen Sie die Bibliothek mit der Gruppen-ID com.google.android.gms aus.
  4. Klicken Sie auf OK , um die Änderungen zu übernehmen und Ihr Projekt zu synchronisieren. Abgeschlossene Abhängigkeiten

6. Anmeldevorgang für die Ansicht am unteren Rand implementieren

Ablauf der Ansicht am unteren Rand

Der Anmeldevorgang für die Ansicht am unteren Rand nutzt die Credential Manager API, um Nutzern eine optimierte Möglichkeit zu bieten, sich auf Android-Geräten mit ihren Google-Konten in Ihrer App anzumelden. Dieser Vorgang ist auf Geschwindigkeit und Komfort ausgelegt, insbesondere für wiederkehrende Nutzer, und sollte beim Start der App ausgelöst werden.

Anmeldeanfrage erstellen

  1. Öffnen Sie zuerst MainActivity.kt und entfernen Sie die Standardfunktionen Greeting() und GreetingPreview().
  2. Fügen Sie nach den vorhandenen Importanweisungen ab Zeile 3 die folgenden Importanweisungen hinzu:
    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. Fügen Sie diese Composable-Funktion unter der MainActivity Klasse in der MainActivity.kt Datei hinzu:
     @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)
     }
    

Codeaufschlüsselung

  • LaunchedEffect(Unit): Löst den Anmeldevorgang sofort aus, wenn die Composable-Funktion zum ersten Mal angezeigt wird.
  • GetGoogleIdOption.Builder(): Konfiguriert die Google ID-Tokenanfrage.
    • setFilterByAuthorizedAccounts(true): Versucht zuerst eine automatische Anmeldung, indem nach Konten gefiltert wird, die der Nutzer bereits für diese App autorisiert hat. So wird die Anmeldung für wiederkehrende Nutzer vereinfacht.
    • setNonce(...): Übergibt eine sichere zufällige Nonce, die für jede Anfrage von generateSecureRandomNonce() generiert wird, um Replay-Angriffe zu verhindern.
  • signIn(request, context): Führt die Anfrage aus. Wenn sie mit einer NoCredentialException fehlschlägt (d. h., es ist kein zuvor autorisiertes Konto vorhanden), wird auf setFilterByAuthorizedAccounts(false) zurückgegriffen, damit der Nutzer ein beliebiges Google-Konto auswählen kann, in dem er auf dem Gerät angemeldet ist.

Anmeldeanfrage senden

Nachdem Sie die Anmeldeanfrage erstellt haben, können Sie den Credential Manager verwenden, um den Anmeldevorgang abzuschließen. Erstellen Sie eine Funktion namens signIn, die die Anfrage ausführt und häufige Ausnahmen verarbeitet, die auftreten können.

Fügen Sie diese Funktion unter der Funktion BottomSheet in der Datei MainActivity.kt hinzu:

@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
    }
}

Codeaufschlüsselung

  • credentialManager.getCredential(...): Ruft die Credential Manager API auf, um die Ansicht am unteren Rand oder das Dialogfeld zur Auswahl des Systemkontos anzuzeigen.
  • delay(250): Hält kurz an, um eine Race-Bedingung zu verhindern, wenn die Ansicht am unteren Rand sofort beim Start der App ausgelöst wird, bevor der Credential Manager-Dienst initialisiert wurde.
  • Ausnahmebehandlung: Fängt häufige Anmeldedatenfehler ab und protokolliert sie (z.B. Abbruch, fehlende Anmeldedaten oder Probleme beim Parsen von Tokens) und gibt Nutzern Feedback über Toasts.

Anmeldevorgang für die Ansicht am unteren Rand auslösen

Aktualisieren Sie die Klasse MainActivity, um BottomSheet() beim Start aufzurufen. Ersetzen Sie YOUR_CLIENT_ID_HERE durch Ihre Webanwendungs-Client-ID:

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)
                }
            }
        }
    }
}

Speichern Sie Ihr Projekt (File > Save (Datei > Speichern)) und führen Sie die Anwendung aus:

  1. Klicken Sie auf die Schaltfläche „Ausführen“:Projekt ausführen
  2. Wenn die App im Emulator gestartet wird, sollte die Ansicht am unteren Rand für die Anmeldung angezeigt werden. Klicken Sie auf Continue (Weiter), um den Vorgang zu testen.Ansicht am unteren Rand
  3. Es sollte eine Toast-Benachrichtigung angezeigt werden, die bestätigt, dass die Anmeldung erfolgreich war.Erfolgreiche Ansicht am unteren Rand

7. Anmeldevorgang für die Schaltfläche implementieren

GIF für den Schaltflächenablauf

Der Anmeldevorgang für die Schaltfläche bietet Nutzern eine explizite Option, sich anzumelden oder zu registrieren. Die Verwendung von Standard-Branding sorgt für eine einheitliche Nutzererfahrung. Verwenden Sie vorab genehmigte Assets, die den Branding-Richtlinien für „Über Google anmelden“ entsprechen.

Markensymbol hinzufügen

  1. Laden Sie die Marken-Assets hier herunter und extrahieren Sie die ZIP-Datei.
  2. Kopieren Sie signin-assets/Android/png@2x/neutral/android_neutral_sq_SI@2x.png.
  3. Fügen Sie die Datei in Android Studio in den Ordner res > drawable ein, benennen Sie sie in siwg_button.png um und klicken Sie auf OK. Schaltfläche hinzufügen

Code für den Anmeldevorgang für die Schaltfläche

Bei diesem Vorgang wird dieselbe Hilfsfunktion signIn wiederverwendet, aber anstelle von GetGoogleIdOption wird GetSignInWithGoogleOption übergeben. Im Gegensatz zum Anmeldevorgang für die Ansicht am unteren Rand werden beim Anmeldevorgang für die explizite Schaltfläche keine Vorabfilterung durchgeführt und keine gespeicherten Anmeldedaten oder Passkeys automatisch angefordert. Fügen Sie diese Composable-Funktion unter der Funktion BottomSheet ein:

@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)
    )
}

Codeaufschlüsselung

  • GetSignInWithGoogleOption: Im Gegensatz zum Anmeldevorgang für die Ansicht am unteren Rand wird bei diesem Vorgang diese Option verwendet, um Nutzer aufzufordern, ihr Google-Konto auszuwählen, ohne automatische Filterung.
  • coroutineScope.launch: Startet eine Coroutine, um die suspend-Funktion signIn asynchron auszuführen, wenn auf die Schaltfläche geklickt wird.
  • Image: Zeigt das gebrandete Drawable siwg_button an und fügt einen Klick-Listener hinzu, um den Vorgang auszulösen.

Schaltfläche zum UI-Layout hinzufügen

Aktualisieren Sie das Layout MainActivity, um sowohl die automatische BottomSheet als auch die explizite ButtonUI vertikal auszurichten:

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)
                    }
                }
            }
        }
    }
}

Anmeldevorgang für die Schaltfläche testen

  1. Führen Sie die Anwendung aus.
  2. Schließen Sie die erste Ansicht am unteren Rand, indem Sie außerhalb des Bereichs der Ansicht klicken.
  3. Klicken Sie auf die Schaltfläche Über Google anmelden , um das Anmeldedialogfeld zu öffnen, und wählen Sie Ihr Konto aus. Anmeldedialogfeld
  4. Überprüfen Sie das Ergebnis: Sehen Sie im Logcat nach, ob Ihr Nutzername/Ihre E-Mail-Adresse ausgegeben wurde.

8. Fazit

Das wars! Sie haben „Über Google anmelden“ mit dem Android Credential Manager erfolgreich implementiert.

Zusätzliche Ressourcen

Vollständiger Code für MainActivity.kt

Hier ist der vollständige Code für MainActivity.kt als Referenz:

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
    }
}