Add basic onboarding screen (#10199)
This commit is contained in:
parent
ab9a26f6bd
commit
8b57169e92
14 changed files with 343 additions and 67 deletions
|
@ -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),
|
||||
|
|
|
@ -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]()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String>,
|
||||
) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) },
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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<Preference> {
|
||||
val context = LocalContext.current
|
||||
val uiPreferences = remember { Injekt.get<UiPreferences>() }
|
||||
|
||||
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() ?: "")
|
||||
|
|
|
@ -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<String>,
|
||||
): ManagedActivityResultLauncher<Uri?, Uri?> {
|
||||
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<DownloadCache>().invalidateCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun storageLocationText(
|
||||
storageDirPref: tachiyomi.core.preference.Preference<String>,
|
||||
): 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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -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) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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<BasePreferences>() }
|
||||
val storagePreferences = remember { Injekt.get<StoragePreferences>() }
|
||||
val uiPreferences = remember { Injekt.get<UiPreferences>() }
|
||||
|
||||
OnboardingScreen(
|
||||
storagePreferences = storagePreferences,
|
||||
uiPreferences = uiPreferences,
|
||||
onComplete = {
|
||||
basePreferences.shownOnboardingFlow().set(true)
|
||||
navigator.pop()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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<InMemoryPreference<*>> = sequenceOf(),
|
||||
initialPreferences: Sequence<InMemoryPreference<*>> = sequenceOf(),
|
||||
) : PreferenceStore {
|
||||
|
||||
private val preferences: Map<String, Preference<*>> =
|
||||
|
|
|
@ -173,6 +173,15 @@
|
|||
<!-- Shortcuts-->
|
||||
<string name="app_not_available">App not available</string>
|
||||
|
||||
<!-- Onboarding -->
|
||||
<string name="pref_onboarding_guide">Onboarding guide</string>
|
||||
<string name="onboarding_heading">Welcome!</string>
|
||||
<string name="onboarding_description">Let\'s set some things up first. You can always change these in the settings later too.</string>
|
||||
<string name="onboarding_action_next">Next</string>
|
||||
<string name="onboarding_action_finish">Get started</string>
|
||||
<string name="onboarding_action_skip">Skip</string>
|
||||
<string name="onboarding_storage_info">Select a storage location where chapter downloads, backups, and local source content will be stored.</string>
|
||||
|
||||
<!-- Preferences -->
|
||||
<!-- Subsections -->
|
||||
<string name="pref_category_general">General</string>
|
||||
|
@ -196,11 +205,10 @@
|
|||
|
||||
<!-- General section -->
|
||||
<string name="pref_category_theme">Theme</string>
|
||||
<string name="pref_theme_mode">Dark mode</string>
|
||||
<string name="theme_system">Follow system</string>
|
||||
<string name="theme_light">Off</string>
|
||||
<string name="theme_dark">On</string>
|
||||
<string name="pref_app_theme">App theme</string>
|
||||
<string name="theme_system">System</string>
|
||||
<string name="theme_light">Light</string>
|
||||
<string name="theme_dark">Dark</string>
|
||||
<string name="theme_monet">Dynamic</string>
|
||||
<string name="theme_greenapple">Green Apple</string>
|
||||
<string name="theme_lavender">Lavender</string>
|
||||
|
|
Reference in a new issue