mirror of
https://github.com/IllusionMan1212/NPS-Browser.git
synced 2026-10-24 02:12:37 +00:00
preference saving
This commit is contained in:
@@ -1,18 +1,51 @@
|
|||||||
package com.illusionware.npsbrowser
|
package com.illusionware.npsbrowser
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Environment
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.datastore.preferences.core.intPreferencesKey
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
enum class ConsoleType {
|
||||||
|
PSVITA,
|
||||||
|
PS3,
|
||||||
|
PSP,
|
||||||
|
PSX,
|
||||||
|
PSM,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class DataType {
|
||||||
|
GAMES,
|
||||||
|
DLC,
|
||||||
|
THEMES,
|
||||||
|
}
|
||||||
|
|
||||||
class Preferences(private val context: Context) {
|
class Preferences(private val context: Context) {
|
||||||
companion object {
|
companion object {
|
||||||
private val Context.dataStore by preferencesDataStore(name = "settings")
|
private val Context.dataStore by preferencesDataStore("settings")
|
||||||
private val APP_THEME = intPreferencesKey("app_theme")
|
private val APP_THEME = intPreferencesKey("app_theme")
|
||||||
private val DATA_LAYOUT = intPreferencesKey("layout")
|
private val DATA_LAYOUT = intPreferencesKey("layout")
|
||||||
private val SEEN_ONBOARDING = booleanPreferencesKey("seen_onboarding")
|
private val SEEN_ONBOARDING = booleanPreferencesKey("seen_onboarding")
|
||||||
|
private val PSV_GAMES = stringPreferencesKey("psv_games")
|
||||||
|
private val PS3_GAMES = stringPreferencesKey("ps3_games")
|
||||||
|
private val PSP_GAMES = stringPreferencesKey("psp_games")
|
||||||
|
private val PSX_GAMES = stringPreferencesKey("psx_games")
|
||||||
|
private val PSM_GAMES = stringPreferencesKey("psm_games")
|
||||||
|
private val PSV_DLC = stringPreferencesKey("psv_dlc")
|
||||||
|
private val PS3_DLC = stringPreferencesKey("ps3_dlc")
|
||||||
|
private val PSP_DLC = stringPreferencesKey("psp_dlc")
|
||||||
|
private val PSV_THEMES = stringPreferencesKey("psv_themes")
|
||||||
|
private val HMAC_KEY = stringPreferencesKey("hmac_key")
|
||||||
|
private val DOWNLOAD_DIR = stringPreferencesKey("download_dir")
|
||||||
|
private val UNPACK_DIR = stringPreferencesKey("unpack_dir")
|
||||||
|
private val UNPACK_IN_DOWNLOAD = booleanPreferencesKey("unpack_in_download")
|
||||||
|
private val DELETE_AFTER_UNPACK = booleanPreferencesKey("delete_after_unpack")
|
||||||
|
private val PKG2ZIP_PARAMS = stringPreferencesKey("pkg2zip_params")
|
||||||
|
private val PKG2ZIP_AUTO_DECRYPT = booleanPreferencesKey("pkg2zip_auto_decrypt")
|
||||||
}
|
}
|
||||||
|
|
||||||
val theme = context.dataStore.data.map { prefs ->
|
val theme = context.dataStore.data.map { prefs ->
|
||||||
@@ -27,6 +60,70 @@ class Preferences(private val context: Context) {
|
|||||||
prefs[SEEN_ONBOARDING] ?: false
|
prefs[SEEN_ONBOARDING] ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val psvGames = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PSV_GAMES] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val ps3Games = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PS3_GAMES] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val pspGames = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PSP_GAMES] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val psxGames = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PSX_GAMES] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val psmGames = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PSM_GAMES] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val psvDLC = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PSV_DLC] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val ps3DLC = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PS3_DLC] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val pspDLC = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PSP_DLC] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val psvThemes = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PSV_THEMES] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val hmacKey = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[HMAC_KEY] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val downloadDir = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[DOWNLOAD_DIR] ?: Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.path ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val unpackDir = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[UNPACK_DIR] ?: Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.path ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val unpackInDownload = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[UNPACK_IN_DOWNLOAD] ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
val deleteAfterUnpack = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[DELETE_AFTER_UNPACK] ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
val pkg2zipParams = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PKG2ZIP_PARAMS] ?: "-x {pkgFile} \"{zRifKey}\""
|
||||||
|
}
|
||||||
|
|
||||||
|
val pkg2zipAutoDecrypt = context.dataStore.data.map{ prefs ->
|
||||||
|
prefs[PKG2ZIP_AUTO_DECRYPT] ?: true
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun changeTheme(theme: Int) {
|
suspend fun changeTheme(theme: Int) {
|
||||||
context.dataStore.edit { prefs ->
|
context.dataStore.edit { prefs ->
|
||||||
prefs[APP_THEME] = theme
|
prefs[APP_THEME] = theme
|
||||||
@@ -44,4 +141,87 @@ class Preferences(private val context: Context) {
|
|||||||
prefs[SEEN_ONBOARDING] = true
|
prefs[SEEN_ONBOARDING] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun setTSVFile(console: ConsoleType, contentType: DataType, file: Uri) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
when (console) {
|
||||||
|
ConsoleType.PSVITA -> {
|
||||||
|
when (contentType) {
|
||||||
|
DataType.GAMES -> prefs[PSV_GAMES] = file.toString()
|
||||||
|
DataType.DLC -> prefs[PSV_DLC] = file.toString()
|
||||||
|
DataType.THEMES -> prefs[PSV_THEMES] = file.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConsoleType.PS3 -> {
|
||||||
|
when (contentType) {
|
||||||
|
DataType.GAMES -> prefs[PS3_GAMES] = file.toString()
|
||||||
|
DataType.DLC -> prefs[PS3_DLC] = file.toString()
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConsoleType.PSP -> {
|
||||||
|
when (contentType) {
|
||||||
|
DataType.GAMES -> prefs[PSP_GAMES] = file.toString()
|
||||||
|
DataType.DLC -> prefs[PSP_DLC] = file.toString()
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConsoleType.PSX -> {
|
||||||
|
when (contentType) {
|
||||||
|
DataType.GAMES -> prefs[PSX_GAMES] = file.toString()
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConsoleType.PSM -> {
|
||||||
|
when (contentType) {
|
||||||
|
DataType.GAMES -> prefs[PSM_GAMES] = file.toString()
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setHMACKey(key: String) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[HMAC_KEY] = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setDownloadDir(dir: String) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[DOWNLOAD_DIR] = dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setUnpackDir(dir: String) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[UNPACK_DIR] = dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setUnpackInDownload(unpackInDownload: Boolean) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[UNPACK_IN_DOWNLOAD] = unpackInDownload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setDeleteAfterUnpack(deleteAfterUnpack: Boolean) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[DELETE_AFTER_UNPACK] = deleteAfterUnpack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setPkg2zipParams(params: String) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[PKG2ZIP_PARAMS] = params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setPkg2zipAutoDecrypt(autoDecrypt: Boolean) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[PKG2ZIP_AUTO_DECRYPT] = autoDecrypt
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package com.illusionware.npsbrowser.ui.pages
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.util.Log
|
import android.net.Uri
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
@@ -68,15 +68,21 @@ import androidx.compose.material3.FilledTonalButton
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.layout.Layout
|
import androidx.compose.ui.layout.Layout
|
||||||
import androidx.compose.ui.layout.layoutId
|
import androidx.compose.ui.layout.layoutId
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
|
import androidx.compose.ui.text.TextRange
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.illusionware.npsbrowser.ConsoleType
|
||||||
|
import com.illusionware.npsbrowser.DataType
|
||||||
|
import com.illusionware.npsbrowser.Preferences
|
||||||
import com.illusionware.npsbrowser.ui.components.NPSIconButton
|
import com.illusionware.npsbrowser.ui.components.NPSIconButton
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -229,6 +235,9 @@ fun OnBoardingSetupTSVs(
|
|||||||
incrementPage: () -> Unit = {},
|
incrementPage: () -> Unit = {},
|
||||||
skipToEnd: () -> Unit = {}
|
skipToEnd: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
val prefs = Preferences(LocalContext.current)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
Scaffold { padding ->
|
Scaffold { padding ->
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
@@ -277,25 +286,52 @@ fun OnBoardingSetupTSVs(
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
TSVGroup(title = "PS Vita") {
|
TSVGroup(title = "PS Vita") {
|
||||||
TSVButton("Games")
|
TSVButton(
|
||||||
TSVButton("Themes")
|
"Games",
|
||||||
TSVButton("DLC")
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PSVITA, DataType.GAMES, it) }}
|
||||||
|
)
|
||||||
|
TSVButton(
|
||||||
|
"Themes",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PSVITA, DataType.THEMES, it) }}
|
||||||
|
)
|
||||||
|
TSVButton(
|
||||||
|
"DLC",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PSVITA, DataType.DLC, it) }}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
TSVGroup(title = "PSP") {
|
TSVGroup(title = "PSP") {
|
||||||
TSVButton("Games")
|
TSVButton(
|
||||||
TSVButton("DLC")
|
"Games",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PSP, DataType.GAMES, it) }}
|
||||||
|
)
|
||||||
|
TSVButton(
|
||||||
|
"DLC",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PSP, DataType.DLC, it) }}
|
||||||
|
)
|
||||||
Box(modifier = Modifier.width(110.dp))
|
Box(modifier = Modifier.width(110.dp))
|
||||||
}
|
}
|
||||||
TSVGroup(title = "PS3") {
|
TSVGroup(title = "PS3") {
|
||||||
TSVButton("Games")
|
TSVButton(
|
||||||
TSVButton("DLC")
|
"Games",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PS3, DataType.GAMES, it) }}
|
||||||
|
)
|
||||||
|
TSVButton(
|
||||||
|
"DLC",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PS3, DataType.DLC, it) }}
|
||||||
|
)
|
||||||
Box(modifier = Modifier.width(110.dp))
|
Box(modifier = Modifier.width(110.dp))
|
||||||
}
|
}
|
||||||
TSVGroup(title = "PSX") {
|
TSVGroup(title = "PSX") {
|
||||||
TSVButton("Games")
|
TSVButton(
|
||||||
|
"Games",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PSX, DataType.GAMES, it) }}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
TSVGroup(title = "PSM") {
|
TSVGroup(title = "PSM") {
|
||||||
TSVButton("Games")
|
TSVButton(
|
||||||
|
"Games",
|
||||||
|
onFinish = { scope.launch { prefs.setTSVFile(ConsoleType.PSM, DataType.GAMES, it) }}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,15 +396,14 @@ fun TSVGroup(title: String, content: @Composable RowScope.() -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TSVButton(title: String) {
|
fun TSVButton(title: String, onFinish: (Uri) -> Unit) {
|
||||||
var hasSelected by rememberSaveable { mutableStateOf(false) }
|
var hasSelected by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
val filePicker = rememberLauncherForActivityResult(
|
val filePicker = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.GetContent(),
|
contract = ActivityResultContracts.GetContent(),
|
||||||
) { fileUri ->
|
) { fileUri ->
|
||||||
if (fileUri != null) {
|
if (fileUri != null) {
|
||||||
// TODO: store file URI in preferences
|
onFinish(fileUri)
|
||||||
Log.d("TSVItem", "fileUri: $fileUri")
|
|
||||||
hasSelected = true
|
hasSelected = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,8 +477,44 @@ fun OnBoardingSetupSettings(
|
|||||||
navigateToHome: () -> Unit = {},
|
navigateToHome: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val clipboard = LocalClipboardManager.current
|
val clipboard = LocalClipboardManager.current
|
||||||
var hmacKey by rememberSaveable { mutableStateOf("") }
|
val prefs = Preferences(LocalContext.current)
|
||||||
var pkg2zipArgs by rememberSaveable { mutableStateOf("") }
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
var hmacKey by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
|
||||||
|
var pkg2zipArgs = prefs.pkg2zipParams.collectAsState(initial = "").value
|
||||||
|
val downloadDir = prefs.downloadDir.collectAsState(initial = "").value
|
||||||
|
val unpackDir = prefs.unpackDir.collectAsState(initial = "").value
|
||||||
|
val unpackInDownload = prefs.unpackInDownload.collectAsState(initial = false).value
|
||||||
|
val deleteAfterUnpack = prefs.deleteAfterUnpack.collectAsState(initial = false).value
|
||||||
|
val autoDecrypt = prefs.pkg2zipAutoDecrypt.collectAsState(initial = true).value
|
||||||
|
|
||||||
|
val downloadDirPicker = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.OpenDocumentTree(),
|
||||||
|
) { dirUri ->
|
||||||
|
if (dirUri != null) {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setDownloadDir(dirUri.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val unpackDirPicker = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.OpenDocumentTree(),
|
||||||
|
) { dirUri ->
|
||||||
|
if (dirUri != null) {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setUnpackDir(dirUri.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHmacKey(key: String) {
|
||||||
|
if (key.isNotEmpty()) {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setHMACKey(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold { padding ->
|
Scaffold { padding ->
|
||||||
Column(
|
Column(
|
||||||
@@ -485,14 +556,21 @@ fun OnBoardingSetupSettings(
|
|||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = hmacKey,
|
value = hmacKey,
|
||||||
onValueChange = { hmacKey = it },
|
onValueChange = {
|
||||||
|
hmacKey = it
|
||||||
|
setHmacKey(it.text)
|
||||||
|
},
|
||||||
label = { Text(text = "HMAC Key") },
|
label = { Text(text = "HMAC Key") },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = "Paste",
|
tooltip = "Paste",
|
||||||
onClick = { hmacKey = clipboard.getText()?.text ?: "" }
|
onClick = {
|
||||||
|
val text = clipboard.getText()?.text ?: ""
|
||||||
|
hmacKey = TextFieldValue(text, selection = TextRange(text.length))
|
||||||
|
setHmacKey(text)
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ContentPaste,
|
imageVector = Icons.Filled.ContentPaste,
|
||||||
@@ -508,36 +586,54 @@ fun OnBoardingSetupSettings(
|
|||||||
text = "Choose your desired directory to download and unpack packages",
|
text = "Choose your desired directory to download and unpack packages",
|
||||||
style = Typography.bodyMedium.copy(color = MaterialTheme.colorScheme.outline),
|
style = Typography.bodyMedium.copy(color = MaterialTheme.colorScheme.outline),
|
||||||
)
|
)
|
||||||
Toggleable(
|
Column {
|
||||||
title = "Unpack in download directory",
|
Toggleable(
|
||||||
)
|
title = "Unpack in download directory",
|
||||||
Toggleable(
|
checked = unpackInDownload,
|
||||||
title = "Delete package after unpacking",
|
onCheckedChange = {
|
||||||
)
|
scope.launch {
|
||||||
Box(
|
prefs.setUnpackInDownload(it)
|
||||||
Modifier.fillMaxWidth().clickable { /*TODO*/ }
|
}
|
||||||
) {
|
},
|
||||||
Column(Modifier.padding(horizontal = 16.dp)) {
|
)
|
||||||
Text(
|
Toggleable(
|
||||||
text = "Download Directory",
|
title = "Delete package after unpacking",
|
||||||
)
|
checked = deleteAfterUnpack,
|
||||||
Text(
|
onCheckedChange = {
|
||||||
text = "sdcard/Downloads",
|
scope.launch {
|
||||||
color = MaterialTheme.colorScheme.outline,
|
prefs.setDeleteAfterUnpack(it)
|
||||||
)
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { downloadDirPicker.launch(Uri.EMPTY) }
|
||||||
|
) {
|
||||||
|
Column(Modifier.padding(16.dp)) {
|
||||||
|
Text(
|
||||||
|
text = "Download Directory",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = Uri.decode(downloadDir).ifEmpty { "None" },
|
||||||
|
color = MaterialTheme.colorScheme.outline,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Box(
|
||||||
Box(
|
Modifier
|
||||||
Modifier.fillMaxWidth().clickable { /*TODO*/ }
|
.fillMaxWidth()
|
||||||
) {
|
.clickable { unpackDirPicker.launch(Uri.EMPTY) }
|
||||||
Column(Modifier.padding(horizontal = 16.dp)) {
|
) {
|
||||||
Text(
|
Column(Modifier.padding(16.dp)) {
|
||||||
text = "Unpack Directory",
|
Text(
|
||||||
)
|
text = "Unpack Directory",
|
||||||
Text(
|
)
|
||||||
text = "sdcard/Unpack",
|
Text(
|
||||||
color = MaterialTheme.colorScheme.outline,
|
text = Uri.decode(unpackDir).ifEmpty { "None" },
|
||||||
)
|
color = MaterialTheme.colorScheme.outline,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -549,11 +645,24 @@ fun OnBoardingSetupSettings(
|
|||||||
)
|
)
|
||||||
Toggleable(
|
Toggleable(
|
||||||
title = "Automatically decrypt downloaded content",
|
title = "Automatically decrypt downloaded content",
|
||||||
|
checked = autoDecrypt,
|
||||||
|
onCheckedChange = {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setPkg2zipAutoDecrypt(it)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
value = pkg2zipArgs,
|
value = pkg2zipArgs,
|
||||||
onValueChange = { pkg2zipArgs = it }, // TODO: validate args are legal ??
|
onValueChange = {
|
||||||
|
pkg2zipArgs = it
|
||||||
|
scope.launch {
|
||||||
|
prefs.setPkg2zipParams(it)
|
||||||
|
}
|
||||||
|
}, // TODO: validate args are legal ??
|
||||||
label = { Text(text = "Pkg2zip args") },
|
label = { Text(text = "Pkg2zip args") },
|
||||||
placeholder = { Text(text = "-x {pkgFile} \"{zRifKey}\"") },
|
placeholder = { Text(text = "-x {pkgFile} \"{zRifKey}\"") },
|
||||||
)
|
)
|
||||||
@@ -576,9 +685,13 @@ fun OnBoardingSetupSettings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Toggleable(title: String) {
|
fun Toggleable(title: String, checked: Boolean, onCheckedChange: (Boolean) -> Unit) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.clickable { /* TODO: */ },
|
modifier = Modifier
|
||||||
|
.clickable onCheckedChange@{
|
||||||
|
onCheckedChange(!checked)
|
||||||
|
}
|
||||||
|
.padding(vertical = 16.dp),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
@@ -586,7 +699,7 @@ fun Toggleable(title: String) {
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
Text(text = title, color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.weight(1.0f))
|
Text(text = title, color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.weight(1.0f))
|
||||||
Switch(checked = true, onCheckedChange = { /* TODO: */ })
|
Switch(checked = checked, onCheckedChange = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -599,20 +712,25 @@ fun OnBoardingBottomButtons(
|
|||||||
skipToEnd: () -> Unit = {},
|
skipToEnd: () -> Unit = {},
|
||||||
navigateToHome: () -> Unit = {},
|
navigateToHome: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
val prefs = Preferences(LocalContext.current)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.fillMaxHeight(0.1f),
|
.fillMaxHeight(0.1f),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = if (page != 2) Arrangement.SpaceBetween else Arrangement.End,
|
||||||
) {
|
) {
|
||||||
FilledTonalButton(
|
if (page == 1) {
|
||||||
onClick = skipToEnd,
|
FilledTonalButton(
|
||||||
colors = ButtonDefaults.filledTonalButtonColors(
|
onClick = skipToEnd,
|
||||||
contentColor = MaterialTheme.colorScheme.primary
|
colors = ButtonDefaults.filledTonalButtonColors(
|
||||||
),
|
contentColor = MaterialTheme.colorScheme.primary
|
||||||
contentPadding = PaddingValues(horizontal = 18.dp)
|
),
|
||||||
) {
|
contentPadding = PaddingValues(horizontal = 18.dp)
|
||||||
Text(text = "Skip")
|
) {
|
||||||
|
Text(text = "Skip")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
if (page != 1) {
|
if (page != 1) {
|
||||||
@@ -621,7 +739,15 @@ fun OnBoardingBottomButtons(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (page == 2) {
|
if (page == 2) {
|
||||||
Button(onClick = navigateToHome, contentPadding = PaddingValues(horizontal = 18.dp)) {
|
Button(
|
||||||
|
onClick = {
|
||||||
|
navigateToHome()
|
||||||
|
scope.launch {
|
||||||
|
prefs.finishOnboarding()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentPadding = PaddingValues(horizontal = 18.dp)
|
||||||
|
) {
|
||||||
Text(text = "Finish")
|
Text(text = "Finish")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.illusionware.npsbrowser.ui.pages
|
package com.illusionware.npsbrowser.ui.pages
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -15,34 +18,36 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.ContentPaste
|
||||||
import androidx.compose.material.icons.outlined.FileDownload
|
import androidx.compose.material.icons.outlined.FileDownload
|
||||||
import androidx.compose.material.icons.outlined.Key
|
import androidx.compose.material.icons.outlined.Key
|
||||||
import androidx.compose.material.icons.outlined.Palette
|
import androidx.compose.material.icons.outlined.Palette
|
||||||
import androidx.compose.material.icons.outlined.Unarchive
|
import androidx.compose.material.icons.outlined.Unarchive
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.illusionware.npsbrowser.Preferences
|
import com.illusionware.npsbrowser.Preferences
|
||||||
import com.illusionware.npsbrowser.R
|
import com.illusionware.npsbrowser.R
|
||||||
import com.illusionware.npsbrowser.ui.components.NPSAlertDialog
|
import com.illusionware.npsbrowser.ui.components.NPSAlertDialog
|
||||||
@@ -54,6 +59,12 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
val Themes = hashMapOf(
|
||||||
|
0 to "Light",
|
||||||
|
1 to "Dark",
|
||||||
|
2 to "Use System Theme"
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun SettingsScreen(navigationGoBack: () -> Unit = {}, onThemeChange: (v: Int) -> Unit = {}) {
|
fun SettingsScreen(navigationGoBack: () -> Unit = {}, onThemeChange: (v: Int) -> Unit = {}) {
|
||||||
@@ -79,46 +90,18 @@ fun SettingsScreen(navigationGoBack: () -> Unit = {}, onThemeChange: (v: Int) ->
|
|||||||
) {
|
) {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(20.dp), modifier = Modifier.padding(vertical = 16.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(20.dp), modifier = Modifier.padding(vertical = 16.dp)) {
|
||||||
Appearance(onThemeChange)
|
Appearance(onThemeChange)
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Updates()
|
||||||
Header("Updates")
|
Downloads()
|
||||||
Spacer(modifier = Modifier.size(2.dp))
|
Pkg2Zip()
|
||||||
DialogSetting(title = "HMAC Key", value = "None", icon = Icons.Outlined.Key)
|
|
||||||
}
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
||||||
Header("Downloads")
|
|
||||||
Spacer(modifier = Modifier.size(2.dp))
|
|
||||||
ToggleSetting(title = "Unpack in Download Directory")
|
|
||||||
DialogSetting(title = "Download Directory", value = "sdcard/Downloads", icon = Icons.Outlined.FileDownload)
|
|
||||||
DialogSetting(title = "Unpack Directory", value = "sdcard/unpack", icon = Icons.Outlined.Unarchive)
|
|
||||||
}
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
||||||
Header("Pkg2zip")
|
|
||||||
Spacer(modifier = Modifier.size(2.dp))
|
|
||||||
ToggleSetting(title = "Automatically decrypt downloaded content")
|
|
||||||
DialogSetting(title = "Decryption params", value = "-x {pkgFile} \"{zRifKey}\"", icon = painterResource(
|
|
||||||
id = R.drawable.lock_open
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun SettingGroup(title: String, content: @Composable () -> Unit) {
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
||||||
Header(title)
|
|
||||||
Spacer(modifier = Modifier.size(2.dp))
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val Themes = hashMapOf(
|
|
||||||
0 to "Light",
|
|
||||||
1 to "Dark",
|
|
||||||
2 to "Use System Theme"
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Appearance(onThemeChange: (v: Int) -> Unit) {
|
fun Appearance(onThemeChange: (v: Int) -> Unit) {
|
||||||
@@ -143,6 +126,197 @@ fun Appearance(onThemeChange: (v: Int) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Updates() {
|
||||||
|
val prefs = Preferences(LocalContext.current)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val clipboard = LocalClipboardManager.current
|
||||||
|
|
||||||
|
val hmacKey = prefs.hmacKey.collectAsState(initial = "").value
|
||||||
|
var hmacOpenDialog by remember { mutableStateOf(false) }
|
||||||
|
var dialogHmacKey by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
SideEffect {
|
||||||
|
dialogHmacKey = hmacKey
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingGroup(title = "Updates") {
|
||||||
|
DialogSetting(
|
||||||
|
title = "HMAC Key",
|
||||||
|
value = hmacKey.ifEmpty { "None" },
|
||||||
|
icon = Icons.Outlined.Key,
|
||||||
|
onClick = { hmacOpenDialog = true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (hmacOpenDialog) {
|
||||||
|
NPSAlertDialog(
|
||||||
|
onDismiss = { hmacOpenDialog = false },
|
||||||
|
title = "HMAC Key",
|
||||||
|
buttons = {
|
||||||
|
TextButton(onClick = { hmacOpenDialog = false }) {
|
||||||
|
Text(text = "Cancel")
|
||||||
|
}
|
||||||
|
TextButton(onClick = {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setHMACKey(dialogHmacKey)
|
||||||
|
}
|
||||||
|
hmacOpenDialog = false
|
||||||
|
}) {
|
||||||
|
Text(text = "Ok")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = dialogHmacKey,
|
||||||
|
onValueChange = { dialogHmacKey = it },
|
||||||
|
label = { Text(text = "HMAC Key") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
singleLine = true,
|
||||||
|
trailingIcon = {
|
||||||
|
NPSIconButton(
|
||||||
|
tooltip = "Paste",
|
||||||
|
onClick = { dialogHmacKey = clipboard.getText()?.text ?: "" }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.ContentPaste,
|
||||||
|
contentDescription = "Paste",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Downloads() {
|
||||||
|
val prefs = Preferences(LocalContext.current)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val downloadDir = prefs.downloadDir.collectAsState(initial = "").value
|
||||||
|
val unpackDir = prefs.unpackDir.collectAsState(initial = "").value
|
||||||
|
val unpackInDownload = prefs.unpackInDownload.collectAsState(initial = false).value
|
||||||
|
val deleteAfterUnpack = prefs.deleteAfterUnpack.collectAsState(initial = false).value
|
||||||
|
|
||||||
|
val downloadDirPicker = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.OpenDocumentTree(),
|
||||||
|
) { dirUri ->
|
||||||
|
if (dirUri != null) {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setDownloadDir(dirUri.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val unpackDirPicker = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.OpenDocumentTree(),
|
||||||
|
) { dirUri ->
|
||||||
|
if (dirUri != null) {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setUnpackDir(dirUri.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingGroup(title = "Downloads") {
|
||||||
|
ToggleSetting(
|
||||||
|
title = "Unpack in Download Directory",
|
||||||
|
checked = unpackInDownload,
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setUnpackInDownload(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
ToggleSetting(
|
||||||
|
title = "Delete package after unpacking",
|
||||||
|
checked = deleteAfterUnpack,
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setDeleteAfterUnpack(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DialogSetting(
|
||||||
|
title = "Download Directory",
|
||||||
|
value = downloadDir,
|
||||||
|
icon = Icons.Outlined.FileDownload,
|
||||||
|
onClick = {
|
||||||
|
downloadDirPicker.launch(Uri.EMPTY)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DialogSetting(
|
||||||
|
title = "Unpack Directory",
|
||||||
|
value = unpackDir,
|
||||||
|
icon = Icons.Outlined.Unarchive,
|
||||||
|
onClick = {
|
||||||
|
unpackDirPicker.launch(Uri.EMPTY)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Pkg2Zip() {
|
||||||
|
val prefs = Preferences(LocalContext.current)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val autoDecrypt = prefs.pkg2zipAutoDecrypt.collectAsState(initial = true).value
|
||||||
|
val pkg2zipParams = prefs.pkg2zipParams.collectAsState(initial = "").value
|
||||||
|
var pkg2zipOpenDialog by remember { mutableStateOf(false) }
|
||||||
|
var dialogPkg2zipParams by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
SideEffect {
|
||||||
|
dialogPkg2zipParams = pkg2zipParams
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingGroup(title = "Pkg2Zip") {
|
||||||
|
ToggleSetting(
|
||||||
|
title = "Automatically decrypt downloaded content",
|
||||||
|
checked = autoDecrypt,
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setPkg2zipAutoDecrypt(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DialogSetting(
|
||||||
|
title = "Decryption params",
|
||||||
|
value = pkg2zipParams.ifEmpty { "None" },
|
||||||
|
icon = painterResource(
|
||||||
|
id = R.drawable.lock_open
|
||||||
|
),
|
||||||
|
onClick = { pkg2zipOpenDialog = true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (pkg2zipOpenDialog) {
|
||||||
|
NPSAlertDialog(
|
||||||
|
onDismiss = { pkg2zipOpenDialog = false },
|
||||||
|
title = "PKG2ZIP Args",
|
||||||
|
buttons = {
|
||||||
|
TextButton(onClick = { pkg2zipOpenDialog = false }) {
|
||||||
|
Text(text = "Cancel")
|
||||||
|
}
|
||||||
|
TextButton(onClick = {
|
||||||
|
scope.launch {
|
||||||
|
prefs.setPkg2zipParams(dialogPkg2zipParams)
|
||||||
|
}
|
||||||
|
pkg2zipOpenDialog = false
|
||||||
|
}) {
|
||||||
|
Text(text = "Ok")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = dialogPkg2zipParams,
|
||||||
|
onValueChange = { dialogPkg2zipParams = it },
|
||||||
|
label = { Text(text = "PKG2ZIP Args") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
singleLine = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ThemeDialog(onDismiss: () -> Unit, onThemeChange: (v: Int) -> Unit) {
|
fun ThemeDialog(onDismiss: () -> Unit, onThemeChange: (v: Int) -> Unit) {
|
||||||
val prefs = Preferences(LocalContext.current)
|
val prefs = Preferences(LocalContext.current)
|
||||||
@@ -187,6 +361,15 @@ fun ThemeDialog(onDismiss: () -> Unit, onThemeChange: (v: Int) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingGroup(title: String, content: @Composable () -> Unit) {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
|
Header(title)
|
||||||
|
Spacer(modifier = Modifier.size(2.dp))
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Header(title: String) {
|
fun Header(title: String) {
|
||||||
Row(Modifier.padding(start = 56.dp)) {
|
Row(Modifier.padding(start = 56.dp)) {
|
||||||
@@ -195,29 +378,7 @@ fun Header(title: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
// TODO: remove the default value for onClick
|
fun DialogSetting(title: String, value: String, icon: Painter, onClick: () -> Unit) {
|
||||||
fun DialogSetting(title: String, value: String, icon: Painter, onClick: () -> Unit = {}) {
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { onClick() }
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
|
||||||
) {
|
|
||||||
Icon(painter = icon, contentDescription = "TODO:", modifier = Modifier.width(56.dp))
|
|
||||||
Column {
|
|
||||||
Text(text = title, color = MaterialTheme.colorScheme.onBackground)
|
|
||||||
Text(text = value, color = ColorSecondaryLight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
// TODO: remove the default value for onClick
|
|
||||||
fun DialogSetting(title: String, value: String, icon: ImageVector, onClick: () -> Unit = {}) {
|
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -227,7 +388,7 @@ fun DialogSetting(title: String, value: String, icon: ImageVector, onClick: () -
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
modifier = Modifier.padding(vertical = 8.dp)
|
||||||
) {
|
) {
|
||||||
Icon(imageVector = icon, contentDescription = "TODO:", modifier = Modifier.width(56.dp))
|
Icon(painter = icon, contentDescription = null, modifier = Modifier.width(56.dp))
|
||||||
Column {
|
Column {
|
||||||
Text(text = title, color = MaterialTheme.colorScheme.onBackground)
|
Text(text = title, color = MaterialTheme.colorScheme.onBackground)
|
||||||
Text(text = value, color = ColorSecondaryLight)
|
Text(text = value, color = ColorSecondaryLight)
|
||||||
@@ -237,9 +398,31 @@ fun DialogSetting(title: String, value: String, icon: ImageVector, onClick: () -
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ToggleSetting(title: String, icon: ImageVector? = null) {
|
fun DialogSetting(title: String, value: String, icon: ImageVector, onClick: () -> Unit) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.clickable { /* TODO: */ },
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onClick() }
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(vertical = 8.dp)
|
||||||
|
) {
|
||||||
|
Icon(imageVector = icon, contentDescription = null, modifier = Modifier.width(56.dp))
|
||||||
|
Column {
|
||||||
|
Text(text = title, color = MaterialTheme.colorScheme.onBackground)
|
||||||
|
Text(text = value, color = ColorSecondaryLight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ToggleSetting(title: String, icon: ImageVector? = null, checked: Boolean, onClick: (Boolean) -> Unit) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.clickable onClick@{
|
||||||
|
onClick(!checked)
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -247,12 +430,12 @@ fun ToggleSetting(title: String, icon: ImageVector? = null) {
|
|||||||
modifier = Modifier.padding(top = 8.dp, end = 24.dp, bottom = 8.dp),
|
modifier = Modifier.padding(top = 8.dp, end = 24.dp, bottom = 8.dp),
|
||||||
) {
|
) {
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
Icon(imageVector = icon, contentDescription = "TODO:", Modifier.width(56.dp))
|
Icon(imageVector = icon, contentDescription = null, Modifier.width(56.dp))
|
||||||
} else {
|
} else {
|
||||||
Box(modifier = Modifier.width(56.dp))
|
Box(modifier = Modifier.width(56.dp))
|
||||||
}
|
}
|
||||||
Text(text = title, color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.weight(1.0f))
|
Text(text = title, color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.weight(1.0f))
|
||||||
Switch(checked = true, onCheckedChange = { /* TODO: */ })
|
Switch(checked = checked, onCheckedChange = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user