mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-21 12:17:12 +03:00
parent
e7b1066e3c
commit
02717061ba
37 changed files with 484 additions and 798 deletions
|
@ -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),
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ReaderViewModel.State>,
|
||||
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<Boolean>) {
|
||||
preference(preferences).toggle()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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<Boolean>) {
|
||||
isChecked = pref.get()
|
||||
setOnCheckedChangeListener { _, isChecked -> pref.set(isChecked) }
|
||||
}
|
||||
|
||||
operator fun <T> Preference<Set<T>>.plusAssign(item: T) {
|
||||
set(get() + item)
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
@file:Suppress("PackageDirectoryMismatch")
|
||||
|
||||
package com.google.android.material.bottomsheet
|
||||
|
||||
import android.view.View
|
||||
|
||||
/**
|
||||
* Returns package-private elevation value
|
||||
*/
|
||||
fun <T : View> BottomSheetBehavior<T>.getElevation(): Float {
|
||||
return elevation.takeIf { it >= 0F } ?: 0F
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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<String>()
|
||||
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<Int>, offset: Int = 0, block: ((Int) -> Unit)? = null) {
|
||||
setSelection(pref.get() - offset)
|
||||
|
||||
popup = makeSettingsPopup(pref, offset, block)
|
||||
setOnTouchListener(popup?.dragToOpenListener)
|
||||
setOnClickListener {
|
||||
popup?.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Enum<T>> bindToPreference(pref: Preference<T>, clazz: Class<T>) {
|
||||
val enumConstants = clazz.enumConstants
|
||||
enumConstants?.indexOf(pref.get())?.let { setSelection(it) }
|
||||
|
||||
popup = makeSettingsPopup(pref, clazz)
|
||||
setOnTouchListener(popup?.dragToOpenListener)
|
||||
setOnClickListener {
|
||||
popup?.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Enum<T>> makeSettingsPopup(preference: Preference<T>, clazz: Class<T>): PopupMenu {
|
||||
return createPopupMenu { pos ->
|
||||
onItemSelectedListener?.invoke(pos)
|
||||
|
||||
val enumConstants = clazz.enumConstants
|
||||
enumConstants?.get(pos)?.let { enumValue -> preference.set(enumValue) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeSettingsPopup(preference: Preference<Int>, intValues: List<Int?>, block: ((Int) -> Unit)? = null): PopupMenu {
|
||||
return createPopupMenu { pos ->
|
||||
preference.set(intValues[pos] ?: 0)
|
||||
block?.invoke(pos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeSettingsPopup(preference: Preference<Int>, 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
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="300"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in">
|
||||
|
||||
<translate
|
||||
android:fromYDelta="100%p"
|
||||
android:toYDelta="0" />
|
||||
|
||||
</set>
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="300"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in">
|
||||
|
||||
<translate
|
||||
android:fromYDelta="0"
|
||||
android:toYDelta="100%p" />
|
||||
|
||||
</set>
|
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z" />
|
||||
</vector>
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/listPreferredItemHeightSmall"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="8dp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/center_guideline"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Title" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/details"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/dropdown_caret"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/center_guideline"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Details" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dropdown_caret"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="1dp"
|
||||
android:src="@drawable/ic_expand_more_24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/details"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/details"
|
||||
app:tint="?android:attr/colorControlNormal"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/center_guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -119,19 +119,6 @@
|
|||
app:srcCompat="@drawable/ic_screen_rotation_24dp"
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_settings_legacy"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_settings"
|
||||
android:padding="@dimen/screen_edge_margin"
|
||||
app:layout_constraintEnd_toStartOf="@+id/action_settings"
|
||||
app:layout_constraintStart_toEndOf="@id/action_rotation"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_settings_24dp"
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_settings"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -140,7 +127,7 @@
|
|||
android:contentDescription="@string/action_settings"
|
||||
android:padding="@dimen/screen_edge_margin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/action_settings_legacy"
|
||||
app:layout_constraintStart_toEndOf="@id/action_rotation"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_settings_24dp"
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pager_prefs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/pager_viewer"
|
||||
android:textAppearance="@style/TextAppearance.Tachiyomi.SectionHeader" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/pager_nav"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/pager_nav"
|
||||
app:title="@string/pref_viewer_nav" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/tapping_inverted"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/invert_tapping_mode"
|
||||
app:title="@string/pref_read_with_tapping_inverted" />
|
||||
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/scale_type"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/image_scale_type"
|
||||
app:title="@string/pref_image_scale_type" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/zoom_start"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/zoom_start"
|
||||
app:title="@string/pref_zoom_start" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,56 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/for_this_series_prefs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/pref_category_for_this_series"
|
||||
android:textAppearance="@style/TextAppearance.Tachiyomi.SectionHeader" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/viewer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/viewers_selector"
|
||||
app:title="@string/pref_category_reading_mode" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/rotation_mode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:entries="@array/rotation_type"
|
||||
app:title="@string/rotation_type" />
|
||||
|
||||
<include
|
||||
android:id="@+id/pager_prefs_group"
|
||||
layout="@layout/reader_pager_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<include
|
||||
android:id="@+id/webtoon_prefs_group"
|
||||
layout="@layout/reader_webtoon_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/webtoon_prefs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/webtoon_viewer"
|
||||
android:textAppearance="@style/TextAppearance.Tachiyomi.SectionHeader" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/webtoon_nav"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/webtoon_nav"
|
||||
app:title="@string/pref_viewer_nav" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/tapping_inverted"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/invert_tapping_mode"
|
||||
app:title="@string/pref_read_with_tapping_inverted" />
|
||||
|
||||
</LinearLayout>
|
|
@ -9,22 +9,6 @@
|
|||
<item>@string/vertical_plus_viewer</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="image_scale_type">
|
||||
<item>@string/scale_type_fit_screen</item>
|
||||
<item>@string/scale_type_stretch</item>
|
||||
<item>@string/scale_type_fit_width</item>
|
||||
<item>@string/scale_type_fit_height</item>
|
||||
<item>@string/scale_type_original_size</item>
|
||||
<item>@string/scale_type_smart_fit</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="zoom_start">
|
||||
<item>@string/zoom_start_automatic</item>
|
||||
<item>@string/zoom_start_left</item>
|
||||
<item>@string/zoom_start_right</item>
|
||||
<item>@string/zoom_start_center</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="rotation_type">
|
||||
<item>@string/label_default</item>
|
||||
<item>@string/rotation_free</item>
|
||||
|
@ -35,31 +19,6 @@
|
|||
<item>@string/rotation_reverse_portrait</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="invert_tapping_mode">
|
||||
<item>@string/tapping_inverted_none</item>
|
||||
<item>@string/tapping_inverted_horizontal</item>
|
||||
<item>@string/tapping_inverted_vertical</item>
|
||||
<item>@string/tapping_inverted_both</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pager_nav">
|
||||
<item>@string/label_default</item>
|
||||
<item>@string/l_nav</item>
|
||||
<item>@string/kindlish_nav</item>
|
||||
<item>@string/edge_nav</item>
|
||||
<item>@string/right_and_left_nav</item>
|
||||
<item>@string/disabled_nav</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="webtoon_nav">
|
||||
<item>@string/label_default</item>
|
||||
<item>@string/l_nav</item>
|
||||
<item>@string/kindlish_nav</item>
|
||||
<item>@string/edge_nav</item>
|
||||
<item>@string/right_and_left_nav</item>
|
||||
<item>@string/disabled_nav</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="playback_options">
|
||||
<item>@string/playback_options_speed</item>
|
||||
<item>@string/playback_options_quality</item>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<resources>
|
||||
<dimen name="dialog_radius">8dp</dimen>
|
||||
|
||||
<dimen name="screen_edge_margin">16dp</dimen>
|
||||
|
||||
<dimen name="appwidget_background_radius">16dp</dimen>
|
||||
|
|
|
@ -20,33 +20,6 @@
|
|||
</style>
|
||||
|
||||
|
||||
<!--===========-->
|
||||
<!--BottomSheet-->
|
||||
<!--===========-->
|
||||
|
||||
<style name="ThemeOverlay.Tachiyomi.BottomSheetDialog" parent="ThemeOverlay.Material3.BottomSheetDialog">
|
||||
<item name="bottomSheetStyle">@style/Widget.Tachiyomi.BottomSheet.Modal</item>
|
||||
<item name="android:windowAnimationStyle">@style/Animation.Tachiyomi.BottomSheetDialog</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Tachiyomi.BottomSheet.Modal" parent="Widget.Material3.BottomSheet.Modal">
|
||||
<item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.Tachiyomi.BottomSheet</item>
|
||||
</style>
|
||||
|
||||
<style name="ShapeAppearanceOverlay.Tachiyomi.BottomSheet" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSizeTopRight">@dimen/dialog_radius</item>
|
||||
<item name="cornerSizeTopLeft">@dimen/dialog_radius</item>
|
||||
<item name="cornerSizeBottomRight">0dp</item>
|
||||
<item name="cornerSizeBottomLeft">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Animation.Tachiyomi.BottomSheetDialog" parent="Animation.AppCompat.Dialog">
|
||||
<item name="android:windowEnterAnimation">@anim/bottom_sheet_slide_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/bottom_sheet_slide_out</item>
|
||||
</style>
|
||||
|
||||
|
||||
<!--===============-->
|
||||
<!--Text Appearance-->
|
||||
<!--===============-->
|
||||
|
@ -71,18 +44,6 @@
|
|||
</style>
|
||||
|
||||
|
||||
<!--=================-->
|
||||
<!--Widgets.TabLayout-->
|
||||
<!--=================-->
|
||||
<style name="Widget.Tachiyomi.TabLayout" parent="Widget.Material3.TabLayout">
|
||||
<item name="tabGravity">center</item>
|
||||
<item name="tabInlineLabel">true</item>
|
||||
<item name="tabMinWidth">75dp</item>
|
||||
<item name="tabMode">scrollable</item>
|
||||
<item name="tabRippleColor">@color/ripple_toolbar_fainter</item>
|
||||
</style>
|
||||
|
||||
|
||||
<!--==============-->
|
||||
<!--Widgets.Switch-->
|
||||
<!--==============-->
|
||||
|
@ -94,16 +55,6 @@
|
|||
<item name="elevationOverlayEnabled">@bool/elevationOverlayEnabled</item>
|
||||
</style>
|
||||
|
||||
<!--==============-->
|
||||
<!--Widgets.Slider-->
|
||||
<!--==============-->
|
||||
<style name="Widget.Tachiyomi.Slider" parent="Widget.Material3.Slider">
|
||||
<item name="labelBehavior">gone</item>
|
||||
<item name="tickVisible">false</item>
|
||||
<item name="trackColorInactive">@color/slider_inactive_track</item>
|
||||
<item name="trackColorActive">@color/slider_active_track</item>
|
||||
</style>
|
||||
|
||||
|
||||
<!--============-->
|
||||
<!--FastScroller-->
|
||||
|
|
|
@ -66,15 +66,12 @@
|
|||
<item name="actionOverflowButtonStyle">@style/Theme.Tachiyomi.ActionButton.Overflow</item>
|
||||
<item name="actionModeCloseDrawable">@drawable/ic_close_24dp</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Tachiyomi</item>
|
||||
<item name="bottomSheetDialogTheme">@style/ThemeOverlay.Tachiyomi.BottomSheetDialog</item>
|
||||
<item name="textInputStyle">@style/Widget.Material3.TextInputLayout.OutlinedBox</item>
|
||||
<item name="appBarLayoutStyle">@style/Widget.Material3.AppBarLayout</item>
|
||||
<item name="toolbarStyle">@style/Widget.Material3.Toolbar.Surface</item>
|
||||
<item name="tabStyle">@style/Widget.Tachiyomi.TabLayout</item>
|
||||
<item name="switchStyle">@style/Widget.Tachiyomi.Switch</item>
|
||||
<item name="materialSwitchStyle">@style/Widget.Material3.CompoundButton.MaterialSwitch</item>
|
||||
<item name="switchPreferenceCompatStyle">@style/Widget.Tachiyomi.Switch</item>
|
||||
<item name="sliderStyle">@style/Widget.Tachiyomi.Slider</item>
|
||||
<item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item>
|
||||
|
||||
<!-- Preference text appearance -->
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -380,7 +380,7 @@
|
|||
<string name="white_background">White</string>
|
||||
<string name="gray_background">Gray</string>
|
||||
<string name="black_background">Black</string>
|
||||
<string name="automatic_background">Automatic</string>
|
||||
<string name="automatic_background">Auto</string>
|
||||
<string name="pref_viewer_type">Default reading mode</string>
|
||||
<string name="l_nav">L shaped</string>
|
||||
<string name="kindlish_nav">Kindle-ish</string>
|
||||
|
|
|
@ -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),
|
||||
) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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<String>,
|
||||
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 = {},
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue