Add login management.

This commit is contained in:
Thomas Andres Gomez 2021-05-08 14:01:57 +02:00
parent 8fbe3c0b7b
commit 45d2fe1336
14 changed files with 366 additions and 158 deletions

6
.idea/misc.xml generated
View file

@ -90,6 +90,12 @@
<entry key="../../../../../layout/compose-model-1620403674440.xml" value="0.2962962962962963" /> <entry key="../../../../../layout/compose-model-1620403674440.xml" value="0.2962962962962963" />
<entry key="../../../../../layout/compose-model-1620414619902.xml" value="0.28125" /> <entry key="../../../../../layout/compose-model-1620414619902.xml" value="0.28125" />
<entry key="../../../../../layout/compose-model-1620421200338.xml" value="0.3" /> <entry key="../../../../../layout/compose-model-1620421200338.xml" value="0.3" />
<entry key="../../../../../layout/compose-model-1620464601516.xml" value="0.28125" />
<entry key="../../../../../layout/compose-model-1620467872903.xml" value="0.23058252427184467" />
<entry key="../../../../../layout/compose-model-1620467872904.xml" value="0.2962962962962963" />
<entry key="../../../../../layout/compose-model-1620467930629.xml" value="0.16300675675675674" />
<entry key="../../../../../layout/compose-model-1620468466480.xml" value="0.5818181818181818" />
<entry key="../../../../../layout/compose-model-1620469838058.xml" value="0.5818181818181818" />
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.2898148148148148" /> <entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.2898148148148148" />
<entry key="app/src/main/res/drawable/ic_baseline_local_library_24.xml" value="0.25462962962962965" /> <entry key="app/src/main/res/drawable/ic_baseline_local_library_24.xml" value="0.25462962962962965" />
<entry key="app/src/main/res/drawable/ic_google.xml" value="0.2962962962962963" /> <entry key="app/src/main/res/drawable/ic_google.xml" value="0.2962962962962963" />

View file

@ -6,6 +6,10 @@ import com.google.gson.GsonBuilder
import com.pixelized.biblib.injection.Bob import com.pixelized.biblib.injection.Bob
import com.pixelized.biblib.network.client.BibLibClient import com.pixelized.biblib.network.client.BibLibClient
import com.pixelized.biblib.network.client.IBibLibClient 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 import com.pixelized.biblib.utils.BitmapCache
class BibLibApplication : Application() { class BibLibApplication : Application() {
@ -18,5 +22,8 @@ class BibLibApplication : Application() {
Bob[Gson::class] = GsonBuilder().create() Bob[Gson::class] = GsonBuilder().create()
Bob[IBibLibClient::class] = BibLibClient() Bob[IBibLibClient::class] = BibLibClient()
Bob[IGoogleSingInRepository::class] = GoogleSingInRepository(this)
Bob[ICredentialRepository::class] = CredentialRepository(this)
} }
} }

View file

@ -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) }
}

View file

@ -0,0 +1,7 @@
package com.pixelized.biblib.repository.credential
interface ICredentialRepository {
var login: String?
var password: String?
var rememberCredential: Boolean
}

View file

@ -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>(
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)
}
}

View file

@ -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<AuthenticationState>,
val launcher: ActivityResultLauncher<Intent>,
)
sealed class AuthenticationState {
object Initial : AuthenticationState()
object Loading : AuthenticationState()
data class Connect(val token: String) : AuthenticationState()
data class Error(val exception: Exception) : AuthenticationState()
}
}

View file

@ -22,17 +22,6 @@ import com.pixelized.biblib.ui.viewmodel.NavigationViewModel.Screen
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private val navigationViewModel: NavigationViewModel by viewModels() 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?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -57,9 +46,9 @@ fun ContentComposable() {
Crossfade(targetState = main) { Crossfade(targetState = main) {
when (it) { when (it) {
is Screen.SplashScreen -> SplashScreenComposable() is Screen.SplashScreen -> SplashScreenComposable(viewModel())
is Screen.LoginScreen -> LoginScreenComposable() is Screen.LoginScreen -> LoginScreenComposable(viewModel(), viewModel())
is Screen.MainScreen -> MainScreenComposable() is Screen.MainScreen -> MainScreenComposable(viewModel())
} }
} }
} }

View file

@ -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 {}
}
}
}

View file

@ -1,10 +1,7 @@
package com.pixelized.biblib.ui.composable.screen package com.pixelized.biblib.ui.composable.screen
import android.util.Log
import android.widget.Toast import androidx.compose.animation.ExperimentalAnimationApi
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
@ -24,7 +21,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
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.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource 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.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp 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.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.theme.BibLibTheme
import com.pixelized.biblib.ui.viewmodel.AuthenticationViewModel import com.pixelized.biblib.ui.viewmodel.AuthenticationViewModel
import com.pixelized.biblib.ui.viewmodel.NavigationViewModel import com.pixelized.biblib.ui.viewmodel.NavigationViewModel
@ -47,41 +40,24 @@ import com.pixelized.biblib.ui.viewmodel.NavigationViewModel
@Composable @Composable
fun LoginScreenComposablePreview() { fun LoginScreenComposablePreview() {
BibLibTheme { BibLibTheme {
LoginScreenComposable() val navigationViewModel = NavigationViewModel()
val authenticationViewModel = AuthenticationViewModel()
LoginScreenComposable(navigationViewModel, authenticationViewModel)
} }
} }
@OptIn(ExperimentalAnimationApi::class)
@Composable @Composable
fun LoginScreenComposable( fun LoginScreenComposable(
navigationViewModel: NavigationViewModel = viewModel(), navigationViewModel: NavigationViewModel,
authenticationViewModel: AuthenticationViewModel = viewModel(), authenticationViewModel: AuthenticationViewModel,
) { ) {
// TODO : c'est de la merde ça Box(
val activity = LocalContext.current as MainActivity modifier = Modifier
val result = remember { mutableStateOf<String?>(null) } .fillMaxWidth()
val launcher = .fillMaxHeight()
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { ) {
try { val loginWithGoogleRequest = authenticationViewModel.prepareLoginWithGoogle()
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 val typography = MaterialTheme.typography
Column( Column(
@ -154,7 +130,7 @@ fun LoginScreenComposable(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = outlinedButtonColors(), colors = outlinedButtonColors(),
onClick = { onClick = {
launcher.launch(activity.googleSignIn.signInIntent) authenticationViewModel.loginWithGoogle(loginWithGoogleRequest)
}) { }) {
Image( Image(
modifier = Modifier.padding(end = 8.dp), modifier = Modifier.padding(end = 8.dp),
@ -163,6 +139,18 @@ fun LoginScreenComposable(
Text(text = stringResource(id = R.string.action_google_sign_in)) 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)
)
}
} }
@Composable @Composable
@ -176,7 +164,7 @@ private fun LoginField(
TextField( TextField(
modifier = modifier, modifier = modifier,
value = login.value ?: "", value = login.value ?: "",
onValueChange = { viewModel.updateLogin(it) }, onValueChange = { viewModel.updateLoginField(it) },
label = { Text(text = stringResource(id = R.string.authentication_login)) }, label = { Text(text = stringResource(id = R.string.authentication_login)) },
colors = outlinedTextFieldColors(), colors = outlinedTextFieldColors(),
maxLines = 1, maxLines = 1,
@ -198,7 +186,7 @@ private fun PasswordField(
TextField( TextField(
modifier = modifier, modifier = modifier,
value = password.value ?: "", value = password.value ?: "",
onValueChange = { viewModel.updatePassword(it) }, onValueChange = { viewModel.updatePasswordField(it) },
label = { Text(text = stringResource(id = R.string.authentication_password)) }, label = { Text(text = stringResource(id = R.string.authentication_password)) },
colors = outlinedTextFieldColors(), colors = outlinedTextFieldColors(),
maxLines = 1, maxLines = 1,

View file

@ -1,7 +1,6 @@
package com.pixelized.biblib.ui.composable.screen package com.pixelized.biblib.ui.composable.screen
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.sharp.ArrowBack 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.runtime.livedata.observeAsState
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel
import com.pixelized.biblib.R import com.pixelized.biblib.R
import com.pixelized.biblib.ui.composable.pages.DetailPageComposable import com.pixelized.biblib.ui.composable.pages.DetailPageComposable
import com.pixelized.biblib.ui.composable.pages.HomePageComposable import com.pixelized.biblib.ui.composable.pages.HomePageComposable
@ -42,13 +40,15 @@ fun ToolbarComposableLightPreview() {
@Composable @Composable
fun MainScreenComposablePreview() { fun MainScreenComposablePreview() {
BibLibTheme { BibLibTheme {
MainScreenComposable() val viewModel = NavigationViewModel()
MainScreenComposable(viewModel)
} }
} }
@Composable @Composable
fun MainScreenComposable() { fun MainScreenComposable(
val navigationViewModel = viewModel<NavigationViewModel>() navigationViewModel: NavigationViewModel
) {
val page by navigationViewModel.page.observeAsState() val page by navigationViewModel.page.observeAsState()
LaunchedEffect(key1 = "MainScreen", block = { LaunchedEffect(key1 = "MainScreen", block = {

View file

@ -11,23 +11,25 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview 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.theme.BibLibTheme
import com.pixelized.biblib.ui.viewmodel.NavigationViewModel import com.pixelized.biblib.ui.viewmodel.NavigationViewModel
import kotlinx.coroutines.* import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Preview @Preview
@Composable @Composable
fun SplashScreenComposablePreview() { fun SplashScreenComposablePreview() {
BibLibTheme { BibLibTheme {
SplashScreenComposable() val viewModel = NavigationViewModel()
SplashScreenComposable(viewModel)
} }
} }
@Composable @Composable
fun SplashScreenComposable() { fun SplashScreenComposable(
val navigationViewModel = viewModel<NavigationViewModel>() navigationViewModel: NavigationViewModel
) {
val typography = MaterialTheme.typography val typography = MaterialTheme.typography
Box( Box(

View file

@ -1,23 +1,22 @@
package com.pixelized.biblib.ui.viewmodel package com.pixelized.biblib.ui.viewmodel
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.util.Log import android.util.Log
import androidx.core.content.edit import androidx.compose.runtime.Composable
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import com.pixelized.biblib.data.network.query.AuthLoginQuery import com.pixelized.biblib.data.network.query.AuthLoginQuery
import com.pixelized.biblib.injection.inject import com.pixelized.biblib.injection.inject
import com.pixelized.biblib.network.client.IBibLibClient 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.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class AuthenticationViewModel(application: Application) : AndroidViewModel(application) { class AuthenticationViewModel : ViewModel() {
private val preferences = application.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE) private val credentialRepository: ICredentialRepository by inject()
private val googleSignIn: IGoogleSingInRepository by inject()
private val client: IBibLibClient by inject() private val client: IBibLibClient by inject()
private val _login = MutableLiveData<String>() private val _login = MutableLiveData<String>()
@ -31,27 +30,27 @@ class AuthenticationViewModel(application: Application) : AndroidViewModel(appli
init { init {
viewModelScope.launch(Dispatchers.Main) { viewModelScope.launch(Dispatchers.Main) {
_login.value = preferences.login _login.value = credentialRepository.login
_password.value = preferences.password _password.value = credentialRepository.password
_rememberCredential.value = preferences.rememberCredential _rememberCredential.value = credentialRepository.rememberCredential
} }
} }
fun updateLogin(login: String) { fun updateLoginField(login: String) {
_login.postValue(login) _login.postValue(login)
} }
fun updatePassword(password: String) { fun updatePasswordField(password: String) {
_password.postValue(password) _password.postValue(password)
} }
fun updateRememberCredential(rememberCredential: Boolean) { fun updateRememberCredential(rememberCredential: Boolean) {
_rememberCredential.postValue(rememberCredential) _rememberCredential.postValue(rememberCredential)
viewModelScope.launch { viewModelScope.launch {
preferences.rememberCredential = rememberCredential credentialRepository.rememberCredential = rememberCredential
if (rememberCredential.not()) { if (rememberCredential.not()) {
preferences.login = null credentialRepository.login = null
preferences.password = null credentialRepository.password = null
} }
} }
} }
@ -63,8 +62,8 @@ class AuthenticationViewModel(application: Application) : AndroidViewModel(appli
fun login() { fun login() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
if (rememberCredential.value == true) { if (rememberCredential.value == true) {
preferences.login = login.value credentialRepository.login = login.value
preferences.password = password.value credentialRepository.password = password.value
} }
// TODO : validation ! // TODO : validation !
val query = AuthLoginQuery( val query = AuthLoginQuery(
@ -77,17 +76,12 @@ class AuthenticationViewModel(application: Application) : AndroidViewModel(appli
} }
} }
private var SharedPreferences.login: String? @Composable
get() = getString(REMEMBER_USER, null) fun prepareLoginWithGoogle(): IGoogleSingInRepository.Request =
set(value) = edit { putString(REMEMBER_USER, value) } googleSignIn.prepareLoginWithGoogle()
private var SharedPreferences.password: String? fun loginWithGoogle(request: IGoogleSingInRepository.Request) =
get() = getString(REMEMBER_PASSWORD, null) googleSignIn.loginWithGoogle(request)
set(value) = edit { putString(REMEMBER_PASSWORD, value) }
private var SharedPreferences.rememberCredential: Boolean
get() = getBoolean(REMEMBER_CREDENTIAL, false)
set(value) = edit { putBoolean(REMEMBER_CREDENTIAL, value) }
companion object { companion object {
const val SHARED_PREF = "BIB_LIB_SHARED_PREF" const val SHARED_PREF = "BIB_LIB_SHARED_PREF"

View file

@ -0,0 +1,3 @@
package com.pixelized.biblib.utils.exception
class MissingTokenException: RuntimeException("Login response miss token data.")

View file

@ -5,6 +5,8 @@
<string name="action_login">Login</string> <string name="action_login">Login</string>
<string name="action_google_sign_in">Sign in with Google</string> <string name="action_google_sign_in">Sign in with Google</string>
<string name="loading">Entering the Imperial Library of Trantor.</string>
<string name="welcome_sign_in">Sign in to BibLib</string> <string name="welcome_sign_in">Sign in to BibLib</string>
<string name="authentication_login">Login</string> <string name="authentication_login">Login</string>