Migrate to google credential to authenticate
This commit is contained in:
parent
b21ecd5449
commit
51ffc9a303
3 changed files with 54 additions and 80 deletions
|
|
@ -123,6 +123,11 @@ dependencies {
|
||||||
// Splash Screen support prior to Android 12
|
// Splash Screen support prior to Android 12
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
|
|
||||||
|
// Google Auth
|
||||||
|
implementation("androidx.credentials:credentials:1.2.2")
|
||||||
|
implementation("androidx.credentials:credentials-play-services-auth:1.2.2")
|
||||||
|
implementation("com.google.android.libraries.identity.googleid:googleid:1.1.0")
|
||||||
|
|
||||||
// Google service
|
// Google service
|
||||||
implementation("com.google.android.gms:play-services-auth:21.2.0")
|
implementation("com.google.android.gms:play-services-auth:21.2.0")
|
||||||
implementation(
|
implementation(
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.credentials.exceptions.GetCredentialCancellationException
|
||||||
|
import androidx.credentials.exceptions.GetCredentialProviderConfigurationException
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.rplexicon.LocalActivity
|
import com.pixelized.rplexicon.LocalActivity
|
||||||
import com.pixelized.rplexicon.LocalSnack
|
import com.pixelized.rplexicon.LocalSnack
|
||||||
|
|
@ -85,10 +87,9 @@ fun AuthenticationScreen(
|
||||||
versionVM: VersionViewModel = hiltViewModel(),
|
versionVM: VersionViewModel = hiltViewModel(),
|
||||||
onSignIn: CoroutineScope.() -> Unit,
|
onSignIn: CoroutineScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
val snack = LocalSnack.current
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val snack = LocalSnack.current
|
||||||
val activity = LocalActivity.current
|
val activity = LocalActivity.current
|
||||||
val state = authenticationVM.rememberAuthenticationState()
|
|
||||||
|
|
||||||
Surface {
|
Surface {
|
||||||
PartyBackground()
|
PartyBackground()
|
||||||
|
|
@ -105,17 +106,22 @@ fun AuthenticationScreen(
|
||||||
)
|
)
|
||||||
|
|
||||||
HandleAuthenticationState(
|
HandleAuthenticationState(
|
||||||
state = state,
|
state = authenticationVM.authenticationState,
|
||||||
onProgress = {
|
onProgress = {
|
||||||
Dialog(onDismissRequest = { }) {
|
Dialog(onDismissRequest = { }) {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSignIn = onSignIn,
|
onSignIn = onSignIn,
|
||||||
onSignInError = {
|
onSignInError = { exception ->
|
||||||
snack.showSnackbar(
|
when (exception) {
|
||||||
message = it?.message ?: context.getString(R.string.error__generic)
|
// Ignore user cancellation.
|
||||||
)
|
is GetCredentialCancellationException -> Unit
|
||||||
|
|
||||||
|
else -> snack.showSnackbar(
|
||||||
|
message = exception?.message ?: context.getString(R.string.error__generic)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,59 @@
|
||||||
package com.pixelized.rplexicon.ui.screens.authentication
|
package com.pixelized.rplexicon.ui.screens.authentication
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.IntentSender.SendIntentException
|
|
||||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.result.ActivityResult
|
|
||||||
import androidx.activity.result.IntentSenderRequest
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.credentials.Credential
|
|
||||||
import androidx.credentials.CredentialManager
|
import androidx.credentials.CredentialManager
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.credentials.GetCredentialRequest
|
||||||
import com.google.android.gms.auth.api.identity.GetSignInIntentRequest
|
import androidx.lifecycle.ViewModel
|
||||||
import com.google.android.gms.auth.api.identity.Identity
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.google.android.gms.auth.api.identity.SignInCredential
|
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
|
||||||
import com.google.firebase.auth.GoogleAuthProvider
|
|
||||||
import com.google.firebase.auth.ktx.auth
|
|
||||||
import com.google.firebase.ktx.Firebase
|
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.android.com/identity/sign-in/credential-manager-siwg
|
||||||
|
*/
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class AuthenticationViewModel @Inject constructor(
|
class AuthenticationViewModel @Inject constructor() : ViewModel() {
|
||||||
application: Application,
|
|
||||||
) : AndroidViewModel(application) {
|
|
||||||
|
|
||||||
private val context: Context get() = getApplication()
|
val authenticationState = mutableStateOf<AuthenticationStateUio>(
|
||||||
private var launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>? = null
|
|
||||||
|
|
||||||
private val state = mutableStateOf<AuthenticationStateUio>(
|
|
||||||
AuthenticationStateUio.Initial
|
AuthenticationStateUio.Initial
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun rememberAuthenticationState(): State<AuthenticationStateUio> {
|
|
||||||
launcher = rememberLauncherForActivityResult(
|
|
||||||
contract = ActivityResultContracts.StartIntentSenderForResult(),
|
|
||||||
onResult = {
|
|
||||||
if (it.resultCode == Activity.RESULT_OK) {
|
|
||||||
state.value = AuthenticationStateUio.Progress
|
|
||||||
// sign in request succeed. retrieve google credential
|
|
||||||
val googleCredential: SignInCredential = Identity
|
|
||||||
.getSignInClient(context)
|
|
||||||
.getSignInCredentialFromIntent(it.data)
|
|
||||||
// build firebase credential
|
|
||||||
val firebaseCredential = GoogleAuthProvider
|
|
||||||
.getCredential(googleCredential.googleIdToken, null)
|
|
||||||
// sign in to Firebase
|
|
||||||
Firebase.auth
|
|
||||||
.signInWithCredential(firebaseCredential)
|
|
||||||
.addOnCompleteListener { task ->
|
|
||||||
if (task.isSuccessful) {
|
|
||||||
state.value = AuthenticationStateUio.Success
|
|
||||||
} else {
|
|
||||||
state.value = AuthenticationStateUio.Failure(task.exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.value = AuthenticationStateUio.Initial
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
fun signIn(activity: Activity) {
|
fun signIn(activity: Activity) {
|
||||||
state.value = AuthenticationStateUio.Initial
|
val credentialManager = CredentialManager.create(context = activity)
|
||||||
|
|
||||||
// build a request to sign in with google credential.
|
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
|
||||||
// At that point we do only use google sign in service
|
.Builder(serverClientId = activity.getString(R.string.google_sign_in_id))
|
||||||
val request: GetSignInIntentRequest = GetSignInIntentRequest.builder()
|
|
||||||
.setServerClientId(context.getString(R.string.google_sign_in_id))
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
// use the pre register launcher to start the sign in request intent.
|
val request: GetCredentialRequest = GetCredentialRequest.Builder()
|
||||||
Identity.getSignInClient(activity).getSignInIntent(request)
|
.addCredentialOption(credentialOption = signInWithGoogleOption)
|
||||||
.addOnSuccessListener { result ->
|
.build()
|
||||||
try {
|
|
||||||
launcher?.launch(
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
IntentSenderRequest.Builder(result.intentSender).build()
|
withContext(Dispatchers.Main) {
|
||||||
|
authenticationState.value = AuthenticationStateUio.Initial
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
credentialManager.getCredential(
|
||||||
|
request = request,
|
||||||
|
context = activity,
|
||||||
|
)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
authenticationState.value = AuthenticationStateUio.Success
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
authenticationState.value = AuthenticationStateUio.Failure(
|
||||||
|
exception = exception,
|
||||||
)
|
)
|
||||||
} catch (exception: SendIntentException) {
|
|
||||||
state.value = AuthenticationStateUio.Failure(exception = exception)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.addOnFailureListener { exception ->
|
}
|
||||||
state.value = AuthenticationStateUio.Failure(exception = exception)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue