From 0ce392bba726c6c51086314ae9fabc3b8e8ecc6d Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Wed, 19 May 2021 11:31:44 +0200 Subject: [PATCH] Composable refactor (prepare for jetpack Navigation) --- .../com/pixelized/biblib/ui/MainActivity.kt | 12 +- .../ui/composable/common/BibLibDrawer.kt | 131 ++++++++ .../ui/composable/common/BibLibToolbar.kt | 77 +++++ .../ui/composable/{items => common}/Image.kt | 0 .../{items => }/dialog/CrossFadeOverlay.kt | 3 +- .../{items => }/dialog/ErrorCard.kt | 2 +- .../{items => }/dialog/LoadingCard.kt | 2 +- .../{items => }/dialog/SuccesCard.kt | 2 +- ...BookThumbnailComposable.kt => BookItem.kt} | 75 +++-- ...{DetailPageComposable.kt => DetailPage.kt} | 175 ++++++----- .../{HomePageComposable.kt => HomePage.kt} | 15 +- .../biblib/ui/composable/screen/HomeScreen.kt | 115 +++++++ ...oginScreenComposable.kt => LoginScreen.kt} | 20 +- .../composable/screen/MainScreenComposable.kt | 285 ------------------ ...ashScreenComposable.kt => SplashScreen.kt} | 218 ++++++++------ .../pixelized/biblib/ui/theme/Animation.kt | 6 +- app/src/main/res/values/strings.xml | 1 + 17 files changed, 597 insertions(+), 542 deletions(-) create mode 100644 app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibDrawer.kt create mode 100644 app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibToolbar.kt rename app/src/main/java/com/pixelized/biblib/ui/composable/{items => common}/Image.kt (100%) rename app/src/main/java/com/pixelized/biblib/ui/composable/{items => }/dialog/CrossFadeOverlay.kt (92%) rename app/src/main/java/com/pixelized/biblib/ui/composable/{items => }/dialog/ErrorCard.kt (97%) rename app/src/main/java/com/pixelized/biblib/ui/composable/{items => }/dialog/LoadingCard.kt (97%) rename app/src/main/java/com/pixelized/biblib/ui/composable/{items => }/dialog/SuccesCard.kt (97%) rename app/src/main/java/com/pixelized/biblib/ui/composable/items/{BookThumbnailComposable.kt => BookItem.kt} (80%) rename app/src/main/java/com/pixelized/biblib/ui/composable/pages/{DetailPageComposable.kt => DetailPage.kt} (54%) rename app/src/main/java/com/pixelized/biblib/ui/composable/pages/{HomePageComposable.kt => HomePage.kt} (79%) create mode 100644 app/src/main/java/com/pixelized/biblib/ui/composable/screen/HomeScreen.kt rename app/src/main/java/com/pixelized/biblib/ui/composable/screen/{LoginScreenComposable.kt => LoginScreen.kt} (96%) delete mode 100644 app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt rename app/src/main/java/com/pixelized/biblib/ui/composable/screen/{SplashScreenComposable.kt => SplashScreen.kt} (55%) 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 8cd0b13..d807041 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt @@ -9,9 +9,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.lifecycle.viewmodel.compose.viewModel -import com.pixelized.biblib.ui.composable.screen.LoginScreenComposable -import com.pixelized.biblib.ui.composable.screen.MainScreenComposable -import com.pixelized.biblib.ui.composable.screen.SplashScreenComposable +import com.pixelized.biblib.ui.composable.screen.LoginScreen +import com.pixelized.biblib.ui.composable.screen.HomeScreen +import com.pixelized.biblib.ui.composable.screen.SplashScreen import com.pixelized.biblib.ui.theme.BibLibTheme import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Screen import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel @@ -43,9 +43,9 @@ fun ContentComposable() { Crossfade(targetState = main) { when (it) { - is Screen.SplashScreen -> SplashScreenComposable() - is Screen.LoginScreen -> LoginScreenComposable() - is Screen.MainScreen -> MainScreenComposable() + is Screen.SplashScreen -> SplashScreen() + is Screen.LoginScreen -> LoginScreen() + is Screen.MainScreen -> HomeScreen() } } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibDrawer.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibDrawer.kt new file mode 100644 index 0000000..1dc5b97 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibDrawer.kt @@ -0,0 +1,131 @@ +package com.pixelized.biblib.ui.composable.common + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CutCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.pixelized.biblib.BuildConfig +import com.pixelized.biblib.R +import com.pixelized.biblib.ui.theme.BibLibTheme +import java.util.* + + +@Composable +fun BibLibDrawer( + onNewClick: () -> Unit = {}, + onBookClick: () -> Unit = {}, + onSeriesClick: () -> Unit = {}, + onAuthorClick: () -> Unit = {}, + onTagClick: () -> Unit = {}, +) { + val typography = MaterialTheme.typography + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + ) { + Card( + shape = CutCornerShape(bottomEnd = 16.dp), + elevation = 8.dp + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + style = typography.body1, + text = "R. Daneel Olivaw" + ) + Text( + style = typography.caption, + text = "r.daneel.olivaw@biblib.com" + ) + } + } + Spacer(modifier = Modifier.height(8.dp)) + DrawerItem( + text = "Nouveautés", + imageVector = Icons.Default.NewReleases, + onClick = onNewClick, + ) + DrawerItem( + text = "Livres", + imageVector = Icons.Default.AutoStories, + onClick = onBookClick, + ) + DrawerItem( + text = "Séries", + imageVector = Icons.Default.AutoAwesomeMotion, + onClick = onSeriesClick, + ) + DrawerItem( + text = "Auteurs", + imageVector = Icons.Default.SupervisorAccount, + onClick = onAuthorClick, + ) + DrawerItem( + text = "Étiquettes", + imageVector = Icons.Default.LocalOffer, + onClick = onTagClick, + ) + Spacer(modifier = Modifier.weight(1f)) + Text( + modifier = Modifier + .align(Alignment.End) + .padding(16.dp), + style = typography.caption, + text = stringResource( + R.string.app_version, + BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()), + BuildConfig.VERSION_NAME, + BuildConfig.VERSION_CODE + ) + ) + } +} + +@Composable +private fun DrawerItem(text: String, imageVector: ImageVector, onClick: () -> Unit) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(52.dp) + .clickable { onClick() }) { + Icon( + modifier = Modifier.padding(horizontal = 16.dp), + imageVector = imageVector, + contentDescription = "" + ) + Text( + text = text, + ) + Spacer(modifier = Modifier.weight(1f)) + Icon( + modifier = Modifier.padding(end = 8.dp), + imageVector = Icons.Default.NavigateNext, + contentDescription = "" + ) + } +} + +@Preview +@Composable +fun BibLibDrawerLightPreview() { + BibLibTheme { + BibLibDrawer() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibToolbar.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibToolbar.kt new file mode 100644 index 0000000..b332c5f --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/common/BibLibToolbar.kt @@ -0,0 +1,77 @@ +package com.pixelized.biblib.ui.composable.common + +import androidx.compose.animation.Crossfade +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.sharp.ArrowBack +import androidx.compose.material.icons.sharp.Menu +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.pixelized.biblib.R +import com.pixelized.biblib.ui.theme.BibLibTheme + + +@Composable +fun BibLibToolbar( + canNavigateBack: Boolean, + onBackPress: () -> Unit, + onDrawerPress: () -> Unit, +) { + TopAppBar( + title = { + Text(stringResource(id = R.string.app_name)) + }, + navigationIcon = { + NavigationIcon( + canNavigateBack, + onBackPress, + onDrawerPress, + ) + } + ) +} + +@Composable +private fun NavigationIcon( + canNavigateBack: Boolean, + onBackPress: () -> Unit, + onDrawerPress: () -> Unit, +) { + Crossfade(targetState = canNavigateBack) { + if (it) { + IconButton(onClick = onBackPress) { + Icon( + imageVector = Icons.Sharp.ArrowBack, + contentDescription = "back" + ) + } + } else { + IconButton(onClick = onDrawerPress) { + Icon( + imageVector = Icons.Sharp.Menu, + contentDescription = "drawer" + ) + } + } + } +} + +@Preview +@Composable +fun BibLibToolbarDarkPreview() { + BibLibTheme(darkTheme = false) { + BibLibToolbar(false, {}, {}) + } +} + +@Preview +@Composable +fun BibLibToolbarLightPreview() { + BibLibTheme(darkTheme = true) { + BibLibToolbar(true, {}, {}) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/items/Image.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/common/Image.kt similarity index 100% rename from app/src/main/java/com/pixelized/biblib/ui/composable/items/Image.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/common/Image.kt diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/CrossFadeOverlay.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/CrossFadeOverlay.kt similarity index 92% rename from app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/CrossFadeOverlay.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/dialog/CrossFadeOverlay.kt index e5e36b1..881d7dc 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/CrossFadeOverlay.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/CrossFadeOverlay.kt @@ -1,7 +1,6 @@ -package com.pixelized.biblib.ui.composable.items.dialog +package com.pixelized.biblib.ui.composable.dialog import androidx.compose.animation.Crossfade -import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/ErrorCard.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/ErrorCard.kt similarity index 97% rename from app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/ErrorCard.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/dialog/ErrorCard.kt index 33fa00c..6915a03 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/ErrorCard.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/ErrorCard.kt @@ -1,4 +1,4 @@ -package com.pixelized.biblib.ui.composable.items.dialog +package com.pixelized.biblib.ui.composable.dialog import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.height diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/LoadingCard.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/LoadingCard.kt similarity index 97% rename from app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/LoadingCard.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/dialog/LoadingCard.kt index 0573c57..0c82dc9 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/LoadingCard.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/LoadingCard.kt @@ -1,4 +1,4 @@ -package com.pixelized.biblib.ui.composable.items.dialog +package com.pixelized.biblib.ui.composable.dialog import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/SuccesCard.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/SuccesCard.kt similarity index 97% rename from app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/SuccesCard.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/dialog/SuccesCard.kt index 4990b52..99bcb74 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/items/dialog/SuccesCard.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/dialog/SuccesCard.kt @@ -1,4 +1,4 @@ -package com.pixelized.biblib.ui.composable.items.dialog +package com.pixelized.biblib.ui.composable.dialog import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.height diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/items/BookThumbnailComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/items/BookItem.kt similarity index 80% rename from app/src/main/java/com/pixelized/biblib/ui/composable/items/BookThumbnailComposable.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/items/BookItem.kt index 2c8f00a..9088059 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/items/BookThumbnailComposable.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/items/BookItem.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.pixelized.biblib.R import com.pixelized.biblib.ui.data.BookThumbnailUio @@ -28,47 +29,31 @@ import com.pixelized.biblib.utils.BitmapCache import com.pixelized.biblib.utils.injection.Bob import com.pixelized.biblib.utils.mock.BookThumbnailMock - -@Preview -@Composable -fun BookThumbnailComposablePreview() { - Bob[BitmapCache::class] = BitmapCache(LocalContext.current) - BibLibTheme { - val mock = BookThumbnailMock() - BookThumbnailComposable(thumbnail = mock.bookThumbnail) - } -} - -@Preview -@Composable -fun BookThumbnailComposablePreviewDark() { - Bob[BitmapCache::class] = BitmapCache(LocalContext.current) - BibLibTheme(darkTheme = true) { - val mock = BookThumbnailMock() - BookThumbnailComposable(thumbnail = mock.bookThumbnail) - } -} +private val THUMBNAIL_WIDTH: Dp = 60.dp +private val THUMBNAIL_HEIGHT: Dp = 96.dp @Composable -fun BookThumbnailComposable( +fun BookItem( thumbnail: BookThumbnailUio, - modifier: Modifier = Modifier, onClick: ((BookThumbnailUio) -> Unit)? = null, ) { val typography = MaterialTheme.typography + Card( - modifier = modifier.clickable { - onClick?.invoke(thumbnail) - }, + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .clickable { + onClick?.invoke(thumbnail) + }, elevation = 4.dp, ) { Row( - modifier = Modifier.height(96.dp), + modifier = Modifier.height(THUMBNAIL_HEIGHT), ) { Image( contentModifier = Modifier - .width(60.dp) - .fillMaxHeight() + .size(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT) .clip(RoundedCornerShape(4.dp)), placeHolder = painterResource(id = R.drawable.ic_launcher_foreground), contentScale = ContentScale.FillBounds, @@ -77,7 +62,9 @@ fun BookThumbnailComposable( contentDescription = thumbnail.title ) Column( - modifier = Modifier.padding(8.dp).weight(1f) + modifier = Modifier + .padding(8.dp) + .weight(1f) ) { Text( style = typography.h6, @@ -91,17 +78,19 @@ fun BookThumbnailComposable( overflow = TextOverflow.Ellipsis, softWrap = false, ) - Row(modifier = Modifier.weight(1f), verticalAlignment = Alignment.Bottom) { + Row( + modifier = Modifier.weight(1f), + verticalAlignment = Alignment.Bottom + ) { Text( style = typography.caption, text = thumbnail.genre, overflow = TextOverflow.Ellipsis, softWrap = false, ) - Spacer(modifier = Modifier.width(4.dp)) Spacer( modifier = Modifier - .width(0.dp) + .widthIn(min = 4.dp) .weight(1f) ) Text( @@ -114,8 +103,28 @@ fun BookThumbnailComposable( } Icon( modifier = Modifier.align(Alignment.CenterVertically), - imageVector = Icons.Default.NavigateNext, contentDescription = "navigate" + imageVector = Icons.Default.NavigateNext, contentDescription = "" ) } } +} + +@Preview +@Composable +fun BookItemLightPreview() { + Bob[BitmapCache::class] = BitmapCache(LocalContext.current) + BibLibTheme { + val mock = BookThumbnailMock() + BookItem(thumbnail = mock.bookThumbnail) + } +} + +@Preview +@Composable +fun BookItemDarkPreview() { + Bob[BitmapCache::class] = BitmapCache(LocalContext.current) + BibLibTheme(darkTheme = true) { + val mock = BookThumbnailMock() + BookItem(thumbnail = mock.bookThumbnail) + } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/pages/DetailPageComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/pages/DetailPage.kt similarity index 54% rename from app/src/main/java/com/pixelized/biblib/ui/composable/pages/DetailPageComposable.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/pages/DetailPage.kt index 7842237..683ed11 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/pages/DetailPageComposable.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/pages/DetailPage.kt @@ -5,10 +5,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Button -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text +import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Send @@ -19,6 +16,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource @@ -40,22 +38,25 @@ import com.pixelized.biblib.utils.mock.BookMock @Composable -fun DetailPageComposable( +fun DetailPage( booksViewModel: IBooksViewModel = viewModel(), bookId: Int ) { - Box( - Modifier.fillMaxWidth().fillMaxHeight() + Surface( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + elevation = 4.dp ) { val book by booksViewModel.getBookDetail(bookId).observeAsState() book?.let { - DetailPageComposable(book = it) + DetailPage(book = it) } } } @Composable -fun DetailPageComposable(book: BookUio) { +fun DetailPage(book: BookUio) { val typography = MaterialTheme.typography Column( @@ -81,32 +82,20 @@ fun DetailPageComposable(book: BookUio) { ) Row(modifier = Modifier.padding(bottom = 16.dp)) { Button( - modifier = Modifier - .weight(1f) - .padding(end = 4.dp), - onClick = { }) { - Icon(imageVector = Icons.Default.Download, contentDescription = "") - Spacer(modifier = Modifier.width(4.dp)) - Text(text = stringResource(id = R.string.action_epub)) - } + modifier = Modifier.padding(end = 4.dp), + imageVector = Icons.Default.Download, + text = stringResource(id = R.string.action_epub) + ) { } Button( - modifier = Modifier - .weight(1f) - .padding(horizontal = 4.dp), - onClick = { }) { - Icon(imageVector = Icons.Default.Download, contentDescription = "") - Spacer(modifier = Modifier.width(4.dp)) - Text(text = stringResource(id = R.string.action_mobi)) - } + modifier = Modifier.padding(horizontal = 4.dp), + imageVector = Icons.Default.Download, + text = stringResource(id = R.string.action_mobi), + ) { } Button( - modifier = Modifier - .weight(1f) - .padding(start = 4.dp), - onClick = { }) { - Icon(imageVector = Icons.Default.Send, contentDescription = "") - Spacer(modifier = Modifier.width(4.dp)) - Text(text = stringResource(id = R.string.action_send)) - } + modifier = Modifier.padding(start = 4.dp), + imageVector = Icons.Default.Send, + text = stringResource(id = R.string.action_send), + ) { } } Text( modifier = Modifier @@ -124,62 +113,29 @@ fun DetailPageComposable(book: BookUio) { text = book.author, ) Row(modifier = Modifier.padding(bottom = 8.dp)) { - Column( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - style = typography.body2, - fontWeight = FontWeight.Bold, - text = stringResource(id = R.string.detail_rating), - ) - Text( - style = typography.body1, - text = book.rating.toString(), - ) - } - Column( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - style = typography.body2, - fontWeight = FontWeight.Bold, - text = stringResource(id = R.string.detail_language), - ) - Text( - style = typography.body1, - text = book.language, - ) - } - Column( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - style = typography.body2, - fontWeight = FontWeight.Bold, - text = stringResource(id = R.string.detail_release), - ) - Text( - style = typography.body1, - text = book.date, - ) - } + TitleLabel( + title = stringResource(id = R.string.detail_rating), + label = book.rating.toString(), + ) + TitleLabel( + title = stringResource(id = R.string.detail_language), + label = book.language, + ) + TitleLabel( + title = stringResource(id = R.string.detail_release), + label = book.date, + ) + } + Row(modifier = Modifier.padding(bottom = 16.dp)) { + TitleLabel( + title = stringResource(id = R.string.detail_genre), + label = book.genre, + ) + TitleLabel( + title = stringResource(id = R.string.detail_series), + label = book.series ?: "-", + ) } - Text( - modifier = Modifier.align(Alignment.CenterHorizontally), - style = typography.body2, - fontWeight = FontWeight.Bold, - text = stringResource(id = R.string.detail_series), - ) - Text( - modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(bottom = 16.dp), - style = typography.body1, - text = book.series ?: "-", - ) Text( style = typography.body1, text = book.description, @@ -188,12 +144,51 @@ fun DetailPageComposable(book: BookUio) { } } +@Composable +private fun RowScope.TitleLabel( + title: String, + label: String, +) { + val typography = MaterialTheme.typography + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + style = typography.body2, + fontWeight = FontWeight.Bold, + text = title, + ) + Text( + style = typography.body1, + text = label, + ) + } +} + +@Composable +private fun RowScope.Button( + modifier: Modifier = Modifier, + imageVector: ImageVector, + text: String, + onClick: () -> Unit, +) { + Button( + modifier = modifier.weight(1f), + onClick = onClick + ) { + Icon(imageVector = imageVector, contentDescription = "") + Spacer(modifier = Modifier.width(4.dp)) + Text(text = text) + } +} + @Preview @Composable -fun DetailPageComposablePreview() { +fun DetailPageLightPreview() { Bob[BitmapCache::class] = BitmapCache(LocalContext.current) BibLibTheme { val mock = BookMock() - DetailPageComposable(mock.book) + DetailPage(mock.book) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/pages/HomePageComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/pages/HomePage.kt similarity index 79% rename from app/src/main/java/com/pixelized/biblib/ui/composable/pages/HomePageComposable.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/pages/HomePage.kt index 8966693..94ae6c3 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/pages/HomePageComposable.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/pages/HomePage.kt @@ -4,8 +4,6 @@ import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState @@ -15,7 +13,7 @@ import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items -import com.pixelized.biblib.ui.composable.items.BookThumbnailComposable +import com.pixelized.biblib.ui.composable.items.BookItem import com.pixelized.biblib.ui.data.BookThumbnailUio import com.pixelized.biblib.ui.viewmodel.book.IBooksViewModel import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel @@ -23,7 +21,7 @@ import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigab @Composable -fun HomePageComposable( +fun HomePage( navigationViewModel: INavigationViewModel, booksViewModel: IBooksViewModel, page: Page?, @@ -32,7 +30,7 @@ fun HomePageComposable( currentPage = page as? Page.Home ?: currentPage // https://issuetracker.google.com/issues/177245496 - val data: LazyPagingItems = when(currentPage) { + val data: LazyPagingItems = when (currentPage) { is Page.Home.New -> booksViewModel.books.collectAsLazyPagingItems() else -> booksViewModel.news.collectAsLazyPagingItems() } @@ -47,12 +45,7 @@ fun HomePageComposable( state = lazyListState, ) { items(data) { thumbnail -> - BookThumbnailComposable( - thumbnail = thumbnail!!, - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - ) { item -> + BookItem(thumbnail = thumbnail!!) { item -> navigationViewModel.navigateTo(Page.Detail(item.id)) } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/HomeScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/HomeScreen.kt new file mode 100644 index 0000000..dcd7ae8 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/HomeScreen.kt @@ -0,0 +1,115 @@ +package com.pixelized.biblib.ui.composable.screen + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.core.tween +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.material.Scaffold +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.* +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.viewmodel.compose.viewModel +import com.pixelized.biblib.ui.composable.common.BibLibDrawer +import com.pixelized.biblib.ui.composable.common.BibLibToolbar +import com.pixelized.biblib.ui.composable.pages.DetailPage +import com.pixelized.biblib.ui.composable.pages.HomePage +import com.pixelized.biblib.ui.theme.Animation +import com.pixelized.biblib.ui.theme.BibLibTheme +import com.pixelized.biblib.ui.viewmodel.book.BooksViewModel +import com.pixelized.biblib.ui.viewmodel.book.IBooksViewModel +import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel +import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Page +import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel +import kotlinx.coroutines.launch + + +@OptIn(ExperimentalAnimationApi::class) +@Composable +fun HomeScreen( + navigationViewModel: INavigationViewModel = viewModel(), + booksViewModel: IBooksViewModel = viewModel(), +) { + // navigation + val page by navigationViewModel.page.observeAsState() + // coroutine + val coroutineScope = rememberCoroutineScope() + // scaffold & toolbar + val canNavigateBack = page !is Page.Home + val scaffoldState = rememberScaffoldState() + + // TODO: + LaunchedEffect(key1 = "MainScreen", block = { + navigationViewModel.navigateTo(Page.Home.New, true) + }) + + Scaffold( + scaffoldState = scaffoldState, + topBar = { + BibLibToolbar( + canNavigateBack, + onBackPress = { + navigationViewModel.navigateBack() + }, + onDrawerPress = { + coroutineScope.launch { scaffoldState.drawerState.open() } + } + ) + }, + drawerContent = { + BibLibDrawer( + onNewClick = { + coroutineScope.launch { scaffoldState.drawerState.close() } + }, + onBookClick = { + coroutineScope.launch { scaffoldState.drawerState.close() } + }, + onSeriesClick = { + coroutineScope.launch { scaffoldState.drawerState.close() } + }, + onAuthorClick = { + coroutineScope.launch { scaffoldState.drawerState.close() } + }, + onTagClick = { + coroutineScope.launch { scaffoldState.drawerState.close() } + }, + ) + } + ) { + HomePage( + navigationViewModel = navigationViewModel, + booksViewModel = booksViewModel, + page = page, + ) + + AnimatedVisibility( + visible = page is Page.Detail, + initiallyVisible = false, + enter = slideInVertically( + animationSpec = tween(Animation.MEDIUM_DURATION), + initialOffsetY = { height -> height }, + ), + exit = slideOutVertically( + animationSpec = tween(Animation.MEDIUM_DURATION), + targetOffsetY = { height -> height }, + ), + ) { + // Small trick to display the detail page during animation exit. + var currentPage by remember { mutableStateOf(null) } + currentPage = page as? Page.Detail ?: currentPage + currentPage?.let { DetailPage(booksViewModel, it.bookId) } + } + } +} + +@Preview +@Composable +fun MainScreenComposablePreview() { + BibLibTheme { + HomeScreen( + INavigationViewModel.Mock(page = Page.Home.New), + IBooksViewModel.Mock() + ) + } +} \ 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/LoginScreen.kt similarity index 96% rename from app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreen.kt index 152ed26..f10b4ad 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/LoginScreen.kt @@ -38,10 +38,10 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.pixelized.biblib.R import com.pixelized.biblib.network.client.IBibLibClient.Companion.REGISTER_URL -import com.pixelized.biblib.ui.composable.items.dialog.CrossFadeOverlay -import com.pixelized.biblib.ui.composable.items.dialog.ErrorCard -import com.pixelized.biblib.ui.composable.items.dialog.LoadingCard -import com.pixelized.biblib.ui.composable.items.dialog.SuccessCard +import com.pixelized.biblib.ui.composable.dialog.CrossFadeOverlay +import com.pixelized.biblib.ui.composable.dialog.ErrorCard +import com.pixelized.biblib.ui.composable.dialog.LoadingCard +import com.pixelized.biblib.ui.composable.dialog.SuccessCard import com.pixelized.biblib.ui.theme.BibLibTheme import com.pixelized.biblib.ui.viewmodel.authentication.AuthenticationViewModel import com.pixelized.biblib.ui.viewmodel.authentication.IAuthenticationViewModel @@ -58,7 +58,7 @@ import kotlinx.coroutines.delay private const val LE_LOAD_BOOK = "LE_LOAD_BOOK" @Composable -fun LoginScreenComposable( +fun LoginScreen( navigationViewModel: INavigationViewModel = viewModel(), credentialViewModel: ICredentialViewModel = viewModel(), authenticationViewModel: IAuthenticationViewModel = viewModel(), @@ -76,13 +76,13 @@ fun LoginScreenComposable( authenticationViewModel, bookViewModel ) - LoginScreenContentComposable(credentialViewModel, authenticationViewModel) - LoginScreenDialogComposable(authenticationViewModel, bookViewModel) + Content(credentialViewModel, authenticationViewModel) + Dialogs(authenticationViewModel, bookViewModel) } } @Composable -private fun LoginScreenContentComposable( +private fun Content( credentialViewModel: ICredentialViewModel, authenticationViewModel: IAuthenticationViewModel, ) { @@ -103,7 +103,7 @@ private fun LoginScreenContentComposable( @OptIn(ExperimentalAnimationApi::class) @Composable -private fun LoginScreenDialogComposable( +private fun Dialogs( authenticationViewModel: IAuthenticationViewModel, booksViewModel: IBooksViewModel, ) { @@ -383,7 +383,7 @@ private fun CredentialRemember( @Composable fun LoginScreenComposablePreview() { BibLibTheme { - LoginScreenComposable( + LoginScreen( navigationViewModel = INavigationViewModel.Mock(), credentialViewModel = ICredentialViewModel.Mock(), authenticationViewModel = IAuthenticationViewModel.Mock(), 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 deleted file mode 100644 index 0bf7a59..0000000 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt +++ /dev/null @@ -1,285 +0,0 @@ -package com.pixelized.biblib.ui.composable.screen - -import androidx.compose.animation.* -import androidx.compose.animation.core.tween -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CutCornerShape -import androidx.compose.material.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material.icons.sharp.ArrowBack -import androidx.compose.material.icons.sharp.Menu -import androidx.compose.runtime.* -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.pixelized.biblib.BuildConfig -import com.pixelized.biblib.R -import com.pixelized.biblib.ui.composable.pages.DetailPageComposable -import com.pixelized.biblib.ui.composable.pages.HomePageComposable -import com.pixelized.biblib.ui.theme.Animation -import com.pixelized.biblib.ui.theme.BibLibTheme -import com.pixelized.biblib.ui.viewmodel.book.BooksViewModel -import com.pixelized.biblib.ui.viewmodel.book.IBooksViewModel -import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel -import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Page -import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel -import kotlinx.coroutines.launch -import java.util.* - - -@OptIn(ExperimentalAnimationApi::class) -@Composable -fun MainScreenComposable( - navigationViewModel: INavigationViewModel = viewModel(), - booksViewModel: IBooksViewModel = viewModel(), -) { - // navigation - val page by navigationViewModel.page.observeAsState() - // coroutine - val coroutineScope = rememberCoroutineScope() - // scaffold & toolbar - val canNavigateBack = page !is Page.Home - val scaffoldState = rememberScaffoldState() - - // TODO: - LaunchedEffect(key1 = "MainScreen", block = { - navigationViewModel.navigateTo(Page.Home.New, true) - }) - - Scaffold( - scaffoldState = scaffoldState, - topBar = { - ToolbarComposable( - canNavigateBack, - onBackPress = { - navigationViewModel.navigateBack() - }, - onDrawerPress = { - coroutineScope.launch { scaffoldState.drawerState.open() } - } - ) - }, - drawerContent = { - DrawerContentComposable( - onNewClick = { - coroutineScope.launch { scaffoldState.drawerState.close() } - }, - onBookClick = { - coroutineScope.launch { scaffoldState.drawerState.close() } - }, - onSeriesClick = { - coroutineScope.launch { scaffoldState.drawerState.close() } - }, - onAuthorClick = { - coroutineScope.launch { scaffoldState.drawerState.close() } - }, - onTagClick = { - coroutineScope.launch { scaffoldState.drawerState.close() } - }, - ) - } - ) { - HomePageComposable( - navigationViewModel = navigationViewModel, - booksViewModel = booksViewModel, - page = page, - ) - - AnimatedVisibility( - visible = page is Page.Detail, - initiallyVisible = false, - enter = slideInVertically( - animationSpec = tween(Animation.MEDIUM_DURATION), - initialOffsetY = { height -> height }, - ), - exit = slideOutVertically( - animationSpec = tween(Animation.MEDIUM_DURATION), - targetOffsetY = { height -> height }, - ), - ) { - // Small trick to display the detail page during animation exit. - var currentPage by remember { mutableStateOf(null) } - currentPage = page as? Page.Detail ?: currentPage - currentPage?.let { DetailPageComposable(booksViewModel, it.bookId) } - } - } -} - -@Composable -fun ToolbarComposable( - canNavigateBack: Boolean, - onBackPress: () -> Unit, - onDrawerPress: () -> Unit, -) { - TopAppBar( - title = { - Text(stringResource(id = R.string.app_name)) - }, - navigationIcon = { - NavigationIcon( - canNavigateBack, - onBackPress, - onDrawerPress, - ) - } - ) -} - -@Composable -fun NavigationIcon( - canNavigateBack: Boolean, - onBackPress: () -> Unit, - onDrawerPress: () -> Unit, -) { - Crossfade(targetState = canNavigateBack) { - if (it) { - IconButton(onClick = onBackPress) { - Icon( - imageVector = Icons.Sharp.ArrowBack, - contentDescription = "back" - ) - } - } else { - IconButton(onClick = onDrawerPress) { - Icon( - imageVector = Icons.Sharp.Menu, - contentDescription = "drawer" - ) - } - } - } -} - -@Composable -fun DrawerContentComposable( - onNewClick: () -> Unit, - onBookClick: () -> Unit, - onSeriesClick: () -> Unit, - onAuthorClick: () -> Unit, - onTagClick: () -> Unit, -) { - val typography = MaterialTheme.typography - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - ) { - Card( - shape = CutCornerShape(bottomEnd = 16.dp), - elevation = 8.dp - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - Text( - style = typography.body1, - text = "R. Daneel Olivaw" - ) - Text( - style = typography.caption, - text = "r.daneel.olivaw@biblib.com" - ) - } - } - Spacer(modifier = Modifier.height(8.dp)) - DrawerLink( - text = "Nouveautés", - imageVector = Icons.Default.NewReleases, - onClick = onNewClick, - ) - DrawerLink( - text = "Livres", - imageVector = Icons.Default.AutoStories, - onClick = onBookClick, - ) - DrawerLink( - text = "Séries", - imageVector = Icons.Default.AutoAwesomeMotion, - onClick = onSeriesClick, - ) - DrawerLink( - text = "Auteurs", - imageVector = Icons.Default.SupervisorAccount, - onClick = onAuthorClick, - ) - DrawerLink( - text = "Étiquettes", - imageVector = Icons.Default.LocalOffer, - onClick = onTagClick, - ) - Spacer(modifier = Modifier.weight(1f)) - Text( - modifier = Modifier - .align(Alignment.End) - .padding(16.dp), - style = typography.caption, - text = stringResource( - R.string.app_version, - BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()), - BuildConfig.VERSION_NAME, - BuildConfig.VERSION_CODE - ) - ) - } -} - -@Composable -fun DrawerLink(text: String, imageVector: ImageVector, onClick: () -> Unit) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .height(52.dp) - .clickable { onClick() }) { - Icon( - modifier = Modifier.padding(horizontal = 16.dp), - imageVector = imageVector, - contentDescription = "" - ) - Text( - text = text, - ) - Spacer(modifier = Modifier.weight(1f)) - Icon( - modifier = Modifier.padding(end = 8.dp), - imageVector = Icons.Default.NavigateNext, - contentDescription = "" - ) - } -} - -@Preview -@Composable -fun ToolbarComposableDarkPreview() { - BibLibTheme(darkTheme = false) { - ToolbarComposable(false, {}, {}) - } -} - -@Preview -@Composable -fun ToolbarComposableLightPreview() { - BibLibTheme(darkTheme = true) { - ToolbarComposable(true, {}, {}) - } -} - -@Preview -@Composable -fun MainScreenComposablePreview() { - BibLibTheme { - MainScreenComposable( - INavigationViewModel.Mock(page = Page.Home.New), - IBooksViewModel.Mock() - ) - } -} \ No newline at end of file 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/SplashScreen.kt similarity index 55% rename from app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt rename to app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreen.kt index 86cedee..838cf48 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/SplashScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.core.tween import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState @@ -16,8 +17,8 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.pixelized.biblib.BuildConfig import com.pixelized.biblib.R -import com.pixelized.biblib.ui.composable.items.dialog.CrossFadeOverlay -import com.pixelized.biblib.ui.composable.items.dialog.ErrorCard +import com.pixelized.biblib.ui.composable.dialog.CrossFadeOverlay +import com.pixelized.biblib.ui.composable.dialog.ErrorCard import com.pixelized.biblib.ui.theme.Animation import com.pixelized.biblib.ui.theme.BibLibTheme import com.pixelized.biblib.ui.viewmodel.authentication.AuthenticationViewModel @@ -30,29 +31,14 @@ import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel import kotlinx.coroutines.delay import java.util.* -private const val LAUNCH_EFFECT_AUTHENTICATION = "LAUNCH_EFFECT_AUTHENTICATION" +private const val LAUNCH_EFFECT_ENTER = "LAUNCH_EFFECT_ENTER" +private const val LAUNCH_EFFECT_EXIT = "LAUNCH_EFFECT_EXIT" private const val LAUNCH_EFFECT_BOOK = "LAUNCH_EFFECT_BOOK" -private const val LAUNCH_EFFECT_NAVIGATION = "LAUNCH_EFFECT_NAVIGATION" -@Preview -@Composable -fun SplashScreenComposablePreview() { - BibLibTheme { - val authenticationViewModel = IAuthenticationViewModel.Mock() - val bookViewModel = IBooksViewModel.Mock() - val navigation = INavigationViewModel.Mock() - SplashScreenComposable( - authenticationViewModel = authenticationViewModel, - bookViewModel = bookViewModel, - navigationViewModel = navigation, - initiallyVisible = true - ) - } -} @OptIn(ExperimentalAnimationApi::class) @Composable -fun SplashScreenComposable( +fun SplashScreen( authenticationViewModel: IAuthenticationViewModel = viewModel(), bookViewModel: IBooksViewModel = viewModel(), navigationViewModel: INavigationViewModel = viewModel(), @@ -60,23 +46,24 @@ fun SplashScreenComposable( ) { val authenticationState: IAuthenticationViewModel.State? by authenticationViewModel.state.observeAsState() val bookState by bookViewModel.state.observeAsState() - val contentVisible = remember { mutableStateOf(false) } + + val contentVisibility = remember { mutableStateOf(false) } // Content - ContentComposable( - visible = contentVisible.value, + Content( + visible = contentVisibility.value, initiallyVisible = initiallyVisible ) // Dialog - AuthenticationError(state = authenticationState as? IAuthenticationViewModel.State.Error) - BookError(state = bookState as? IBooksViewModel.State.Error) + AuthenticationError( + state = authenticationState as? IAuthenticationViewModel.State.Error + ) + BookError( + state = bookState as? IBooksViewModel.State.Error + ) - LaunchedEffect(LAUNCH_EFFECT_AUTHENTICATION) { - delay(Animation.SHORT_DURATION.toLong()) - contentVisible.value = true - delay(Animation.LONG_DURATION.toLong()) - delay(Animation.SHORT_DURATION.toLong()) + HandleEnterAnimation(contentVisibility) { authenticationViewModel.autoLogin() } @@ -86,16 +73,12 @@ fun SplashScreenComposable( bookViewModel.updateBooks() } (bookState as? IBooksViewModel.State.Finished)?.let { - LaunchedEffect(LAUNCH_EFFECT_NAVIGATION) { - contentVisible.value = false - delay(Animation.LONG_DURATION.toLong()) + HandleExitAnimation(contentVisibility) { navigationViewModel.navigateTo(Screen.MainScreen) } } } else { - LaunchedEffect(LAUNCH_EFFECT_NAVIGATION) { - contentVisible.value = false - delay(Animation.LONG_DURATION.toLong()) + HandleExitAnimation(contentVisibility) { navigationViewModel.navigateTo(Screen.LoginScreen) } } @@ -104,81 +87,83 @@ fun SplashScreenComposable( @OptIn(ExperimentalAnimationApi::class) @Composable -private fun ContentComposable( +private fun Content( duration: Int = Animation.LONG_DURATION, visible: Boolean = false, initiallyVisible: Boolean = false, ) { val typography = MaterialTheme.typography - Box( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - .padding(16.dp), - ) { - Column( + Surface { + Box( modifier = Modifier - .width(240.dp) - .align(Alignment.Center) + .fillMaxHeight() + .fillMaxWidth() + .padding(16.dp), ) { - AnimatedVisibility( - visible = visible, - initiallyVisible = initiallyVisible, - enter = fadeIn(animationSpec = tween(duration)) - + slideInVertically( - initialOffsetY = { height -> -height }, - animationSpec = tween(duration) - ), - exit = fadeOut(animationSpec = tween(duration)) - + slideOutVertically( - targetOffsetY = { height -> -height }, - animationSpec = tween(duration) - ), + Column( + modifier = Modifier + .width(240.dp) + .align(Alignment.Center) ) { - Text( - style = typography.h4, - text = stringResource(id = R.string.splash_welcome) - ) + AnimatedVisibility( + visible = visible, + initiallyVisible = initiallyVisible, + enter = fadeIn(animationSpec = tween(duration)) + + slideInVertically( + initialOffsetY = { height -> -height }, + animationSpec = tween(duration) + ), + exit = fadeOut(animationSpec = tween(duration)) + + slideOutVertically( + targetOffsetY = { height -> -height }, + animationSpec = tween(duration) + ), + ) { + Text( + style = typography.h4, + text = stringResource(id = R.string.splash_welcome) + ) + } + AnimatedVisibility( + modifier = Modifier.align(Alignment.End), + visible = visible, + initiallyVisible = initiallyVisible, + enter = fadeIn(animationSpec = tween(duration)) + + slideInVertically( + initialOffsetY = { height -> height }, + animationSpec = tween(duration) + ), + exit = fadeOut(animationSpec = tween(duration)) + + slideOutVertically( + targetOffsetY = { height -> height }, + animationSpec = tween(duration) + ), + ) { + Text( + style = typography.h4, + text = stringResource(id = R.string.app_name) + ) + } } - AnimatedVisibility( - modifier = Modifier.align(Alignment.End), - visible = visible, - initiallyVisible = initiallyVisible, - enter = fadeIn(animationSpec = tween(duration)) - + slideInVertically( - initialOffsetY = { height -> height }, - animationSpec = tween(duration) - ), - exit = fadeOut(animationSpec = tween(duration)) - + slideOutVertically( - targetOffsetY = { height -> height }, - animationSpec = tween(duration) - ), - ) { - Text( - style = typography.h4, - text = stringResource(id = R.string.app_name) - ) - } - } - AnimatedVisibility( - modifier = Modifier.align(Alignment.BottomEnd), - visible = visible, - initiallyVisible = initiallyVisible, - enter = fadeIn(animationSpec = tween(duration)), - exit = fadeOut(animationSpec = tween(duration)), - ) { - Text( - style = typography.caption, - text = stringResource( - R.string.app_version, - BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()), - BuildConfig.VERSION_NAME, - BuildConfig.VERSION_CODE + AnimatedVisibility( + modifier = Modifier.align(Alignment.BottomEnd), + visible = visible, + initiallyVisible = initiallyVisible, + enter = fadeIn(animationSpec = tween(duration)), + exit = fadeOut(animationSpec = tween(duration)), + ) { + Text( + style = typography.caption, + text = stringResource( + R.string.app_version, + BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()), + BuildConfig.VERSION_NAME, + BuildConfig.VERSION_CODE + ) ) - ) + } } } } @@ -225,4 +210,39 @@ private fun BookError(state: IBooksViewModel.State.Error?) { ) } } +} + +@Composable +fun HandleEnterAnimation(contentVisibility: MutableState, then: suspend () -> Unit) { + LaunchedEffect(LAUNCH_EFFECT_ENTER) { + delay(Animation.SHORT_DURATION.toLong()) + contentVisibility.value = true + delay(Animation.LONG_DURATION.toLong() + Animation.SHORT_DURATION.toLong()) + then() + } +} + +@Composable +fun HandleExitAnimation(contentVisibility: MutableState, then: suspend () -> Unit) { + LaunchedEffect(LAUNCH_EFFECT_EXIT) { + contentVisibility.value = false + delay(Animation.LONG_DURATION.toLong()) + then() + } +} + +@Preview +@Composable +fun SplashScreenPreview() { + BibLibTheme(darkTheme = true) { + val authenticationViewModel = IAuthenticationViewModel.Mock() + val bookViewModel = IBooksViewModel.Mock() + val navigation = INavigationViewModel.Mock() + SplashScreen( + authenticationViewModel = authenticationViewModel, + bookViewModel = bookViewModel, + navigationViewModel = navigation, + initiallyVisible = true + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/theme/Animation.kt b/app/src/main/java/com/pixelized/biblib/ui/theme/Animation.kt index a0a1b65..6211db9 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/theme/Animation.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/theme/Animation.kt @@ -2,7 +2,7 @@ package com.pixelized.biblib.ui.theme object Animation { - const val SHORT_DURATION = 300 - const val MEDIUM_DURATION = 500 - const val LONG_DURATION = 1000 + const val SHORT_DURATION = 250 + const val MEDIUM_DURATION = SHORT_DURATION * 2 + const val LONG_DURATION = MEDIUM_DURATION * 2 } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d48518a..d804f20 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -35,5 +35,6 @@ Rating Language Release + Genre Series \ No newline at end of file