diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt index 791e131807..af23735e98 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -24,6 +24,8 @@ class BasePreferences( fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType) + fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false) + enum class ExtensionInstaller(val titleRes: StringResource) { LEGACY(MR.strings.ext_installer_legacy), PACKAGEINSTALLER(MR.strings.ext_installer_packageinstaller), diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt new file mode 100644 index 0000000000..0bfdfbb93e --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt @@ -0,0 +1,68 @@ +package eu.kanade.presentation.more.onboarding + +import androidx.compose.animation.AnimatedContent +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.RocketLaunch +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import eu.kanade.domain.ui.UiPreferences +import soup.compose.material.motion.animation.materialSharedAxisX +import soup.compose.material.motion.animation.rememberSlideDistance +import tachiyomi.domain.storage.service.StoragePreferences +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.screens.InfoScreen + +@Composable +fun OnboardingScreen( + storagePreferences: StoragePreferences, + uiPreferences: UiPreferences, + onComplete: () -> Unit, +) { + var currentStep by remember { mutableIntStateOf(0) } + val steps: List<@Composable () -> Unit> = listOf( + { ThemeStep(uiPreferences = uiPreferences) }, + { StorageStep(storagePref = storagePreferences.baseStorageDirectory()) }, + // TODO: prompt for notification permissions when bumping target to Android 13 + ) + val isLastStep = currentStep == steps.size - 1 + val slideDistance = rememberSlideDistance() + + InfoScreen( + icon = Icons.Outlined.RocketLaunch, + headingText = stringResource(MR.strings.onboarding_heading), + subtitleText = stringResource(MR.strings.onboarding_description), + acceptText = stringResource( + if (isLastStep) { + MR.strings.onboarding_action_finish + } else { + MR.strings.onboarding_action_next + }, + ), + onAcceptClick = { + if (!isLastStep) { + currentStep++ + } else { + onComplete() + } + }, + rejectText = stringResource(MR.strings.onboarding_action_skip), + onRejectClick = onComplete, + ) { + AnimatedContent( + targetState = currentStep, + transitionSpec = { + materialSharedAxisX( + forward = true, + slideDistance = slideDistance, + ) + }, + label = "stepContent", + ) { + steps[it]() + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt new file mode 100644 index 0000000000..b4204a0e46 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt @@ -0,0 +1,41 @@ +package eu.kanade.presentation.more.onboarding + +import android.content.ActivityNotFoundException +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.more.settings.screen.SettingsDataScreen +import eu.kanade.tachiyomi.util.system.toast +import tachiyomi.core.preference.Preference +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.components.material.Button +import tachiyomi.presentation.core.i18n.stringResource + +@Composable +internal fun StorageStep( + storagePref: Preference, +) { + val context = LocalContext.current + val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref) + + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(stringResource(MR.strings.onboarding_storage_info)) + + Button( + onClick = { + try { + pickStorageLocation.launch(null) + } catch (e: ActivityNotFoundException) { + context.toast(MR.strings.file_picker_error) + } + }, + ) { + Text(SettingsDataScreen.storageLocationText(storagePref)) + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt new file mode 100644 index 0000000000..69951e0b53 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt @@ -0,0 +1,40 @@ +package eu.kanade.presentation.more.onboarding + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode +import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget +import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget +import tachiyomi.presentation.core.util.collectAsState + +@Composable +internal fun ThemeStep( + uiPreferences: UiPreferences, +) { + val themeModePref = uiPreferences.themeMode() + val themeMode by themeModePref.collectAsState() + + val appThemePref = uiPreferences.appTheme() + val appTheme by appThemePref.collectAsState() + + val amoledPref = uiPreferences.themeDarkAmoled() + val amoled by amoledPref.collectAsState() + + Column { + AppThemeModePreferenceWidget( + value = themeMode, + onItemClick = { + themeModePref.set(it) + setAppCompatDelegateThemeMode(it) + }, + ) + + AppThemePreferenceWidget( + value = appTheme, + amoled = amoled, + onItemClick = { appThemePref.set(it) }, + ) + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 474036247e..56f6065674 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -43,6 +43,7 @@ import eu.kanade.tachiyomi.network.PREF_DOH_NJALLA import eu.kanade.tachiyomi.network.PREF_DOH_QUAD101 import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9 import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN +import eu.kanade.tachiyomi.ui.more.OnboardingScreen import eu.kanade.tachiyomi.util.CrashLogUtil import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isReleaseBuildType @@ -110,6 +111,10 @@ object SettingsAdvancedScreen : SearchableSettings { title = stringResource(MR.strings.pref_debug_info), onClick = { navigator.push(DebugInfoScreen()) }, ), + Preference.PreferenceItem.TextPreference( + title = stringResource(MR.strings.pref_onboarding_guide), + onClick = { navigator.push(OnboardingScreen()) }, + ), ), ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 292169175f..8523de930b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -2,8 +2,8 @@ package eu.kanade.presentation.more.settings.screen import android.app.Activity import android.content.Context -import android.os.Build import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.ReadOnlyComposable @@ -19,13 +19,11 @@ 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.AppThemeModePreferenceWidget import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.toast -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.merge import org.xmlpull.v1.XmlPullParser import tachiyomi.core.i18n.stringResource import tachiyomi.i18n.MR @@ -43,72 +41,59 @@ object SettingsAppearanceScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val context = LocalContext.current val uiPreferences = remember { Injekt.get() } return listOf( - getThemeGroup(context = context, uiPreferences = uiPreferences), - getDisplayGroup(context = context, uiPreferences = uiPreferences), + getThemeGroup(uiPreferences = uiPreferences), + getDisplayGroup(uiPreferences = uiPreferences), ) } @Composable private fun getThemeGroup( - context: Context, uiPreferences: UiPreferences, ): Preference.PreferenceGroup { + val context = LocalContext.current + val themeModePref = uiPreferences.themeMode() val themeMode by themeModePref.collectAsState() val appThemePref = uiPreferences.appTheme() + val appTheme by appThemePref.collectAsState() 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) }, - ) + ) { + Column { + AppThemeModePreferenceWidget( + value = themeMode, + onItemClick = { + themeModePref.set(it) + setAppCompatDelegateThemeMode(it) + }, + ) + + AppThemePreferenceWidget( + value = appTheme, + amoled = amoled, + onItemClick = { appThemePref.set(it) }, + ) + } }, Preference.PreferenceItem.SwitchPreference( pref = amoledPref, title = stringResource(MR.strings.pref_dark_theme_pure_black), enabled = themeMode != ThemeMode.LIGHT, + onValueChanged = { + (context as? Activity)?.let { ActivityCompat.recreate(it) } + true + }, ), ), ) @@ -116,9 +101,10 @@ object SettingsAppearanceScreen : SearchableSettings { @Composable private fun getDisplayGroup( - context: Context, uiPreferences: UiPreferences, ): Preference.PreferenceGroup { + val context = LocalContext.current + val langs = remember { getLangs(context) } var currentLanguage by remember { mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "") diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index a4fb1b6858..e273f05e56 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -7,6 +7,7 @@ import android.net.Uri import android.os.Environment import android.text.format.Formatter import android.widget.Toast +import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Box @@ -80,13 +81,12 @@ object SettingsDataScreen : SearchableSettings { } @Composable - private fun getStorageLocationPref( - storagePreferences: StoragePreferences, - ): Preference.PreferenceItem.TextPreference { + fun storageLocationPicker( + storageDirPref: tachiyomi.core.preference.Preference, + ): ManagedActivityResultLauncher { val context = LocalContext.current - val storageDirPref = storagePreferences.baseStorageDirectory() - val storageDir by storageDirPref.collectAsState() - val pickStorageLocation = rememberLauncherForActivityResult( + + return rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocumentTree(), ) { uri -> if (uri != null) { @@ -101,13 +101,31 @@ object SettingsDataScreen : SearchableSettings { Injekt.get().invalidateCache() } } + } + + @Composable + fun storageLocationText( + storageDirPref: tachiyomi.core.preference.Preference, + ): String { + val context = LocalContext.current + val storageDir by storageDirPref.collectAsState() + + return remember(storageDir) { + val file = UniFile.fromUri(context, storageDir.toUri()) + file?.filePath ?: file?.uri?.toString() + } ?: stringResource(MR.strings.invalid_location, storageDir) + } + + @Composable + private fun getStorageLocationPref( + storagePreferences: StoragePreferences, + ): Preference.PreferenceItem.TextPreference { + val context = LocalContext.current + val pickStorageLocation = storageLocationPicker(storagePreferences.baseStorageDirectory()) return Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_storage_location), - subtitle = remember(storageDir) { - val file = UniFile.fromUri(context, storageDir.toUri()) - file?.filePath ?: file?.uri?.toString() - } ?: stringResource(MR.strings.invalid_location, storageDir), + subtitle = storageLocationText(storagePreferences.baseStorageDirectory()), onClick = { try { pickStorageLocation.launch(null) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemeModePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemeModePreferenceWidget.kt new file mode 100644 index 0000000000..d1901745b6 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemeModePreferenceWidget.kt @@ -0,0 +1,56 @@ +package eu.kanade.presentation.more.settings.widget + +import android.os.Build +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MultiChoiceSegmentedButtonRow +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import eu.kanade.domain.ui.model.ThemeMode +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.stringResource + +private val options = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mapOf( + ThemeMode.SYSTEM to MR.strings.theme_system, + ThemeMode.LIGHT to MR.strings.theme_light, + ThemeMode.DARK to MR.strings.theme_dark, + ) +} else { + mapOf( + ThemeMode.LIGHT to MR.strings.theme_light, + ThemeMode.DARK to MR.strings.theme_dark, + ) +} + +@Composable +internal fun AppThemeModePreferenceWidget( + value: ThemeMode, + onItemClick: (ThemeMode) -> Unit, +) { + BasePreferenceWidget( + subcomponent = { + MultiChoiceSegmentedButtonRow( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = PrefsHorizontalPadding), + ) { + options.onEachIndexed { index, (mode, labelRes) -> + SegmentedButton( + checked = mode == value, + onCheckedChange = { onItemClick(mode) }, + shape = SegmentedButtonDefaults.itemShape( + index, + options.size, + ), + ) { + Text(stringResource(labelRes)) + } + } + } + }, + ) +} diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt index 5b75997222..2be6e03a48 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt @@ -1,5 +1,6 @@ package eu.kanade.presentation.more.settings.widget +import android.app.Activity import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -36,9 +37,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp +import androidx.core.app.ActivityCompat import eu.kanade.domain.ui.model.AppTheme import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.presentation.theme.TachiyomiTheme @@ -51,13 +54,11 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha @Composable internal fun AppThemePreferenceWidget( - title: String, value: AppTheme, amoled: Boolean, onItemClick: (AppTheme) -> Unit, ) { BasePreferenceWidget( - title = title, subcomponent = { AppThemesList( currentTheme = value, @@ -74,6 +75,7 @@ private fun AppThemesList( amoled: Boolean, onItemClick: (AppTheme) -> Unit, ) { + val context = LocalContext.current val appThemes = remember { AppTheme.entries .filterNot { it.titleRes == null || (it == AppTheme.MONET && !DeviceUtil.isDynamicColorAvailable) } @@ -97,7 +99,10 @@ private fun AppThemesList( ) { AppThemePreviewItem( selected = currentTheme == appTheme, - onClick = { onItemClick(appTheme) }, + onClick = { + onItemClick(appTheme) + (context as? Activity)?.let { ActivityCompat.recreate(it) } + }, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt index 13319c9485..561f34df32 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt @@ -126,12 +126,12 @@ object HomeScreen : Screen() { materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) togetherWith materialFadeThroughOut(durationMillis = TabFadeDuration) }, - content = { - tabNavigator.saveableState(key = "currentTab", it) { - it.Content() - } - }, - ) + label = "tabContent", + ) { + tabNavigator.saveableState(key = "currentTab", it) { + it.Content() + } + } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 998781730a..d6750551bb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -73,6 +73,7 @@ import eu.kanade.tachiyomi.ui.deeplink.DeepLinkScreen import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.more.NewUpdateScreen +import eu.kanade.tachiyomi.ui.more.OnboardingScreen import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim import eu.kanade.tachiyomi.util.system.openInBrowser @@ -251,6 +252,7 @@ class MainActivity : BaseActivity() { HandleOnNewIntent(context = context, navigator = navigator) CheckForUpdates() + ShowOnboarding() } var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) } @@ -342,6 +344,17 @@ class MainActivity : BaseActivity() { } } + @Composable + private fun ShowOnboarding() { + val navigator = LocalNavigator.currentOrThrow + + LaunchedEffect(Unit) { + if (!preferences.shownOnboardingFlow().get()) { + navigator.push(OnboardingScreen()) + } + } + } + /** * Sets custom splash screen exit animation on devices prior to Android 12. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt new file mode 100644 index 0000000000..0cbd7e6254 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.ui.more + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.domain.base.BasePreferences +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.presentation.more.onboarding.OnboardingScreen +import eu.kanade.presentation.util.Screen +import tachiyomi.domain.storage.service.StoragePreferences +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class OnboardingScreen : Screen() { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + val basePreferences = remember { Injekt.get() } + val storagePreferences = remember { Injekt.get() } + val uiPreferences = remember { Injekt.get() } + + OnboardingScreen( + storagePreferences = storagePreferences, + uiPreferences = uiPreferences, + onComplete = { + basePreferences.shownOnboardingFlow().set(true) + navigator.pop() + }, + ) + } +} diff --git a/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt b/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt index ad3937d915..83106999f4 100644 --- a/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt +++ b/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.stateIn * Local-copy implementation of PreferenceStore mostly for test and preview purposes */ class InMemoryPreferenceStore( - private val initialPreferences: Sequence> = sequenceOf(), + initialPreferences: Sequence> = sequenceOf(), ) : PreferenceStore { private val preferences: Map> = diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 1d4febdd66..fb190bda9b 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -173,6 +173,15 @@ App not available + + Onboarding guide + Welcome! + Let\'s set some things up first. You can always change these in the settings later too. + Next + Get started + Skip + Select a storage location where chapter downloads, backups, and local source content will be stored. + General @@ -196,11 +205,10 @@ Theme - Dark mode - Follow system - Off - On App theme + System + Light + Dark Dynamic Green Apple Lavender