From 8500add09f475093e4e861f42508496e0f1fd68c Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Mon, 10 Oct 2022 02:52:56 +0700 Subject: [PATCH] EmptyScreen: Compose-ify and apply content padding (#8177) * Apply content padding to empty screen except the empty screens in browse * compose-ify EmptyScreen * center face when action show * fix padding * apply content padding to browse tabs * fix duplicate bottom insets --- .../presentation/browse/BrowseSourceScreen.kt | 29 ++- .../browse/ExtensionDetailsScreen.kt | 5 +- .../browse/ExtensionFilterScreen.kt | 6 +- .../presentation/browse/ExtensionsScreen.kt | 15 +- .../presentation/browse/MigrateMangaScreen.kt | 6 +- .../browse/MigrateSourceScreen.kt | 15 +- .../browse/SourcesFilterScreen.kt | 6 +- .../presentation/browse/SourcesScreen.kt | 15 +- .../presentation/category/CategoryScreen.kt | 7 +- .../presentation/components/EmptyScreen.kt | 220 ++++++++++++++++-- .../presentation/components/TabbedScreen.kt | 10 +- .../presentation/history/HistoryScreen.kt | 11 +- .../history/components/HistoryContent.kt | 4 +- .../presentation/library/LibraryScreen.kt | 28 ++- .../library/components/LazyLibraryGrid.kt | 3 +- .../library/components/LibraryContent.kt | 18 +- .../library/components/LibraryList.kt | 3 +- .../eu/kanade/presentation/more/MoreScreen.kt | 4 +- .../presentation/updates/UpdatesScreen.kt | 72 +++--- .../ui/browse/extension/ExtensionsTab.kt | 3 +- .../migration/sources/MigrateSourcesTab.kt | 3 +- .../tachiyomi/ui/browse/source/SourcesTab.kt | 3 +- .../ui/download/DownloadController.kt | 5 +- .../eu/kanade/tachiyomi/widget/EmptyView.kt | 87 ++----- .../widget/TachiyomiBottomNavigationView.kt | 28 ++- app/src/main/res/layout/common_view_empty.xml | 36 --- app/src/main/res/values/styles.xml | 10 - 27 files changed, 413 insertions(+), 239 deletions(-) delete mode 100644 app/src/main/res/layout/common_view_empty.xml diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt index 63e9f69f5..268ba482a 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt @@ -13,6 +13,9 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.HelpOutline +import androidx.compose.material.icons.filled.Public +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.outlined.Favorite import androidx.compose.material.icons.outlined.FilterList import androidx.compose.material.icons.outlined.NewReleases @@ -49,6 +52,7 @@ import eu.kanade.presentation.browse.components.BrowseSourceToolbar import eu.kanade.presentation.components.AppStateBanners import eu.kanade.presentation.components.Divider import eu.kanade.presentation.components.EmptyScreen +import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.presentation.components.ExtendedFloatingActionButton import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.Scaffold @@ -56,7 +60,6 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter import eu.kanade.tachiyomi.ui.more.MoreController -import eu.kanade.tachiyomi.widget.EmptyView @Composable fun BrowseSourceScreen( @@ -248,13 +251,29 @@ fun BrowseSourceContent( message = getErrorMessage(errorState), actions = if (state.source is LocalSource) { listOf( - EmptyView.Action(R.string.local_source_help_guide, R.drawable.ic_help_24dp) { onLocalSourceHelpClick() }, + EmptyScreenAction( + stringResId = R.string.local_source_help_guide, + icon = Icons.Default.HelpOutline, + onClick = onLocalSourceHelpClick, + ), ) } else { listOf( - EmptyView.Action(R.string.action_retry, R.drawable.ic_refresh_24dp) { mangaList.refresh() }, - EmptyView.Action(R.string.action_open_in_web_view, R.drawable.ic_public_24dp) { onWebViewClick() }, - EmptyView.Action(R.string.label_help, R.drawable.ic_help_24dp) { onHelpClick() }, + EmptyScreenAction( + stringResId = R.string.action_retry, + icon = Icons.Default.Refresh, + onClick = mangaList::refresh, + ), + EmptyScreenAction( + stringResId = R.string.action_open_in_web_view, + icon = Icons.Default.Public, + onClick = onWebViewClick, + ), + EmptyScreenAction( + stringResId = R.string.label_help, + icon = Icons.Default.HelpOutline, + onClick = onHelpClick, + ), ) }, ) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index ca063b2e0..f8b20483e 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -133,7 +133,10 @@ private fun ExtensionDetails( ) { when { presenter.isLoading -> LoadingScreen() - presenter.extension == null -> EmptyScreen(textResource = R.string.empty_screen) + presenter.extension == null -> EmptyScreen( + textResource = R.string.empty_screen, + modifier = Modifier.padding(contentPadding), + ) else -> { val context = LocalContext.current val extension = presenter.extension diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt index c913dc3fe..7b965a802 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items import androidx.compose.material3.Switch import androidx.compose.runtime.Composable @@ -37,7 +38,10 @@ fun ExtensionFilterScreen( ) { contentPadding -> when { presenter.isLoading -> LoadingScreen() - presenter.isEmpty -> EmptyScreen(textResource = R.string.empty_screen) + presenter.isEmpty -> EmptyScreen( + textResource = R.string.empty_screen, + modifier = Modifier.padding(contentPadding), + ) else -> { SourceFilterContent( contentPadding = contentPadding, diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index 56cc104ac..830e13801 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -5,11 +5,9 @@ import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues -import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.items @@ -55,11 +53,11 @@ import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsPresenter import eu.kanade.tachiyomi.util.system.LocaleHelper -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding @Composable fun ExtensionScreen( presenter: ExtensionsPresenter, + contentPadding: PaddingValues, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, onInstallExtension: (Extension.Available) -> Unit, @@ -77,10 +75,14 @@ fun ExtensionScreen( ) { when { presenter.isLoading -> LoadingScreen() - presenter.isEmpty -> EmptyScreen(R.string.empty_screen) + presenter.isEmpty -> EmptyScreen( + textResource = R.string.empty_screen, + modifier = Modifier.padding(contentPadding), + ) else -> { ExtensionContent( state = presenter, + contentPadding = contentPadding, onLongClickItem = onLongClickItem, onClickItemCancel = onClickItemCancel, onInstallExtension = onInstallExtension, @@ -98,6 +100,7 @@ fun ExtensionScreen( @Composable private fun ExtensionContent( state: ExtensionsState, + contentPadding: PaddingValues, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, onInstallExtension: (Extension.Available) -> Unit, @@ -110,7 +113,7 @@ private fun ExtensionContent( var trustState by remember { mutableStateOf(null) } FastScrollLazyColumn( - contentPadding = bottomNavPadding + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues, + contentPadding = contentPadding + topPaddingValues, ) { items( items = state.items, diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt index d4025571f..736a11f45 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -39,7 +40,10 @@ fun MigrateMangaScreen( ) { contentPadding -> when { presenter.isLoading -> LoadingScreen() - presenter.isEmpty -> EmptyScreen(textResource = R.string.empty_screen) + presenter.isEmpty -> EmptyScreen( + textResource = R.string.empty_screen, + modifier = Modifier.padding(contentPadding), + ) else -> { MigrateMangaContent( contentPadding = contentPadding, diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt index b204784af..cf8a61598 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt @@ -3,10 +3,8 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues -import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons @@ -42,20 +40,24 @@ import eu.kanade.presentation.util.topPaddingValues import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesPresenter import eu.kanade.tachiyomi.util.system.copyToClipboard -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding @Composable fun MigrateSourceScreen( presenter: MigrationSourcesPresenter, + contentPadding: PaddingValues, onClickItem: (Source) -> Unit, ) { val context = LocalContext.current when { presenter.isLoading -> LoadingScreen() - presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_library) + presenter.isEmpty -> EmptyScreen( + textResource = R.string.information_empty_library, + modifier = Modifier.padding(contentPadding), + ) else -> MigrateSourceList( list = presenter.items, + contentPadding = contentPadding, onClickItem = onClickItem, onLongClickItem = { source -> val sourceId = source.id.toString() @@ -72,6 +74,7 @@ fun MigrateSourceScreen( @Composable private fun MigrateSourceList( list: List>, + contentPadding: PaddingValues, onClickItem: (Source) -> Unit, onLongClickItem: (Source) -> Unit, sortingMode: SetMigrateSorting.Mode, @@ -80,7 +83,7 @@ private fun MigrateSourceList( onToggleSortingDirection: () -> Unit, ) { ScrollbarLazyColumn( - contentPadding = bottomNavPadding + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues, + contentPadding = contentPadding + topPaddingValues, ) { stickyHeader(key = "header") { Row( diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt index 9e7bf4722..70da795af 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items import androidx.compose.material3.Checkbox import androidx.compose.material3.Switch @@ -43,7 +44,10 @@ fun SourcesFilterScreen( ) { contentPadding -> when { presenter.isLoading -> LoadingScreen() - presenter.isEmpty -> EmptyScreen(textResource = R.string.source_filter_empty_screen) + presenter.isEmpty -> EmptyScreen( + textResource = R.string.source_filter_empty_screen, + modifier = Modifier.padding(contentPadding), + ) else -> { SourcesFilterContent( contentPadding = contentPadding, diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt index 7d24b6bcf..74da046ec 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt @@ -2,10 +2,8 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons @@ -40,12 +38,12 @@ import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.browse.source.SourcesPresenter import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.toast -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding import kotlinx.coroutines.flow.collectLatest @Composable fun SourcesScreen( presenter: SourcesPresenter, + contentPadding: PaddingValues, onClickItem: (Source, String) -> Unit, onClickDisable: (Source) -> Unit, onClickPin: (Source) -> Unit, @@ -53,10 +51,14 @@ fun SourcesScreen( val context = LocalContext.current when { presenter.isLoading -> LoadingScreen() - presenter.isEmpty -> EmptyScreen(R.string.source_empty_screen) + presenter.isEmpty -> EmptyScreen( + textResource = R.string.source_empty_screen, + modifier = Modifier.padding(contentPadding), + ) else -> { SourceList( state = presenter, + contentPadding = contentPadding, onClickItem = onClickItem, onClickDisable = onClickDisable, onClickPin = onClickPin, @@ -77,12 +79,13 @@ fun SourcesScreen( @Composable private fun SourceList( state: SourcesState, + contentPadding: PaddingValues, onClickItem: (Source, String) -> Unit, onClickDisable: (Source) -> Unit, onClickPin: (Source) -> Unit, ) { ScrollbarLazyColumn( - contentPadding = bottomNavPadding + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues, + contentPadding = contentPadding + topPaddingValues, ) { items( items = state.items, diff --git a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt index 0e161e793..d189e3430 100644 --- a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt @@ -1,9 +1,11 @@ package eu.kanade.presentation.category import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import eu.kanade.presentation.category.components.CategoryContent @@ -48,7 +50,10 @@ fun CategoryScreen( val context = LocalContext.current when { presenter.isLoading -> LoadingScreen() - presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_category) + presenter.isEmpty -> EmptyScreen( + textResource = R.string.information_empty_category, + modifier = Modifier.padding(paddingValues), + ) else -> { CategoryContent( state = presenter, diff --git a/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt b/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt index c653bd90c..7b7f889c5 100644 --- a/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt @@ -1,23 +1,49 @@ package eu.kanade.presentation.components -import android.view.ViewGroup +import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.annotation.StringRes -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.paddingFromBaseline +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.HelpOutline +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.layoutId import androidx.compose.ui.res.stringResource -import androidx.compose.ui.viewinterop.AndroidView -import eu.kanade.tachiyomi.widget.EmptyView +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.theme.TachiyomiTheme +import eu.kanade.presentation.util.secondaryItemAlpha +import eu.kanade.tachiyomi.R +import kotlin.random.Random @Composable fun EmptyScreen( @StringRes textResource: Int, - actions: List? = null, + modifier: Modifier = Modifier, + actions: List? = null, ) { EmptyScreen( message = stringResource(textResource), + modifier = modifier, actions = actions, ) } @@ -25,24 +51,174 @@ fun EmptyScreen( @Composable fun EmptyScreen( message: String, - actions: List? = null, + modifier: Modifier = Modifier, + actions: List? = null, ) { - Box( - modifier = Modifier - .fillMaxSize(), - ) { - AndroidView( - factory = { context -> - EmptyView(context).apply { - layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - ) - show(message, actions) + val face = remember { getRandomErrorFace() } + Layout( + content = { + Column( + modifier = Modifier + .layoutId("face") + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = face, + modifier = Modifier.secondaryItemAlpha(), + fontFamily = FontFamily.Monospace, + style = MaterialTheme.typography.displayMedium, + ) + + Text( + text = message, + modifier = Modifier.paddingFromBaseline(top = 24.dp), + style = MaterialTheme.typography.bodyMedium, + ) + } + if (!actions.isNullOrEmpty()) { + Row( + modifier = Modifier + .layoutId("actions") + .padding( + top = 24.dp, + start = horizontalPadding, + end = horizontalPadding, + ), + horizontalArrangement = Arrangement.spacedBy(space = 8.dp), + ) { + actions.forEach { + ActionButton( + modifier = Modifier.weight(1f), + title = stringResource(id = it.stringResId), + icon = it.icon, + onClick = it.onClick, + ) + } } - }, - modifier = Modifier - .align(Alignment.Center), - ) + } + }, + modifier = modifier.fillMaxSize(), + ) { measurables, constraints -> + val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0) + val facePlaceable = measurables.first { it.layoutId == "face" } + .measure(looseConstraints) + val actionsPlaceable = measurables.firstOrNull { it.layoutId == "actions" } + ?.measure(looseConstraints) + + layout(constraints.maxWidth, constraints.maxHeight) { + val faceY = (constraints.maxHeight - facePlaceable.height) / 2 + facePlaceable.placeRelative( + x = (constraints.maxWidth - facePlaceable.width) / 2, + y = faceY, + ) + + actionsPlaceable?.placeRelative( + x = (constraints.maxWidth - actionsPlaceable.width) / 2, + y = faceY + facePlaceable.height, + ) + } } } + +@Composable +private fun ActionButton( + modifier: Modifier = Modifier, + title: String, + icon: ImageVector, + onClick: () -> Unit, +) { + TextButton( + modifier = modifier, + onClick = onClick, + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + imageVector = icon, + contentDescription = null, + ) + Spacer(Modifier.height(4.dp)) + Text( + text = title, + textAlign = TextAlign.Center, + ) + } + } +} + +@Preview( + name = "Light", + widthDp = 400, + heightDp = 400, +) +@Preview( + name = "Dark", + widthDp = 400, + heightDp = 400, + uiMode = UI_MODE_NIGHT_YES, +) +@Composable +private fun NoActionPreview() { + TachiyomiTheme { + Surface { + EmptyScreen( + textResource = R.string.empty_screen, + ) + } + } +} + +@Preview( + name = "Light", + widthDp = 400, + heightDp = 400, +) +@Preview( + name = "Dark", + widthDp = 400, + heightDp = 400, + uiMode = UI_MODE_NIGHT_YES, +) +@Composable +private fun WithActionPreview() { + TachiyomiTheme { + Surface { + EmptyScreen( + textResource = R.string.empty_screen, + actions = listOf( + EmptyScreenAction( + stringResId = R.string.action_retry, + icon = Icons.Default.Refresh, + onClick = {}, + ), + EmptyScreenAction( + stringResId = R.string.getting_started_guide, + icon = Icons.Default.HelpOutline, + onClick = {}, + ), + ), + ) + } + } +} + +data class EmptyScreenAction( + @StringRes val stringResId: Int, + val icon: ImageVector, + val onClick: () -> Unit, +) + +private val horizontalPadding = 24.dp + +private val ERROR_FACES = listOf( + "(・o・;)", + "Σ(ಠ_ಠ)", + "ಥ_ಥ", + "(˘・_・˘)", + "(; ̄Д ̄)", + "(・Д・。", +) + +private fun getRandomErrorFace(): String { + return ERROR_FACES[Random.nextInt(ERROR_FACES.size)] +} diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt index 1cc113728..4f747c7f2 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.components import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.fillMaxSize @@ -17,6 +18,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.rememberPagerState +import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView import kotlinx.coroutines.launch @Composable @@ -95,7 +97,11 @@ fun TabbedScreen( state = state, verticalAlignment = Alignment.Top, ) { page -> - tabs[page].content() + tabs[page].content( + TachiyomiBottomNavigationView.withBottomNavPadding( + PaddingValues(bottom = contentPadding.calculateBottomPadding()), + ), + ) } } } @@ -105,5 +111,5 @@ data class TabContent( @StringRes val titleRes: Int, val badgeNumber: Int? = null, val actions: List = emptyList(), - val content: @Composable () -> Unit, + val content: @Composable (contentPadding: PaddingValues) -> Unit, ) diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt index e56876b09..2e72570d9 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt @@ -1,9 +1,11 @@ package eu.kanade.presentation.history +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import eu.kanade.domain.history.model.HistoryWithRelations import eu.kanade.presentation.components.EmptyScreen @@ -19,6 +21,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter.Dialog import eu.kanade.tachiyomi.util.system.toast +import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView import kotlinx.coroutines.flow.collectLatest import java.util.Date @@ -41,15 +44,19 @@ fun HistoryScreen( }, ) { contentPadding -> val items by presenter.getHistory().collectAsState(initial = null) + val contentPaddingWithNavBar = TachiyomiBottomNavigationView.withBottomNavPadding(contentPadding) items.let { if (it == null) { LoadingScreen() } else if (it.isEmpty()) { - EmptyScreen(textResource = R.string.information_no_recent_manga) + EmptyScreen( + textResource = R.string.information_no_recent_manga, + modifier = Modifier.padding(contentPaddingWithNavBar), + ) } else { HistoryContent( history = it, - contentPadding = contentPadding, + contentPadding = contentPaddingWithNavBar, onClickCover = onClickCover, onClickResume = onClickResume, onClickDelete = { item -> presenter.dialog = Dialog.Delete(item) }, diff --git a/app/src/main/java/eu/kanade/presentation/history/components/HistoryContent.kt b/app/src/main/java/eu/kanade/presentation/history/components/HistoryContent.kt index ffd6c993c..b1e40b18a 100644 --- a/app/src/main/java/eu/kanade/presentation/history/components/HistoryContent.kt +++ b/app/src/main/java/eu/kanade/presentation/history/components/HistoryContent.kt @@ -11,8 +11,6 @@ import eu.kanade.presentation.components.RelativeDateHeader import eu.kanade.presentation.components.ScrollbarLazyColumn import eu.kanade.presentation.history.HistoryUiModel import eu.kanade.presentation.util.plus -import eu.kanade.presentation.util.topPaddingValues -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.text.DateFormat @@ -30,7 +28,7 @@ fun HistoryContent( val dateFormat: DateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } ScrollbarLazyColumn( - contentPadding = contentPadding + bottomNavPadding + topPaddingValues, + contentPadding = contentPadding, ) { items( items = history, diff --git a/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt index 23f350735..e6e645a2f 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt @@ -1,17 +1,26 @@ package eu.kanade.presentation.library import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.HelpOutline import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler import eu.kanade.domain.category.model.Category import eu.kanade.domain.library.model.display +import eu.kanade.presentation.components.EmptyScreen +import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.presentation.components.LibraryBottomActionMenu import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.library.components.LibraryContent import eu.kanade.presentation.library.components.LibraryToolbar +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.library.LibraryPresenter +import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView @Composable fun LibraryScreen( @@ -60,9 +69,26 @@ fun LibraryScreen( ) }, ) { paddingValues -> + val contentPadding = TachiyomiBottomNavigationView.withBottomNavPadding(paddingValues) + if (presenter.searchQuery.isNullOrEmpty() && presenter.isLibraryEmpty) { + val handler = LocalUriHandler.current + EmptyScreen( + textResource = R.string.information_empty_library, + modifier = Modifier.padding(contentPadding), + actions = listOf( + EmptyScreenAction( + stringResId = R.string.getting_started_guide, + icon = Icons.Default.HelpOutline, + onClick = { handler.openUri("https://tachiyomi.org/help/guides/getting-started") }, + ), + ), + ) + return@Scaffold + } + LibraryContent( state = presenter, - contentPadding = paddingValues, + contentPadding = contentPadding, currentPage = { presenter.activeCategory }, isLibraryEmpty = presenter.isLibraryEmpty, showPageTabs = presenter.tabVisibility, diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LazyLibraryGrid.kt b/app/src/main/java/eu/kanade/presentation/library/components/LazyLibraryGrid.kt index d2b8419ce..a2b9a5e50 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LazyLibraryGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LazyLibraryGrid.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.zIndex import eu.kanade.presentation.components.FastScrollLazyVerticalGrid import eu.kanade.presentation.util.plus import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding @Composable fun LazyLibraryGrid( @@ -27,7 +26,7 @@ fun LazyLibraryGrid( FastScrollLazyVerticalGrid( columns = if (columns == 0) GridCells.Adaptive(128.dp) else GridCells.Fixed(columns), modifier = modifier, - contentPadding = contentPadding + bottomNavPadding + PaddingValues(12.dp), + contentPadding = contentPadding + PaddingValues(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), content = content, diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt index 84477a5c3..821be17d3 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt @@ -15,18 +15,15 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.platform.LocalUriHandler import com.google.accompanist.pager.rememberPagerState import eu.kanade.core.prefs.PreferenceMutableState import eu.kanade.domain.category.model.Category import eu.kanade.domain.library.model.LibraryDisplayMode import eu.kanade.domain.library.model.LibraryManga -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.SwipeRefresh import eu.kanade.presentation.library.LibraryState -import eu.kanade.tachiyomi.R +import eu.kanade.presentation.util.plus import eu.kanade.tachiyomi.ui.library.LibraryItem -import eu.kanade.tachiyomi.widget.EmptyView import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -100,19 +97,6 @@ fun LibraryContent( }, enabled = state.selectionMode.not(), ) { - if (state.searchQuery.isNullOrEmpty() && isLibraryEmpty) { - val handler = LocalUriHandler.current - EmptyScreen( - R.string.information_empty_library, - listOf( - EmptyView.Action(R.string.getting_started_guide, R.drawable.ic_help_24dp) { - handler.openUri("https://tachiyomi.org/help/guides/getting-started") - }, - ), - ) - return@SwipeRefresh - } - LibraryPager( state = pagerState, contentPadding = PaddingValues(bottom = contentPadding.calculateBottomPadding()), diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt index 901291305..e7644a0b3 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt @@ -31,7 +31,6 @@ import eu.kanade.presentation.util.selectedBackground import eu.kanade.presentation.util.verticalPadding import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.library.LibraryItem -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding @Composable fun LibraryList( @@ -45,7 +44,7 @@ fun LibraryList( ) { FastScrollLazyColumn( modifier = Modifier.fillMaxSize(), - contentPadding = bottomNavPadding + contentPadding, + contentPadding = contentPadding, ) { item { if (searchQuery.isNullOrEmpty().not()) { diff --git a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt index 7f87e77ee..edb7ccbc0 100644 --- a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt @@ -27,7 +27,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.more.DownloadQueueState import eu.kanade.tachiyomi.ui.more.MoreController import eu.kanade.tachiyomi.ui.more.MorePresenter -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding +import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView @Composable fun MoreScreen( @@ -43,7 +43,7 @@ fun MoreScreen( ScrollbarLazyColumn( modifier = Modifier.statusBarsPadding(), - contentPadding = bottomNavPadding, + contentPadding = TachiyomiBottomNavigationView.withBottomNavPadding(), ) { item { LogoHeader() diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt index bb870ed89..3d3413783 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.FlipToBack @@ -33,7 +34,6 @@ import eu.kanade.presentation.components.MangaBottomActionMenu import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.components.SwipeRefresh import eu.kanade.presentation.components.VerticalFastScroller -import eu.kanade.presentation.util.plus import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.library.LibraryUpdateService @@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.ui.recent.updates.UpdatesPresenter import eu.kanade.tachiyomi.ui.recent.updates.UpdatesPresenter.Dialog import eu.kanade.tachiyomi.ui.recent.updates.UpdatesPresenter.Event import eu.kanade.tachiyomi.util.system.toast -import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding +import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -96,13 +96,17 @@ fun UpdateScreen( ) }, ) { contentPadding -> + val contentPaddingWithNavBar = TachiyomiBottomNavigationView.withBottomNavPadding(contentPadding) when { presenter.isLoading -> LoadingScreen() - presenter.uiModels.isEmpty() -> EmptyScreen(textResource = R.string.information_no_recent) + presenter.uiModels.isEmpty() -> EmptyScreen( + textResource = R.string.information_no_recent, + modifier = Modifier.padding(contentPaddingWithNavBar), + ) else -> { UpdateScreenContent( presenter = presenter, - contentPadding = contentPadding, + contentPadding = contentPaddingWithNavBar, onUpdateLibrary = onUpdateLibrary, onClickCover = onClickCover, ) @@ -120,10 +124,6 @@ private fun UpdateScreenContent( ) { val context = LocalContext.current val updatesListState = rememberLazyListState() - - // During selection mode bottom nav is not visible - val contentPaddingWithNavBar = contentPadding + bottomNavPadding - val scope = rememberCoroutineScope() var isRefreshing by remember { mutableStateOf(false) } @@ -140,39 +140,35 @@ private fun UpdateScreenContent( } }, enabled = presenter.selectionMode.not(), - indicatorPadding = contentPaddingWithNavBar, + indicatorPadding = contentPadding, ) { - if (presenter.uiModels.isEmpty()) { - EmptyScreen(textResource = R.string.information_no_recent) - } else { - VerticalFastScroller( - listState = updatesListState, - topContentPadding = contentPaddingWithNavBar.calculateTopPadding(), - endContentPadding = contentPaddingWithNavBar.calculateEndPadding(LocalLayoutDirection.current), + VerticalFastScroller( + listState = updatesListState, + topContentPadding = contentPadding.calculateTopPadding(), + endContentPadding = contentPadding.calculateEndPadding(LocalLayoutDirection.current), + ) { + LazyColumn( + modifier = Modifier.fillMaxHeight(), + state = updatesListState, + contentPadding = contentPadding, ) { - LazyColumn( - modifier = Modifier.fillMaxHeight(), - state = updatesListState, - contentPadding = contentPaddingWithNavBar, - ) { - if (presenter.lastUpdated > 0L) { - updatesLastUpdatedItem(presenter.lastUpdated) - } - - updatesUiItems( - uiModels = presenter.uiModels, - selectionMode = presenter.selectionMode, - onUpdateSelected = presenter::toggleSelection, - onClickCover = onClickCover, - onClickUpdate = { - val intent = ReaderActivity.newIntent(context, it.update.mangaId, it.update.chapterId) - context.startActivity(intent) - }, - onDownloadChapter = presenter::downloadChapters, - relativeTime = presenter.relativeTime, - dateFormat = presenter.dateFormat, - ) + if (presenter.lastUpdated > 0L) { + updatesLastUpdatedItem(presenter.lastUpdated) } + + updatesUiItems( + uiModels = presenter.uiModels, + selectionMode = presenter.selectionMode, + onUpdateSelected = presenter::toggleSelection, + onClickCover = onClickCover, + onClickUpdate = { + val intent = ReaderActivity.newIntent(context, it.update.mangaId, it.update.chapterId) + context.startActivity(intent) + }, + onDownloadChapter = presenter::downloadChapters, + relativeTime = presenter.relativeTime, + dateFormat = presenter.dateFormat, + ) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt index 6f8f5df11..a6f017b43 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt @@ -34,9 +34,10 @@ fun extensionsTab( onClick = { router?.pushController(ExtensionFilterController()) }, ), ), - content = { + content = { contentPadding -> ExtensionScreen( presenter = presenter, + contentPadding = contentPadding, onLongClickItem = { extension -> when (extension) { is Extension.Available -> presenter.installExtension(extension) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourcesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourcesTab.kt index 903197508..14d530bc6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourcesTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourcesTab.kt @@ -31,9 +31,10 @@ fun migrateSourcesTab( }, ), ), - content = { + content = { contentPadding -> MigrateSourceScreen( presenter = presenter, + contentPadding = contentPadding, onClickItem = { source -> router?.pushController( MigrationMangaController( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt index 7f455f5d2..019f9d88b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt @@ -32,9 +32,10 @@ fun sourcesTab( onClick = { router?.pushController(SourceFilterController()) }, ), ), - content = { + content = { contentPadding -> SourcesScreen( presenter = presenter, + contentPadding = contentPadding, onClickItem = { source, query -> presenter.onOpenSource(source) router?.pushController(BrowseSourceController(source, query)) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt index 026c3127e..4da7375e2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt @@ -256,7 +256,10 @@ class DownloadController : }, ) { contentPadding -> if (downloadList.isEmpty()) { - EmptyScreen(textResource = R.string.information_no_downloads) + EmptyScreen( + textResource = R.string.information_no_downloads, + modifier = Modifier.padding(contentPadding), + ) return@Scaffold } val density = LocalDensity.current diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/EmptyView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/EmptyView.kt index f0f368bac..2e4d63b53 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/EmptyView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/EmptyView.kt @@ -1,26 +1,24 @@ package eu.kanade.tachiyomi.widget import android.content.Context -import android.content.res.ColorStateList import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.LinearLayout -import android.widget.RelativeLayout -import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import androidx.appcompat.view.ContextThemeWrapper +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.AbstractComposeView import androidx.core.view.isVisible -import com.google.android.material.button.MaterialButton -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.databinding.CommonViewEmptyBinding -import eu.kanade.tachiyomi.util.system.getThemeColor -import kotlin.random.Random +import eu.kanade.presentation.components.EmptyScreen +import eu.kanade.presentation.theme.TachiyomiTheme class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - RelativeLayout(context, attrs) { + AbstractComposeView(context, attrs) { - private val binding: CommonViewEmptyBinding = - CommonViewEmptyBinding.inflate(LayoutInflater.from(context), this, true) + var message by mutableStateOf("") /** * Hide the information view @@ -33,62 +31,17 @@ class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? * Show the information view * @param textResource text of information view */ - fun show(@StringRes textResource: Int, actions: List? = null) { - show(context.getString(textResource), actions) - } - - fun show(message: String, actions: List? = null) { - binding.textFace.text = getRandomErrorFace() - binding.textLabel.text = message - - binding.actionsContainer.removeAllViews() - val buttonContext = ContextThemeWrapper(context, R.style.Widget_Tachiyomi_Button_ActionButton) - val buttonColor = ColorStateList.valueOf(context.getThemeColor(R.attr.colorOnBackground)) - actions?.forEach { - val button = MaterialButton( - buttonContext, - null, - R.attr.borderlessButtonStyle, - ).apply { - layoutParams = LinearLayout.LayoutParams( - 0, - LinearLayout.LayoutParams.WRAP_CONTENT, - 1f / actions.size, - ) - - setTextColor(buttonColor) - iconTint = buttonColor - - setIconResource(it.iconResId) - setText(it.stringResId) - - setOnClickListener(it.listener) - } - - binding.actionsContainer.addView(button) - } - + fun show(@StringRes textResource: Int) { + message = context.getString(textResource) this.isVisible = true } - companion object { - private val ERROR_FACES = listOf( - "(・o・;)", - "Σ(ಠ_ಠ)", - "ಥ_ಥ", - "(˘・_・˘)", - "(; ̄Д ̄)", - "(・Д・。", - ) - - fun getRandomErrorFace(): String { - return ERROR_FACES[Random.nextInt(ERROR_FACES.size)] + @Composable + override fun Content() { + TachiyomiTheme { + CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) { + EmptyScreen(message = message) + } } } - - data class Action( - @StringRes val stringResId: Int, - @DrawableRes val iconResId: Int, - val listener: OnClickListener, - ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiBottomNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiBottomNavigationView.kt index 079e9d81a..002a5f029 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiBottomNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiBottomNavigationView.kt @@ -9,10 +9,16 @@ import android.os.Parcelable import android.util.AttributeSet import android.view.ViewPropertyAnimator import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.max import androidx.customview.view.AbsSavedState import androidx.interpolator.view.animation.FastOutLinearInInterpolator import androidx.interpolator.view.animation.LinearOutSlowInInterpolator @@ -58,7 +64,7 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor( override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) - bottomNavPadding = PaddingValues(bottom = h.pxToDp.dp) + bottomNavPadding = h.pxToDp.dp } /** @@ -74,6 +80,7 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor( SLIDE_UP_ANIMATION_DURATION, LinearOutSlowInInterpolator(), ) + bottomNavPadding = height.pxToDp.dp } /** @@ -89,6 +96,7 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor( SLIDE_DOWN_ANIMATION_DURATION, FastOutLinearInInterpolator(), ) + bottomNavPadding = 0.dp } private fun animateTranslation(targetY: Float, duration: Long, interpolator: TimeInterpolator) { @@ -149,7 +157,21 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor( private const val SLIDE_UP_ANIMATION_DURATION = 225L private const val SLIDE_DOWN_ANIMATION_DURATION = 175L - var bottomNavPadding by mutableStateOf(PaddingValues()) - private set + private var bottomNavPadding by mutableStateOf(0.dp) + + /** + * Merges [bottomNavPadding] to the origin's [PaddingValues] bottom side. + */ + @ReadOnlyComposable + @Composable + fun withBottomNavPadding(origin: PaddingValues = PaddingValues()): PaddingValues { + val layoutDirection = LocalLayoutDirection.current + return PaddingValues( + start = origin.calculateStartPadding(layoutDirection), + top = origin.calculateTopPadding(), + end = origin.calculateEndPadding(layoutDirection), + bottom = max(origin.calculateBottomPadding(), bottomNavPadding), + ) + } } } diff --git a/app/src/main/res/layout/common_view_empty.xml b/app/src/main/res/layout/common_view_empty.xml deleted file mode 100644 index 74875c478..000000000 --- a/app/src/main/res/layout/common_view_empty.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 11674d142..89bd2a8e2 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -81,16 +81,6 @@ - - - -