Code

Create GoogleSignInHelper.kt — the Google ID token

In the code column — GoogleSignInHelper.kt. One suspend function: CredentialManager shows the Google account picker, and if the user chooses one — it returns an ID token (a signed JWT). Note setServerClientId(...google_web_client_id) — we pass the Web client ID here, not the Android one.

Add the dependencies and strings.xml:

implementation(libs.androidx.credentials)
implementation(libs.androidx.credentials.play.services.auth)
implementation(libs.googleid)                 // GetGoogleIdOption
<!-- res/values/strings.xml -->
<string name="google_web_client_id">1001517404205-...apps.googleusercontent.com</string>

Next — the same contract as 8a. PicaApi gains the endpoint:

@POST("auth/google")
suspend fun googleAuth(@Body request: GoogleAuthRequest): AuthResponse   // ← the same AuthResponse!

@Serializable data class GoogleAuthRequest(@SerialName("id_token") val idToken: String)

AuthRepository — a new function that saves the token to the same store:

suspend fun googleSignIn(idToken: String) {
    val response = api.googleAuth(GoogleAuthRequest(idToken))
    tokenStorage.saveToken(response.token)   // same storage as email/password
}

AuthViewModelgoogleSignIn(idToken) mirrors the authenticate() logic (isLoading, isLoggedIn, error). And LoginScreen — a button below an "or" divider:

OutlinedButton(
    onClick = {
        scope.launch {
            try {
                val idToken = getGoogleIdToken(context)
                if (idToken != null) viewModel.googleSignIn(idToken)
            } catch (e: Exception) {
                // user dismissed the picker or no accounts available — ignore
            }
        }
    },
    enabled = !state.isLoading,
    modifier = Modifier.fillMaxWidth()
) { Text("Sign in with Google") }

Tip. setFilterByAuthorizedAccounts(false) shows all the phone's Google accounts (not just ones previously used with this app). true is nice for "returning" users (a quieter flow), but a first sign-in needs false.