Reimplement appbar color overlay on scroll (#7663)

only on updates and history screen for now, but the required changes on app bar
is there.

also fix missing incognito-downloaded mode indicator on history screen
This commit is contained in:
Ivan Iskandar 2022-08-01 09:24:19 +07:00 committed by GitHub
parent 322f3a07e8
commit 737cf9898d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 44 deletions

View File

@ -2,11 +2,7 @@ package eu.kanade.presentation.components
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
@ -18,6 +14,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -51,19 +48,63 @@ fun AppBar(
// Banners // Banners
downloadedOnlyMode: Boolean = false, downloadedOnlyMode: Boolean = false,
incognitoMode: Boolean = false, incognitoMode: Boolean = false,
scrollBehavior: TopAppBarScrollBehavior? = null,
) { ) {
val isActionMode by derivedStateOf { actionModeCounter > 0 } val isActionMode by derivedStateOf { actionModeCounter > 0 }
val backgroundColor = if (isActionMode) { AppBar(
TopAppBarDefaults.smallTopAppBarColors().containerColor(1f).value modifier = modifier,
titleContent = {
if (isActionMode) {
AppBarTitle(actionModeCounter.toString())
} else { } else {
MaterialTheme.colorScheme.surface AppBarTitle(title, subtitle)
} }
},
navigateUp = navigateUp,
navigationIcon = navigationIcon,
actions = {
if (isActionMode) {
actionModeActions()
} else {
actions()
}
},
isActionMode = isActionMode,
onCancelActionMode = onCancelActionMode,
downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode,
scrollBehavior = scrollBehavior,
)
}
@Composable
fun AppBar(
modifier: Modifier = Modifier,
// Title
titleContent: @Composable () -> Unit,
// Up button
navigateUp: (() -> Unit)? = null,
navigationIcon: ImageVector = Icons.Default.ArrowBack,
// Menu
actions: @Composable RowScope.() -> Unit = {},
// Action mode
isActionMode: Boolean,
onCancelActionMode: () -> Unit = {},
// Banners
downloadedOnlyMode: Boolean = false,
incognitoMode: Boolean = false,
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
val scrollFraction = if (isActionMode) 1f else scrollBehavior?.state?.overlappedFraction ?: 0f
val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(scrollFraction)
Column( Column(
modifier = modifier.drawBehind { drawRect(backgroundColor) }, modifier = modifier.drawBehind { drawRect(backgroundColor) },
) { ) {
SmallTopAppBar( SmallTopAppBar(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)), modifier = Modifier.statusBarsPadding(),
navigationIcon = { navigationIcon = {
if (isActionMode) { if (isActionMode) {
IconButton(onClick = onCancelActionMode) { IconButton(onClick = onCancelActionMode) {
@ -83,25 +124,14 @@ fun AppBar(
} }
} }
}, },
title = { title = titleContent,
if (isActionMode) { actions = actions,
AppBarTitle(actionModeCounter.toString())
} else {
AppBarTitle(title, subtitle)
}
},
actions = {
if (isActionMode) {
actionModeActions()
} else {
actions()
}
},
// Background handled by parent // Background handled by parent
colors = TopAppBarDefaults.smallTopAppBarColors( colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = Color.Transparent, containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent, scrolledContainerColor = Color.Transparent,
), ),
scrollBehavior = scrollBehavior,
) )
if (downloadedOnlyMode) { if (downloadedOnlyMode) {

View File

@ -1,10 +1,18 @@
package eu.kanade.presentation.history package eu.kanade.presentation.history
import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.paging.LoadState import androidx.paging.LoadState
import eu.kanade.domain.history.model.HistoryWithRelations import eu.kanade.domain.history.model.HistoryWithRelations
@ -30,10 +38,21 @@ fun HistoryScreen(
onClickResume: (HistoryWithRelations) -> Unit, onClickResume: (HistoryWithRelations) -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val insetPaddingValue = WindowInsets.navigationBars
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)
.asPaddingValues()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold( Scaffold(
modifier = Modifier.safeDrawingPadding(), modifier = Modifier
.padding(insetPaddingValue)
.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { topBar = {
HistoryToolbar(state = presenter) HistoryToolbar(
state = presenter,
incognitoMode = presenter.isIncognitoMode,
downloadedOnlyMode = presenter.isDownloadOnly,
scrollBehavior = scrollBehavior,
)
}, },
) { contentPadding -> ) { contentPadding ->
val items = presenter.getLazyHistory() val items = presenter.getLazyHistory()

View File

@ -9,8 +9,7 @@ import androidx.compose.material.icons.outlined.Search
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -19,6 +18,7 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.AppBar
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter
import eu.kanade.tachiyomi.ui.recent.history.HistoryState import eu.kanade.tachiyomi.ui.recent.history.HistoryState
@ -27,17 +27,25 @@ import kotlinx.coroutines.delay
@Composable @Composable
fun HistoryToolbar( fun HistoryToolbar(
state: HistoryState, state: HistoryState,
scrollBehavior: TopAppBarScrollBehavior,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
) { ) {
if (state.searchQuery == null) { if (state.searchQuery == null) {
HistoryRegularToolbar( HistoryRegularToolbar(
onClickSearch = { state.searchQuery = "" }, onClickSearch = { state.searchQuery = "" },
onClickDelete = { state.dialog = HistoryPresenter.Dialog.DeleteAll }, onClickDelete = { state.dialog = HistoryPresenter.Dialog.DeleteAll },
incognitoMode = incognitoMode,
downloadedOnlyMode = downloadedOnlyMode,
scrollBehavior = scrollBehavior,
) )
} else { } else {
HistorySearchToolbar( HistorySearchToolbar(
searchQuery = state.searchQuery!!, searchQuery = state.searchQuery!!,
onChangeSearchQuery = { state.searchQuery = it }, onChangeSearchQuery = { state.searchQuery = it },
onClickCloseSearch = { state.searchQuery = null }, onClickCloseSearch = { state.searchQuery = null },
incognitoMode = incognitoMode,
downloadedOnlyMode = downloadedOnlyMode,
) )
} }
} }
@ -46,11 +54,12 @@ fun HistoryToolbar(
fun HistoryRegularToolbar( fun HistoryRegularToolbar(
onClickSearch: () -> Unit, onClickSearch: () -> Unit,
onClickDelete: () -> Unit, onClickDelete: () -> Unit,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
scrollBehavior: TopAppBarScrollBehavior,
) { ) {
SmallTopAppBar( AppBar(
title = { title = stringResource(id = R.string.history),
Text(text = stringResource(id = R.string.history))
},
actions = { actions = {
IconButton(onClick = onClickSearch) { IconButton(onClick = onClickSearch) {
Icon(Icons.Outlined.Search, contentDescription = "search") Icon(Icons.Outlined.Search, contentDescription = "search")
@ -59,6 +68,9 @@ fun HistoryRegularToolbar(
Icon(Icons.Outlined.DeleteSweep, contentDescription = "delete") Icon(Icons.Outlined.DeleteSweep, contentDescription = "delete")
} }
}, },
downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode,
scrollBehavior = scrollBehavior,
) )
} }
@ -67,15 +79,12 @@ fun HistorySearchToolbar(
searchQuery: String, searchQuery: String,
onChangeSearchQuery: (String) -> Unit, onChangeSearchQuery: (String) -> Unit,
onClickCloseSearch: () -> Unit, onClickCloseSearch: () -> Unit,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
) { ) {
val focusRequester = remember { FocusRequester.Default } val focusRequester = remember { FocusRequester.Default }
SmallTopAppBar( AppBar(
navigationIcon = { titleContent = {
IconButton(onClick = onClickCloseSearch) {
Icon(Icons.Outlined.ArrowBack, contentDescription = "delete")
}
},
title = {
BasicTextField( BasicTextField(
value = searchQuery, value = searchQuery,
onValueChange = onChangeSearchQuery, onValueChange = onChangeSearchQuery,
@ -87,6 +96,11 @@ fun HistorySearchToolbar(
cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground), cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground),
) )
}, },
navigationIcon = Icons.Outlined.ArrowBack,
navigateUp = onClickCloseSearch,
isActionMode = false,
downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode,
) )
LaunchedEffect(focusRequester) { LaunchedEffect(focusRequester) {
// TODO: https://issuetracker.google.com/issues/204502668 // TODO: https://issuetracker.google.com/issues/204502668

View File

@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -20,9 +19,13 @@ import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.SelectAll import androidx.compose.material.icons.filled.SelectAll
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -57,7 +60,9 @@ fun UpdateScreen(
onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit, onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit,
) { ) {
val updatesListState = rememberLazyListState() val updatesListState = rememberLazyListState()
val insetPaddingValue = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues() val insetPaddingValue = WindowInsets.navigationBars
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)
.asPaddingValues()
val internalOnBackPressed = { val internalOnBackPressed = {
if (presenter.selectionMode) { if (presenter.selectionMode) {
@ -76,9 +81,11 @@ fun UpdateScreen(
} }
} }
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold( Scaffold(
modifier = Modifier modifier = Modifier
.padding(insetPaddingValue), .padding(insetPaddingValue)
.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { topBar = {
UpdatesAppBar( UpdatesAppBar(
incognitoMode = presenter.isIncognitoMode, incognitoMode = presenter.isIncognitoMode,
@ -88,6 +95,7 @@ fun UpdateScreen(
onSelectAll = { presenter.toggleAllSelection(true) }, onSelectAll = { presenter.toggleAllSelection(true) },
onInvertSelection = { presenter.invertSelection() }, onInvertSelection = { presenter.invertSelection() },
onCancelActionMode = { presenter.toggleAllSelection(false) }, onCancelActionMode = { presenter.toggleAllSelection(false) },
scrollBehavior = scrollBehavior,
) )
}, },
bottomBar = { bottomBar = {
@ -185,6 +193,7 @@ fun UpdatesAppBar(
onSelectAll: () -> Unit, onSelectAll: () -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
onCancelActionMode: () -> Unit, onCancelActionMode: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior,
) { ) {
AppBar( AppBar(
modifier = modifier, modifier = modifier,
@ -215,6 +224,7 @@ fun UpdatesAppBar(
}, },
downloadedOnlyMode = downloadedOnlyMode, downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode, incognitoMode = incognitoMode,
scrollBehavior = scrollBehavior,
) )
} }

View File

@ -22,6 +22,7 @@ import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
import eu.kanade.domain.history.model.HistoryWithRelations import eu.kanade.domain.history.model.HistoryWithRelations
import eu.kanade.presentation.history.HistoryUiModel import eu.kanade.presentation.history.HistoryUiModel
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
@ -45,11 +46,16 @@ class HistoryPresenter(
private val deleteHistoryTable: DeleteHistoryTable = Injekt.get(), private val deleteHistoryTable: DeleteHistoryTable = Injekt.get(),
private val removeHistoryById: RemoveHistoryById = Injekt.get(), private val removeHistoryById: RemoveHistoryById = Injekt.get(),
private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(), private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(),
preferences: PreferencesHelper = Injekt.get(),
) : BasePresenter<HistoryController>(), HistoryState by state { ) : BasePresenter<HistoryController>(), HistoryState by state {
private val _events: Channel<Event> = Channel(Int.MAX_VALUE) private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState()
val isIncognitoMode: Boolean by preferences.incognitoMode().asState()
@Composable @Composable
fun getLazyHistory(): LazyPagingItems<HistoryUiModel> { fun getLazyHistory(): LazyPagingItems<HistoryUiModel> {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()