Migrate deprecated Google SignIn to Credentials Manager
This commit is contained in:
@ -104,8 +104,9 @@ dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
|
||||
implementation(libs.gms.playServicesBase)
|
||||
implementation(libs.gms.playServicesAuth)
|
||||
implementation(libs.androidx.credentials.auth)
|
||||
implementation(libs.androidx.credentials)
|
||||
implementation(libs.googleid)
|
||||
|
||||
val bom = platform(libs.androidx.compose.bom)
|
||||
implementation(bom)
|
||||
|
||||
5
android/Omnivore/app/proguard-rules.pro
vendored
5
android/Omnivore/app/proguard-rules.pro
vendored
@ -19,3 +19,8 @@
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-if class androidx.credentials.CredentialManager
|
||||
-keep class androidx.credentials.playservices.** {
|
||||
*;
|
||||
}
|
||||
|
||||
@ -1,15 +1,9 @@
|
||||
package app.omnivore.omnivore.feature.library
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.*
|
||||
import android.widget.Toast
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@ -39,7 +33,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.Instant
|
||||
import java.util.jar.Manifest
|
||||
import javax.inject.Inject
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
@ -35,9 +35,7 @@ import app.omnivore.omnivore.graphql.generated.ValidateUsernameQuery
|
||||
import app.omnivore.omnivore.utils.Constants
|
||||
import app.omnivore.omnivore.utils.ResourceProvider
|
||||
import com.apollographql.apollo3.ApolloClient
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.common.api.ApiException
|
||||
import com.google.android.gms.tasks.Task
|
||||
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.intercom.android.sdk.Intercom
|
||||
import kotlinx.coroutines.Job
|
||||
@ -359,9 +357,8 @@ class OnboardingViewModel @Inject constructor(
|
||||
setErrorMessage(resourceProvider.getString(R.string.login_view_model_google_auth_error_msg))
|
||||
}
|
||||
|
||||
fun handleGoogleAuthTask(task: Task<GoogleSignInAccount>) {
|
||||
val result = task.getResult(ApiException::class.java)
|
||||
val googleIdToken = result?.idToken ?: ""
|
||||
fun handleGoogleAuthCredential(credential: GoogleIdTokenCredential) {
|
||||
val googleIdToken = credential.idToken
|
||||
|
||||
// If token is missing then set the error message
|
||||
if (googleIdToken.isEmpty()) {
|
||||
|
||||
@ -17,7 +17,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -27,28 +26,25 @@ import app.omnivore.omnivore.feature.onboarding.OnboardingViewModel
|
||||
import app.omnivore.omnivore.feature.onboarding.auth.provider.AppleAuthButton
|
||||
import app.omnivore.omnivore.feature.onboarding.auth.provider.GoogleAuthButton
|
||||
import app.omnivore.omnivore.navigation.Routes
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
|
||||
@Composable
|
||||
fun AuthProviderScreen(
|
||||
welcomeNavController: NavHostController,
|
||||
viewModel: OnboardingViewModel
|
||||
) {
|
||||
val isGoogleAuthAvailable: Boolean =
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(LocalContext.current) == 0
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 64.dp)
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 64.dp)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.width(500.dp)
|
||||
) {
|
||||
if (isGoogleAuthAvailable) {
|
||||
GoogleAuthButton(viewModel)
|
||||
}
|
||||
GoogleAuthButton(viewModel)
|
||||
|
||||
AppleAuthButton(viewModel)
|
||||
|
||||
@ -66,7 +62,10 @@ fun AuthProviderScreen(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
) {
|
||||
Text(text = stringResource(R.string.welcome_screen_action_continue_with_email), modifier = Modifier.padding(vertical = 6.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.welcome_screen_action_continue_with_email),
|
||||
modifier = Modifier.padding(vertical = 6.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1.0F))
|
||||
@ -80,7 +79,7 @@ fun AuthProviderScreen(
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
){
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.welcome_screen_action_self_hosting_options),
|
||||
textDecoration = TextDecoration.Underline
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
package app.omnivore.omnivore.feature.onboarding.auth.provider
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@ -17,53 +13,64 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.credentials.CredentialManager
|
||||
import androidx.credentials.CustomCredential
|
||||
import androidx.credentials.GetCredentialRequest
|
||||
import app.omnivore.omnivore.BuildConfig
|
||||
import app.omnivore.omnivore.R
|
||||
import app.omnivore.omnivore.feature.onboarding.OnboardingViewModel
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
import com.google.android.gms.tasks.Task
|
||||
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
|
||||
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun GoogleAuthButton(viewModel: OnboardingViewModel) {
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val credentialManager = remember { CredentialManager.create(context) }
|
||||
|
||||
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
||||
.requestIdToken(BuildConfig.OMNIVORE_GAUTH_SERVER_CLIENT_ID).requestEmail().build()
|
||||
val googleIdOption = remember {
|
||||
GetSignInWithGoogleOption.Builder(BuildConfig.OMNIVORE_GAUTH_SERVER_CLIENT_ID)
|
||||
.build()
|
||||
}
|
||||
|
||||
val startForResult =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val intent = result.data
|
||||
if (result.data != null) {
|
||||
val task: Task<GoogleSignInAccount> =
|
||||
GoogleSignIn.getSignedInAccountFromIntent(intent)
|
||||
viewModel.handleGoogleAuthTask(task)
|
||||
}
|
||||
} else {
|
||||
viewModel.showGoogleErrorMessage()
|
||||
}
|
||||
}
|
||||
val request = remember {
|
||||
GetCredentialRequest.Builder()
|
||||
.addCredentialOption(googleIdOption)
|
||||
.build()
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
val googleSignIn = GoogleSignIn.getClient(context, signInOptions)
|
||||
|
||||
googleSignIn.silentSignIn().addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
viewModel.handleGoogleAuthTask(task)
|
||||
} else {
|
||||
startForResult.launch(googleSignIn.signInIntent)
|
||||
scope.launch {
|
||||
try {
|
||||
val credential = credentialManager.getCredential(
|
||||
request = request,
|
||||
context = context,
|
||||
).credential
|
||||
if (credential is CustomCredential &&
|
||||
credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL
|
||||
) {
|
||||
viewModel.handleGoogleAuthCredential(
|
||||
GoogleIdTokenCredential
|
||||
.createFrom(credential.data)
|
||||
)
|
||||
} else {
|
||||
viewModel.showGoogleErrorMessage()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
viewModel.showGoogleErrorMessage()
|
||||
}
|
||||
}.addOnFailureListener {
|
||||
startForResult.launch(googleSignIn.signInIntent)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
@ -80,7 +87,10 @@ fun GoogleAuthButton(viewModel: OnboardingViewModel) {
|
||||
contentDescription = "",
|
||||
modifier = Modifier.padding(end = 10.dp)
|
||||
)
|
||||
Text(text = stringResource(R.string.google_auth_text), modifier = Modifier.padding(vertical = 6.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.google_auth_text),
|
||||
modifier = Modifier.padding(vertical = 6.dp)
|
||||
)
|
||||
if (viewModel.isLoading) {
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
CircularProgressIndicator(
|
||||
|
||||
@ -6,16 +6,22 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.credentials.ClearCredentialStateRequest
|
||||
import androidx.credentials.CredentialManager
|
||||
import app.omnivore.omnivore.R
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun LogoutDialog(onClose: (Boolean) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val credentialManager = remember { CredentialManager.create(context) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
AlertDialog(onDismissRequest = { onClose(false) },
|
||||
title = { Text(text = stringResource(R.string.logout_dialog_title)) },
|
||||
text = {
|
||||
@ -28,12 +34,10 @@ fun LogoutDialog(onClose: (Boolean) -> Unit) {
|
||||
contentColor = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
onClick = {
|
||||
val signInOptions =
|
||||
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).build()
|
||||
|
||||
val googleSignIn = GoogleSignIn.getClient(context, signInOptions)
|
||||
googleSignIn.signOut()
|
||||
onClose(true)
|
||||
scope.launch {
|
||||
credentialManager.clearCredentialState(ClearCredentialStateRequest())
|
||||
onClose(true)
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(R.string.logout_dialog_action_confirm))
|
||||
}
|
||||
|
||||
@ -24,14 +24,14 @@ junit4 = "4.13.2"
|
||||
kotlin = "2.0.0"
|
||||
ksp = "2.0.0-1.0.21"
|
||||
kotlinxCoroutines = "1.8.0"
|
||||
playServices = "18.5.0"
|
||||
playServicesAuth = "21.2.0"
|
||||
androidxCredentials = "1.2.2"
|
||||
posthog = "2.0.3"
|
||||
pspdfkit = "8.9.1"
|
||||
retrofit = "2.11.0"
|
||||
room = "2.6.1"
|
||||
workManager = "2.9.0"
|
||||
hiltWork = "1.2.0"
|
||||
googleid = "1.1.1"
|
||||
|
||||
[libraries]
|
||||
accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanistFlowLayout" }
|
||||
@ -65,8 +65,9 @@ chiptextfield-m3 = { group = "io.github.dokar3", name = "chiptextfield-m3", vers
|
||||
coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
|
||||
compose-markdown = { group = "com.github.jeziellago", name = "compose-markdown", version.ref = "composeMarkdown" }
|
||||
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
|
||||
gms-playServicesAuth = { group = "com.google.android.gms", name = "play-services-auth", version.ref = "playServicesAuth" }
|
||||
gms-playServicesBase = { group = "com.google.android.gms", name = "play-services-base", version.ref = "playServices" }
|
||||
androidx-credentials = { group = "androidx.credentials", name = "credentials", version.ref = "androidxCredentials" }
|
||||
androidx-credentials-auth = { group = "androidx.credentials", name = "credentials-play-services-auth", version.ref = "androidxCredentials" }
|
||||
googleid = { group = "com.google.android.libraries.identity.googleid", name = "googleid", version.ref = "googleid" }
|
||||
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
|
||||
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
|
||||
intercom = { group = "io.intercom.android", name = "intercom-sdk", version.ref = "intercom" }
|
||||
|
||||
Reference in New Issue
Block a user