From 1c9f856e656147eb6b70c89e7756920da60355e1 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Tue, 5 Jul 2022 08:38:19 +0200 Subject: [PATCH] Add logout. --- app/build.gradle | 4 +- .../com/pixelized/biblib/ui/MainActivity.kt | 8 +--- .../pixelized/biblib/ui/composable/Search.kt | 21 +++++++--- .../ui/navigation/screen/ScreenNavHost.kt | 11 +++++ .../viewModel/AuthenticationViewModel.kt | 10 ++++- .../biblib/ui/screen/detail/DetailScreen.kt | 41 +++++++++++++++---- .../ui/screen/launch/LauncherViewModel.kt | 13 +++--- .../biblib/ui/screen/profile/ProfileScreen.kt | 9 ++++ .../ui/screen/profile/ProfileViewModel.kt | 9 ++++ .../biblib/ui/theme/dimen/BibLibDimen.kt | 1 + .../biblib/utils/extention/ContextEx.kt | 6 +++ 11 files changed, 103 insertions(+), 30 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 851a122..9175246 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,7 +68,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion '1.2.0-rc01' + kotlinCompilerExtensionVersion '1.2.0-rc02' } packagingOptions { @@ -88,7 +88,7 @@ dependencies { implementation 'androidx.activity:activity-compose:1.4.0' // Android Compose - implementation "androidx.compose.ui:ui:1.2.0-rc01" + implementation "androidx.compose.ui:ui:1.2.0-rc02" implementation "androidx.compose.material:material:1.1.1" implementation "androidx.compose.runtime:runtime-livedata:1.1.1" implementation "androidx.compose.ui:ui-tooling-preview:1.1.1" 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 636b2fa..031d8cf 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt @@ -4,14 +4,10 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface -import androidx.compose.ui.graphics.Color import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat -import com.google.accompanist.systemuicontroller.SystemUiController -import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.pixelized.biblib.ui.composable.SystemThemeColor import com.pixelized.biblib.ui.navigation.screen.ScreenNavHost import com.pixelized.biblib.ui.screen.launch.LauncherViewModel @@ -33,7 +29,7 @@ class MainActivity : ComponentActivity() { // splashscreen management installSplashScreen().apply { setKeepOnScreenCondition { - launcherViewModel.isLoading + launcherViewModel.isLoadingDone.not() } } @@ -43,7 +39,7 @@ class MainActivity : ComponentActivity() { SystemThemeColor { Surface(color = MaterialTheme.colors.background) { // Handle the main Navigation - if (launcherViewModel.isLoading.not()) { + if (launcherViewModel.isLoadingDone) { ScreenNavHost( startDestination = launcherViewModel.startDestination ) diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/Search.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/Search.kt index 3032ac6..a7b0239 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/Search.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/Search.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.* import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Search import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -79,16 +80,24 @@ fun Search( ), onValueChange = { search = it } ) - avatar?.let { - IconButton( - modifier = Modifier.padding(end = horizontalPadding), - onClick = onAvatar, - ) { + + IconButton( + modifier = Modifier.padding(end = horizontalPadding), + onClick = onAvatar, + ) { + if (avatar != null) { GlideImage( modifier = Modifier.clip(CircleShape).size(32.dp), previewPlaceholder = R.drawable.ic_google, contentScale = ContentScale.Fit, - imageModel = it, + imageModel = avatar, + ) + } else { + Icon( + modifier = Modifier.clip(CircleShape).size(32.dp), + tint = MaterialTheme.colors.onSurface, + imageVector = Icons.Default.Person, + contentDescription = null, ) } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt b/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt index 3346968..67ca030 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt @@ -45,6 +45,17 @@ fun ScreenNavHost( } } +fun NavHostController.navigateToAuthentication() { + navigate(Screen.Authentication.route) { + launchSingleTop = true + restoreState = true + popUpTo(0) { + saveState = true + inclusive = true + } + } +} + fun NavHostController.navigateToHome() { navigate(Screen.Home.route) { launchSingleTop = true diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/authentication/viewModel/AuthenticationViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/authentication/viewModel/AuthenticationViewModel.kt index cec3c17..534d5e4 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/authentication/viewModel/AuthenticationViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/authentication/viewModel/AuthenticationViewModel.kt @@ -5,13 +5,17 @@ import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.common.api.ApiException import com.pixelized.biblib.network.client.IBibLibClient import com.pixelized.biblib.network.data.query.AuthLoginQuery +import com.pixelized.biblib.repository.credential.CredentialRepository_Factory +import com.pixelized.biblib.repository.credential.ICredentialRepository import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository import com.pixelized.biblib.ui.composable.StateUio import com.pixelized.biblib.utils.exception.MissingGoogleTokenException @@ -24,6 +28,7 @@ import javax.inject.Inject @HiltViewModel class AuthenticationViewModel @Inject constructor( + private val credentialRepository: ICredentialRepository, private val googleSignIn: IGoogleSingInRepository, private val client: IBibLibClient, ) : ViewModel() { @@ -45,6 +50,7 @@ class AuthenticationViewModel @Inject constructor( val response = client.service.login(query) val idToken = response.token ?: throw MissingTokenException() client.token = idToken + credentialRepository.bearer = response.token _authenticationProcess.value = StateUio.Success(Unit) } catch (exception: Exception) { Log.e("AuthenticationViewModel", exception.message, exception) @@ -68,7 +74,7 @@ class AuthenticationViewModel @Inject constructor( val task = GoogleSignIn.getSignedInAccountFromIntent(it.data) val account = task.getResult(ApiException::class.java) val googleToken = account?.idToken ?: throw MissingGoogleTokenException() - val response = client.service.loginWithGoogle(googleToken) + val response = client.service.loginWithGoogle(token = googleToken) val token = response.token ?: throw MissingTokenException() client.token = token _authenticationProcess.value = StateUio.Success(Unit) diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt index 2792b80..e15a65e 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt @@ -4,6 +4,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.activity.compose.BackHandler import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -12,6 +13,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.NavigateNext import androidx.compose.material.icons.filled.Send import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -21,6 +23,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.text.HtmlCompat @@ -70,8 +73,13 @@ fun DetailScreen( val user by derivedStateOf { profileViewModel.user } if (user.isSuccessful()) { DetailScreenSendContent( - modifier = Modifier.navigationBarsPadding(), + modifier = Modifier + .navigationBarsPadding() + .padding(bottom = MaterialTheme.bibLib.dimen.dp16), emails = (user as StateUio.Success).value.amazonEmails, + onAction = { + + } ) } } @@ -273,6 +281,7 @@ private fun TitleLabel( private fun DetailScreenSendContent( modifier: Modifier = Modifier, emails: List, + onAction: (email: String) -> Unit = default(), ) { Column( modifier = modifier.fillMaxWidth(), @@ -287,18 +296,32 @@ private fun DetailScreenSendContent( ) LazyColumn { - items(items = emails) { - Box( + items(items = emails) { email -> + Row( modifier = Modifier - .height(height = MaterialTheme.bibLib.dimen.dp48) + .clickable(onClick = { onAction(email) }) + .height(height = MaterialTheme.bibLib.dimen.dp52) .fillMaxWidth(), - contentAlignment = Alignment.CenterStart + verticalAlignment = Alignment.CenterVertically, ) { Text( - modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp16), + modifier = Modifier + .padding( + start = MaterialTheme.bibLib.dimen.dp16, + end = MaterialTheme.bibLib.dimen.dp8, + ) + .weight(1f), style = MaterialTheme.typography.body1, color = MaterialTheme.colors.onSurface, - text = it + maxLines = 1, + overflow = TextOverflow.Ellipsis, + text = email + ) + + Icon( + modifier = Modifier.padding(end = MaterialTheme.bibLib.dimen.dp16), + imageVector = Icons.Default.NavigateNext, + contentDescription = null ) } } @@ -333,8 +356,8 @@ private fun DetailScreenSendPreview() { BibLibTheme { DetailScreenSendContent( emails = listOf( - "R.Giskard.Reventlov.Kindle@gmailcom", - "R.Daneel.Olivaw.Kindle@gmailcom", + "R.Giskard.Reventlov.Kindle@gmail.com", + "R.Daneel.Olivaw.Kindle@gmail.com", ), ) } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/launch/LauncherViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/launch/LauncherViewModel.kt index 4c12d19..ad2fd3b 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/launch/LauncherViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/launch/LauncherViewModel.kt @@ -2,6 +2,7 @@ package com.pixelized.biblib.ui.screen.launch import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.pixelized.biblib.network.client.IBibLibClient @@ -26,8 +27,8 @@ class LauncherViewModel @Inject constructor( private val apiCache: IAPICacheRepository, ) : ViewModel() { - private val _isLoading = mutableStateOf(true) - val isLoading: Boolean by _isLoading + var isLoadingDone by mutableStateOf(false) + private set var startDestination: Screen = Screen.Authentication private set @@ -36,14 +37,15 @@ class LauncherViewModel @Inject constructor( viewModelScope.launch(Dispatchers.IO) { // Try to Authenticate if (autoLoginWithGoogle() || autologinWithCredential()) { - startDestination = Screen.Home // Update book if (loadNewBooks()) { loadAllBooks() } + // Change the start destination + startDestination = Screen.Home } // Update loading state. - _isLoading.value = false + isLoadingDone = true } } @@ -73,12 +75,13 @@ class LauncherViewModel @Inject constructor( private suspend fun autologinWithCredential(): Boolean { val login = credentialRepository.login val password = credentialRepository.password - return if (login != null && password != null) { + return if (login != null && password != null && credentialRepository.bearer != null) { try { val query = AuthLoginQuery(login, password) client.service.login(query).let { response -> if (response.token != null) { client.token = response.token + credentialRepository.bearer = response.token true } else { false diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileScreen.kt index 27575f4..e5bbe86 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileScreen.kt @@ -16,6 +16,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import com.pixelized.biblib.network.client.IBibLibClient import com.pixelized.biblib.ui.composable.StateUio +import com.pixelized.biblib.ui.navigation.screen.LocalScreenNavHostController +import com.pixelized.biblib.ui.navigation.screen.navigateToAuthentication +import com.pixelized.biblib.ui.navigation.screen.navigateToProfile import com.pixelized.biblib.ui.theme.BibLibTheme import com.pixelized.biblib.utils.extention.bibLib import com.pixelized.biblib.utils.extention.default @@ -25,6 +28,8 @@ fun ProfileScreen( viewModel: ProfileViewModel = hiltViewModel(), ) { val context = LocalContext.current + val navigation = LocalScreenNavHostController.current + when (val user = viewModel.user) { is StateUio.Progress -> Unit is StateUio.Success -> ProfileScreenContent( @@ -34,6 +39,10 @@ fun ProfileScreen( val intent = Intent(Intent.ACTION_VIEW, Uri.parse(IBibLibClient.EDIT_PROFILE)) context.startActivity(intent) }, + onLogoutClick = { + viewModel.logout() + navigation.navigateToAuthentication() + } ) is StateUio.Failure -> Unit } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileViewModel.kt index 010eda2..cf95c18 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/profile/ProfileViewModel.kt @@ -9,6 +9,8 @@ import androidx.lifecycle.viewModelScope import com.pixelized.biblib.model.user.User import com.pixelized.biblib.network.client.IBibLibClient import com.pixelized.biblib.network.factory.UserFactory +import com.pixelized.biblib.repository.credential.ICredentialRepository +import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository import com.pixelized.biblib.ui.composable.StateUio import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers @@ -17,6 +19,8 @@ import javax.inject.Inject @HiltViewModel class ProfileViewModel @Inject constructor( + private val credentialRepository: ICredentialRepository, + private val googleSignIn: IGoogleSingInRepository, private val client: IBibLibClient, ) : ViewModel() { @@ -41,6 +45,11 @@ class ProfileViewModel @Inject constructor( } } + fun logout() { + credentialRepository.bearer = null + googleSignIn.client.signOut() + } + private fun User.toUio() = UserUio( username = username, firstname = firstname, diff --git a/app/src/main/java/com/pixelized/biblib/ui/theme/dimen/BibLibDimen.kt b/app/src/main/java/com/pixelized/biblib/ui/theme/dimen/BibLibDimen.kt index 94680df..3a129ee 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/theme/dimen/BibLibDimen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/theme/dimen/BibLibDimen.kt @@ -15,6 +15,7 @@ data class BibLibDimen( val dp16: Dp = 16.dp, val dp32: Dp = 32.dp, val dp48: Dp = 48.dp, + val dp52: Dp = 52.dp, val dp64: Dp = 64.dp, val dialog: Dialog = Dialog(), val thumbnail: BookThumbnail = BookThumbnail(), diff --git a/app/src/main/java/com/pixelized/biblib/utils/extention/ContextEx.kt b/app/src/main/java/com/pixelized/biblib/utils/extention/ContextEx.kt index 5dc9903..4de9be3 100644 --- a/app/src/main/java/com/pixelized/biblib/utils/extention/ContextEx.kt +++ b/app/src/main/java/com/pixelized/biblib/utils/extention/ContextEx.kt @@ -16,4 +16,10 @@ fun Context.notImplemented() { fun default(): () -> Unit { val context = LocalContext.current return { context.notImplemented() } +} + +@Composable +fun default(): (T) -> Unit { + val context = LocalContext.current + return { context.notImplemented() } } \ No newline at end of file