Last commit merged: 34b9c82cd0
This commit is contained in:
LuftVerbot 2023-10-27 20:27:23 +02:00
parent 564a959bab
commit f51b36a759
38 changed files with 383 additions and 442 deletions

View file

@ -56,7 +56,7 @@ import tachiyomi.domain.category.anime.interactor.RenameAnimeCategory
import tachiyomi.domain.category.anime.interactor.ReorderAnimeCategory import tachiyomi.domain.category.anime.interactor.ReorderAnimeCategory
import tachiyomi.domain.category.anime.interactor.ResetAnimeCategoryFlags import tachiyomi.domain.category.anime.interactor.ResetAnimeCategoryFlags
import tachiyomi.domain.category.anime.interactor.SetAnimeCategories import tachiyomi.domain.category.anime.interactor.SetAnimeCategories
import tachiyomi.domain.category.anime.interactor.SetDisplayModeForAnimeCategory import tachiyomi.domain.category.anime.interactor.SetAnimeDisplayMode
import tachiyomi.domain.category.anime.interactor.SetSortModeForAnimeCategory import tachiyomi.domain.category.anime.interactor.SetSortModeForAnimeCategory
import tachiyomi.domain.category.anime.interactor.UpdateAnimeCategory import tachiyomi.domain.category.anime.interactor.UpdateAnimeCategory
import tachiyomi.domain.category.anime.repository.AnimeCategoryRepository import tachiyomi.domain.category.anime.repository.AnimeCategoryRepository
@ -68,8 +68,8 @@ import tachiyomi.domain.category.manga.interactor.HideMangaCategory
import tachiyomi.domain.category.manga.interactor.RenameMangaCategory import tachiyomi.domain.category.manga.interactor.RenameMangaCategory
import tachiyomi.domain.category.manga.interactor.ReorderMangaCategory import tachiyomi.domain.category.manga.interactor.ReorderMangaCategory
import tachiyomi.domain.category.manga.interactor.ResetMangaCategoryFlags import tachiyomi.domain.category.manga.interactor.ResetMangaCategoryFlags
import tachiyomi.domain.category.manga.interactor.SetDisplayModeForMangaCategory
import tachiyomi.domain.category.manga.interactor.SetMangaCategories import tachiyomi.domain.category.manga.interactor.SetMangaCategories
import tachiyomi.domain.category.manga.interactor.SetMangaDisplayMode
import tachiyomi.domain.category.manga.interactor.SetSortModeForMangaCategory import tachiyomi.domain.category.manga.interactor.SetSortModeForMangaCategory
import tachiyomi.domain.category.manga.interactor.UpdateMangaCategory import tachiyomi.domain.category.manga.interactor.UpdateMangaCategory
import tachiyomi.domain.category.manga.repository.MangaCategoryRepository import tachiyomi.domain.category.manga.repository.MangaCategoryRepository
@ -151,7 +151,7 @@ class DomainModule : InjektModule {
addFactory { GetAnimeCategories(get()) } addFactory { GetAnimeCategories(get()) }
addFactory { GetVisibleAnimeCategories(get()) } addFactory { GetVisibleAnimeCategories(get()) }
addFactory { ResetAnimeCategoryFlags(get(), get()) } addFactory { ResetAnimeCategoryFlags(get(), get()) }
addFactory { SetDisplayModeForAnimeCategory(get(), get()) } addFactory { SetAnimeDisplayMode(get()) }
addFactory { SetSortModeForAnimeCategory(get(), get()) } addFactory { SetSortModeForAnimeCategory(get(), get()) }
addFactory { CreateAnimeCategoryWithName(get(), get()) } addFactory { CreateAnimeCategoryWithName(get(), get()) }
addFactory { RenameAnimeCategory(get()) } addFactory { RenameAnimeCategory(get()) }
@ -164,7 +164,7 @@ class DomainModule : InjektModule {
addFactory { GetMangaCategories(get()) } addFactory { GetMangaCategories(get()) }
addFactory { GetVisibleMangaCategories(get()) } addFactory { GetVisibleMangaCategories(get()) }
addFactory { ResetMangaCategoryFlags(get(), get()) } addFactory { ResetMangaCategoryFlags(get(), get()) }
addFactory { SetDisplayModeForMangaCategory(get(), get()) } addFactory { SetMangaDisplayMode(get()) }
addFactory { SetSortModeForMangaCategory(get(), get()) } addFactory { SetSortModeForMangaCategory(get(), get()) }
addFactory { CreateMangaCategoryWithName(get(), get()) } addFactory { CreateMangaCategoryWithName(get(), get()) }
addFactory { RenameMangaCategory(get()) } addFactory { RenameMangaCategory(get()) }

View file

@ -43,7 +43,7 @@ fun AnimeLibraryContent(
onRefresh: (Category?) -> Boolean, onRefresh: (Category?) -> Boolean,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
getNumberOfAnimeForCategory: (Category) -> Int?, getNumberOfAnimeForCategory: (Category) -> Int?,
getDisplayModeForPage: @Composable (Int) -> LibraryDisplayMode, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
getAnimeLibraryForPage: (Int) -> List<AnimeLibraryItem>, getAnimeLibraryForPage: (Int) -> List<AnimeLibraryItem>,
) { ) {
@ -103,7 +103,7 @@ fun AnimeLibraryContent(
selectedAnime = selection, selectedAnime = selection,
searchQuery = searchQuery, searchQuery = searchQuery,
onGlobalSearchClicked = onGlobalSearchClicked, onGlobalSearchClicked = onGlobalSearchClicked,
getDisplayModeForPage = getDisplayModeForPage, getDisplayMode = getDisplayMode,
getColumnsForOrientation = getColumnsForOrientation, getColumnsForOrientation = getColumnsForOrientation,
getLibraryForPage = getAnimeLibraryForPage, getLibraryForPage = getAnimeLibraryForPage,
onClickAnime = onClickAnime, onClickAnime = onClickAnime,

View file

@ -26,7 +26,7 @@ fun AnimeLibraryPager(
selectedAnime: List<LibraryAnime>, selectedAnime: List<LibraryAnime>,
searchQuery: String?, searchQuery: String?,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
getDisplayModeForPage: @Composable (Int) -> LibraryDisplayMode, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
getLibraryForPage: (Int) -> List<AnimeLibraryItem>, getLibraryForPage: (Int) -> List<AnimeLibraryItem>,
onClickAnime: (LibraryAnime) -> Unit, onClickAnime: (LibraryAnime) -> Unit,
@ -54,7 +54,7 @@ fun AnimeLibraryPager(
return@HorizontalPager return@HorizontalPager
} }
val displayMode = getDisplayModeForPage(page) val displayMode by getDisplayMode(page)
val columns by if (displayMode != LibraryDisplayMode.List) { val columns by if (displayMode != LibraryDisplayMode.List) {
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

View file

@ -33,7 +33,6 @@ import tachiyomi.domain.entries.TriStateFilter
import tachiyomi.domain.library.anime.model.AnimeLibrarySort import tachiyomi.domain.library.anime.model.AnimeLibrarySort
import tachiyomi.domain.library.anime.model.sort import tachiyomi.domain.library.anime.model.sort
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.display
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
@ -45,7 +44,7 @@ import tachiyomi.presentation.core.components.SortItem
fun AnimeLibrarySettingsDialog( fun AnimeLibrarySettingsDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
screenModel: AnimeLibrarySettingsScreenModel, screenModel: AnimeLibrarySettingsScreenModel,
category: Category, category: Category?,
) { ) {
TabbedDialog( TabbedDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@ -69,7 +68,6 @@ fun AnimeLibrarySettingsDialog(
screenModel = screenModel, screenModel = screenModel,
) )
2 -> DisplayPage( 2 -> DisplayPage(
category = category,
screenModel = screenModel, screenModel = screenModel,
) )
} }
@ -148,7 +146,7 @@ private fun ColumnScope.FilterPage(
@Composable @Composable
private fun ColumnScope.SortPage( private fun ColumnScope.SortPage(
category: Category, category: Category?,
screenModel: AnimeLibrarySettingsScreenModel, screenModel: AnimeLibrarySettingsScreenModel,
) { ) {
val sortingMode = category.sort.type val sortingMode = category.sort.type
@ -182,10 +180,10 @@ private fun ColumnScope.SortPage(
@Composable @Composable
private fun ColumnScope.DisplayPage( private fun ColumnScope.DisplayPage(
category: Category,
screenModel: AnimeLibrarySettingsScreenModel, screenModel: AnimeLibrarySettingsScreenModel,
) { ) {
HeadingItem(R.string.action_display_mode) HeadingItem(R.string.action_display_mode)
val displayMode by screenModel.libraryPreferences.libraryDisplayMode().collectAsState()
listOf( listOf(
R.string.action_display_grid to LibraryDisplayMode.CompactGrid, R.string.action_display_grid to LibraryDisplayMode.CompactGrid,
R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid, R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid,
@ -194,12 +192,12 @@ private fun ColumnScope.DisplayPage(
).map { (titleRes, mode) -> ).map { (titleRes, mode) ->
RadioItem( RadioItem(
label = stringResource(titleRes), label = stringResource(titleRes),
selected = category.display == mode, selected = displayMode == mode,
onClick = { screenModel.setDisplayMode(category, mode) }, onClick = { screenModel.setDisplayMode(mode) },
) )
} }
if (category.display != LibraryDisplayMode.List) { if (displayMode != LibraryDisplayMode.List) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View file

@ -43,7 +43,7 @@ fun MangaLibraryContent(
onRefresh: (Category?) -> Boolean, onRefresh: (Category?) -> Boolean,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
getNumberOfMangaForCategory: (Category) -> Int?, getNumberOfMangaForCategory: (Category) -> Int?,
getDisplayModeForPage: @Composable (Int) -> LibraryDisplayMode, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
getLibraryForPage: (Int) -> List<MangaLibraryItem>, getLibraryForPage: (Int) -> List<MangaLibraryItem>,
) { ) {
@ -103,7 +103,7 @@ fun MangaLibraryContent(
selectedManga = selection, selectedManga = selection,
searchQuery = searchQuery, searchQuery = searchQuery,
onGlobalSearchClicked = onGlobalSearchClicked, onGlobalSearchClicked = onGlobalSearchClicked,
getDisplayModeForPage = getDisplayModeForPage, getDisplayMode = getDisplayMode,
getColumnsForOrientation = getColumnsForOrientation, getColumnsForOrientation = getColumnsForOrientation,
getLibraryForPage = getLibraryForPage, getLibraryForPage = getLibraryForPage,
onClickManga = onClickManga, onClickManga = onClickManga,

View file

@ -35,7 +35,7 @@ fun MangaLibraryPager(
selectedManga: List<LibraryManga>, selectedManga: List<LibraryManga>,
searchQuery: String?, searchQuery: String?,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
getDisplayModeForPage: @Composable (Int) -> LibraryDisplayMode, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
getLibraryForPage: (Int) -> List<MangaLibraryItem>, getLibraryForPage: (Int) -> List<MangaLibraryItem>,
onClickManga: (LibraryManga) -> Unit, onClickManga: (LibraryManga) -> Unit,
@ -63,7 +63,7 @@ fun MangaLibraryPager(
return@HorizontalPager return@HorizontalPager
} }
val displayMode = getDisplayModeForPage(page) val displayMode by getDisplayMode(page)
val columns by if (displayMode != LibraryDisplayMode.List) { val columns by if (displayMode != LibraryDisplayMode.List) {
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

View file

@ -33,7 +33,6 @@ import tachiyomi.domain.entries.TriStateFilter
import tachiyomi.domain.library.manga.model.MangaLibrarySort import tachiyomi.domain.library.manga.model.MangaLibrarySort
import tachiyomi.domain.library.manga.model.sort import tachiyomi.domain.library.manga.model.sort
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.display
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
@ -45,7 +44,7 @@ import tachiyomi.presentation.core.components.SortItem
fun MangaLibrarySettingsDialog( fun MangaLibrarySettingsDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
screenModel: MangaLibrarySettingsScreenModel, screenModel: MangaLibrarySettingsScreenModel,
category: Category, category: Category?,
) { ) {
TabbedDialog( TabbedDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@ -69,7 +68,6 @@ fun MangaLibrarySettingsDialog(
screenModel = screenModel, screenModel = screenModel,
) )
2 -> DisplayPage( 2 -> DisplayPage(
category = category,
screenModel = screenModel, screenModel = screenModel,
) )
} }
@ -148,7 +146,7 @@ private fun ColumnScope.FilterPage(
@Composable @Composable
private fun ColumnScope.SortPage( private fun ColumnScope.SortPage(
category: Category, category: Category?,
screenModel: MangaLibrarySettingsScreenModel, screenModel: MangaLibrarySettingsScreenModel,
) { ) {
val sortingMode = category.sort.type val sortingMode = category.sort.type
@ -181,10 +179,10 @@ private fun ColumnScope.SortPage(
@Composable @Composable
private fun ColumnScope.DisplayPage( private fun ColumnScope.DisplayPage(
category: Category,
screenModel: MangaLibrarySettingsScreenModel, screenModel: MangaLibrarySettingsScreenModel,
) { ) {
HeadingItem(R.string.action_display_mode) HeadingItem(R.string.action_display_mode)
val displayMode by screenModel.libraryPreferences.libraryDisplayMode().collectAsState()
listOf( listOf(
R.string.action_display_grid to LibraryDisplayMode.CompactGrid, R.string.action_display_grid to LibraryDisplayMode.CompactGrid,
R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid, R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid,
@ -193,12 +191,12 @@ private fun ColumnScope.DisplayPage(
).map { (titleRes, mode) -> ).map { (titleRes, mode) ->
RadioItem( RadioItem(
label = stringResource(titleRes), label = stringResource(titleRes),
selected = category.display == mode, selected = displayMode == mode,
onClick = { screenModel.setDisplayMode(category, mode) }, onClick = { screenModel.setDisplayMode(mode) },
) )
} }
if (category.display != LibraryDisplayMode.List) { if (displayMode != LibraryDisplayMode.List) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View file

@ -3,6 +3,7 @@ package eu.kanade.presentation.more.settings.screen
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.os.Build
import android.provider.Settings import android.provider.Settings
import android.webkit.WebStorage import android.webkit.WebStorage
import android.webkit.WebView import android.webkit.WebView
@ -73,6 +74,7 @@ import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
object SettingsAdvancedScreen : SearchableSettings { object SettingsAdvancedScreen : SearchableSettings {
@ReadOnlyComposable @ReadOnlyComposable
@Composable @Composable
@StringRes @StringRes
@ -87,44 +89,65 @@ object SettingsAdvancedScreen : SearchableSettings {
val basePreferences = remember { Injekt.get<BasePreferences>() } val basePreferences = remember { Injekt.get<BasePreferences>() }
val networkPreferences = remember { Injekt.get<NetworkPreferences>() } val networkPreferences = remember { Injekt.get<NetworkPreferences>() }
return listOf( return buildList {
Preference.PreferenceItem.SwitchPreference( addAll(
pref = basePreferences.acraEnabled(), listOf(
title = stringResource(R.string.pref_enable_acra), Preference.PreferenceItem.SwitchPreference(
subtitle = stringResource(R.string.pref_acra_summary), pref = basePreferences.acraEnabled(),
enabled = isPreviewBuildType || isReleaseBuildType, title = stringResource(R.string.pref_enable_acra),
), subtitle = stringResource(R.string.pref_acra_summary),
Preference.PreferenceItem.TextPreference( enabled = isPreviewBuildType || isReleaseBuildType,
title = stringResource(R.string.pref_dump_crash_logs), ),
subtitle = stringResource(R.string.pref_dump_crash_logs_summary), Preference.PreferenceItem.TextPreference(
onClick = { title = stringResource(R.string.pref_dump_crash_logs),
scope.launch { subtitle = stringResource(R.string.pref_dump_crash_logs_summary),
CrashLogUtil(context).dumpLogs() onClick = {
} scope.launch {
}, CrashLogUtil(context).dumpLogs()
), }
Preference.PreferenceItem.SwitchPreference( },
pref = networkPreferences.verboseLogging(), ),
title = stringResource(R.string.pref_verbose_logging), Preference.PreferenceItem.SwitchPreference(
subtitle = stringResource(R.string.pref_verbose_logging_summary), pref = networkPreferences.verboseLogging(),
onValueChanged = { title = stringResource(R.string.pref_verbose_logging),
context.toast(R.string.requires_app_restart) subtitle = stringResource(R.string.pref_verbose_logging_summary),
true onValueChanged = {
}, context.toast(R.string.requires_app_restart)
), true
Preference.PreferenceItem.TextPreference( },
title = stringResource(R.string.pref_debug_info), ),
onClick = { navigator.push(DebugInfoScreen) }, Preference.PreferenceItem.TextPreference(
), title = stringResource(R.string.pref_debug_info),
getBackgroundActivityGroup(), onClick = { navigator.push(DebugInfoScreen) },
getDataGroup(), ),
getNetworkGroup(networkPreferences = networkPreferences), ),
getLibraryGroup(), )
getExtensionsGroup(basePreferences = basePreferences), if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// SY --> add(
getDataSaverGroup(), Preference.PreferenceItem.TextPreference(
// SY <-- title = stringResource(R.string.pref_manage_notifications),
) onClick = {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
}
context.startActivity(intent)
},
),
)
}
addAll(
listOf(
getBackgroundActivityGroup(),
getDataGroup(),
getNetworkGroup(networkPreferences = networkPreferences),
getLibraryGroup(),
getExtensionsGroup(basePreferences = basePreferences),
// SY -->
getDataSaverGroup(),
// SY <--
),
)
}
} }
@Composable @Composable

View file

@ -4,14 +4,18 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.os.LocaleListCompat
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.TabletUiMode import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.domain.ui.model.ThemeMode
@ -19,10 +23,14 @@ import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.util.collectAsState import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.home.HomeScreen
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import org.xmlpull.v1.XmlPullParser
import tachiyomi.domain.library.service.LibraryPreferences
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Date import java.util.Date
@ -103,9 +111,61 @@ object SettingsAppearanceScreen : SearchableSettings {
context: Context, context: Context,
uiPreferences: UiPreferences, uiPreferences: UiPreferences,
): Preference.PreferenceGroup { ): Preference.PreferenceGroup {
val langs = remember { getLangs(context) }
var currentLanguage by remember { mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "") }
LaunchedEffect(currentLanguage) {
val locale = if (currentLanguage.isEmpty()) {
LocaleListCompat.getEmptyLocaleList()
} else {
LocaleListCompat.forLanguageTags(currentLanguage)
}
AppCompatDelegate.setApplicationLocales(locale)
}
val libraryPrefs = remember { Injekt.get<LibraryPreferences>() }
LaunchedEffect(Unit) {
libraryPrefs.bottomNavStyle().changes()
.drop(1)
.collectLatest { value ->
HomeScreen.tabs = when (value) {
0 -> HomeScreen.tabsNoHistory
1 -> HomeScreen.tabsNoUpdates
else -> HomeScreen.tabsNoManga
}
(context as? Activity)?.let {
ActivityCompat.recreate(it)
}
}
}
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(R.string.pref_category_display), title = stringResource(R.string.pref_category_display),
preferenceItems = listOf( preferenceItems = listOf(
Preference.PreferenceItem.ListPreference(
pref = libraryPrefs.bottomNavStyle(),
title = stringResource(R.string.pref_bottom_nav_style),
entries = mapOf(
0 to stringResource(R.string.pref_bottom_nav_no_history),
1 to stringResource(R.string.pref_bottom_nav_no_updates),
2 to stringResource(R.string.pref_bottom_nav_no_manga),
),
),
Preference.PreferenceItem.SwitchPreference(
pref = libraryPrefs.isDefaultHomeTabLibraryManga(),
title = stringResource(R.string.pref_default_home_tab_library),
enabled = libraryPrefs.bottomNavStyle().get() != 2,
),
Preference.PreferenceItem.BasicListPreference(
value = currentLanguage,
title = stringResource(R.string.pref_app_language),
entries = langs,
onValueChanged = { newValue ->
currentLanguage = newValue
true
},
),
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = uiPreferences.tabletUiMode(), pref = uiPreferences.tabletUiMode(),
title = stringResource(R.string.pref_tablet_ui_mode), title = stringResource(R.string.pref_tablet_ui_mode),
@ -146,6 +206,31 @@ object SettingsAppearanceScreen : SearchableSettings {
), ),
) )
} }
private fun getLangs(context: Context): Map<String, String> {
val langs = mutableListOf<Pair<String, String>>()
val parser = context.resources.getXml(R.xml.locales_config)
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && parser.name == "locale") {
for (i in 0 until parser.attributeCount) {
if (parser.getAttributeName(i) == "name") {
val langTag = parser.getAttributeValue(i)
val displayName = LocaleHelper.getDisplayName(langTag)
if (displayName.isNotEmpty()) {
langs.add(Pair(langTag, displayName))
}
}
}
}
eventType = parser.next()
}
langs.sortBy { it.second }
langs.add(0, Pair("", context.getString(R.string.label_default)))
return langs.toMap()
}
} }
private val DateFormats = listOf( private val DateFormats = listOf(

View file

@ -1,149 +0,0 @@
package eu.kanade.presentation.more.settings.screen
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.Settings
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.app.ActivityCompat
import androidx.core.os.LocaleListCompat
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.home.HomeScreen
import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
import org.xmlpull.v1.XmlPullParser
import tachiyomi.domain.library.service.LibraryPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object SettingsGeneralScreen : SearchableSettings {
@Composable
@ReadOnlyComposable
@StringRes
override fun getTitleRes() = R.string.pref_category_general
@Composable
override fun getPreferences(): List<Preference> {
val libraryPrefs = remember { Injekt.get<LibraryPreferences>() }
val context = LocalContext.current
LaunchedEffect(Unit) {
libraryPrefs.bottomNavStyle().changes()
.drop(1)
.collectLatest { value ->
HomeScreen.tabs = when (value) {
0 -> HomeScreen.tabsNoHistory
1 -> HomeScreen.tabsNoUpdates
else -> HomeScreen.tabsNoManga
}
(context as? Activity)?.let {
ActivityCompat.recreate(it)
}
}
}
val langs = remember { getLangs(context) }
var currentLanguage by remember { mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "") }
return buildList {
add(
Preference.PreferenceItem.ListPreference(
pref = libraryPrefs.bottomNavStyle(),
title = stringResource(R.string.pref_bottom_nav_style),
entries = mapOf(
0 to stringResource(R.string.pref_bottom_nav_no_history),
1 to stringResource(R.string.pref_bottom_nav_no_updates),
2 to stringResource(R.string.pref_bottom_nav_no_manga),
),
),
)
add(
Preference.PreferenceItem.SwitchPreference(
pref = libraryPrefs.isDefaultHomeTabLibraryManga(),
title = stringResource(R.string.pref_default_home_tab_library),
enabled = libraryPrefs.bottomNavStyle().get() != 2,
),
)
add(
Preference.PreferenceItem.SwitchPreference(
pref = libraryPrefs.newShowUpdatesCount(),
title = stringResource(R.string.pref_library_update_show_tab_badge),
),
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
add(
Preference.PreferenceItem.TextPreference(
title = stringResource(R.string.pref_manage_notifications),
onClick = {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
}
context.startActivity(intent)
},
),
)
}
add(
Preference.PreferenceItem.BasicListPreference(
value = currentLanguage,
title = stringResource(R.string.pref_app_language),
entries = langs,
onValueChanged = { newValue ->
currentLanguage = newValue
true
},
),
)
LaunchedEffect(currentLanguage) {
val locale = if (currentLanguage.isEmpty()) {
LocaleListCompat.getEmptyLocaleList()
} else {
LocaleListCompat.forLanguageTags(currentLanguage)
}
AppCompatDelegate.setApplicationLocales(locale)
}
}
}
private fun getLangs(context: Context): Map<String, String> {
val langs = mutableListOf<Pair<String, String>>()
val parser = context.resources.getXml(R.xml.locales_config)
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && parser.name == "locale") {
for (i in 0 until parser.attributeCount) {
if (parser.getAttributeName(i) == "name") {
val langTag = parser.getAttributeValue(i)
val displayName = LocaleHelper.getDisplayName(langTag)
if (displayName.isNotEmpty()) {
langs.add(Pair(langTag, displayName))
}
}
}
}
eventType = parser.next()
}
langs.sortBy { it.second }
langs.add(0, Pair("", context.getString(R.string.label_default)))
return langs.toMap()
}
}

View file

@ -365,7 +365,6 @@ object SettingsLibraryScreen : SearchableSettings {
Preference.PreferenceItem.InfoPreference( Preference.PreferenceItem.InfoPreference(
title = stringResource(R.string.pref_update_release_grace_period_info), title = stringResource(R.string.pref_update_release_grace_period_info),
).takeIf { ENTRY_OUTSIDE_RELEASE_PERIOD in libraryUpdateMangaRestriction && isDevFlavor }, ).takeIf { ENTRY_OUTSIDE_RELEASE_PERIOD in libraryUpdateMangaRestriction && isDevFlavor },
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(R.string.pref_update_anime_release_grace_period), title = stringResource(R.string.pref_update_anime_release_grace_period),
subtitle = listOf( subtitle = listOf(
@ -377,6 +376,11 @@ object SettingsLibraryScreen : SearchableSettings {
Preference.PreferenceItem.InfoPreference( Preference.PreferenceItem.InfoPreference(
title = stringResource(R.string.pref_update_release_grace_period_info), title = stringResource(R.string.pref_update_release_grace_period_info),
).takeIf { ENTRY_OUTSIDE_RELEASE_PERIOD in libraryUpdateAnimeRestriction && isDevFlavor }, ).takeIf { ENTRY_OUTSIDE_RELEASE_PERIOD in libraryUpdateAnimeRestriction && isDevFlavor },
Preference.PreferenceItem.SwitchPreference(
pref = libraryPreferences.newShowUpdatesCount(),
title = stringResource(R.string.pref_library_update_show_tab_badge),
),
), ),
) )
} }

View file

@ -21,7 +21,6 @@ import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Security import androidx.compose.material.icons.outlined.Security
import androidx.compose.material.icons.outlined.SettingsBackupRestore import androidx.compose.material.icons.outlined.SettingsBackupRestore
import androidx.compose.material.icons.outlined.Sync import androidx.compose.material.icons.outlined.Sync
import androidx.compose.material.icons.outlined.Tune
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
@ -187,12 +186,6 @@ object SettingsMainScreen : Screen() {
) )
private val items = listOf( private val items = listOf(
Item(
titleRes = R.string.pref_category_general,
subtitleRes = R.string.pref_general_summary,
icon = Icons.Outlined.Tune,
screen = SettingsGeneralScreen,
),
Item( Item(
titleRes = R.string.pref_category_appearance, titleRes = R.string.pref_category_appearance,
subtitleRes = R.string.pref_appearance_summary, subtitleRes = R.string.pref_appearance_summary,

View file

@ -283,7 +283,6 @@ private fun getLocalizedBreadcrumb(path: String, node: String?): String {
} }
private val settingScreens = listOf( private val settingScreens = listOf(
SettingsGeneralScreen,
SettingsAppearanceScreen, SettingsAppearanceScreen,
SettingsLibraryScreen, SettingsLibraryScreen,
SettingsReaderScreen, SettingsReaderScreen,

View file

@ -32,7 +32,6 @@ object SettingsSecurityScreen : SearchableSettings {
val authSupported = remember { context.isAuthenticationSupported() } val authSupported = remember { context.isAuthenticationSupported() }
val useAuthPref = securityPreferences.useAuthenticator() val useAuthPref = securityPreferences.useAuthenticator()
val useAuth by useAuthPref.collectAsState() val useAuth by useAuthPref.collectAsState()
return listOf( return listOf(

View file

@ -58,6 +58,7 @@ import tachiyomi.domain.items.episode.model.Episode
import tachiyomi.domain.library.anime.LibraryAnime import tachiyomi.domain.library.anime.LibraryAnime
import tachiyomi.domain.library.anime.model.AnimeLibrarySort import tachiyomi.domain.library.anime.model.AnimeLibrarySort
import tachiyomi.domain.library.anime.model.sort import tachiyomi.domain.library.anime.model.sort
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.anime.service.AnimeSourceManager import tachiyomi.domain.source.anime.service.AnimeSourceManager
import tachiyomi.domain.track.anime.interactor.GetTracksPerAnime import tachiyomi.domain.track.anime.interactor.GetTracksPerAnime
@ -524,6 +525,10 @@ class AnimeLibraryScreenModel(
} }
} }
fun getDisplayMode(): PreferenceMutableState<LibraryDisplayMode> {
return libraryPreferences.libraryDisplayMode().asState(coroutineScope)
}
fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> { fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> {
return (if (isLandscape) libraryPreferences.animeLandscapeColumns() else libraryPreferences.animePortraitColumns()).asState(coroutineScope) return (if (isLandscape) libraryPreferences.animeLandscapeColumns() else libraryPreferences.animePortraitColumns()).asState(coroutineScope)
} }

View file

@ -8,8 +8,7 @@ import eu.kanade.tachiyomi.util.preference.toggle
import tachiyomi.core.preference.Preference import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.getAndSet import tachiyomi.core.preference.getAndSet
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.category.anime.interactor.GetAnimeCategories import tachiyomi.domain.category.anime.interactor.SetAnimeDisplayMode
import tachiyomi.domain.category.anime.interactor.SetDisplayModeForAnimeCategory
import tachiyomi.domain.category.anime.interactor.SetSortModeForAnimeCategory import tachiyomi.domain.category.anime.interactor.SetSortModeForAnimeCategory
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.domain.entries.TriStateFilter import tachiyomi.domain.entries.TriStateFilter
@ -22,8 +21,7 @@ import uy.kohesive.injekt.api.get
class AnimeLibrarySettingsScreenModel( class AnimeLibrarySettingsScreenModel(
val preferences: BasePreferences = Injekt.get(), val preferences: BasePreferences = Injekt.get(),
val libraryPreferences: LibraryPreferences = Injekt.get(), val libraryPreferences: LibraryPreferences = Injekt.get(),
private val getCategories: GetAnimeCategories = Injekt.get(), private val setAnimeDisplayMode: SetAnimeDisplayMode = Injekt.get(),
private val setDisplayModeForCategory: SetDisplayModeForAnimeCategory = Injekt.get(),
private val setSortModeForCategory: SetSortModeForAnimeCategory = Injekt.get(), private val setSortModeForCategory: SetSortModeForAnimeCategory = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(), private val trackManager: TrackManager = Injekt.get(),
) : ScreenModel { ) : ScreenModel {
@ -45,13 +43,11 @@ class AnimeLibrarySettingsScreenModel(
toggleFilter { libraryPreferences.filterTrackedAnime(id) } toggleFilter { libraryPreferences.filterTrackedAnime(id) }
} }
fun setDisplayMode(category: Category, mode: LibraryDisplayMode) { fun setDisplayMode(mode: LibraryDisplayMode) {
coroutineScope.launchIO { setAnimeDisplayMode.await(mode)
setDisplayModeForCategory.await(category, mode)
}
} }
fun setSort(category: Category, mode: AnimeLibrarySort.Type, direction: AnimeLibrarySort.Direction) { fun setSort(category: Category?, mode: AnimeLibrarySort.Type, direction: AnimeLibrarySort.Direction) {
coroutineScope.launchIO { coroutineScope.launchIO {
setSortModeForCategory.await(category, mode, direction) setSortModeForCategory.await(category, mode, direction)
} }

View file

@ -54,7 +54,6 @@ import tachiyomi.domain.category.model.Category
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.items.episode.model.Episode import tachiyomi.domain.items.episode.model.Episode
import tachiyomi.domain.library.anime.LibraryAnime import tachiyomi.domain.library.anime.LibraryAnime
import tachiyomi.domain.library.model.display
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
@ -212,7 +211,7 @@ object AnimeLibraryTab : Tab() {
navigator.push(GlobalAnimeSearchScreen(screenModel.state.value.searchQuery ?: "")) navigator.push(GlobalAnimeSearchScreen(screenModel.state.value.searchQuery ?: ""))
}, },
getNumberOfAnimeForCategory = { state.getAnimeCountForCategory(it) }, getNumberOfAnimeForCategory = { state.getAnimeCountForCategory(it) },
getDisplayModeForPage = { state.categories[it].display }, getDisplayMode = { screenModel.getDisplayMode() },
getColumnsForOrientation = { screenModel.getColumnsPreferenceForCurrentOrientation(it) }, getColumnsForOrientation = { screenModel.getColumnsPreferenceForCurrentOrientation(it) },
) { state.getAnimelibItemsByPage(it) } ) { state.getAnimelibItemsByPage(it) }
} }

View file

@ -58,6 +58,7 @@ import tachiyomi.domain.items.chapter.model.Chapter
import tachiyomi.domain.library.manga.LibraryManga import tachiyomi.domain.library.manga.LibraryManga
import tachiyomi.domain.library.manga.model.MangaLibrarySort import tachiyomi.domain.library.manga.model.MangaLibrarySort
import tachiyomi.domain.library.manga.model.sort import tachiyomi.domain.library.manga.model.sort
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.manga.service.MangaSourceManager import tachiyomi.domain.source.manga.service.MangaSourceManager
import tachiyomi.domain.track.manga.interactor.GetTracksPerManga import tachiyomi.domain.track.manga.interactor.GetTracksPerManga
@ -518,6 +519,10 @@ class MangaLibraryScreenModel(
} }
} }
fun getDisplayMode(): PreferenceMutableState<LibraryDisplayMode> {
return libraryPreferences.libraryDisplayMode().asState(coroutineScope)
}
fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> { fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> {
return (if (isLandscape) libraryPreferences.mangaLandscapeColumns() else libraryPreferences.mangaPortraitColumns()).asState(coroutineScope) return (if (isLandscape) libraryPreferences.mangaLandscapeColumns() else libraryPreferences.mangaPortraitColumns()).asState(coroutineScope)
} }

View file

@ -8,8 +8,7 @@ import eu.kanade.tachiyomi.util.preference.toggle
import tachiyomi.core.preference.Preference import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.getAndSet import tachiyomi.core.preference.getAndSet
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.category.manga.interactor.GetMangaCategories import tachiyomi.domain.category.manga.interactor.SetMangaDisplayMode
import tachiyomi.domain.category.manga.interactor.SetDisplayModeForMangaCategory
import tachiyomi.domain.category.manga.interactor.SetSortModeForMangaCategory import tachiyomi.domain.category.manga.interactor.SetSortModeForMangaCategory
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.domain.entries.TriStateFilter import tachiyomi.domain.entries.TriStateFilter
@ -22,8 +21,7 @@ import uy.kohesive.injekt.api.get
class MangaLibrarySettingsScreenModel( class MangaLibrarySettingsScreenModel(
val preferences: BasePreferences = Injekt.get(), val preferences: BasePreferences = Injekt.get(),
val libraryPreferences: LibraryPreferences = Injekt.get(), val libraryPreferences: LibraryPreferences = Injekt.get(),
private val getCategories: GetMangaCategories = Injekt.get(), private val setMangaDisplayMode: SetMangaDisplayMode = Injekt.get(),
private val setDisplayModeForCategory: SetDisplayModeForMangaCategory = Injekt.get(),
private val setSortModeForCategory: SetSortModeForMangaCategory = Injekt.get(), private val setSortModeForCategory: SetSortModeForMangaCategory = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(), private val trackManager: TrackManager = Injekt.get(),
) : ScreenModel { ) : ScreenModel {
@ -45,13 +43,11 @@ class MangaLibrarySettingsScreenModel(
toggleFilter { libraryPreferences.filterTrackedManga(id) } toggleFilter { libraryPreferences.filterTrackedManga(id) }
} }
fun setDisplayMode(category: Category, mode: LibraryDisplayMode) { fun setDisplayMode(mode: LibraryDisplayMode) {
coroutineScope.launchIO { setMangaDisplayMode.await(mode)
setDisplayModeForCategory.await(category, mode)
}
} }
fun setSort(category: Category, mode: MangaLibrarySort.Type, direction: MangaLibrarySort.Direction) { fun setSort(category: Category?, mode: MangaLibrarySort.Type, direction: MangaLibrarySort.Direction) {
coroutineScope.launchIO { coroutineScope.launchIO {
setSortModeForCategory.await(category, mode, direction) setSortModeForCategory.await(category, mode, direction)
} }

View file

@ -53,7 +53,6 @@ import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.library.manga.LibraryManga import tachiyomi.domain.library.manga.LibraryManga
import tachiyomi.domain.library.model.display
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
@ -213,7 +212,7 @@ object MangaLibraryTab : Tab() {
navigator.push(GlobalMangaSearchScreen(screenModel.state.value.searchQuery ?: "")) navigator.push(GlobalMangaSearchScreen(screenModel.state.value.searchQuery ?: ""))
}, },
getNumberOfMangaForCategory = { state.getMangaCountForCategory(it) }, getNumberOfMangaForCategory = { state.getMangaCountForCategory(it) },
getDisplayModeForPage = { state.categories[it].display }, getDisplayMode = { screenModel.getDisplayMode() },
getColumnsForOrientation = { screenModel.getColumnsPreferenceForCurrentOrientation(it) }, getColumnsForOrientation = { screenModel.getColumnsPreferenceForCurrentOrientation(it) },
) { state.getLibraryItemsByPage(it) } ) { state.getLibraryItemsByPage(it) }
} }

View file

@ -15,7 +15,6 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import android.view.Gravity
import android.view.KeyEvent import android.view.KeyEvent
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -24,7 +23,6 @@ import android.view.View.LAYER_TYPE_HARDWARE
import android.view.WindowManager import android.view.WindowManager
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.AnimationUtils import android.view.animation.AnimationUtils
import android.widget.FrameLayout
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -36,7 +34,6 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.internal.ToolbarUtils import com.google.android.material.internal.ToolbarUtils
@ -70,7 +67,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.preference.toggle import eu.kanade.tachiyomi.util.preference.toggle
import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.util.system.hasDisplayCutout import eu.kanade.tachiyomi.util.system.hasDisplayCutout
import eu.kanade.tachiyomi.util.system.isNightMode import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toShareIntent
@ -662,12 +658,7 @@ class ReaderActivity : BaseActivity() {
supportActionBar?.title = manga.title supportActionBar?.title = manga.title
val loadingIndicatorContext = createReaderThemeContext() loadingIndicator = ReaderProgressIndicator(this)
loadingIndicator = ReaderProgressIndicator(loadingIndicatorContext).apply {
updateLayoutParams<FrameLayout.LayoutParams> {
gravity = Gravity.CENTER
}
}
binding.readerContainer.addView(loadingIndicator) binding.readerContainer.addView(loadingIndicator)
startPostponedEnterTransition() startPostponedEnterTransition()

View file

@ -2,13 +2,20 @@ package eu.kanade.tachiyomi.ui.reader.viewer
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.Gravity
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.IntRange import androidx.annotation.IntRange
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.view.isVisible
import com.google.android.material.progressindicator.CircularProgressIndicator import com.google.android.material.progressindicator.CircularProgressIndicator
import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.presentation.core.components.CombinedCircularProgressIndicator
/** /**
* A wrapper for [CircularProgressIndicator] that always rotates. * A wrapper for [CircularProgressIndicator] that always rotates.
@ -19,76 +26,31 @@ class ReaderProgressIndicator @JvmOverloads constructor(
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = 0, defStyleAttr: Int = 0,
) : FrameLayout(context, attrs, defStyleAttr) { ) : AbstractComposeView(context, attrs, defStyleAttr) {
private val indicator: CircularProgressIndicator
private val rotateAnimation by lazy {
RotateAnimation(
0F,
360F,
Animation.RELATIVE_TO_SELF,
0.5F,
Animation.RELATIVE_TO_SELF,
0.5F,
).apply {
interpolator = LinearInterpolator()
repeatCount = Animation.INFINITE
duration = 4000
}
}
init { init {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT) layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER)
indicator = CircularProgressIndicator(context) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool)
indicator.max = 100
indicator.isIndeterminate = true
addView(indicator)
} }
override fun onAttachedToWindow() { private var progress by mutableFloatStateOf(0f)
super.onAttachedToWindow()
updateRotateAnimation()
}
override fun onDetachedFromWindow() { @Composable
super.onDetachedFromWindow() override fun Content() {
updateRotateAnimation() TachiyomiTheme {
CombinedCircularProgressIndicator(progress = progress)
}
} }
fun show() { fun show() {
indicator.show() isVisible = true
updateRotateAnimation()
} }
fun hide() { fun hide() {
indicator.hide() isVisible = false
updateRotateAnimation()
} }
/** fun setProgress(@IntRange(from = 0, to = 100) progress: Int) {
* Sets the current indicator progress to the specified value. this.progress = progress / 100f
*
* @param progress Indicator will be set indeterminate if this value is 0
*/
fun setProgress(@IntRange(from = 0, to = 100) progress: Int, animated: Boolean = true) {
if (progress > 0) {
indicator.setProgressCompat(progress, animated)
} else if (!indicator.isIndeterminate) {
indicator.hide()
indicator.isIndeterminate = true
indicator.show()
}
updateRotateAnimation()
}
private fun updateRotateAnimation() {
if (isAttachedToWindow && indicator.isShown && !indicator.isIndeterminate) {
if (animation == null) {
startAnimation(rotateAnimation)
}
} else {
clearAnimation()
}
} }
} }

View file

@ -2,10 +2,8 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import eu.kanade.tachiyomi.databinding.ReaderErrorBinding import eu.kanade.tachiyomi.databinding.ReaderErrorBinding
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.InsertPage
@ -46,11 +44,7 @@ class PagerPageHolder(
/** /**
* Loading progress bar to indicate the current progress. * Loading progress bar to indicate the current progress.
*/ */
private val progressIndicator: ReaderProgressIndicator = ReaderProgressIndicator(readerThemedContext).apply { private val progressIndicator: ReaderProgressIndicator = ReaderProgressIndicator(readerThemedContext)
updateLayoutParams<LayoutParams> {
gravity = Gravity.CENTER
}
}
/** /**
* Error layout to show when the image fails to load. * Error layout to show when the image fails to load.

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.reader.viewer.webtoon package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import android.content.res.Resources import android.content.res.Resources
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
@ -119,7 +118,7 @@ class WebtoonPageHolder(
removeErrorLayout() removeErrorLayout()
frame.recycle() frame.recycle()
progressIndicator.setProgress(0, animated = false) progressIndicator.setProgress(0)
} }
/** /**
@ -288,7 +287,6 @@ class WebtoonPageHolder(
val progress = ReaderProgressIndicator(context).apply { val progress = ReaderProgressIndicator(context).apply {
updateLayoutParams<FrameLayout.LayoutParams> { updateLayoutParams<FrameLayout.LayoutParams> {
gravity = Gravity.CENTER_HORIZONTAL
updateMargins(top = parentHeight / 4) updateMargins(top = parentHeight / 4)
} }
} }

View file

@ -13,8 +13,8 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.more.settings.screen.AboutScreen import eu.kanade.presentation.more.settings.screen.AboutScreen
import eu.kanade.presentation.more.settings.screen.SettingsAppearanceScreen
import eu.kanade.presentation.more.settings.screen.SettingsBackupScreen import eu.kanade.presentation.more.settings.screen.SettingsBackupScreen
import eu.kanade.presentation.more.settings.screen.SettingsGeneralScreen
import eu.kanade.presentation.more.settings.screen.SettingsMainScreen import eu.kanade.presentation.more.settings.screen.SettingsMainScreen
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.LocalBackPress
@ -59,7 +59,7 @@ class SettingsScreen private constructor(
} else if (toAbout) { } else if (toAbout) {
AboutScreen AboutScreen
} else { } else {
SettingsGeneralScreen SettingsAppearanceScreen
}, },
) { ) {
val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal) val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)

View file

@ -15,9 +15,7 @@ class CreateAnimeCategoryWithName(
private val initialFlags: Long private val initialFlags: Long
get() { get() {
val sort = preferences.libraryAnimeSortingMode().get() val sort = preferences.libraryAnimeSortingMode().get()
return preferences.libraryDisplayMode().get().flag or return sort.type.flag or sort.direction.flag
sort.type.flag or
sort.direction.flag
} }
suspend fun await(name: String): Result = withNonCancellableContext { suspend fun await(name: String): Result = withNonCancellableContext {

View file

@ -10,8 +10,7 @@ class ResetAnimeCategoryFlags(
) { ) {
suspend fun await() { suspend fun await() {
val display = preferences.libraryDisplayMode().get()
val sort = preferences.libraryAnimeSortingMode().get() val sort = preferences.libraryAnimeSortingMode().get()
categoryRepository.updateAllAnimeCategoryFlags(display + sort.type + sort.direction) categoryRepository.updateAllAnimeCategoryFlags(sort.type + sort.direction)
} }
} }

View file

@ -0,0 +1,13 @@
package tachiyomi.domain.category.anime.interactor
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.service.LibraryPreferences
class SetAnimeDisplayMode(
private val preferences: LibraryPreferences,
) {
fun await(display: LibraryDisplayMode) {
preferences.libraryDisplayMode().set(display)
}
}

View file

@ -1,34 +0,0 @@
package tachiyomi.domain.category.anime.interactor
import tachiyomi.domain.category.anime.repository.AnimeCategoryRepository
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.plus
import tachiyomi.domain.library.service.LibraryPreferences
class SetDisplayModeForAnimeCategory(
private val preferences: LibraryPreferences,
private val categoryRepository: AnimeCategoryRepository,
) {
suspend fun await(categoryId: Long, display: LibraryDisplayMode) {
val category = categoryRepository.getAnimeCategory(categoryId) ?: return
val flags = category.flags + display
if (preferences.categorizedDisplaySettings().get()) {
categoryRepository.updatePartialAnimeCategory(
CategoryUpdate(
id = category.id,
flags = flags,
),
)
} else {
preferences.libraryDisplayMode().set(display)
categoryRepository.updateAllAnimeCategoryFlags(flags)
}
}
suspend fun await(category: Category, display: LibraryDisplayMode) {
await(category.id, display)
}
}

View file

@ -12,10 +12,10 @@ class SetSortModeForAnimeCategory(
private val categoryRepository: AnimeCategoryRepository, private val categoryRepository: AnimeCategoryRepository,
) { ) {
suspend fun await(categoryId: Long, type: AnimeLibrarySort.Type, direction: AnimeLibrarySort.Direction) { suspend fun await(categoryId: Long?, type: AnimeLibrarySort.Type, direction: AnimeLibrarySort.Direction) {
val category = categoryRepository.getAnimeCategory(categoryId) ?: return val category = categoryId?.let { categoryRepository.getAnimeCategory(it) }
val flags = category.flags + type + direction val flags = (category?.flags ?: 0) + type + direction
if (preferences.categorizedDisplaySettings().get()) { if (category != null && preferences.categorizedDisplaySettings().get()) {
categoryRepository.updatePartialAnimeCategory( categoryRepository.updatePartialAnimeCategory(
CategoryUpdate( CategoryUpdate(
id = category.id, id = category.id,
@ -28,7 +28,7 @@ class SetSortModeForAnimeCategory(
} }
} }
suspend fun await(category: Category, type: AnimeLibrarySort.Type, direction: AnimeLibrarySort.Direction) { suspend fun await(category: Category?, type: AnimeLibrarySort.Type, direction: AnimeLibrarySort.Direction) {
await(category.id, type, direction) await(category?.id, type, direction)
} }
} }

View file

@ -15,9 +15,7 @@ class CreateMangaCategoryWithName(
private val initialFlags: Long private val initialFlags: Long
get() { get() {
val sort = preferences.libraryMangaSortingMode().get() val sort = preferences.libraryMangaSortingMode().get()
return preferences.libraryDisplayMode().get().flag or return sort.type.flag or sort.direction.flag
sort.type.flag or
sort.direction.flag
} }
suspend fun await(name: String): Result = withNonCancellableContext { suspend fun await(name: String): Result = withNonCancellableContext {

View file

@ -10,8 +10,7 @@ class ResetMangaCategoryFlags(
) { ) {
suspend fun await() { suspend fun await() {
val display = preferences.libraryDisplayMode().get()
val sort = preferences.libraryMangaSortingMode().get() val sort = preferences.libraryMangaSortingMode().get()
categoryRepository.updateAllMangaCategoryFlags(display + sort.type + sort.direction) categoryRepository.updateAllMangaCategoryFlags(sort.type + sort.direction)
} }
} }

View file

@ -1,34 +0,0 @@
package tachiyomi.domain.category.manga.interactor
import tachiyomi.domain.category.manga.repository.MangaCategoryRepository
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.plus
import tachiyomi.domain.library.service.LibraryPreferences
class SetDisplayModeForMangaCategory(
private val preferences: LibraryPreferences,
private val categoryRepository: MangaCategoryRepository,
) {
suspend fun await(categoryId: Long, display: LibraryDisplayMode) {
val category = categoryRepository.getMangaCategory(categoryId) ?: return
val flags = category.flags + display
if (preferences.categorizedDisplaySettings().get()) {
categoryRepository.updatePartialMangaCategory(
CategoryUpdate(
id = category.id,
flags = flags,
),
)
} else {
preferences.libraryDisplayMode().set(display)
categoryRepository.updateAllMangaCategoryFlags(flags)
}
}
suspend fun await(category: Category, display: LibraryDisplayMode) {
await(category.id, display)
}
}

View file

@ -0,0 +1,13 @@
package tachiyomi.domain.category.manga.interactor
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.service.LibraryPreferences
class SetMangaDisplayMode(
private val preferences: LibraryPreferences,
) {
fun await(display: LibraryDisplayMode) {
preferences.libraryDisplayMode().set(display)
}
}

View file

@ -12,10 +12,10 @@ class SetSortModeForMangaCategory(
private val categoryRepository: MangaCategoryRepository, private val categoryRepository: MangaCategoryRepository,
) { ) {
suspend fun await(categoryId: Long, type: MangaLibrarySort.Type, direction: MangaLibrarySort.Direction) { suspend fun await(categoryId: Long?, type: MangaLibrarySort.Type, direction: MangaLibrarySort.Direction) {
val category = categoryRepository.getMangaCategory(categoryId) ?: return val category = categoryId?.let { categoryRepository.getMangaCategory(it) }
val flags = category.flags + type + direction val flags = (category?.flags ?: 0) + type + direction
if (preferences.categorizedDisplaySettings().get()) { if (category != null && preferences.categorizedDisplaySettings().get()) {
categoryRepository.updatePartialMangaCategory( categoryRepository.updatePartialMangaCategory(
CategoryUpdate( CategoryUpdate(
id = category.id, id = category.id,
@ -28,7 +28,7 @@ class SetSortModeForMangaCategory(
} }
} }
suspend fun await(category: Category, type: MangaLibrarySort.Type, direction: MangaLibrarySort.Direction) { suspend fun await(category: Category?, type: MangaLibrarySort.Type, direction: MangaLibrarySort.Direction) {
await(category.id, type, direction) await(category?.id, type, direction)
} }
} }

View file

@ -1,17 +1,11 @@
package tachiyomi.domain.library.model package tachiyomi.domain.library.model
import tachiyomi.domain.category.model.Category sealed class LibraryDisplayMode {
sealed class LibraryDisplayMode( object CompactGrid : LibraryDisplayMode()
override val flag: Long, object ComfortableGrid : LibraryDisplayMode()
) : FlagWithMask { object List : LibraryDisplayMode()
object CoverOnlyGrid : LibraryDisplayMode()
override val mask: Long = 0b00000011L
object CompactGrid : LibraryDisplayMode(0b00000000)
object ComfortableGrid : LibraryDisplayMode(0b00000001)
object List : LibraryDisplayMode(0b00000010)
object CoverOnlyGrid : LibraryDisplayMode(0b00000011)
object Serializer { object Serializer {
fun deserialize(serialized: String): LibraryDisplayMode { fun deserialize(serialized: String): LibraryDisplayMode {
@ -27,13 +21,6 @@ sealed class LibraryDisplayMode(
val values by lazy { setOf(CompactGrid, ComfortableGrid, List, CoverOnlyGrid) } val values by lazy { setOf(CompactGrid, ComfortableGrid, List, CoverOnlyGrid) }
val default = CompactGrid val default = CompactGrid
fun valueOf(flag: Long?): LibraryDisplayMode {
if (flag == null) return default
return values
.find { mode -> mode.flag == flag and mode.mask }
?: default
}
fun deserialize(serialized: String): LibraryDisplayMode { fun deserialize(serialized: String): LibraryDisplayMode {
return when (serialized) { return when (serialized) {
"COMFORTABLE_GRID" -> ComfortableGrid "COMFORTABLE_GRID" -> ComfortableGrid
@ -54,6 +41,3 @@ sealed class LibraryDisplayMode(
} }
} }
} }
val Category?.display: LibraryDisplayMode
get() = LibraryDisplayMode.valueOf(this?.flags)

View file

@ -280,7 +280,7 @@
<string name="default_category">Default category</string> <string name="default_category">Default category</string>
<string name="default_category_summary">Always ask</string> <string name="default_category_summary">Always ask</string>
<string name="categorized_display_settings">Per-category settings for sort and display</string> <string name="categorized_display_settings">Per-category settings for sort</string>
<plurals name="num_categories"> <plurals name="num_categories">
<item quantity="one">%d category</item> <item quantity="one">%d category</item>
<item quantity="other">%d categories</item> <item quantity="other">%d categories</item>

View file

@ -0,0 +1,110 @@
package tachiyomi.presentation.core.components
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.tooling.preview.Preview
/**
* A combined [CircularProgressIndicator] that always rotates.
*
* By always rotating we give the feedback to the user that the application isn't 'stuck'.
*/
@Composable
fun CombinedCircularProgressIndicator(
progress: Float,
) {
val animatedProgress by animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
label = "progress",
)
AnimatedContent(
targetState = progress == 0f,
transitionSpec = { fadeIn() togetherWith fadeOut() },
label = "progressState",
) { indeterminate ->
if (indeterminate) {
// Indeterminate
CircularProgressIndicator()
} else {
// Determinate
val infiniteTransition = rememberInfiniteTransition(label = "infiniteRotation")
val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing),
repeatMode = RepeatMode.Restart,
),
label = "rotation",
)
CircularProgressIndicator(
progress = animatedProgress,
modifier = Modifier.rotate(rotation),
)
}
}
}
@Preview
@Composable
private fun CombinedCircularProgressIndicatorPreview() {
var progress by remember { mutableFloatStateOf(0f) }
MaterialTheme {
Scaffold(
bottomBar = {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
progress = when (progress) {
0f -> 0.15f
0.15f -> 0.25f
0.25f -> 0.5f
0.5f -> 0.75f
0.75f -> 0.95f
else -> 0f
}
},
) {
Text("change")
}
},
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.padding(it),
) {
CombinedCircularProgressIndicator(progress = progress)
}
}
}
}