diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4b6f6d9bc..02a73ce97 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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", diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 424965a58..d2166fee0 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -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, 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, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index b1a2eca4e..d986cb454 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -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, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 512312f62..bc91b9da9 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -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() } - 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 - }, - ), - ), - ) - } } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 30749e011..cb9cbec8b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -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), diff --git a/app/src/main/java/eu/kanade/presentation/util/WindowSizeClass.kt b/app/src/main/java/eu/kanade/presentation/util/WindowSizeClass.kt deleted file mode 100644 index 1f5ee9e4d..000000000 --- a/app/src/main/java/eu/kanade/presentation/util/WindowSizeClass.kt +++ /dev/null @@ -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 - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 6d2a3a7f9..a627afcdf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index a1c7b6bdb..c3e7b2aba 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -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 { 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() }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt index 904c18e01..b9ac2e98a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt @@ -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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 401e5e1ad..9ba2d3385 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -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().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) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt index b7e6a41df..2ad8326f3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt @@ -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 diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index b75a13897..732bc4dc5 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -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" } diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index df7fe818e..2783ef910 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -187,7 +187,6 @@ Yotsuba Tidal Wave Pure black dark mode - Navigation Side navigation icon alignment Top Center