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 com.hippo.unifile.UniFile
|
||||
import dev.yokai.domain.storage.StoragePreferences
|
||||
import dev.yokai.presentation.settings.storageLocationText
|
||||
import dev.yokai.presentation.theme.Size
|
||||
import eu.kanade.tachiyomi.R
|
||||
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