mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
feat: WIP Compose Settings
This commit is contained in:
parent
999dd62386
commit
f28ed2cfb3
4 changed files with 270 additions and 17 deletions
|
@ -28,6 +28,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import dev.yokai.domain.storage.StoragePreferences
|
import dev.yokai.domain.storage.StoragePreferences
|
||||||
|
import dev.yokai.presentation.settings.storageLocationText
|
||||||
import dev.yokai.presentation.theme.Size
|
import dev.yokai.presentation.theme.Size
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.core.preference.Preference
|
import eu.kanade.tachiyomi.core.preference.Preference
|
||||||
|
@ -122,20 +123,3 @@ fun storageLocationPicker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun storageLocationText(
|
|
||||||
storageDirPref: Preference<String>,
|
|
||||||
): String {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val storageDir by storageDirPref.collectAsState()
|
|
||||||
|
|
||||||
if (storageDir == storageDirPref.defaultValue()) {
|
|
||||||
return stringResource(R.string.no_location_set)
|
|
||||||
}
|
|
||||||
|
|
||||||
return remember(storageDir) {
|
|
||||||
val file = UniFile.fromUri(context, storageDir.toUri())
|
|
||||||
file?.filePath
|
|
||||||
} ?: stringResource(R.string.invalid_location, storageDir)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package dev.yokai.presentation.settings
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import dev.yokai.presentation.component.preference.Preference
|
||||||
|
|
||||||
|
interface ComposableSettings {
|
||||||
|
fun getOnBackPress(): () -> Unit = {}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
@StringRes
|
||||||
|
fun getTitleRes(): Int
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun getPreferences(): List<Preference>
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RowScope.AppBarAction() {}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Content() {
|
||||||
|
SettingsScaffold(
|
||||||
|
title = stringResource(getTitleRes()),
|
||||||
|
itemsProvider = { getPreferences() },
|
||||||
|
onBackPress = getOnBackPress(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package dev.yokai.presentation.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.util.fastForEachIndexed
|
||||||
|
import dev.yokai.presentation.AppBarType
|
||||||
|
import dev.yokai.presentation.YokaiScaffold
|
||||||
|
import dev.yokai.presentation.component.Gap
|
||||||
|
import dev.yokai.presentation.component.preference.Preference
|
||||||
|
import dev.yokai.presentation.component.preference.PreferenceItem
|
||||||
|
import dev.yokai.presentation.component.preference.widget.PreferenceGroupHeader
|
||||||
|
import eu.kanade.tachiyomi.core.preference.collectAsState
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsScaffold(
|
||||||
|
title: String,
|
||||||
|
appBarType: AppBarType? = null,
|
||||||
|
onBackPress: (() -> Unit)? = null,
|
||||||
|
itemsProvider: @Composable () -> List<Preference>,
|
||||||
|
) {
|
||||||
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
|
val useLargeAppBar by preferences.useLargeToolbar().collectAsState()
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
YokaiScaffold(
|
||||||
|
onNavigationIconClicked = onBackPress ?: {},
|
||||||
|
title = title,
|
||||||
|
appBarType = appBarType ?: if (useLargeAppBar) AppBarType.LARGE else AppBarType.SMALL,
|
||||||
|
scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(
|
||||||
|
state = rememberTopAppBarState(),
|
||||||
|
canScroll = { listState.firstVisibleItemIndex > 0 || listState.firstVisibleItemScrollOffset > 0 },
|
||||||
|
),
|
||||||
|
) { innerPadding ->
|
||||||
|
PreferenceScreen(
|
||||||
|
items = itemsProvider(),
|
||||||
|
listState = listState,
|
||||||
|
contentPadding = innerPadding,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PreferenceScreen(
|
||||||
|
items: List<Preference>,
|
||||||
|
listState: LazyListState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
contentPadding: PaddingValues = PaddingValues(0.dp),
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = modifier,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
state = listState
|
||||||
|
) {
|
||||||
|
val highlightKey = null as String? // TODO
|
||||||
|
items.fastForEachIndexed { i, preference ->
|
||||||
|
when (preference) {
|
||||||
|
is Preference.PreferenceGroup -> {
|
||||||
|
if (!preference.enabled) return@fastForEachIndexed
|
||||||
|
|
||||||
|
item {
|
||||||
|
Column {
|
||||||
|
PreferenceGroupHeader(title = preference.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items(preference.preferenceItems) { item ->
|
||||||
|
PreferenceItem(item = item, highlightKey = highlightKey)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
if (i < items.lastIndex) {
|
||||||
|
Gap(padding = 12.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Preference.PreferenceItem<*> -> item {
|
||||||
|
PreferenceItem(item = preference, highlightKey = highlightKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
package dev.yokai.presentation.settings
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
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.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
|
import dev.yokai.domain.storage.StoragePreferences
|
||||||
|
import dev.yokai.presentation.component.preference.Preference
|
||||||
|
import dev.yokai.presentation.component.preference.widget.BasePreferenceWidget
|
||||||
|
import dev.yokai.presentation.component.preference.widget.PrefsHorizontalPadding
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.core.preference.collectAsState
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
object SettingsDataScreen : ComposableSettings {
|
||||||
|
@Composable
|
||||||
|
override fun getTitleRes(): Int = R.string.data_and_storage
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun getPreferences(): List<Preference> {
|
||||||
|
val storagePreferences: StoragePreferences by injectLazy()
|
||||||
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
|
|
||||||
|
return ImmutableList.of(
|
||||||
|
getStorageLocationPreference(storagePreferences = storagePreferences),
|
||||||
|
getBackupAndRestoreGroup(preferences = preferences),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun storageLocationPicker(
|
||||||
|
baseStorageDirectory: eu.kanade.tachiyomi.core.preference.Preference<String>,
|
||||||
|
): ManagedActivityResultLauncher<Uri?, Uri?> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
return rememberLauncherForActivityResult(contract = ActivityResultContracts.OpenDocumentTree()) { uri ->
|
||||||
|
if (uri != null) {
|
||||||
|
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
|
||||||
|
context.contentResolver.takePersistableUriPermission(uri, flags)
|
||||||
|
UniFile.fromUri(context, uri)?.let {
|
||||||
|
baseStorageDirectory.set(it.uri.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getStorageLocationPreference(storagePreferences: StoragePreferences): Preference.PreferenceItem.TextPreference {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val pickStoragePicker = storageLocationPicker(storagePreferences.baseStorageDirectory())
|
||||||
|
|
||||||
|
return Preference.PreferenceItem.TextPreference(
|
||||||
|
title = stringResource(R.string.storage_location),
|
||||||
|
subtitle = storageLocationText(storagePreferences.baseStorageDirectory()),
|
||||||
|
onClick = {
|
||||||
|
try {
|
||||||
|
pickStoragePicker.launch(null)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
context.toast(R.string.file_picker_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getBackupAndRestoreGroup(preferences: PreferencesHelper): Preference.PreferenceGroup {
|
||||||
|
return Preference.PreferenceGroup(
|
||||||
|
title = stringResource(R.string.backup_and_restore),
|
||||||
|
preferenceItems = ImmutableList.of(
|
||||||
|
Preference.PreferenceItem.CustomPreference(
|
||||||
|
title = stringResource(R.string.backup_and_restore),
|
||||||
|
) {
|
||||||
|
BasePreferenceWidget(
|
||||||
|
subcomponent = {
|
||||||
|
MultiChoiceSegmentedButtonRow(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(intrinsicSize = IntrinsicSize.Min)
|
||||||
|
.padding(horizontal = PrefsHorizontalPadding),
|
||||||
|
) {
|
||||||
|
SegmentedButton(
|
||||||
|
modifier = Modifier.fillMaxHeight(),
|
||||||
|
checked = false,
|
||||||
|
onCheckedChange = {},
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(0, 2),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.create_backup))
|
||||||
|
}
|
||||||
|
SegmentedButton(
|
||||||
|
modifier = Modifier.fillMaxHeight(),
|
||||||
|
checked = false,
|
||||||
|
onCheckedChange = {},
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(1, 2),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.restore_backup))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun storageLocationText(
|
||||||
|
storageDirPref: eu.kanade.tachiyomi.core.preference.Preference<String>,
|
||||||
|
): String {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val storageDir by storageDirPref.collectAsState()
|
||||||
|
|
||||||
|
if (storageDir == storageDirPref.defaultValue()) {
|
||||||
|
return stringResource(R.string.no_location_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remember(storageDir) {
|
||||||
|
val file = UniFile.fromUri(context, storageDir.toUri())
|
||||||
|
file?.filePath
|
||||||
|
} ?: stringResource(R.string.invalid_location, storageDir)
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue