diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt index bffa08926..ecc02b3e4 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -3,6 +3,7 @@ package eu.kanade.domain.base import android.content.Context import android.content.pm.PackageManager import android.os.Build +import androidx.annotation.StringRes import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isReleaseBuildType @@ -23,7 +24,7 @@ class BasePreferences( fun deviceHasPip() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) - enum class ExtensionInstaller(val titleResId: Int) { + enum class ExtensionInstaller(@StringRes val titleResId: Int) { LEGACY(R.string.ext_installer_legacy), PACKAGEINSTALLER(R.string.ext_installer_packageinstaller), SHIZUKU(R.string.ext_installer_shizuku), diff --git a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt index 121c73b2f..8d7412f8e 100644 --- a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -25,10 +26,11 @@ import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.HeadingItem -import tachiyomi.presentation.core.components.RadioItem +import tachiyomi.presentation.core.components.SettingsFlowRow import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TriStateItem +import tachiyomi.presentation.core.components.material.ChoiceChip @Composable fun AnimeLibrarySettingsDialog( @@ -168,23 +170,26 @@ private fun ColumnScope.SortPage( } } +private val displayModes = listOf( + R.string.action_display_grid to LibraryDisplayMode.CompactGrid, + R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid, + R.string.action_display_cover_only_grid to LibraryDisplayMode.CoverOnlyGrid, + R.string.action_display_list to LibraryDisplayMode.List, +) + @Composable private fun ColumnScope.DisplayPage( screenModel: AnimeLibrarySettingsScreenModel, ) { - HeadingItem(R.string.action_display_mode) val displayMode by screenModel.libraryPreferences.libraryDisplayMode().collectAsState() - listOf( - R.string.action_display_grid to LibraryDisplayMode.CompactGrid, - R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid, - R.string.action_display_cover_only_grid to LibraryDisplayMode.CoverOnlyGrid, - R.string.action_display_list to LibraryDisplayMode.List, - ).map { (titleRes, mode) -> - RadioItem( - label = stringResource(titleRes), - selected = displayMode == mode, - onClick = { screenModel.setDisplayMode(mode) }, - ) + SettingsFlowRow(R.string.action_display_mode) { + displayModes.map { (titleRes, mode) -> + ChoiceChip( + isSelected = displayMode == mode, + onClick = { screenModel.setDisplayMode(mode) }, + content = { Text(stringResource(titleRes)) }, + ) + } } if (displayMode != LibraryDisplayMode.List) { diff --git a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt index f5222fc5c..d787d0712 100644 --- a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -25,10 +26,11 @@ import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.HeadingItem -import tachiyomi.presentation.core.components.RadioItem +import tachiyomi.presentation.core.components.SettingsFlowRow import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TriStateItem +import tachiyomi.presentation.core.components.material.ChoiceChip @Composable fun MangaLibrarySettingsDialog( @@ -167,23 +169,26 @@ private fun ColumnScope.SortPage( } } +private val displayModes = listOf( + R.string.action_display_grid to LibraryDisplayMode.CompactGrid, + R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid, + R.string.action_display_cover_only_grid to LibraryDisplayMode.CoverOnlyGrid, + R.string.action_display_list to LibraryDisplayMode.List, +) + @Composable private fun ColumnScope.DisplayPage( screenModel: MangaLibrarySettingsScreenModel, ) { - HeadingItem(R.string.action_display_mode) val displayMode by screenModel.libraryPreferences.libraryDisplayMode().collectAsState() - listOf( - R.string.action_display_grid to LibraryDisplayMode.CompactGrid, - R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid, - R.string.action_display_cover_only_grid to LibraryDisplayMode.CoverOnlyGrid, - R.string.action_display_list to LibraryDisplayMode.List, - ).map { (titleRes, mode) -> - RadioItem( - label = stringResource(titleRes), - selected = displayMode == mode, - onClick = { screenModel.setDisplayMode(mode) }, - ) + SettingsFlowRow(R.string.action_display_mode) { + displayModes.map { (titleRes, mode) -> + ChoiceChip( + isSelected = displayMode == mode, + onClick = { screenModel.setDisplayMode(mode) }, + content = { Text(stringResource(titleRes)) }, + ) + } } if (displayMode != LibraryDisplayMode.List) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index 8268ed29c..5e7a75efd 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -7,7 +7,6 @@ import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.util.collectAsState @@ -168,9 +167,9 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = navModePref, title = stringResource(R.string.pref_viewer_nav), - entries = stringArrayResource(id = R.array.pager_nav).let { - it.indices.zip(it).toMap() - }, + entries = ReaderPreferences.TapZones + .mapIndexed { index, it -> index to stringResource(it) } + .toMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPreferences.pagerNavInverted(), @@ -186,25 +185,16 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = imageScaleTypePref, title = stringResource(R.string.pref_image_scale_type), - entries = mapOf( - 1 to stringResource(R.string.scale_type_fit_screen), - 2 to stringResource(R.string.scale_type_stretch), - 3 to stringResource(R.string.scale_type_fit_width), - 4 to stringResource(R.string.scale_type_fit_height), - 5 to stringResource(R.string.scale_type_original_size), - 6 to stringResource(R.string.scale_type_smart_fit), - ), + entries = ReaderPreferences.ImageScaleType + .mapIndexed { index, it -> index + 1 to stringResource(it) } + .toMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPreferences.zoomStart(), title = stringResource(R.string.pref_zoom_start), - entries = mapOf( - 1 to stringResource(R.string.zoom_start_automatic), - 2 to stringResource(R.string.zoom_start_left), - 3 to stringResource(R.string.zoom_start_right), - 4 to stringResource(R.string.zoom_start_center), - ), - + entries = ReaderPreferences.ZoomStart + .mapIndexed { index, it -> index + 1 to stringResource(it) } + .toMap(), ), Preference.PreferenceItem.SwitchPreference( pref = readerPreferences.cropBorders(), @@ -269,9 +259,9 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = navModePref, title = stringResource(R.string.pref_viewer_nav), - entries = stringArrayResource(id = R.array.webtoon_nav).let { - it.indices.zip(it).toMap() - }, + entries = ReaderPreferences.TapZones + .mapIndexed { index, it -> index to stringResource(it) } + .toMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPreferences.webtoonNavInverted(), diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt index 06bbb37b1..78f20cb8f 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.reader.settings import android.os.Build import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.res.stringResource @@ -15,8 +16,9 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel import tachiyomi.core.preference.getAndSet import tachiyomi.presentation.core.components.CheckboxItem -import tachiyomi.presentation.core.components.SelectItem +import tachiyomi.presentation.core.components.SettingsFlowRow import tachiyomi.presentation.core.components.SliderItem +import tachiyomi.presentation.core.components.material.ChoiceChip @Composable internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) { @@ -122,12 +124,14 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) ) val colorFilterMode by screenModel.preferences.colorFilterMode().collectAsState() - SelectItem( - label = stringResource(R.string.pref_color_filter_mode), - options = colorFilterModes.toTypedArray(), - selectedIndex = colorFilterMode, - ) { - screenModel.preferences.colorFilterMode().set(it) + SettingsFlowRow(R.string.pref_color_filter_mode) { + colorFilterModes.mapIndexed { index, it -> + ChoiceChip( + isSelected = colorFilterMode == index, + onClick = { screenModel.preferences.colorFilterMode().set(index) }, + content = { Text(it) }, + ) + } } } diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt index 02738eeda..990a1423d 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.reader.settings import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.res.stringResource @@ -9,25 +10,27 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel import tachiyomi.presentation.core.components.CheckboxItem -import tachiyomi.presentation.core.components.HeadingItem -import tachiyomi.presentation.core.components.RadioItem +import tachiyomi.presentation.core.components.SettingsFlowRow +import tachiyomi.presentation.core.components.material.ChoiceChip + +private val themes = listOf( + R.string.black_background to 1, + R.string.gray_background to 2, + R.string.white_background to 0, + R.string.automatic_background to 3, +) @Composable internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) { - // TODO: show this in a nicer way - HeadingItem(R.string.pref_reader_theme) val readerTheme by screenModel.preferences.readerTheme().collectAsState() - listOf( - R.string.black_background to 1, - R.string.gray_background to 2, - R.string.white_background to 0, - R.string.automatic_background to 3, - ).map { (titleRes, theme) -> - RadioItem( - label = stringResource(titleRes), - selected = readerTheme == theme, - onClick = { screenModel.preferences.readerTheme().set(theme) }, - ) + SettingsFlowRow(R.string.pref_reader_theme) { + themes.map { (labelRes, value) -> + ChoiceChip( + isSelected = readerTheme == value, + onClick = { screenModel.preferences.readerTheme().set(value) }, + content = { Text(stringResource(labelRes)) }, + ) + } } val showPageNumber by screenModel.preferences.showPageNumber().collectAsState() diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt index 44f1bc27f..cf5b08997 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt @@ -23,9 +23,6 @@ fun ReaderSettingsDialog( onHideMenus: () -> Unit, screenModel: ReaderSettingsScreenModel, ) { - // TODO: undimming doesn't seem to work - val window = (LocalView.current.parent as? DialogWindowProvider)?.window - val tabTitles = listOf( stringResource(R.string.pref_category_reading_mode), stringResource(R.string.pref_category_general), @@ -33,16 +30,6 @@ fun ReaderSettingsDialog( ) val pagerState = rememberPagerState { tabTitles.size } - LaunchedEffect(pagerState.currentPage) { - if (pagerState.currentPage == 2) { - window?.setDimAmount(0f) - onHideMenus() - } else { - window?.setDimAmount(0.75f) - onShowMenus() - } - } - TabbedDialog( onDismissRequest = { onDismissRequest() @@ -51,6 +38,18 @@ fun ReaderSettingsDialog( tabTitles = tabTitles, pagerState = pagerState, ) { page -> + val window = (LocalView.current.parent as? DialogWindowProvider)?.window + + LaunchedEffect(pagerState.currentPage) { + if (pagerState.currentPage == 2) { + window?.setDimAmount(0f) + onHideMenus() + } else { + window?.setDimAmount(0.5f) + onShowMenus() + } + } + Column( modifier = Modifier .padding(vertical = TabbedDialogPaddings.Vertical) diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt index ff710a76d..0a726f8b2 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt @@ -2,40 +2,100 @@ package eu.kanade.presentation.reader.settings import androidx.compose.foundation.layout.ColumnScope import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.res.stringResource +import eu.kanade.domain.entries.manga.model.orientationType +import eu.kanade.domain.entries.manga.model.readingModeType import eu.kanade.presentation.util.collectAsState import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType +import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer import eu.kanade.tachiyomi.util.system.isReleaseBuildType import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.HeadingItem +import tachiyomi.presentation.core.components.SelectItem import tachiyomi.presentation.core.components.SliderItem import java.text.NumberFormat +private val readingModeOptions = ReadingModeType.values().map { it.stringRes to it } +private val orientationTypeOptions = OrientationType.values().map { it.stringRes to it } +private val tappingInvertModeOptions = ReaderPreferences.TappingInvertMode.values().map { it.titleResId to it } + @Composable internal fun ColumnScope.ReadingModePage(screenModel: ReaderSettingsScreenModel) { HeadingItem(R.string.pref_category_for_this_series) + val manga by screenModel.mangaFlow.collectAsState() - // Reading mode - // Rotation type + val readingMode = remember(manga) { ReadingModeType.fromPreference(manga?.readingModeType?.toInt()) } + SelectItem( + label = stringResource(R.string.pref_category_reading_mode), + options = readingModeOptions.map { stringResource(it.first) }.toTypedArray(), + selectedIndex = readingModeOptions.indexOfFirst { it.second == readingMode }, + ) { + screenModel.onChangeReadingMode(readingModeOptions[it].second) + } - // if (pager) - PagerViewerSettings(screenModel) + val orientationType = remember(manga) { OrientationType.fromPreference(manga?.orientationType?.toInt()) } + SelectItem( + label = stringResource(R.string.rotation_type), + options = orientationTypeOptions.map { stringResource(it.first) }.toTypedArray(), + selectedIndex = orientationTypeOptions.indexOfFirst { it.second == orientationType }, + ) { + screenModel.onChangeOrientation(orientationTypeOptions[it].second) + } - WebtoonViewerSettings(screenModel) + val viewer by screenModel.viewerFlow.collectAsState() + if (viewer is WebtoonViewer) { + WebtoonViewerSettings(screenModel) + } else { + PagerViewerSettings(screenModel) + } } @Composable private fun ColumnScope.PagerViewerSettings(screenModel: ReaderSettingsScreenModel) { HeadingItem(R.string.pager_viewer) - // Tap zones - // Invert tap zones - // Scale type - // Zoom start position + val navigationModePager by screenModel.preferences.navigationModePager().collectAsState() + SelectItem( + label = stringResource(R.string.pref_viewer_nav), + options = ReaderPreferences.TapZones.map { stringResource(it) }.toTypedArray(), + selectedIndex = navigationModePager, + onSelect = { screenModel.preferences.navigationModePager().set(it) }, + ) + + if (navigationModePager != 5) { + val pagerNavInverted by screenModel.preferences.pagerNavInverted().collectAsState() + SelectItem( + label = stringResource(R.string.pref_read_with_tapping_inverted), + options = tappingInvertModeOptions.map { stringResource(it.first) }.toTypedArray(), + selectedIndex = tappingInvertModeOptions.indexOfFirst { it.second == pagerNavInverted }, + onSelect = { + screenModel.preferences.pagerNavInverted().set(tappingInvertModeOptions[it].second) + }, + ) + } + + val imageScaleType by screenModel.preferences.imageScaleType().collectAsState() + SelectItem( + label = stringResource(R.string.pref_image_scale_type), + options = ReaderPreferences.ImageScaleType.map { stringResource(it) }.toTypedArray(), + selectedIndex = imageScaleType - 1, + onSelect = { screenModel.preferences.imageScaleType().set(it + 1) }, + ) + + val zoomStart by screenModel.preferences.zoomStart().collectAsState() + SelectItem( + label = stringResource(R.string.pref_zoom_start), + options = ReaderPreferences.ZoomStart.map { stringResource(it) }.toTypedArray(), + selectedIndex = zoomStart - 1, + onSelect = { screenModel.preferences.zoomStart().set(it + 1) }, + ) val cropBorders by screenModel.preferences.cropBorders().collectAsState() CheckboxItem( @@ -111,8 +171,25 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM HeadingItem(R.string.webtoon_viewer) - // TODO: Tap zones - // TODO: Invert tap zones + val navigationModeWebtoon by screenModel.preferences.navigationModeWebtoon().collectAsState() + SelectItem( + label = stringResource(R.string.pref_viewer_nav), + options = ReaderPreferences.TapZones.map { stringResource(it) }.toTypedArray(), + selectedIndex = navigationModeWebtoon, + onSelect = { screenModel.preferences.navigationModeWebtoon().set(it) }, + ) + + if (navigationModeWebtoon != 5) { + val webtoonNavInverted by screenModel.preferences.webtoonNavInverted().collectAsState() + SelectItem( + label = stringResource(R.string.pref_read_with_tapping_inverted), + options = tappingInvertModeOptions.map { stringResource(it.first) }.toTypedArray(), + selectedIndex = tappingInvertModeOptions.indexOfFirst { it.second == webtoonNavInverted }, + onSelect = { + screenModel.preferences.webtoonNavInverted().set(tappingInvertModeOptions[it].second) + }, + ) + } val webtoonSidePadding by screenModel.preferences.webtoonSidePadding().collectAsState() SliderItem( @@ -146,7 +223,7 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM if (dualPageSplitWebtoon) { val dualPageInvertWebtoon by screenModel.preferences.dualPageInvertWebtoon() - .collectAsState() + .collectAsState() CheckboxItem( label = stringResource(R.string.pref_dual_page_invert), checked = dualPageInvertWebtoon, @@ -158,7 +235,7 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM if (!isReleaseBuildType) { val longStripSplitWebtoon by screenModel.preferences.longStripSplitWebtoon() - .collectAsState() + .collectAsState() CheckboxItem( label = stringResource(R.string.pref_long_strip_split), checked = longStripSplitWebtoon, 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 125b9bbfe..f905dcc24 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 @@ -131,6 +131,10 @@ class MainActivity : BaseActivity() { private var navigator: Navigator? = null + init { + registerSecureActivity(this) + } + override fun onCreate(savedInstanceState: Bundle?) { val isLaunch = savedInstanceState == null @@ -477,10 +481,6 @@ class MainActivity : BaseActivity() { return true } - init { - registerSecureActivity(this) - } - companion object { // Splash screen private const val SPLASH_MIN_DURATION = 500 // ms diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 08cb31f94..20f5ced69 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -69,7 +69,6 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel -import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsSheet import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer @@ -394,7 +393,13 @@ class ReaderActivity : BaseActivity() { binding.dialogRoot.setComposeContent { val state by viewModel.state.collectAsState() - val settingsScreenModel = remember { ReaderSettingsScreenModel() } + val settingsScreenModel = remember { + ReaderSettingsScreenModel( + readerState = viewModel.state, + onChangeReadingMode = viewModel::setMangaReadingMode, + onChangeOrientation = viewModel::setMangaOrientationType, + ) + } val onDismissRequest = viewModel::closeDialog when (state.dialog) { @@ -488,7 +493,7 @@ class ReaderActivity : BaseActivity() { ) { val newReadingMode = ReadingModeType.fromPreference(itemId) - viewModel.setMangaReadingMode(newReadingMode.flagValue) + viewModel.setMangaReadingMode(newReadingMode) menuToggleToast?.cancel() if (!readerPreferences.showReadingMode().get()) { @@ -542,7 +547,7 @@ class ReaderActivity : BaseActivity() { ) { val newOrientation = OrientationType.fromPreference(itemId) - viewModel.setMangaOrientationType(newOrientation.flagValue) + viewModel.setMangaOrientationType(newOrientation) menuToggleToast?.cancel() menuToggleToast = toast(newOrientation.stringRes) @@ -551,16 +556,6 @@ class ReaderActivity : BaseActivity() { } // Settings sheet - with(binding.actionSettingsLegacy) { - setTooltip(R.string.action_settings) - - var readerSettingSheet: ReaderSettingsSheet? = null - - setOnClickListener { - if (readerSettingSheet?.isShowing == true) return@setOnClickListener - readerSettingSheet = ReaderSettingsSheet(this@ReaderActivity).apply { show() } - } - } with(binding.actionSettings) { setTooltip(R.string.action_settings) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index f75e7f775..c05a18a09 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -621,10 +621,10 @@ class ReaderViewModel( /** * Updates the viewer position for the open manga. */ - fun setMangaReadingMode(readingModeType: Int) { + fun setMangaReadingMode(readingModeType: ReadingModeType) { val manga = manga ?: return runBlocking(Dispatchers.IO) { - setMangaViewerFlags.awaitSetMangaReadingMode(manga.id, readingModeType.toLong()) + setMangaViewerFlags.awaitSetMangaReadingMode(manga.id, readingModeType.flagValue.toLong()) val currChapters = state.value.viewerChapters if (currChapters != null) { // Save current page @@ -657,10 +657,10 @@ class ReaderViewModel( /** * Updates the orientation type for the open manga. */ - fun setMangaOrientationType(rotationType: Int) { + fun setMangaOrientationType(rotationType: OrientationType) { val manga = manga ?: return viewModelScope.launchIO { - setMangaViewerFlags.awaitSetOrientationType(manga.id, rotationType.toLong()) + setMangaViewerFlags.awaitSetOrientationType(manga.id, rotationType.flagValue.toLong()) val currChapters = state.value.viewerChapters if (currChapters != null) { // Save current page diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt index fb7df6282..0125a78a4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt @@ -18,8 +18,6 @@ enum class OrientationType(val prefValue: Int, val flag: Int, @StringRes val str companion object { const val MASK = 0x00000038 - fun fromPreference(preference: Int?): OrientationType = values().find { it.flagValue == preference } ?: FREE - - fun fromSpinner(position: Int?) = values().find { value -> value.prefValue == position } ?: DEFAULT + fun fromPreference(preference: Int?): OrientationType = values().find { it.flagValue == preference } ?: DEFAULT } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt index 1997e3495..236d57e4e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt @@ -1,5 +1,7 @@ package eu.kanade.tachiyomi.ui.reader.setting +import androidx.annotation.StringRes +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.isReleaseBuildType import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.getEnum @@ -124,11 +126,15 @@ class ReaderPreferences( // endregion - enum class TappingInvertMode(val shouldInvertHorizontal: Boolean = false, val shouldInvertVertical: Boolean = false) { - NONE, - HORIZONTAL(shouldInvertHorizontal = true), - VERTICAL(shouldInvertVertical = true), - BOTH(shouldInvertHorizontal = true, shouldInvertVertical = true), + enum class TappingInvertMode( + @StringRes val titleResId: Int, + val shouldInvertHorizontal: Boolean = false, + val shouldInvertVertical: Boolean = false, + ) { + NONE(R.string.tapping_inverted_none), + HORIZONTAL(R.string.tapping_inverted_horizontal, shouldInvertHorizontal = true), + VERTICAL(R.string.tapping_inverted_vertical, shouldInvertVertical = true), + BOTH(R.string.tapping_inverted_both, shouldInvertHorizontal = true, shouldInvertVertical = true), } enum class ReaderHideThreshold(val threshold: Int) { @@ -141,5 +147,30 @@ class ReaderPreferences( companion object { const val WEBTOON_PADDING_MIN = 0 const val WEBTOON_PADDING_MAX = 25 + + val TapZones = listOf( + R.string.label_default, + R.string.l_nav, + R.string.kindlish_nav, + R.string.edge_nav, + R.string.right_and_left_nav, + R.string.disabled_nav, + ) + + val ImageScaleType = listOf( + R.string.scale_type_fit_screen, + R.string.scale_type_stretch, + R.string.scale_type_fit_width, + R.string.scale_type_fit_height, + R.string.scale_type_original_size, + R.string.scale_type_smart_fit, + ) + + val ZoomStart = listOf( + R.string.zoom_start_automatic, + R.string.zoom_start_left, + R.string.zoom_start_right, + R.string.zoom_start_center, + ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt index 6a9ad2936..11125ac77 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt @@ -1,15 +1,35 @@ package eu.kanade.tachiyomi.ui.reader.setting import cafe.adriel.voyager.core.model.ScreenModel +import eu.kanade.presentation.util.ioCoroutineScope +import eu.kanade.tachiyomi.ui.reader.ReaderViewModel import eu.kanade.tachiyomi.util.preference.toggle +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import tachiyomi.core.preference.Preference import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class ReaderSettingsScreenModel( + readerState: StateFlow, + val onChangeReadingMode: (ReadingModeType) -> Unit, + val onChangeOrientation: (OrientationType) -> Unit, val preferences: ReaderPreferences = Injekt.get(), ) : ScreenModel { + val viewerFlow = readerState + .map { it.viewer } + .distinctUntilChanged() + .stateIn(ioCoroutineScope, SharingStarted.Lazily, null) + + val mangaFlow = readerState + .map { it.manga } + .distinctUntilChanged() + .stateIn(ioCoroutineScope, SharingStarted.Lazily, null) + fun togglePreference(preference: (ReaderPreferences) -> Preference) { preference(preferences).toggle() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt deleted file mode 100644 index dc449c2a3..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt +++ /dev/null @@ -1,89 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader.setting - -import android.os.Bundle -import androidx.core.view.isVisible -import androidx.lifecycle.lifecycleScope -import com.google.android.material.bottomsheet.BottomSheetDialog -import eu.kanade.domain.entries.manga.model.orientationType -import eu.kanade.domain.entries.manga.model.readingModeType -import eu.kanade.tachiyomi.databinding.ReaderReadingModeSettingsBinding -import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer -import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import uy.kohesive.injekt.injectLazy - -class ReaderSettingsSheet( - private val activity: ReaderActivity, -) : BottomSheetDialog(activity) { - - private val readerPreferences: ReaderPreferences by injectLazy() - - private lateinit var binding: ReaderReadingModeSettingsBinding - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - binding = ReaderReadingModeSettingsBinding.inflate(activity.layoutInflater) - setContentView(binding.root) - - initGeneralPreferences() - - when (activity.viewModel.state.value.viewer) { - is PagerViewer -> initPagerPreferences() - is WebtoonViewer -> initWebtoonPreferences() - } - } - - private fun initGeneralPreferences() { - binding.viewer.onItemSelectedListener = { position -> - val readingModeType = ReadingModeType.fromSpinner(position) - activity.viewModel.setMangaReadingMode(readingModeType.flagValue) - - val mangaViewer = activity.viewModel.getMangaReadingMode() - if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) { - initWebtoonPreferences() - } else { - initPagerPreferences() - } - } - binding.viewer.setSelection(activity.viewModel.manga?.readingModeType?.let { ReadingModeType.fromPreference(it.toInt()).prefValue } ?: ReadingModeType.DEFAULT.prefValue) - - binding.rotationMode.onItemSelectedListener = { position -> - val rotationType = OrientationType.fromSpinner(position) - activity.viewModel.setMangaOrientationType(rotationType.flagValue) - } - binding.rotationMode.setSelection(activity.viewModel.manga?.orientationType?.let { OrientationType.fromPreference(it.toInt()).prefValue } ?: OrientationType.DEFAULT.prefValue) - } - - private fun initPagerPreferences() { - binding.webtoonPrefsGroup.root.isVisible = false - binding.pagerPrefsGroup.root.isVisible = true - - binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), ReaderPreferences.TappingInvertMode::class.java) - - binding.pagerPrefsGroup.pagerNav.bindToPreference(readerPreferences.navigationModePager()) - readerPreferences.navigationModePager().changes() - .onEach { - val isTappingEnabled = it != 5 - binding.pagerPrefsGroup.tappingInverted.isVisible = isTappingEnabled - } - .launchIn(activity.lifecycleScope) - binding.pagerPrefsGroup.scaleType.bindToPreference(readerPreferences.imageScaleType(), 1) - - binding.pagerPrefsGroup.zoomStart.bindToPreference(readerPreferences.zoomStart(), 1) - } - - private fun initWebtoonPreferences() { - binding.pagerPrefsGroup.root.isVisible = false - binding.webtoonPrefsGroup.root.isVisible = true - - binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), ReaderPreferences.TappingInvertMode::class.java) - - binding.webtoonPrefsGroup.webtoonNav.bindToPreference(readerPreferences.navigationModeWebtoon()) - readerPreferences.navigationModeWebtoon().changes() - .onEach { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 } - .launchIn(activity.lifecycleScope) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt index 88e1a3f02..73975e28f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt @@ -29,8 +29,6 @@ enum class ReadingModeType(val prefValue: Int, @StringRes val stringRes: Int, @D return mode == LEFT_TO_RIGHT || mode == RIGHT_TO_LEFT || mode == VERTICAL } - fun fromSpinner(position: Int?) = values().find { value -> value.prefValue == position } ?: DEFAULT - fun toViewer(preference: Int?, activity: ReaderActivity): Viewer { return when (fromPreference(preference)) { LEFT_TO_RIGHT -> L2RPagerViewer(activity) diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt index c6076b790..77c3e9bff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt @@ -1,18 +1,9 @@ package eu.kanade.tachiyomi.util.preference -import android.widget.CompoundButton import eu.kanade.core.preference.PreferenceMutableState import kotlinx.coroutines.CoroutineScope import tachiyomi.core.preference.Preference -/** - * Binds a checkbox or switch view with a boolean preference. - */ -fun CompoundButton.bindToPreference(pref: Preference) { - isChecked = pref.get() - setOnCheckedChangeListener { _, isChecked -> pref.set(isChecked) } -} - operator fun Preference>.plusAssign(item: T) { set(get() + item) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/BottomSheetBehaviorExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/BottomSheetBehaviorExtensions.kt deleted file mode 100644 index a5da1a432..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/BottomSheetBehaviorExtensions.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package com.google.android.material.bottomsheet - -import android.view.View - -/** - * Returns package-private elevation value - */ -fun BottomSheetBehavior.getElevation(): Float { - return elevation.takeIf { it >= 0F } ?: 0F -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt index fc5a2fc1f..1a60b8e1e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt @@ -1,30 +1,7 @@ package eu.kanade.tachiyomi.util.view -import android.content.Context -import android.graphics.Color import android.view.Window import android.view.WindowManager -import com.google.android.material.elevation.ElevationOverlayProvider -import eu.kanade.tachiyomi.util.system.getResourceColor -import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim - -/** - * Sets navigation bar color to transparent if system's config_navBarNeedsScrim is false, - * otherwise it will use the theme navigationBarColor with 70% opacity. - * - * @see isNavigationBarNeedsScrim - */ -fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float = 0F) { - navigationBarColor = if (context.isNavigationBarNeedsScrim()) { - // Set navbar scrim 70% of navigationBarColor - ElevationOverlayProvider(context).compositeOverlayIfNeeded( - context.getResourceColor(android.R.attr.navigationBarColor, 0.7F), - elevation, - ) - } else { - Color.TRANSPARENT - } -} fun Window.setSecureScreen(enabled: Boolean) { if (enabled) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/MaterialSpinnerView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/MaterialSpinnerView.kt deleted file mode 100644 index 57289daf1..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/MaterialSpinnerView.kt +++ /dev/null @@ -1,154 +0,0 @@ -package eu.kanade.tachiyomi.widget - -import android.annotation.SuppressLint -import android.content.Context -import android.util.AttributeSet -import android.view.Gravity -import android.view.LayoutInflater -import android.view.MenuItem -import android.widget.FrameLayout -import androidx.appcompat.content.res.AppCompatResources -import androidx.appcompat.view.menu.MenuBuilder -import androidx.appcompat.widget.PopupMenu -import androidx.core.content.withStyledAttributes -import androidx.core.view.forEach -import androidx.core.view.get -import androidx.core.view.size -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.databinding.PrefSpinnerBinding -import eu.kanade.tachiyomi.util.system.getResourceColor -import tachiyomi.core.preference.Preference - -class MaterialSpinnerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - FrameLayout(context, attrs) { - - private var entries = emptyList() - private var selectedPosition = 0 - private var popup: PopupMenu? = null - - var onItemSelectedListener: ((Int) -> Unit)? = null - set(value) { - field = value - if (value != null) { - popup = makeSettingsPopup() - setOnTouchListener(popup?.dragToOpenListener) - setOnClickListener { - popup?.show() - } - } - } - - private val emptyIcon by lazy { - AppCompatResources.getDrawable(context, R.drawable.ic_blank_24dp) - } - private val checkmarkIcon by lazy { - AppCompatResources.getDrawable(context, R.drawable.ic_check_24dp)?.mutate()?.apply { - setTint(context.getResourceColor(android.R.attr.textColorPrimary)) - } - } - - private val binding = PrefSpinnerBinding.inflate(LayoutInflater.from(context), this, false) - - init { - addView(binding.root) - - context.withStyledAttributes(set = attrs, attrs = R.styleable.MaterialSpinnerView) { - val title = getString(R.styleable.MaterialSpinnerView_title).orEmpty() - binding.title.text = title - - val viewEntries = getTextArray(R.styleable.MaterialSpinnerView_android_entries) - .orEmpty() - .map { it.toString() } - entries = viewEntries - binding.details.text = viewEntries.firstOrNull().orEmpty() - } - } - - fun setSelection(selection: Int) { - if (selectedPosition < (popup?.menu?.size ?: 0)) { - popup?.menu?.getItem(selectedPosition)?.let { - it.icon = emptyIcon - } - } - selectedPosition = selection - popup?.menu?.getItem(selectedPosition)?.let { - it.icon = checkmarkIcon - } - binding.details.text = entries.getOrNull(selection).orEmpty() - } - - fun bindToPreference(pref: Preference, offset: Int = 0, block: ((Int) -> Unit)? = null) { - setSelection(pref.get() - offset) - - popup = makeSettingsPopup(pref, offset, block) - setOnTouchListener(popup?.dragToOpenListener) - setOnClickListener { - popup?.show() - } - } - - fun > bindToPreference(pref: Preference, clazz: Class) { - val enumConstants = clazz.enumConstants - enumConstants?.indexOf(pref.get())?.let { setSelection(it) } - - popup = makeSettingsPopup(pref, clazz) - setOnTouchListener(popup?.dragToOpenListener) - setOnClickListener { - popup?.show() - } - } - - private fun > makeSettingsPopup(preference: Preference, clazz: Class): PopupMenu { - return createPopupMenu { pos -> - onItemSelectedListener?.invoke(pos) - - val enumConstants = clazz.enumConstants - enumConstants?.get(pos)?.let { enumValue -> preference.set(enumValue) } - } - } - - private fun makeSettingsPopup(preference: Preference, intValues: List, block: ((Int) -> Unit)? = null): PopupMenu { - return createPopupMenu { pos -> - preference.set(intValues[pos] ?: 0) - block?.invoke(pos) - } - } - - private fun makeSettingsPopup(preference: Preference, offset: Int = 0, block: ((Int) -> Unit)? = null): PopupMenu { - return createPopupMenu { pos -> - preference.set(pos + offset) - block?.invoke(pos) - } - } - - private fun makeSettingsPopup(): PopupMenu { - return createPopupMenu { pos -> - onItemSelectedListener?.invoke(pos) - } - } - - private fun menuClicked(menuItem: MenuItem): Int { - val pos = menuItem.itemId - setSelection(pos) - return pos - } - - @SuppressLint("RestrictedApi") - fun createPopupMenu(onItemClick: (Int) -> Unit): PopupMenu { - val popup = PopupMenu(context, this, Gravity.END, R.attr.actionOverflowMenuStyle, 0) - entries.forEachIndexed { index, entry -> - popup.menu.add(0, index, 0, entry) - } - (popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true) - popup.menu.forEach { - it.icon = emptyIcon - } - popup.menu[selectedPosition].icon = checkmarkIcon - popup.setOnMenuItemClickListener { menuItem -> - val pos = menuClicked(menuItem) - onItemClick(pos) - true - } - return popup - } -} diff --git a/app/src/main/res/anim/bottom_sheet_slide_in.xml b/app/src/main/res/anim/bottom_sheet_slide_in.xml deleted file mode 100644 index 74f052a3f..000000000 --- a/app/src/main/res/anim/bottom_sheet_slide_in.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/app/src/main/res/anim/bottom_sheet_slide_out.xml b/app/src/main/res/anim/bottom_sheet_slide_out.xml deleted file mode 100644 index 553c827a5..000000000 --- a/app/src/main/res/anim/bottom_sheet_slide_out.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_expand_more_24dp.xml b/app/src/main/res/drawable/ic_expand_more_24dp.xml deleted file mode 100644 index 5f5ba4e44..000000000 --- a/app/src/main/res/drawable/ic_expand_more_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/pref_spinner.xml b/app/src/main/res/layout/pref_spinner.xml deleted file mode 100644 index c7335fa1f..000000000 --- a/app/src/main/res/layout/pref_spinner.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/reader_activity.xml b/app/src/main/res/layout/reader_activity.xml index 07f01e3fa..b451ecf4f 100644 --- a/app/src/main/res/layout/reader_activity.xml +++ b/app/src/main/res/layout/reader_activity.xml @@ -119,19 +119,6 @@ app:srcCompat="@drawable/ic_screen_rotation_24dp" app:tint="?attr/colorOnSurface" /> - - diff --git a/app/src/main/res/layout/reader_pager_settings.xml b/app/src/main/res/layout/reader_pager_settings.xml deleted file mode 100644 index 4b9e1f877..000000000 --- a/app/src/main/res/layout/reader_pager_settings.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/reader_reading_mode_settings.xml b/app/src/main/res/layout/reader_reading_mode_settings.xml deleted file mode 100644 index e825c229a..000000000 --- a/app/src/main/res/layout/reader_reading_mode_settings.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/reader_webtoon_settings.xml b/app/src/main/res/layout/reader_webtoon_settings.xml deleted file mode 100644 index 00e970665..000000000 --- a/app/src/main/res/layout/reader_webtoon_settings.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 030c8bcc8..d19dcd207 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -9,22 +9,6 @@ @string/vertical_plus_viewer - - @string/scale_type_fit_screen - @string/scale_type_stretch - @string/scale_type_fit_width - @string/scale_type_fit_height - @string/scale_type_original_size - @string/scale_type_smart_fit - - - - @string/zoom_start_automatic - @string/zoom_start_left - @string/zoom_start_right - @string/zoom_start_center - - @string/label_default @string/rotation_free @@ -35,31 +19,6 @@ @string/rotation_reverse_portrait - - @string/tapping_inverted_none - @string/tapping_inverted_horizontal - @string/tapping_inverted_vertical - @string/tapping_inverted_both - - - - @string/label_default - @string/l_nav - @string/kindlish_nav - @string/edge_nav - @string/right_and_left_nav - @string/disabled_nav - - - - @string/label_default - @string/l_nav - @string/kindlish_nav - @string/edge_nav - @string/right_and_left_nav - @string/disabled_nav - - @string/playback_options_speed @string/playback_options_quality diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f6f722017..e82288767 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,6 +1,4 @@ - 8dp - 16dp 16dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index fdcafe4c4..14dc1a13a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -20,33 +20,6 @@ - - - - - - - - - - - - - @@ -71,18 +44,6 @@ - - - - - - @@ -94,16 +55,6 @@ @bool/elevationOverlayEnabled - - - - - diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 9c6772a05..55dbab87d 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -66,15 +66,12 @@ @style/Theme.Tachiyomi.ActionButton.Overflow @drawable/ic_close_24dp @style/PreferenceThemeOverlay.Tachiyomi - @style/ThemeOverlay.Tachiyomi.BottomSheetDialog @style/Widget.Material3.TextInputLayout.OutlinedBox @style/Widget.Material3.AppBarLayout @style/Widget.Material3.Toolbar.Surface - @style/Widget.Tachiyomi.TabLayout @style/Widget.Tachiyomi.Switch @style/Widget.Material3.CompoundButton.MaterialSwitch @style/Widget.Tachiyomi.Switch - @style/Widget.Tachiyomi.Slider @style/Widget.Material3.CardView.Elevated diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt b/core/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt index 54d185883..38ba749d2 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -11,67 +11,60 @@ import java.io.File import java.util.concurrent.TimeUnit class NetworkHelper( - context: Context, + private val context: Context, private val preferences: NetworkPreferences, ) { - private val cacheDir = File(context.cacheDir, "network_cache") - private val cacheSize = 5L * 1024 * 1024 // 5 MiB - val cookieJar = AndroidCookieJar() - private val userAgentInterceptor by lazy { - UserAgentInterceptor(::defaultUserAgentProvider) - } - private val cloudflareInterceptor by lazy { - CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider) - } + val client: OkHttpClient = run { + val builder = OkHttpClient.Builder() + .cookieJar(cookieJar) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .callTimeout(2, TimeUnit.MINUTES) + .cache( + Cache( + directory = File(context.cacheDir, "network_cache"), + maxSize = 5L * 1024 * 1024, // 5 MiB + ), + ) + .addInterceptor(UncaughtExceptionInterceptor()) + .addInterceptor(UserAgentInterceptor(::defaultUserAgentProvider)) - private val baseClientBuilder: OkHttpClient.Builder - get() { - val builder = OkHttpClient.Builder() - .cookieJar(cookieJar) - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .callTimeout(2, TimeUnit.MINUTES) - .addInterceptor(UncaughtExceptionInterceptor()) - .addInterceptor(userAgentInterceptor) - - if (preferences.verboseLogging().get()) { - val httpLoggingInterceptor = HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.HEADERS - } - builder.addNetworkInterceptor(httpLoggingInterceptor) + if (preferences.verboseLogging().get()) { + val httpLoggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.HEADERS } - - builder.addInterceptor(cloudflareInterceptor) - - when (preferences.dohProvider().get()) { - PREF_DOH_CLOUDFLARE -> builder.dohCloudflare() - PREF_DOH_GOOGLE -> builder.dohGoogle() - PREF_DOH_ADGUARD -> builder.dohAdGuard() - PREF_DOH_QUAD9 -> builder.dohQuad9() - PREF_DOH_ALIDNS -> builder.dohAliDNS() - PREF_DOH_DNSPOD -> builder.dohDNSPod() - PREF_DOH_360 -> builder.doh360() - PREF_DOH_QUAD101 -> builder.dohQuad101() - PREF_DOH_MULLVAD -> builder.dohMullvad() - PREF_DOH_CONTROLD -> builder.dohControlD() - PREF_DOH_NJALLA -> builder.dohNajalla() - PREF_DOH_SHECAN -> builder.dohShecan() - } - - return builder + builder.addNetworkInterceptor(httpLoggingInterceptor) } - val client by lazy { baseClientBuilder.cache(Cache(cacheDir, cacheSize)).build() } + builder.addInterceptor(CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider)) + + when (preferences.dohProvider().get()) { + PREF_DOH_CLOUDFLARE -> builder.dohCloudflare() + PREF_DOH_GOOGLE -> builder.dohGoogle() + PREF_DOH_ADGUARD -> builder.dohAdGuard() + PREF_DOH_QUAD9 -> builder.dohQuad9() + PREF_DOH_ALIDNS -> builder.dohAliDNS() + PREF_DOH_DNSPOD -> builder.dohDNSPod() + PREF_DOH_360 -> builder.doh360() + PREF_DOH_QUAD101 -> builder.dohQuad101() + PREF_DOH_MULLVAD -> builder.dohMullvad() + PREF_DOH_CONTROLD -> builder.dohControlD() + PREF_DOH_NJALLA -> builder.dohNajalla() + PREF_DOH_SHECAN -> builder.dohShecan() + } + + builder.build() + } /** * @deprecated Since extension-lib 1.5 */ @Deprecated("The regular client handles Cloudflare by default") @Suppress("UNUSED") - val cloudflareClient = client + val cloudflareClient: OkHttpClient = client fun defaultUserAgentProvider() = preferences.defaultUserAgent().get().trim() } diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 6839031f5..387dbc444 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -380,7 +380,7 @@ White Gray Black - Automatic + Auto Default reading mode L shaped Kindle-ish diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt index 285c7f200..363298c85 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt @@ -6,6 +6,8 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.FlowRowScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer @@ -73,7 +75,10 @@ fun HeadingItem( style = MaterialTheme.typography.header, modifier = Modifier .fillMaxWidth() - .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), ) } @@ -215,7 +220,10 @@ fun SelectItem( modifier = Modifier .menuAnchor() .fillMaxWidth() - .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), label = { Text(text = label) }, value = options[selectedIndex].toString(), onValueChange = {}, @@ -271,7 +279,10 @@ fun TriStateItem( }, ) .fillMaxWidth() - .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp), ) { @@ -466,6 +477,26 @@ fun TextItem( ) } +@Composable +fun SettingsFlowRow( + @StringRes labelRes: Int, + content: @Composable FlowRowScope.() -> Unit, +) { + Column { + HeadingItem(labelRes) + FlowRow( + modifier = Modifier.padding( + start = SettingsItemsPaddings.Horizontal, + top = 0.dp, + end = SettingsItemsPaddings.Horizontal, + bottom = SettingsItemsPaddings.Vertical, + ), + horizontalArrangement = Arrangement.spacedBy(4.dp), + content = content, + ) + } +} + @Composable private fun BaseSettingsItem( label: String, @@ -476,7 +507,10 @@ private fun BaseSettingsItem( modifier = Modifier .clickable(onClick = onClick) .fillMaxWidth() - .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp), ) { diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Chip.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Chip.kt new file mode 100644 index 000000000..55d988f52 --- /dev/null +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Chip.kt @@ -0,0 +1,60 @@ +package tachiyomi.presentation.core.components.material + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredHeight +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ProvideTextStyle +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +fun Chip( + modifier: Modifier = Modifier, + backgroundColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.15f), + contentColor: Color = MaterialTheme.colorScheme.onSurface, + onClick: () -> Unit = {}, + content: @Composable () -> Unit, +) { + Surface( + modifier = Modifier, + shape = CircleShape, + color = backgroundColor, + contentColor = contentColor, + onClick = {}, + ) { + Row( + modifier = modifier.clickable(onClick = onClick) + .widthIn(min = 56.dp) + .requiredHeight(32.dp) + .padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + ProvideTextStyle(MaterialTheme.typography.bodySmall, content) + } + } +} + +@Composable +fun ChoiceChip( + modifier: Modifier = Modifier, + isSelected: Boolean, + onClick: () -> Unit = {}, + selectedBackgroundColor: Color = MaterialTheme.colorScheme.primary, + selectedContentColor: Color = MaterialTheme.colorScheme.onPrimary, + content: @Composable () -> Unit, +) { + if (isSelected) { + Chip(modifier, selectedBackgroundColor, selectedContentColor, onClick, content) + } else { + Chip(modifier, onClick = onClick, content = content) + } +} diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/SegmentedButtons.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/SegmentedButtons.kt new file mode 100644 index 000000000..7656af007 --- /dev/null +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/SegmentedButtons.kt @@ -0,0 +1,90 @@ +package tachiyomi.presentation.core.components.material + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview + +val StartItemShape = RoundedCornerShape(topStartPercent = 100, bottomStartPercent = 100) +val MiddleItemShape = RoundedCornerShape(0) +val EndItemShape = RoundedCornerShape(topEndPercent = 100, bottomEndPercent = 100) + +@Composable +fun SegmentedButtons( + modifier: Modifier = Modifier, + entries: List, + selectedIndex: Int, + onClick: (Int) -> Unit, +) { + Row( + modifier = modifier, + ) { + entries.mapIndexed { index, label -> + val shape = remember(entries, index) { + when (index) { + 0 -> StartItemShape + entries.lastIndex -> EndItemShape + else -> MiddleItemShape + } + } + + if (index == selectedIndex) { + Button( + modifier = Modifier.weight(1f), + shape = shape, + onClick = { onClick(index) }, + ) { + Text( + text = label, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } else { + OutlinedButton( + modifier = Modifier.weight(1f), + shape = shape, + onClick = { onClick(index) }, + ) { + Text( + text = label, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } + } + } +} + +@Preview +@Composable +private fun SegmentedButtonsPreview() { + Column { + SegmentedButtons( + entries = listOf( + "Day", + "Week", + "Month", + "Year", + ), + selectedIndex = 1, + onClick = {}, + ) + + SegmentedButtons( + entries = listOf( + "Foo", + "Bar", + ), + selectedIndex = 1, + onClick = {}, + ) + } +}