Tweak tablet UI mode setting (#8262)

This commit is contained in:
stevenyomi 2022-10-22 22:15:12 +08:00 committed by GitHub
parent b3557e844c
commit d558f9e1d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 76 additions and 87 deletions

View File

@ -166,7 +166,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)
@ -324,7 +323,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

@ -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
@ -72,7 +71,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreenState
fun MangaScreen(
state: MangaScreenState.Success,
snackbarHostState: SnackbarHostState,
windowWidthSizeClass: WindowWidthSizeClass,
isTabletUi: Boolean,
onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
@ -105,7 +104,7 @@ fun MangaScreen(
onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,
) {
if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
if (!isTabletUi) {
MangaScreenSmallImpl(
state = state,
snackbarHostState = snackbarHostState,
@ -136,7 +135,6 @@ fun MangaScreen(
} else {
MangaScreenLargeImpl(
state = state,
windowWidthSizeClass = windowWidthSizeClass,
snackbarHostState = snackbarHostState,
onBackClicked = onBackClicked,
onChapterClicked = onChapterClicked,
@ -308,7 +306,7 @@ private fun MangaScreenSmallImpl(
contentType = MangaScreenItem.INFO_BOX,
) {
MangaInfoBox(
windowWidthSizeClass = WindowWidthSizeClass.Compact,
isTabletUi = false,
appBarPadding = topPadding,
title = state.manga.title,
author = state.manga.author,
@ -373,7 +371,6 @@ private fun MangaScreenSmallImpl(
@Composable
fun MangaScreenLargeImpl(
state: MangaScreenState.Success,
windowWidthSizeClass: WindowWidthSizeClass,
snackbarHostState: SnackbarHostState,
onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit,
@ -505,7 +502,7 @@ fun MangaScreenLargeImpl(
.verticalScroll(rememberScrollState()),
) {
MangaInfoBox(
windowWidthSizeClass = windowWidthSizeClass,
isTabletUi = true,
appBarPadding = contentPadding.calculateTopPadding(),
title = state.manga.title,
author = state.manga.author,

View File

@ -43,7 +43,6 @@ import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.SuggestionChipDefaults
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
@ -88,7 +87,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?,
@ -123,7 +122,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

@ -27,8 +27,6 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.library.service.LibraryPreferences
import eu.kanade.domain.manga.repository.MangaRepository
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.collectAsState
import eu.kanade.tachiyomi.R
@ -110,7 +108,6 @@ class SettingsAdvancedScreen : SearchableSettings {
getNetworkGroup(networkPreferences = networkPreferences),
getLibraryGroup(),
getExtensionsGroup(basePreferences = basePreferences),
getDisplayGroup(),
)
}
@ -390,24 +387,4 @@ 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
},
),
),
)
}
}

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),
)
}
@ -99,18 +101,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

@ -65,7 +65,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
@ -610,7 +610,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
@ -25,7 +26,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
@ -52,6 +52,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.isTabletUi
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.launch
@ -112,10 +113,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>