Tweak tablet UI mode setting (#8262)

(cherry picked from commit d558f9e1d6bcc8eb8f69359e6e7516f7ea24a408)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt
This commit is contained in:
stevenyomi 2022-10-22 22:15:12 +08:00 committed by Jobobby04
parent 2774df208e
commit 46fe5dfd4f
15 changed files with 84 additions and 93 deletions

View File

@ -152,7 +152,6 @@ dependencies {
implementation(compose.activity)
implementation(compose.foundation)
implementation(compose.material3.core)
implementation(compose.material3.windowsizeclass)
implementation(compose.material3.adapter)
implementation(compose.material.icons)
implementation(compose.animation)
@ -334,7 +333,6 @@ tasks {
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
"-opt-in=androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",

View File

@ -37,7 +37,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
@ -49,13 +48,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.util.calculateWindowWidthSizeClass
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.isTabletUi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
@ -250,7 +250,8 @@ fun LibraryBottomActionMenu(
}
// SY -->
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null
val moveMarkPrev = onDeleteClicked != null && calculateWindowWidthSizeClass() == WindowWidthSizeClass.Compact
val configuration = LocalConfiguration.current
val moveMarkPrev = onDeleteClicked != null && remember { !configuration.isTabletUi() }
var overFlowOpen by remember { mutableStateOf(false) }
// SY <--
Row(

View File

@ -18,7 +18,6 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -26,6 +25,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
@ -38,8 +38,8 @@ import eu.kanade.presentation.components.Pill
import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.presentation.library.LibraryState
import eu.kanade.presentation.theme.active
import eu.kanade.presentation.util.calculateWindowWidthSizeClass
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.isTabletUi
@Composable
fun LibraryToolbar(
@ -148,7 +148,8 @@ fun LibraryRegularToolbar(
Icon(Icons.Outlined.FilterList, contentDescription = stringResource(R.string.action_filter), tint = filterTint)
}
// SY -->
val moveGlobalUpdate = showSyncExh && calculateWindowWidthSizeClass() == WindowWidthSizeClass.Compact
val configuration = LocalConfiguration.current
val moveGlobalUpdate = showSyncExh && remember { !configuration.isTabletUi() }
if (!moveGlobalUpdate) {
IconButton(onClick = onClickRefresh) {
Icon(Icons.Outlined.Refresh, contentDescription = stringResource(R.string.pref_category_library_update))

View File

@ -30,7 +30,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@ -94,7 +93,7 @@ import exh.ui.metadata.adapters.TsuminoDescription
fun MangaScreen(
state: MangaScreenState.Success,
snackbarHostState: SnackbarHostState,
windowWidthSizeClass: WindowWidthSizeClass,
isTabletUi: Boolean,
onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
@ -136,7 +135,7 @@ fun MangaScreen(
onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,
) {
if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
if (!isTabletUi) {
MangaScreenSmallImpl(
state = state,
snackbarHostState = snackbarHostState,
@ -176,7 +175,6 @@ fun MangaScreen(
} else {
MangaScreenLargeImpl(
state = state,
windowWidthSizeClass = windowWidthSizeClass,
snackbarHostState = snackbarHostState,
onBackClicked = onBackClicked,
onChapterClicked = onChapterClicked,
@ -375,7 +373,7 @@ private fun MangaScreenSmallImpl(
contentType = MangaScreenItem.INFO_BOX,
) {
MangaInfoBox(
windowWidthSizeClass = WindowWidthSizeClass.Compact,
isTabletUi = false,
appBarPadding = topPadding,
title = state.manga.title,
author = state.manga.author,
@ -489,7 +487,6 @@ private fun MangaScreenSmallImpl(
@Composable
fun MangaScreenLargeImpl(
state: MangaScreenState.Success,
windowWidthSizeClass: WindowWidthSizeClass,
snackbarHostState: SnackbarHostState,
onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit,
@ -640,7 +637,7 @@ fun MangaScreenLargeImpl(
.verticalScroll(rememberScrollState()),
) {
MangaInfoBox(
windowWidthSizeClass = windowWidthSizeClass,
isTabletUi = true,
appBarPadding = contentPadding.calculateTopPadding(),
title = state.manga.title,
author = state.manga.author,

View File

@ -41,7 +41,6 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@ -86,7 +85,7 @@ private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTIL
@Composable
fun MangaInfoBox(
modifier: Modifier = Modifier,
windowWidthSizeClass: WindowWidthSizeClass,
isTabletUi: Boolean,
appBarPadding: Dp,
title: String,
author: String?,
@ -121,7 +120,7 @@ fun MangaInfoBox(
// Manga & source info
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
if (!isTabletUi) {
MangaAndSourceTitlesSmall(
appBarPadding = appBarPadding,
coverDataProvider = coverDataProvider,

View File

@ -46,8 +46,6 @@ import eu.kanade.domain.library.service.LibraryPreferences
import eu.kanade.domain.manga.interactor.GetAllManga
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.util.LocalRouter
import eu.kanade.presentation.util.collectAsState
@ -142,7 +140,6 @@ class SettingsAdvancedScreen : SearchableSettings {
getNetworkGroup(networkPreferences = networkPreferences),
getLibraryGroup(),
getExtensionsGroup(basePreferences = basePreferences),
getDisplayGroup(),
// SY -->
getDownloaderGroup(),
getDataSaverGroup(),
@ -454,26 +451,6 @@ class SettingsAdvancedScreen : SearchableSettings {
)
}
@Composable
private fun getDisplayGroup(): Preference.PreferenceGroup {
val context = LocalContext.current
val uiPreferences = remember { Injekt.get<UiPreferences>() }
return Preference.PreferenceGroup(
title = stringResource(R.string.pref_category_display),
preferenceItems = listOf(
Preference.PreferenceItem.ListPreference(
pref = uiPreferences.tabletUiMode(),
title = stringResource(R.string.pref_tablet_ui_mode),
entries = TabletUiMode.values().associateWith { stringResource(it.titleResId) },
onValueChanged = {
context.toast(R.string.requires_app_restart)
true
},
),
),
)
}
// SY -->
@Composable
fun CleanupDownloadsDialog(

View File

@ -13,12 +13,14 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.app.ActivityCompat
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.isAutoTabletUiAvailable
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.merge
@ -40,7 +42,7 @@ class SettingsAppearanceScreen : SearchableSettings {
return listOf(
getThemeGroup(context = context, uiPreferences = uiPreferences),
getNavigationGroup(context = context, uiPreferences = uiPreferences),
getDisplayGroup(context = context, uiPreferences = uiPreferences),
getTimestampGroup(uiPreferences = uiPreferences),
// SY -->
getNavbarGroup(uiPreferences = uiPreferences),
@ -102,18 +104,38 @@ class SettingsAppearanceScreen : SearchableSettings {
}
@Composable
private fun getNavigationGroup(
private fun getDisplayGroup(
context: Context,
uiPreferences: UiPreferences,
): Preference.PreferenceGroup {
val tabletUiModePref = uiPreferences.tabletUiMode()
val tabletUiMode by tabletUiModePref.collectAsState()
val isTabletUiAvailable = remember(tabletUiMode) { // won't survive config change
when (tabletUiMode) {
TabletUiMode.AUTOMATIC -> context.resources.configuration.isAutoTabletUiAvailable()
TabletUiMode.NEVER -> false
else -> true
}
}
return Preference.PreferenceGroup(
title = stringResource(R.string.pref_category_navigation),
enabled = remember(context) { context.isTablet() },
title = stringResource(R.string.pref_category_display),
preferenceItems = listOf(
Preference.PreferenceItem.ListPreference(
pref = tabletUiModePref,
title = stringResource(R.string.pref_tablet_ui_mode),
entries = TabletUiMode.values().associateWith { stringResource(it.titleResId) },
onValueChanged = {
context.toast(R.string.requires_app_restart)
true
},
),
Preference.PreferenceItem.ListPreference(
pref = uiPreferences.sideNavIconAlignment(),
title = stringResource(R.string.pref_side_nav_icon_alignment),
subtitle = "%s",
enabled = isTabletUiAvailable,
entries = mapOf(
0 to stringResource(R.string.alignment_top),
1 to stringResource(R.string.alignment_center),

View File

@ -1,24 +0,0 @@
package eu.kanade.presentation.util
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
@ReadOnlyComposable
fun calculateWindowWidthSizeClass(): WindowWidthSizeClass {
val configuration = LocalConfiguration.current
return fromWidth(configuration.smallestScreenWidthDp.dp)
}
private fun fromWidth(width: Dp): WindowWidthSizeClass {
require(width >= 0.dp) { "Width must not be negative" }
return when {
width < 720.dp -> WindowWidthSizeClass.Compact // Was 600
width < 840.dp -> WindowWidthSizeClass.Medium
else -> WindowWidthSizeClass.Expanded
}
}

View File

@ -67,7 +67,7 @@ import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.preference.asHotFlow
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getThemeColor
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.isTabletUi
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
@ -669,7 +669,7 @@ class MainActivity : BaseActivity() {
binding.appbar.isVisible = !isComposeController
binding.controllerContainer.enableScrollingBehavior(!isComposeController)
if (!isTablet()) {
if (!isTabletUi()) {
// Save lift state
if (isPush) {
if (router.backstackSize > 1) {

View File

@ -13,6 +13,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalConfiguration
import androidx.core.os.bundleOf
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
@ -27,7 +28,6 @@ import eu.kanade.presentation.manga.DownloadAction
import eu.kanade.presentation.manga.MangaScreen
import eu.kanade.presentation.manga.components.DeleteChaptersDialog
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
import eu.kanade.presentation.util.calculateWindowWidthSizeClass
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.download.model.Download
@ -63,6 +63,7 @@ import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.getParcelableCompat
import eu.kanade.tachiyomi.util.system.isTabletUi
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast
import exh.md.similar.MangaDexSimilarController
@ -159,10 +160,13 @@ class MangaController : FullComposeController<MangaPresenter> {
val isHttpSource = remember { successState.source is HttpSource }
val scope = rememberCoroutineScope()
val configuration = LocalConfiguration.current
val isTabletUi = remember { configuration.isTabletUi() } // won't survive config change
MangaScreen(
state = successState,
snackbarHostState = snackbarHostState,
windowWidthSizeClass = calculateWindowWidthSizeClass(),
isTabletUi = isTabletUi,
onBackClicked = router::popCurrentController,
onChapterClicked = this::openChapter,
onDownloadChapter = this::onDownloadChapters.takeIf { !successState.source.isLocalOrStub() },

View File

@ -6,9 +6,10 @@ import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.with
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalConfiguration
import androidx.core.os.bundleOf
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.ScreenTransition
@ -19,8 +20,8 @@ import eu.kanade.presentation.more.settings.screen.SettingsGeneralScreen
import eu.kanade.presentation.more.settings.screen.SettingsMainScreen
import eu.kanade.presentation.util.LocalBackPress
import eu.kanade.presentation.util.LocalRouter
import eu.kanade.presentation.util.calculateWindowWidthSizeClass
import eu.kanade.tachiyomi.ui.base.controller.BasicFullComposeController
import eu.kanade.tachiyomi.util.system.isTabletUi
class SettingsMainController(bundle: Bundle = bundleOf()) : BasicFullComposeController(bundle) {
@ -39,8 +40,9 @@ class SettingsMainController(bundle: Bundle = bundleOf()) : BasicFullComposeCont
@Composable
override fun ComposeContent() {
CompositionLocalProvider(LocalRouter provides router) {
val widthSizeClass = calculateWindowWidthSizeClass()
if (widthSizeClass == WindowWidthSizeClass.Compact) {
val configuration = LocalConfiguration.current
val isTabletUi = remember { configuration.isTabletUi() } // won't survive config change
if (!isTabletUi) {
Navigator(
screen = if (toBackupScreen) {
SettingsBackupScreen()

View File

@ -50,8 +50,6 @@ import java.io.File
import kotlin.math.max
import kotlin.math.roundToInt
private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720
/**
* Copies a string to clipboard
*
@ -263,28 +261,46 @@ fun Context.createFileInCacheDir(name: String): File {
return file
}
/**
* We consider anything with a width of >= 720dp as a tablet, i.e. with layouts in layout-sw720dp.
*/
fun Context.isTablet(): Boolean {
return resources.configuration.smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_DP
private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720
// some tablets have screen width like 711dp = 1600px / 2.25
private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700
// make sure icons on the nav rail fit
private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
fun Context.isTabletUi(): Boolean {
return resources.configuration.isTabletUi()
}
fun Configuration.isTabletUi(): Boolean {
return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
}
fun Configuration.isAutoTabletUiAvailable(): Boolean {
return smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
}
// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
fun Context.prepareTabletUiContext(): Context {
val configuration = resources.configuration
val expected = when (Injekt.get<UiPreferences>().tabletUiMode().get()) {
TabletUiMode.AUTOMATIC -> isTablet()
TabletUiMode.AUTOMATIC ->
configuration.smallestScreenWidthDp >= when (configuration.orientation) {
Configuration.ORIENTATION_PORTRAIT -> TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP
else -> TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
}
TabletUiMode.ALWAYS -> true
TabletUiMode.LANDSCAPE -> configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
TabletUiMode.NEVER -> false
}
if (configuration.smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_DP != expected) {
if (configuration.isTabletUi() != expected) {
val overrideConf = Configuration()
overrideConf.setTo(configuration)
overrideConf.smallestScreenWidthDp = if (expected) {
overrideConf.smallestScreenWidthDp.coerceAtLeast(TABLET_UI_MIN_SCREEN_WIDTH_DP)
overrideConf.smallestScreenWidthDp.coerceAtLeast(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP)
} else {
overrideConf.smallestScreenWidthDp.coerceAtMost(TABLET_UI_MIN_SCREEN_WIDTH_DP - 1)
overrideConf.smallestScreenWidthDp.coerceAtMost(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP - 1)
}
return createConfigurationContext(overrideConf)
}

View File

@ -14,7 +14,7 @@ import androidx.core.view.isVisible
import androidx.customview.view.AbsSavedState
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayout
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.isTabletUi
import eu.kanade.tachiyomi.util.view.findChild
/**
@ -48,7 +48,7 @@ class TachiyomiCoordinatorLayout @JvmOverloads constructor(
) {
super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
// Disable elevation overlay when tabs are visible
if (context.isTablet().not()) {
if (context.isTabletUi().not()) {
if (target is ComposeView) {
val scrollCondition = if (type == ViewCompat.TYPE_NON_TOUCH) {
dyUnconsumed >= 0

View File

@ -13,7 +13,6 @@ ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose
ui-util = { module = "androidx.compose.ui:ui-util", version.ref = "compose" }
material3-core = { module = "androidx.compose.material3:material3", version.ref = "material3" }
material3-windowsizeclass = { module = "androidx.compose.material3:material3-window-size-class", version.ref = "material3" }
material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.20"
material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" }

View File

@ -187,7 +187,6 @@
<string name="theme_yotsuba">Yotsuba</string>
<string name="theme_tidalwave">Tidal Wave</string>
<string name="pref_dark_theme_pure_black">Pure black dark mode</string>
<string name="pref_category_navigation">Navigation</string>
<string name="pref_side_nav_icon_alignment">Side navigation icon alignment</string>
<string name="alignment_top">Top</string>
<string name="alignment_center">Center</string>