Skip to content

Commit

Permalink
Merge pull request #240 from fmasa/drop-anonymous-auth
Browse files Browse the repository at this point in the history
Remove fallback to anonymous auth
  • Loading branch information
fmasa authored Jan 13, 2024
2 parents 53af18b + ddfac7a commit da5aaf6
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 86 deletions.
112 changes: 66 additions & 46 deletions app/src/main/kotlin/cz/muni/fi/rpg/ui/startup/StartupScreen.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package cz.muni.fi.rpg.ui.startup

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.material.AlertDialog
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
Expand All @@ -13,65 +16,96 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.google.android.gms.auth.api.signin.GoogleSignIn
import cz.frantisekmasa.wfrp_master.common.Str
import cz.frantisekmasa.wfrp_master.common.auth.AuthenticationManager
import cz.frantisekmasa.wfrp_master.common.auth.LocalWebClientId
import cz.frantisekmasa.wfrp_master.common.core.shared.SettingsStorage
import cz.frantisekmasa.wfrp_master.common.core.shared.edit
import cz.frantisekmasa.wfrp_master.common.core.shared.Resources
import cz.frantisekmasa.wfrp_master.common.core.shared.drawableResource
import cz.frantisekmasa.wfrp_master.common.core.ui.flow.collectWithLifecycle
import cz.frantisekmasa.wfrp_master.common.settings.AppSettings
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.VisualOnlyIconDescription
import cz.frantisekmasa.wfrp_master.common.shell.SplashScreen
import dev.icerock.moko.resources.compose.stringResource
import io.github.aakira.napier.Napier
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import org.kodein.di.compose.localDI
import org.kodein.di.instance

@Composable
fun StartupScreen(authenticationManager: AuthenticationManager) {
SplashScreen()
fun StartupScreen(authenticationManager: AuthenticationManager): Unit = Box {

var signInButtonVisible by rememberSaveable { mutableStateOf(false) }

val authenticated by authenticationManager.authenticated.collectWithLifecycle()

Napier.d("Authenticated: $authenticated")

if (authenticated != false) {
SplashScreen()
// We could not determine whether user is logged in yet or there is delay between
// recompositions (user is already authenticated, but startup screen is still visible)
return
}

val coroutineScope = rememberCoroutineScope()
val webClientId = LocalWebClientId.current
val context = LocalContext.current

var showAnonymousAuthenticationDialog by rememberSaveable { mutableStateOf(false) }
val startGoogleSignInFlow = rememberGoogleSignInLauncher(
authenticationManager = authenticationManager,
onFailure = { signInButtonVisible = true },
)

if (showAnonymousAuthenticationDialog) {
val settings: SettingsStorage by localDI().instance()
LaunchedEffect(Unit) {
withContext(Dispatchers.Default) {
// If user signed in via Google before, try to sign in him directly
if (authenticationManager.attemptToRestoreExistingGoogleSignIn(context, webClientId)) {
return@withContext
}

withContext(Dispatchers.Main) { startGoogleSignInFlow() }
}
}

AnonymousAuthenticationExplanationDialog(
onDismissRequest = {
coroutineScope.launch {
settings.edit(AppSettings.GOOGLE_SIGN_IN_DISMISSED, true)
authenticationManager.authenticateAnonymously()
if (signInButtonVisible) {
SplashScreen {
Button(
onClick = startGoogleSignInFlow
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Image(
drawableResource(Resources.Drawable.GoogleLogo),
VisualOnlyIconDescription,
)
Text(stringResource(Str.authentication_button_sign_in))
}
showAnonymousAuthenticationDialog = false
}
)
}
} else {
SplashScreen()
}
}

val context = LocalContext.current
@Composable
private fun rememberGoogleSignInLauncher(
authenticationManager: AuthenticationManager,
onFailure: () -> Unit,
): () -> Unit {
val webClientId = LocalWebClientId.current
val contract = remember(authenticationManager, webClientId) { authenticationManager.googleSignInContract(webClientId) }
val googleSignInLauncher = key(contract, coroutineScope) {
rememberLauncherForActivityResult(contract) { result ->
val coroutineScope = rememberCoroutineScope()

return key(contract, coroutineScope) {
val launcher = rememberLauncherForActivityResult(contract) { result ->
if (result.resultCode == 0) {
Napier.d("Google Sign-In dialog was dismissed")
showAnonymousAuthenticationDialog = true
onFailure()
return@rememberLauncherForActivityResult
}

Expand All @@ -82,35 +116,21 @@ fun StartupScreen(authenticationManager: AuthenticationManager) {
.idToken
?.let { idToken -> authenticationManager.signInWithGoogleToken(idToken) }
} catch (e: Throwable) {
showAnonymousAuthenticationDialog = true
onFailure()
}
}
}
}

LaunchedEffect(null) {
withContext(Dispatchers.Default) {
// If user signed in via Google before, try to sign in him directly
if (authenticationManager.attemptToRestoreExistingGoogleSignIn(context, webClientId)) {
return@withContext
return@key remember(launcher, onFailure) {
{
try {
launcher.launch(GoogleSignInCode)
} catch (e: Throwable) {
onFailure()
}
}

withContext(Dispatchers.Main) { googleSignInLauncher.launch(GoogleSignInCode) }
}
}
}

@Composable
private fun AnonymousAuthenticationExplanationDialog(onDismissRequest: () -> Unit) {
AlertDialog(
onDismissRequest = onDismissRequest,
text = { Text(stringResource(Str.authentication_startup_google_sign_in_failed)) },
confirmButton = {
TextButton(onClick = onDismissRequest) {
Text(stringResource(Str.common_ui_button_ok).uppercase())
}
}
)
}

private const val GoogleSignInCode = 100
Original file line number Diff line number Diff line change
Expand Up @@ -142,24 +142,6 @@ class AuthenticationManager(private val auth: FirebaseAuth) : UserProvider {
}
}

/**
* @return true if user was successfully authenticated and false otherwise
*/
suspend fun authenticateAnonymously(): Boolean {
check(auth.currentUser == null) { "User is already authenticated" }

return try {
Napier.d("Starting Firebase anonymous sign in")
auth.signInAnonymously().await()
Napier.d("User has signed in successfully")

true
} catch (e: Throwable) {
Napier.e("Anonymous sign-in has failed", e)
false
}
}

private fun googleClient(context: Context, webClientId: String) = GoogleSignIn.getClient(
context,
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package cz.frantisekmasa.wfrp_master.common.shell

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
Expand All @@ -13,28 +15,38 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import cz.frantisekmasa.wfrp_master.common.core.shared.Resources
import cz.frantisekmasa.wfrp_master.common.core.shared.drawableResource
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.Spacing
import cz.frantisekmasa.wfrp_master.common.core.ui.theme.Theme
import cz.frantisekmasa.wfrp_master.common.localization.FixedStrings

@Composable
fun SplashScreen() {
fun SplashScreen(content: (@Composable () -> Unit)? = null) {
Box(
modifier = Modifier
.fillMaxSize()
.splashBackground(),
.splashBackground()
.padding(Spacing.bodyPadding),
contentAlignment = Alignment.Center,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Image(
drawableResource(Resources.Drawable.SplashScreenIcon),
FixedStrings.appName,
Modifier.size(140.dp)
)
Text(
FixedStrings.appName,
style = MaterialTheme.typography.h6,
color = Theme.fixedColors.splashScreenContent,
)
Column(
verticalArrangement = Arrangement.spacedBy(Spacing.small, Alignment.CenterVertically),
horizontalAlignment = Alignment.CenterHorizontally
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
drawableResource(Resources.Drawable.SplashScreenIcon),
FixedStrings.appName,
Modifier.size(140.dp)
)
Text(
FixedStrings.appName,
style = MaterialTheme.typography.h6,
color = Theme.fixedColors.splashScreenContent,
)
}
content?.invoke()
}
}
}
1 change: 0 additions & 1 deletion common/src/commonMain/resources/MR/base/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
<string name="authentication_messages_reset_password_email_sent">Email with instructions for password reset was sent</string>
<string name="authentication_messages_signed_in_as">You are signed-in as</string>
<string name="authentication_messages_unknown_error">Unknown error occurred</string>
<string name="authentication_startup_google_sign_in_failed">We could not sign you in via Google.\nYour data will be tied to this device, but you can always sign in later in Settings.</string>
<string name="blessings_button_add_non_compendium">…or add non-Compendium blessing</string>
<string name="blessings_label_duration">Duration</string>
<string name="blessings_label_effect">Effect</string>
Expand Down
2 changes: 0 additions & 2 deletions common/src/commonMain/resources/MR/de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@
<string name="authentication_messages_email_not_found">Keine E-Mail gefunden</string>
<string name="careers_comma_separated_skills_helper">Kommagetrennte Liste von Fähigkeiten</string>
<string name="blessings_label_name">Name</string>
<string name="authentication_startup_google_sign_in_failed">Wir konnten Sie nicht über Google anmelden.
\nIhre Daten werden an dieses Gerät gebunden, aber Sie können sich später jederzeit in den Einstellungen anmelden.</string>
<string name="character_button_unlink_from_player">Verknüpfung vom Spieler lösen</string>
<string name="authentication_messages_reset_password_email_sent">E-Mail mit Anweisungen zum Zurücksetzen des Kennworts wurde gesendet</string>
<string name="blessings_label_duration">Dauer</string>
Expand Down
2 changes: 0 additions & 2 deletions common/src/commonMain/resources/MR/es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,6 @@
<string name="tests_roll">Lanzar</string>
<string name="characteristics_fellowship">Empatía</string>
<string name="compendium_json_import_prompt_warning">El formato de exportación no es definitivo aún</string>
<string name="authentication_startup_google_sign_in_failed">No pudimos ingresarle a través de Google.
\nTus datos se vincularán a este dispositivo, y podrás ingresar más tarde desde los Ajustes.</string>
<string name="weapons_reach_very_long">Muy Larga</string>
<string name="combat_hit_locations_left_leg">Pierna Izquierda</string>
<string name="character_button_unlink_from_player">Desenlazar del Jugador</string>
Expand Down
2 changes: 0 additions & 2 deletions common/src/commonMain/resources/MR/fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,6 @@
<string name="authentication_messages_not_signed_in_description">Vous n\'êtes pas connecté.
\nLa connexion vous permet de conserver l\'accès aux parties entre les appareils.</string>
<string name="authentication_messages_lose_access_to_parties">Vous perdrez l\'accès à ces groupes :</string>
<string name="authentication_startup_google_sign_in_failed">Nous n\'avons pas pu vous connecter via Google.
\nVos données seront liées à cet appareil, mais vous pourrez toujours vous connecter ultérieurement dans les paramètres.</string>
<string name="authentication_messages_unknown_error">Une erreur inconnue s\'est produite</string>
<string name="blessings_label_range">Portée</string>
<string name="calendar_icon_previous_month">Mois précédent</string>
Expand Down
2 changes: 0 additions & 2 deletions common/src/commonMain/resources/MR/it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,6 @@
<string name="about_body">Questa applicazione è stata creata da František Maša e dai suoi colleghi studenti, ora è sviluppata
\nnel tempo libero di František Maša.
\nSe ti piace l\'app, per favore dai la tua valutazione di WFRP Master su Google Play.</string>
<string name="authentication_startup_google_sign_in_failed">Non siamo riusciti a farti accedere tramite Google.
\nI tuoi dati saranno associati a questo dispositivo, ma potrai sempre accedere in un secondo momento nelle Impostazioni.</string>
<string name="conditions_prone">Prono</string>
<string name="conditions_blinded">Accecato</string>
<string name="conditions_fatigued">Affaticato</string>
Expand Down

0 comments on commit da5aaf6

Please sign in to comment.