mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Tweak tablet UI mode setting (#8262)
This commit is contained in:
		| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|                     }, | ||||
|                 ), | ||||
|             ), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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 | ||||
|     } | ||||
| } | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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() }, | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user