HomeSettings.kt (12709B) - raw


      1 package me.rhunk.snapenhance.ui.manager.pages.home
      2 
      3 import android.content.SharedPreferences
      4 import androidx.compose.foundation.clickable
      5 import androidx.compose.foundation.layout.*
      6 import androidx.compose.foundation.rememberScrollState
      7 import androidx.compose.foundation.verticalScroll
      8 import androidx.compose.material.icons.Icons
      9 import androidx.compose.material.icons.automirrored.filled.OpenInNew
     10 import androidx.compose.material3.*
     11 import androidx.compose.runtime.*
     12 import androidx.compose.ui.Alignment
     13 import androidx.compose.ui.Modifier
     14 import androidx.compose.ui.text.font.FontWeight
     15 import androidx.compose.ui.unit.dp
     16 import androidx.compose.ui.unit.sp
     17 import androidx.compose.ui.window.Dialog
     18 import androidx.core.content.edit
     19 import androidx.core.net.toUri
     20 import androidx.navigation.NavBackStackEntry
     21 import kotlinx.coroutines.launch
     22 import me.rhunk.snapenhance.common.action.EnumAction
     23 import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
     24 import me.rhunk.snapenhance.common.ui.rememberAsyncMutableState
     25 import me.rhunk.snapenhance.ui.manager.Routes
     26 import me.rhunk.snapenhance.ui.setup.Requirements
     27 import me.rhunk.snapenhance.ui.util.ActivityLauncherHelper
     28 import me.rhunk.snapenhance.ui.util.AlertDialogs
     29 import me.rhunk.snapenhance.ui.util.saveFile
     30 
     31 class HomeSettings : Routes.Route() {
     32     private lateinit var activityLauncherHelper: ActivityLauncherHelper
     33     private val dialogs by lazy { AlertDialogs(context.translation) }
     34 
     35     override val init: () -> Unit = {
     36         activityLauncherHelper = ActivityLauncherHelper(context.activity!!)
     37     }
     38 
     39     @Composable
     40     private fun RowTitle(title: String) {
     41         Text(text = title, modifier = Modifier.padding(16.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold)
     42     }
     43 
     44     @Composable
     45     private fun PreferenceToggle(sharedPreferences: SharedPreferences, key: String, text: String) {
     46         val realKey = "debug_$key"
     47         var value by remember { mutableStateOf(sharedPreferences.getBoolean(realKey, false)) }
     48 
     49         Row(
     50             modifier = Modifier
     51                 .fillMaxWidth()
     52                 .heightIn(min = 55.dp)
     53                 .clickable {
     54                     value = !value
     55                     sharedPreferences
     56                         .edit() {
     57                             putBoolean(realKey, value)
     58                         }
     59                 },
     60             horizontalArrangement = Arrangement.SpaceBetween,
     61             verticalAlignment = Alignment.CenterVertically
     62         ) {
     63             Text(text = text, modifier = Modifier.padding(end = 16.dp), fontSize = 14.sp)
     64             Switch(checked = value, onCheckedChange = {
     65                 value = it
     66                 sharedPreferences.edit().putBoolean(realKey, it).apply()
     67             }, modifier = Modifier.padding(end = 26.dp))
     68         }
     69     }
     70 
     71     @Composable
     72     private fun RowAction(key: String, requireConfirmation: Boolean = false, action: () -> Unit) {
     73         var confirmationDialog by remember {
     74             mutableStateOf(false)
     75         }
     76 
     77         fun takeAction() {
     78             if (requireConfirmation) {
     79                 confirmationDialog = true
     80             } else {
     81                 action()
     82             }
     83         }
     84 
     85         if (requireConfirmation && confirmationDialog) {
     86             Dialog(onDismissRequest = { confirmationDialog = false }) {
     87                 dialogs.ConfirmDialog(title = context.translation["manager.dialogs.action_confirm.title"], onConfirm = {
     88                     action()
     89                     confirmationDialog = false
     90                 }, onDismiss = {
     91                     confirmationDialog = false
     92                 })
     93             }
     94         }
     95 
     96         ShiftedRow(
     97             modifier = Modifier
     98                 .fillMaxWidth()
     99                 .heightIn(min = 55.dp)
    100                 .clickable {
    101                     takeAction()
    102                 },
    103             horizontalArrangement = Arrangement.SpaceBetween,
    104             verticalAlignment = Alignment.CenterVertically
    105         ) {
    106             Column(
    107                 modifier = Modifier.weight(1f),
    108             ) {
    109                 Text(text = context.translation["actions.$key.name"], fontSize = 16.sp, fontWeight = FontWeight.Bold, lineHeight = 20.sp)
    110                 context.translation.getOrNull("actions.$key.description")?.let { Text(text = it, fontSize = 12.sp, fontWeight = FontWeight.Light, lineHeight = 15.sp) }
    111             }
    112             IconButton(onClick = { takeAction() },
    113                 modifier = Modifier.padding(end = 2.dp)
    114             ) {
    115                 Icon(
    116                     imageVector = Icons.AutoMirrored.Filled.OpenInNew,
    117                     contentDescription = null,
    118                     modifier = Modifier.size(24.dp)
    119                 )
    120             }
    121         }
    122     }
    123 
    124     @Composable
    125     private fun ShiftedRow(
    126         modifier: Modifier = Modifier,
    127         horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    128         verticalAlignment: Alignment.Vertical = Alignment.Top,
    129         content: @Composable RowScope.() -> Unit
    130     ) {
    131         Row(
    132             modifier = modifier.padding(start = 26.dp),
    133             horizontalArrangement = horizontalArrangement,
    134             verticalAlignment = verticalAlignment
    135         ) { content(this) }
    136     }
    137 
    138     @OptIn(ExperimentalMaterial3Api::class)
    139     override val content: @Composable (NavBackStackEntry) -> Unit = {
    140         Column(
    141             modifier = Modifier
    142                 .fillMaxSize()
    143                 .verticalScroll(rememberScrollState())
    144         ) {
    145             RowTitle(title = translation["actions_title"])
    146             EnumAction.entries.forEach { enumAction ->
    147                 RowAction(key = enumAction.key) {
    148                     context.launchActionIntent(enumAction)
    149                 }
    150             }
    151             RowAction(key = "regen_mappings") {
    152                 context.checkForRequirements(Requirements.MAPPINGS)
    153             }
    154             RowAction(key = "change_language") {
    155                 context.checkForRequirements(Requirements.LANGUAGE)
    156             }
    157             RowTitle(title = translation["message_logger_title"])
    158             ShiftedRow {
    159                 Column(
    160                     verticalArrangement = Arrangement.spacedBy(4.dp),
    161                 ) {
    162                     var storedMessagesCount by rememberAsyncMutableState(defaultValue = 0) {
    163                         context.messageLogger.getStoredMessageCount()
    164                     }
    165                     var storedStoriesCount by rememberAsyncMutableState(defaultValue = 0) {
    166                         context.messageLogger.getStoredStoriesCount()
    167                     }
    168                     Row(
    169                         horizontalArrangement = Arrangement.spacedBy(10.dp),
    170                         verticalAlignment = Alignment.CenterVertically,
    171                         modifier = Modifier
    172                             .fillMaxWidth()
    173                             .padding(5.dp)
    174                     ) {
    175                         Column(
    176                             modifier = Modifier.weight(1f),
    177                             verticalArrangement = Arrangement.spacedBy(2.dp),
    178                         ) {
    179                             Text(
    180                                 translation.format("message_logger_summary",
    181                                 "messageCount" to storedMessagesCount.toString(),
    182                                 "storyCount" to storedStoriesCount.toString()
    183                             ), maxLines = 2)
    184                         }
    185                         Button(onClick = {
    186                             runCatching {
    187                                 activityLauncherHelper.saveFile("message_logger.db", "application/octet-stream") { uri ->
    188                                     context.androidContext.contentResolver.openOutputStream(uri.toUri())?.use { outputStream ->
    189                                         context.messageLogger.databaseFile.inputStream().use { inputStream ->
    190                                             inputStream.copyTo(outputStream)
    191                                         }
    192                                     }
    193                                 }
    194                             }.onFailure {
    195                                 context.log.error("Failed to export database", it)
    196                                 context.longToast("Failed to export database! ${it.localizedMessage}")
    197                             }
    198                         }) {
    199                             Text(text = translation["export_button"])
    200                         }
    201                         Button(onClick = {
    202                             runCatching {
    203                                 context.messageLogger.purgeAll()
    204                                 storedMessagesCount = 0
    205                                 storedStoriesCount = 0
    206                             }.onFailure {
    207                                 context.log.error("Failed to clear messages", it)
    208                                 context.longToast("Failed to clear messages! ${it.localizedMessage}")
    209                             }.onSuccess {
    210                                 context.shortToast(translation["success_toast"])
    211                             }
    212                         }) {
    213                             Text(text = translation["clear_button"])
    214                         }
    215                     }
    216                     OutlinedButton(
    217                         modifier = Modifier
    218                             .fillMaxWidth()
    219                             .padding(5.dp),
    220                         onClick = {
    221                             routes.loggerHistory.navigate()
    222                         }
    223                     ) {
    224                         Text(translation["view_logger_history_button"])
    225                     }
    226                 }
    227             }
    228 
    229             RowTitle(title = translation["debug_title"])
    230             Row(
    231                 horizontalArrangement = Arrangement.SpaceBetween,
    232                 verticalAlignment = Alignment.CenterVertically,
    233             ) {
    234                 var selectedFileType by remember { mutableStateOf(InternalFileHandleType.entries.first()) }
    235                 Box(
    236                     modifier = Modifier
    237                         .weight(1f)
    238                         .padding(start = 26.dp)
    239                 ) {
    240                     var expanded by remember { mutableStateOf(false) }
    241 
    242                     ExposedDropdownMenuBox(
    243                         expanded = expanded,
    244                         onExpandedChange = { expanded = it },
    245                         modifier = Modifier.fillMaxWidth(0.7f)
    246                     ) {
    247                         TextField(
    248                             value = selectedFileType.fileName,
    249                             onValueChange = {},
    250                             readOnly = true,
    251                             modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable)
    252                         )
    253 
    254                         ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
    255                             InternalFileHandleType.entries.forEach { fileType ->
    256                                 DropdownMenuItem(onClick = {
    257                                     expanded = false
    258                                     selectedFileType = fileType
    259                                 }, text = {
    260                                     Text(text = fileType.fileName)
    261                                 })
    262                             }
    263                         }
    264                     }
    265                 }
    266                 Button(onClick = {
    267                     runCatching {
    268                         context.coroutineScope.launch {
    269                             selectedFileType.resolve(context.androidContext).delete()
    270                         }
    271                     }.onFailure {
    272                         context.log.error("Failed to clear file", it)
    273                         context.longToast("Failed to clear file! ${it.localizedMessage}")
    274                     }.onSuccess {
    275                         context.shortToast(translation["success_toast"])
    276                     }
    277                 }) {
    278                     Text(translation["clear_button"])
    279                 }
    280             }
    281             ShiftedRow {
    282                 Column(
    283                     verticalArrangement = Arrangement.spacedBy(4.dp),
    284                 ) {
    285                     PreferenceToggle(context.sharedPreferences, key = "test_mode", text = "Test Mode (FOR DEBUGGING ONLY)")
    286                     PreferenceToggle(context.sharedPreferences, key = "disable_feature_loading", text = "Disable Feature Loading")
    287                     PreferenceToggle(context.sharedPreferences, key = "disable_mapper", text = "Disable Auto Mapper")
    288                 }
    289             }
    290             Spacer(modifier = Modifier.height(50.dp))
    291         }
    292     }
    293 }