From 45d2fe1336c19fa7995fbb0f7c84d94b435d08e1 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Sat, 8 May 2021 14:01:57 +0200 Subject: [PATCH] Add login management. --- .idea/misc.xml | 6 + .../com/pixelized/biblib/BibLibApplication.kt | 7 + .../credential/CredentialRepository.kt | 42 ++++ .../credential/ICredentialRepository.kt | 7 + .../googlesignin/GoogleSingInRepository.kt | 56 +++++ .../googlesignin/IGoogleSingInRepository.kt | 30 +++ .../com/pixelized/biblib/ui/MainActivity.kt | 17 +- .../ui/composable/items/WaitingComposable.kt | 82 +++++++ .../screen/LoginScreenComposable.kt | 200 ++++++++---------- .../composable/screen/MainScreenComposable.kt | 10 +- .../screen/SplashScreenComposable.kt | 12 +- .../ui/viewmodel/AuthenticationViewModel.kt | 50 ++--- .../utils/exception/MissingTokenException.kt | 3 + app/src/main/res/values/strings.xml | 2 + 14 files changed, 366 insertions(+), 158 deletions(-) create mode 100644 app/src/main/java/com/pixelized/biblib/repository/credential/CredentialRepository.kt create mode 100644 app/src/main/java/com/pixelized/biblib/repository/credential/ICredentialRepository.kt create mode 100644 app/src/main/java/com/pixelized/biblib/repository/googlesignin/GoogleSingInRepository.kt create mode 100644 app/src/main/java/com/pixelized/biblib/repository/googlesignin/IGoogleSingInRepository.kt create mode 100644 app/src/main/java/com/pixelized/biblib/ui/composable/items/WaitingComposable.kt create mode 100644 app/src/main/java/com/pixelized/biblib/utils/exception/MissingTokenException.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 2c29f05..6412fe1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -90,6 +90,12 @@ + + + + + + diff --git a/app/src/main/java/com/pixelized/biblib/BibLibApplication.kt b/app/src/main/java/com/pixelized/biblib/BibLibApplication.kt index fc1db66..4bd7a92 100644 --- a/app/src/main/java/com/pixelized/biblib/BibLibApplication.kt +++ b/app/src/main/java/com/pixelized/biblib/BibLibApplication.kt @@ -6,6 +6,10 @@ import com.google.gson.GsonBuilder import com.pixelized.biblib.injection.Bob import com.pixelized.biblib.network.client.BibLibClient import com.pixelized.biblib.network.client.IBibLibClient +import com.pixelized.biblib.repository.credential.CredentialRepository +import com.pixelized.biblib.repository.credential.ICredentialRepository +import com.pixelized.biblib.repository.googlesignin.GoogleSingInRepository +import com.pixelized.biblib.repository.googlesignin.IGoogleSingInRepository import com.pixelized.biblib.utils.BitmapCache class BibLibApplication : Application() { @@ -18,5 +22,8 @@ class BibLibApplication : Application() { Bob[Gson::class] = GsonBuilder().create() Bob[IBibLibClient::class] = BibLibClient() + + Bob[IGoogleSingInRepository::class] = GoogleSingInRepository(this) + Bob[ICredentialRepository::class] = CredentialRepository(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/repository/credential/CredentialRepository.kt b/app/src/main/java/com/pixelized/biblib/repository/credential/CredentialRepository.kt new file mode 100644 index 0000000..f99b7bc --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/repository/credential/CredentialRepository.kt @@ -0,0 +1,42 @@ +package com.pixelized.biblib.repository.credential + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit +import com.pixelized.biblib.ui.viewmodel.AuthenticationViewModel + +class CredentialRepository(application: Application) : ICredentialRepository { + private val preferences = + application.getSharedPreferences(AuthenticationViewModel.SHARED_PREF, Context.MODE_PRIVATE) + + override var login + get() = preferences.login + set(value) { + preferences.login = value + } + + override var password: String? + get() = preferences.password + set(value) { + preferences.password = value + } + + override var rememberCredential: Boolean + get() = preferences.rememberCredential + set(value) { + preferences.rememberCredential = value + } + + private var SharedPreferences.login: String? + get() = getString(AuthenticationViewModel.REMEMBER_USER, null) + set(value) = edit { putString(AuthenticationViewModel.REMEMBER_USER, value) } + + private var SharedPreferences.password: String? + get() = getString(AuthenticationViewModel.REMEMBER_PASSWORD, null) + set(value) = edit { putString(AuthenticationViewModel.REMEMBER_PASSWORD, value) } + + private var SharedPreferences.rememberCredential: Boolean + get() = getBoolean(AuthenticationViewModel.REMEMBER_CREDENTIAL, false) + set(value) = edit { putBoolean(AuthenticationViewModel.REMEMBER_CREDENTIAL, value) } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/repository/credential/ICredentialRepository.kt b/app/src/main/java/com/pixelized/biblib/repository/credential/ICredentialRepository.kt new file mode 100644 index 0000000..ce7771e --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/repository/credential/ICredentialRepository.kt @@ -0,0 +1,7 @@ +package com.pixelized.biblib.repository.credential + +interface ICredentialRepository { + var login: String? + var password: String? + var rememberCredential: Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/repository/googlesignin/GoogleSingInRepository.kt b/app/src/main/java/com/pixelized/biblib/repository/googlesignin/GoogleSingInRepository.kt new file mode 100644 index 0000000..4741e35 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/repository/googlesignin/GoogleSingInRepository.kt @@ -0,0 +1,56 @@ +package com.pixelized.biblib.repository.googlesignin + +import android.app.Application +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +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.GoogleSignInClient +import com.google.android.gms.auth.api.signin.GoogleSignInOptions +import com.google.android.gms.common.api.ApiException +import com.pixelized.biblib.R +import com.pixelized.biblib.utils.exception.MissingTokenException + +class GoogleSingInRepository(application: Application) : IGoogleSingInRepository { + + override val googleSignInOption: GoogleSignInOptions by lazy { + GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken(application.getString(R.string.biblib_server_id)) + .requestEmail() + .build() + } + + override val googleSignIn: GoogleSignInClient by lazy { + GoogleSignIn.getClient(application, googleSignInOption) + } + + @Composable + override fun prepareLoginWithGoogle(): IGoogleSingInRepository.Request { + val result = remember { + mutableStateOf( + IGoogleSingInRepository.AuthenticationState.Initial + ) + } + val launcher = rememberLauncherForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { + try { + val task = GoogleSignIn.getSignedInAccountFromIntent(it.data) + val account: GoogleSignInAccount? = task.getResult(ApiException::class.java) + val idToken = account?.idToken ?: throw MissingTokenException() + result.value = IGoogleSingInRepository.AuthenticationState.Connect(idToken) + } catch (exception: Exception) { + result.value = IGoogleSingInRepository.AuthenticationState.Error(exception) + } + } + return IGoogleSingInRepository.Request(result, launcher) + } + + override fun loginWithGoogle(request: IGoogleSingInRepository.Request) { + request.result.value = IGoogleSingInRepository.AuthenticationState.Loading + request.launcher.launch(googleSignIn.signInIntent) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/repository/googlesignin/IGoogleSingInRepository.kt b/app/src/main/java/com/pixelized/biblib/repository/googlesignin/IGoogleSingInRepository.kt new file mode 100644 index 0000000..7d4ea62 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/repository/googlesignin/IGoogleSingInRepository.kt @@ -0,0 +1,30 @@ +package com.pixelized.biblib.repository.googlesignin + +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import com.google.android.gms.auth.api.signin.GoogleSignInClient +import com.google.android.gms.auth.api.signin.GoogleSignInOptions + +interface IGoogleSingInRepository { + val googleSignInOption: GoogleSignInOptions + val googleSignIn: GoogleSignInClient + + @Composable + fun prepareLoginWithGoogle(): Request + + fun loginWithGoogle(request: Request) + + data class Request( + val result: MutableState, + val launcher: ActivityResultLauncher, + ) + + sealed class AuthenticationState { + object Initial : AuthenticationState() + object Loading : AuthenticationState() + data class Connect(val token: String) : AuthenticationState() + data class Error(val exception: Exception) : AuthenticationState() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt b/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt index 4521eba..7ee46aa 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt @@ -22,17 +22,6 @@ import com.pixelized.biblib.ui.viewmodel.NavigationViewModel.Screen class MainActivity : ComponentActivity() { private val navigationViewModel: NavigationViewModel by viewModels() - private val googleSignInOption by lazy { - GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestIdToken(getString(R.string.biblib_server_id)) - .requestEmail() - .build() - } - - val googleSignIn by lazy { - GoogleSignIn.getClient(this, googleSignInOption) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -57,9 +46,9 @@ fun ContentComposable() { Crossfade(targetState = main) { when (it) { - is Screen.SplashScreen -> SplashScreenComposable() - is Screen.LoginScreen -> LoginScreenComposable() - is Screen.MainScreen -> MainScreenComposable() + is Screen.SplashScreen -> SplashScreenComposable(viewModel()) + is Screen.LoginScreen -> LoginScreenComposable(viewModel(), viewModel()) + is Screen.MainScreen -> MainScreenComposable(viewModel()) } } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/items/WaitingComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/items/WaitingComposable.kt new file mode 100644 index 0000000..58c1826 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/items/WaitingComposable.kt @@ -0,0 +1,82 @@ +package com.pixelized.biblib.ui.composable.items + +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.Card +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.pixelized.biblib.R +import com.pixelized.biblib.ui.theme.BibLibTheme + + +@Preview +@Composable +fun WaitingComposableLightPreview() { + BibLibTheme(darkTheme = false) { + WaitingComposable( + visible = true, + message = stringResource(id = R.string.loading) + ) + } +} + +@Preview +@Composable +fun WaitingComposableDarkPreview() { + BibLibTheme(darkTheme = true) { + WaitingComposable( + visible = true, + message = stringResource(id = R.string.loading) + ) + } +} + +@Composable +fun WaitingComposable( + visible: Boolean, + modifier: Modifier = Modifier, + message: String? = null +) { + Crossfade( + modifier = modifier, + targetState = visible + ) { + if (it) { + Card(elevation = 8.dp) { + Column( + modifier = Modifier + .width(200.dp) + .padding(16.dp) + ) { + CircularProgressIndicator( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(16.dp) + ) + if (message?.isNotEmpty() == true) { + val typography = MaterialTheme.typography + Text( + modifier = Modifier.align(Alignment.CenterHorizontally), + style = typography.body1, + textAlign = TextAlign.Center, + text = message + ) + } + } + } + } else { + Box {} + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt index 92c64c9..6d914c3 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt @@ -1,10 +1,7 @@ package com.pixelized.biblib.ui.composable.screen -import android.util.Log -import android.widget.Toast -import androidx.activity.ComponentActivity -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts + +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -24,7 +21,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -33,12 +29,9 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.google.android.gms.auth.api.signin.GoogleSignIn -import com.google.android.gms.auth.api.signin.GoogleSignInAccount -import com.google.android.gms.common.api.ApiException import com.pixelized.biblib.R -import com.pixelized.biblib.ui.MainActivity +import com.pixelized.biblib.repository.googlesignin.IGoogleSingInRepository.AuthenticationState.Loading +import com.pixelized.biblib.ui.composable.items.WaitingComposable import com.pixelized.biblib.ui.theme.BibLibTheme import com.pixelized.biblib.ui.viewmodel.AuthenticationViewModel import com.pixelized.biblib.ui.viewmodel.NavigationViewModel @@ -47,121 +40,116 @@ import com.pixelized.biblib.ui.viewmodel.NavigationViewModel @Composable fun LoginScreenComposablePreview() { BibLibTheme { - LoginScreenComposable() + val navigationViewModel = NavigationViewModel() + val authenticationViewModel = AuthenticationViewModel() + LoginScreenComposable(navigationViewModel, authenticationViewModel) } } +@OptIn(ExperimentalAnimationApi::class) @Composable fun LoginScreenComposable( - navigationViewModel: NavigationViewModel = viewModel(), - authenticationViewModel: AuthenticationViewModel = viewModel(), + navigationViewModel: NavigationViewModel, + authenticationViewModel: AuthenticationViewModel, ) { - // TODO : c'est de la merde ça - val activity = LocalContext.current as MainActivity - val result = remember { mutableStateOf(null) } - val launcher = - rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { - try { - val task = GoogleSignIn.getSignedInAccountFromIntent(it.data) - val account: GoogleSignInAccount? = task.getResult(ApiException::class.java) - - val idToken = account?.idToken -// if (idToken != null) { -// viewModel.loginWithGoogle(idToken).observeLogin() -// } else { -// Toast.makeText(requireActivity(), "GoogleSignIn missing Token", Toast.LENGTH_SHORT).show() -// } - Log.e("AuthLoginFragment", "idToken: $idToken") - } catch (exception: Exception) { -// Toast.makeText(requireActivity(), "GoogleSignIn exception: ${exception.message}", Toast.LENGTH_SHORT).show() - Log.e("AuthLoginFragment", exception.message, exception) - } -// // Here we just update the state, but you could imagine -// // pre-processing the result, or updating a MutableSharedFlow that -// // your composable collects -// result.value = it - } - - val typography = MaterialTheme.typography - - Column( + Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight() - .verticalScroll(rememberScrollState()) - .padding(16.dp) ) { - Spacer(modifier = Modifier.weight(1f)) + val loginWithGoogleRequest = authenticationViewModel.prepareLoginWithGoogle() + val typography = MaterialTheme.typography - Text( - modifier = Modifier - .padding(vertical = 16.dp) - .align(alignment = Alignment.CenterHorizontally), - style = typography.h4, - text = stringResource(id = R.string.welcome_sign_in) - ) - - Spacer(modifier = Modifier.weight(1f)) - - val focusRequester = remember { FocusRequester() } - val localFocus = LocalFocusManager.current - LoginField( - viewModel = authenticationViewModel, + Column( modifier = Modifier .fillMaxWidth() - .padding(bottom = 16.dp), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), - keyboardActions = KeyboardActions { focusRequester.requestFocus() } - ) - PasswordField( - viewModel = authenticationViewModel, - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 16.dp) - .focusRequester(focusRequester), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions { localFocus.clearFocus() } - ) - CredentialRemember( - viewModel = authenticationViewModel, - modifier = Modifier - .height(48.dp) - .padding(bottom = 16.dp) - ) - Row( - modifier = Modifier - .padding(bottom = 16.dp) - .align(Alignment.End) + .fillMaxHeight() + .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { + Spacer(modifier = Modifier.weight(1f)) + + Text( + modifier = Modifier + .padding(vertical = 16.dp) + .align(alignment = Alignment.CenterHorizontally), + style = typography.h4, + text = stringResource(id = R.string.welcome_sign_in) + ) + + Spacer(modifier = Modifier.weight(1f)) + + val focusRequester = remember { FocusRequester() } + val localFocus = LocalFocusManager.current + LoginField( + viewModel = authenticationViewModel, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + keyboardActions = KeyboardActions { focusRequester.requestFocus() } + ) + PasswordField( + viewModel = authenticationViewModel, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp) + .focusRequester(focusRequester), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions { localFocus.clearFocus() } + ) + CredentialRemember( + viewModel = authenticationViewModel, + modifier = Modifier + .height(48.dp) + .padding(bottom = 16.dp) + ) + Row( + modifier = Modifier + .padding(bottom = 16.dp) + .align(Alignment.End) + ) { + Button( + modifier = Modifier.padding(end = 8.dp), + colors = outlinedButtonColors(), + onClick = { + authenticationViewModel.register() + }) { + Text(text = stringResource(id = R.string.action_register)) + } + Button(onClick = { + authenticationViewModel.login() + }) { + Text(text = stringResource(id = R.string.action_login)) + } + } + + Spacer(modifier = Modifier.weight(2f)) + Button( - modifier = Modifier.padding(end = 8.dp), + modifier = Modifier.fillMaxWidth(), colors = outlinedButtonColors(), onClick = { - authenticationViewModel.register() + authenticationViewModel.loginWithGoogle(loginWithGoogleRequest) }) { - Text(text = stringResource(id = R.string.action_register)) - } - Button(onClick = { - authenticationViewModel.login() - }) { - Text(text = stringResource(id = R.string.action_login)) + Image( + modifier = Modifier.padding(end = 8.dp), + painter = painterResource(id = R.drawable.ic_google), contentDescription = "" + ) + Text(text = stringResource(id = R.string.action_google_sign_in)) } } - Spacer(modifier = Modifier.weight(2f)) - - Button( - modifier = Modifier.fillMaxWidth(), - colors = outlinedButtonColors(), - onClick = { - launcher.launch(activity.googleSignIn.signInIntent) - }) { - Image( - modifier = Modifier.padding(end = 8.dp), - painter = painterResource(id = R.drawable.ic_google), contentDescription = "" - ) - Text(text = stringResource(id = R.string.action_google_sign_in)) + var waiting: Boolean by remember { mutableStateOf(false) } + waiting = when (loginWithGoogleRequest.result.value) { + is Loading -> true + else -> false } + WaitingComposable( + modifier = Modifier.align(Alignment.Center), + visible = waiting, + message = stringResource(id = R.string.loading) + ) } } @@ -176,7 +164,7 @@ private fun LoginField( TextField( modifier = modifier, value = login.value ?: "", - onValueChange = { viewModel.updateLogin(it) }, + onValueChange = { viewModel.updateLoginField(it) }, label = { Text(text = stringResource(id = R.string.authentication_login)) }, colors = outlinedTextFieldColors(), maxLines = 1, @@ -198,7 +186,7 @@ private fun PasswordField( TextField( modifier = modifier, value = password.value ?: "", - onValueChange = { viewModel.updatePassword(it) }, + onValueChange = { viewModel.updatePasswordField(it) }, label = { Text(text = stringResource(id = R.string.authentication_password)) }, colors = outlinedTextFieldColors(), maxLines = 1, diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt index 1a01b9b..792986a 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt @@ -1,7 +1,6 @@ package com.pixelized.biblib.ui.composable.screen import androidx.compose.animation.Crossfade -import androidx.compose.animation.core.tween import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.sharp.ArrowBack @@ -12,7 +11,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.lifecycle.viewmodel.compose.viewModel import com.pixelized.biblib.R import com.pixelized.biblib.ui.composable.pages.DetailPageComposable import com.pixelized.biblib.ui.composable.pages.HomePageComposable @@ -42,13 +40,15 @@ fun ToolbarComposableLightPreview() { @Composable fun MainScreenComposablePreview() { BibLibTheme { - MainScreenComposable() + val viewModel = NavigationViewModel() + MainScreenComposable(viewModel) } } @Composable -fun MainScreenComposable() { - val navigationViewModel = viewModel() +fun MainScreenComposable( + navigationViewModel: NavigationViewModel +) { val page by navigationViewModel.page.observeAsState() LaunchedEffect(key1 = "MainScreen", block = { diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt index 77f8125..4378a33 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt @@ -11,23 +11,25 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import androidx.lifecycle.viewmodel.compose.viewModel import com.pixelized.biblib.ui.theme.BibLibTheme import com.pixelized.biblib.ui.viewmodel.NavigationViewModel -import kotlinx.coroutines.* +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Preview @Composable fun SplashScreenComposablePreview() { BibLibTheme { - SplashScreenComposable() + val viewModel = NavigationViewModel() + SplashScreenComposable(viewModel) } } @Composable -fun SplashScreenComposable() { - val navigationViewModel = viewModel() +fun SplashScreenComposable( + navigationViewModel: NavigationViewModel +) { val typography = MaterialTheme.typography Box( diff --git a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/AuthenticationViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/AuthenticationViewModel.kt index 2c1ce03..963de79 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/AuthenticationViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/AuthenticationViewModel.kt @@ -1,23 +1,22 @@ package com.pixelized.biblib.ui.viewmodel -import android.app.Application -import android.content.Context -import android.content.SharedPreferences import android.util.Log -import androidx.core.content.edit -import androidx.lifecycle.AndroidViewModel +import androidx.compose.runtime.Composable import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.compose.viewModel import com.pixelized.biblib.data.network.query.AuthLoginQuery import com.pixelized.biblib.injection.inject import com.pixelized.biblib.network.client.IBibLibClient +import com.pixelized.biblib.repository.credential.ICredentialRepository +import com.pixelized.biblib.repository.googlesignin.IGoogleSingInRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -class AuthenticationViewModel(application: Application) : AndroidViewModel(application) { - private val preferences = application.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE) +class AuthenticationViewModel : ViewModel() { + private val credentialRepository: ICredentialRepository by inject() + private val googleSignIn: IGoogleSingInRepository by inject() private val client: IBibLibClient by inject() private val _login = MutableLiveData() @@ -31,27 +30,27 @@ class AuthenticationViewModel(application: Application) : AndroidViewModel(appli init { viewModelScope.launch(Dispatchers.Main) { - _login.value = preferences.login - _password.value = preferences.password - _rememberCredential.value = preferences.rememberCredential + _login.value = credentialRepository.login + _password.value = credentialRepository.password + _rememberCredential.value = credentialRepository.rememberCredential } } - fun updateLogin(login: String) { + fun updateLoginField(login: String) { _login.postValue(login) } - fun updatePassword(password: String) { + fun updatePasswordField(password: String) { _password.postValue(password) } fun updateRememberCredential(rememberCredential: Boolean) { _rememberCredential.postValue(rememberCredential) viewModelScope.launch { - preferences.rememberCredential = rememberCredential + credentialRepository.rememberCredential = rememberCredential if (rememberCredential.not()) { - preferences.login = null - preferences.password = null + credentialRepository.login = null + credentialRepository.password = null } } } @@ -63,8 +62,8 @@ class AuthenticationViewModel(application: Application) : AndroidViewModel(appli fun login() { viewModelScope.launch(Dispatchers.IO) { if (rememberCredential.value == true) { - preferences.login = login.value - preferences.password = password.value + credentialRepository.login = login.value + credentialRepository.password = password.value } // TODO : validation ! val query = AuthLoginQuery( @@ -77,17 +76,12 @@ class AuthenticationViewModel(application: Application) : AndroidViewModel(appli } } - private var SharedPreferences.login: String? - get() = getString(REMEMBER_USER, null) - set(value) = edit { putString(REMEMBER_USER, value) } + @Composable + fun prepareLoginWithGoogle(): IGoogleSingInRepository.Request = + googleSignIn.prepareLoginWithGoogle() - private var SharedPreferences.password: String? - get() = getString(REMEMBER_PASSWORD, null) - set(value) = edit { putString(REMEMBER_PASSWORD, value) } - - private var SharedPreferences.rememberCredential: Boolean - get() = getBoolean(REMEMBER_CREDENTIAL, false) - set(value) = edit { putBoolean(REMEMBER_CREDENTIAL, value) } + fun loginWithGoogle(request: IGoogleSingInRepository.Request) = + googleSignIn.loginWithGoogle(request) companion object { const val SHARED_PREF = "BIB_LIB_SHARED_PREF" diff --git a/app/src/main/java/com/pixelized/biblib/utils/exception/MissingTokenException.kt b/app/src/main/java/com/pixelized/biblib/utils/exception/MissingTokenException.kt new file mode 100644 index 0000000..78bf430 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/utils/exception/MissingTokenException.kt @@ -0,0 +1,3 @@ +package com.pixelized.biblib.utils.exception + +class MissingTokenException: RuntimeException("Login response miss token data.") \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45c5ab1..447fae5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,6 +5,8 @@ Login Sign in with Google + Entering the Imperial Library of Trantor. + Sign in to BibLib Login