feat: WIP Compose Settings

This commit is contained in:
Ahmad Ansori Palembani 2024-05-31 09:51:28 +07:00
parent 999dd62386
commit f28ed2cfb3
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
4 changed files with 270 additions and 17 deletions

View file

@ -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)
}

View file

@ -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(),
)
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}