tachiyomi/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt

218 lines
8.5 KiB
Kotlin
Raw Normal View History

package eu.kanade.presentation.more.settings.screen
import android.app.Activity
import android.content.Context
import android.os.Build
2023-06-04 23:07:29 +02:00
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
2023-06-04 23:07:29 +02:00
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
2023-06-04 23:07:29 +02:00
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.core.app.ActivityCompat
2023-06-04 23:07:29 +02:00
import androidx.core.os.LocaleListCompat
import eu.kanade.domain.ui.UiPreferences
2022-10-22 16:15:12 +02:00
import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
import eu.kanade.tachiyomi.R
2023-06-04 23:07:29 +02:00
import eu.kanade.tachiyomi.util.system.LocaleHelper
2022-10-22 16:15:12 +02:00
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.merge
2023-06-04 23:07:29 +02:00
import org.xmlpull.v1.XmlPullParser
import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
2023-12-09 05:11:53 +01:00
import java.time.Instant
object SettingsAppearanceScreen : SearchableSettings {
@ReadOnlyComposable
@Composable
override fun getTitleRes() = MR.strings.pref_category_appearance
@Composable
override fun getPreferences(): List<Preference> {
val context = LocalContext.current
val uiPreferences = remember { Injekt.get<UiPreferences>() }
return listOf(
getThemeGroup(context = context, uiPreferences = uiPreferences),
2022-10-22 16:15:12 +02:00
getDisplayGroup(context = context, uiPreferences = uiPreferences),
)
}
@Composable
private fun getThemeGroup(
context: Context,
uiPreferences: UiPreferences,
): Preference.PreferenceGroup {
val themeModePref = uiPreferences.themeMode()
val themeMode by themeModePref.collectAsState()
val appThemePref = uiPreferences.appTheme()
val amoledPref = uiPreferences.themeDarkAmoled()
val amoled by amoledPref.collectAsState()
LaunchedEffect(themeMode) {
setAppCompatDelegateThemeMode(themeMode)
}
LaunchedEffect(Unit) {
merge(appThemePref.changes(), amoledPref.changes())
.drop(2)
.collectLatest { (context as? Activity)?.let { ActivityCompat.recreate(it) } }
}
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_theme),
preferenceItems = listOf(
Preference.PreferenceItem.ListPreference(
pref = themeModePref,
title = stringResource(MR.strings.pref_theme_mode),
entries = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mapOf(
ThemeMode.SYSTEM to stringResource(MR.strings.theme_system),
ThemeMode.LIGHT to stringResource(MR.strings.theme_light),
ThemeMode.DARK to stringResource(MR.strings.theme_dark),
)
} else {
mapOf(
ThemeMode.LIGHT to stringResource(MR.strings.theme_light),
ThemeMode.DARK to stringResource(MR.strings.theme_dark),
)
},
),
Preference.PreferenceItem.CustomPreference(
title = stringResource(MR.strings.pref_app_theme),
) { item ->
val value by appThemePref.collectAsState()
AppThemePreferenceWidget(
title = item.title,
value = value,
amoled = amoled,
onItemClick = { appThemePref.set(it) },
)
},
Preference.PreferenceItem.SwitchPreference(
pref = amoledPref,
title = stringResource(MR.strings.pref_dark_theme_pure_black),
enabled = themeMode != ThemeMode.LIGHT,
),
),
)
}
@Composable
2022-10-22 16:15:12 +02:00
private fun getDisplayGroup(
context: Context,
uiPreferences: UiPreferences,
): Preference.PreferenceGroup {
2023-06-04 23:07:29 +02:00
val langs = remember { getLangs(context) }
2023-11-05 04:28:41 +01:00
var currentLanguage by remember {
mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "")
}
2023-12-09 05:11:53 +01:00
val now = remember { Instant.now().toEpochMilli() }
2023-06-04 23:07:29 +02:00
val dateFormat by uiPreferences.dateFormat().collectAsState()
val formattedNow = remember(dateFormat) {
UiPreferences.dateFormat(dateFormat).format(now)
}
2023-06-04 23:07:29 +02:00
LaunchedEffect(currentLanguage) {
val locale = if (currentLanguage.isEmpty()) {
LocaleListCompat.getEmptyLocaleList()
} else {
LocaleListCompat.forLanguageTags(currentLanguage)
}
AppCompatDelegate.setApplicationLocales(locale)
}
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_display),
preferenceItems = listOf(
2023-06-04 23:07:29 +02:00
Preference.PreferenceItem.BasicListPreference(
value = currentLanguage,
title = stringResource(MR.strings.pref_app_language),
2023-06-04 23:07:29 +02:00
entries = langs,
onValueChanged = { newValue ->
currentLanguage = newValue
true
},
),
2022-10-22 16:15:12 +02:00
Preference.PreferenceItem.ListPreference(
pref = uiPreferences.tabletUiMode(),
title = stringResource(MR.strings.pref_tablet_ui_mode),
entries = TabletUiMode.entries.associateWith { stringResource(it.titleRes) },
2022-10-22 16:15:12 +02:00
onValueChanged = {
context.toast(MR.strings.requires_app_restart)
2022-10-22 16:15:12 +02:00
true
},
),
Preference.PreferenceItem.ListPreference(
pref = uiPreferences.dateFormat(),
title = stringResource(MR.strings.pref_date_format),
entries = DateFormats
.associateWith {
val formattedDate = UiPreferences.dateFormat(it).format(now)
"${it.ifEmpty { stringResource(MR.strings.label_default) }} ($formattedDate)"
},
),
Preference.PreferenceItem.SwitchPreference(
pref = uiPreferences.relativeTime(),
title = stringResource(MR.strings.pref_relative_format),
subtitle = stringResource(
MR.strings.pref_relative_format_summary,
stringResource(MR.strings.relative_time_today),
formattedNow,
),
),
),
)
}
2023-06-04 23:07:29 +02:00
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") {
2023-07-19 01:12:04 +02:00
for (i in 0..<parser.attributeCount) {
2023-06-04 23:07:29 +02:00
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.stringResource(MR.strings.label_default)))
2023-06-04 23:07:29 +02:00
return langs.toMap()
}
}
private val DateFormats = listOf(
"", // Default
"MM/dd/yy",
"dd/MM/yy",
"yyyy-MM-dd",
"dd MMM yyyy",
"MMM dd, yyyy",
)