diff --git a/.idea/misc.xml b/.idea/misc.xml index d852bbf00..7146cb182 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + diff --git a/domain/use-case/src/main/kotlin/com/android/geto/domain/usecase/AddAppSettingUseCase.kt b/domain/use-case/src/main/kotlin/com/android/geto/domain/usecase/AddAppSettingUseCase.kt index c5a099780..3544f9283 100644 --- a/domain/use-case/src/main/kotlin/com/android/geto/domain/usecase/AddAppSettingUseCase.kt +++ b/domain/use-case/src/main/kotlin/com/android/geto/domain/usecase/AddAppSettingUseCase.kt @@ -23,7 +23,6 @@ import com.android.geto.domain.model.AddAppSettingResult import com.android.geto.domain.model.AddAppSettingResult.FAILED import com.android.geto.domain.model.AddAppSettingResult.SUCCESS import com.android.geto.domain.model.AppSetting -import com.android.geto.domain.model.SettingType import com.android.geto.domain.repository.AppSettingsRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext @@ -33,28 +32,8 @@ class AddAppSettingUseCase @Inject constructor( @param:Dispatcher(Default) private val defaultDispatcher: CoroutineDispatcher, private val appSettingsRepository: AppSettingsRepository, ) { - suspend operator fun invoke( - componentName: String, - id: Int, - enabled: Boolean, - settingType: SettingType, - label: String, - key: String, - valueOnLaunch: String, - valueOnRevert: String, - ): AddAppSettingResult { + suspend operator fun invoke(appSetting: AppSetting): AddAppSettingResult { return withContext(defaultDispatcher) { - val appSetting = AppSetting( - id = id, - enabled = enabled, - settingType = settingType, - componentName = componentName, - label = label, - key = key, - valueOnLaunch = valueOnLaunch, - valueOnRevert = valueOnRevert, - ) - val keys = appSettingsRepository.getAppSettingsByComponentName(componentName = appSetting.componentName) .map { it.key } diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsScreen.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsScreen.kt index 90d8d236a..681763462 100644 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsScreen.kt +++ b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsScreen.kt @@ -58,8 +58,9 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.snapshotFlow +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -90,15 +91,9 @@ import com.android.geto.domain.model.RequestPinShortcutResult.UpdateImmutableSho import com.android.geto.domain.model.RequestPinShortcutResult.UpdateSuccess import com.android.geto.domain.model.SecureSetting import com.android.geto.domain.model.SettingType -import com.android.geto.feature.appsettings.dialog.appsetting.AppSettingDialog -import com.android.geto.feature.appsettings.dialog.appsetting.AppSettingDialogState -import com.android.geto.feature.appsettings.dialog.appsetting.rememberAppSettingDialogState -import com.android.geto.feature.appsettings.dialog.shortcut.ShortcutDialog -import com.android.geto.feature.appsettings.dialog.shortcut.ShortcutDialogState -import com.android.geto.feature.appsettings.dialog.shortcut.rememberShortcutDialogState -import com.android.geto.feature.appsettings.dialog.template.TemplateDialog -import com.android.geto.feature.appsettings.dialog.template.TemplateDialogState -import com.android.geto.feature.appsettings.dialog.template.rememberTemplateDialogState +import com.android.geto.feature.appsettings.dialog.AppSettingDialog +import com.android.geto.feature.appsettings.dialog.ShortcutDialog +import com.android.geto.feature.appsettings.dialog.TemplateDialog import com.android.geto.feature.appsettings.navigation.AppSettingsRouteData import com.android.geto.framework.notificationmanager.AndroidNotificationManagerWrapper import com.android.geto.framework.notificationmanager.AndroidNotificationManagerWrapper.Companion.ACTION_REVERT_SETTINGS @@ -107,10 +102,6 @@ import com.android.geto.framework.notificationmanager.AndroidNotificationManager import com.android.geto.ui.local.LocalLauncherApps import com.android.geto.ui.local.LocalNotificationManager import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.onEach @Composable internal fun AppSettingsRoute( @@ -184,15 +175,7 @@ internal fun AppSettingsScreen( onRevertAppSettings: () -> Unit, onCheckAppSetting: (appSetting: AppSetting) -> Unit, onDeleteAppSetting: (appSetting: AppSetting) -> Unit, - onAddAppSetting: ( - id: Int, - enabled: Boolean, - settingType: SettingType, - label: String, - key: String, - valueOnLaunch: String, - valueOnRevert: String, - ) -> Unit, + onAddAppSetting: (AppSetting) -> Unit, onRequestPinShortcut: ( icon: ByteArray?, shortLabel: String, @@ -205,19 +188,16 @@ internal fun AppSettingsScreen( onResetAddAppSettingResult: () -> Unit, onNavigationIconClick: () -> Unit, ) { - val appSettingDialogState = rememberAppSettingDialogState() + var showAppSettingDialog by remember { mutableStateOf(false) } - val shortcutDialogState = rememberShortcutDialogState() + var showShortcutDialog by remember { mutableStateOf(false) } - val templateDialogState = rememberTemplateDialogState() + var showTemplateDialog by remember { mutableStateOf(false) } AppSettingsLaunchedEffects( appSettingsRouteData = appSettingsRouteData, snackbarHostState = snackbarHostState, - appSettingDialogState = appSettingDialogState, - shortcutDialogState = shortcutDialogState, activityIcon = activityIcon, - secureSettings = secureSettings, addAppSettingResult = addAppSettingResult, applyAppSettingsResult = applyAppSettingsResult, revertAppSettingsResult = revertAppSettingsResult, @@ -226,15 +206,27 @@ internal fun AppSettingsScreen( onResetRevertAppSettingsResult = onResetRevertAppSettingsResult, onResetRequestPinShortcutResult = onResetRequestPinShortcutResult, onResetAddAppSettingResult = onResetAddAppSettingResult, - onGetSecureSettingsByName = onGetSecureSettingsByName, ) AppSettingsDialogs( - appSettingDialogState = appSettingDialogState, - shortcutDialogState = shortcutDialogState, appSettingTemplates = appSettingTemplates, - templateDialogState = templateDialogState, + componentName = appSettingsRouteData.componentName, + icon = activityIcon, + secureSettings = secureSettings, + showAppSettingDialog = showAppSettingDialog, + showShortcutDialog = showShortcutDialog, + showTemplateDialog = showTemplateDialog, onAddAppSetting = onAddAppSetting, + onDismissAppSettingDialog = { + showAppSettingDialog = false + }, + onDismissShortcutDialog = { + showShortcutDialog = false + }, + onDismissTemplateDialog = { + showTemplateDialog = false + }, + onGetSecureSettingsByName = onGetSecureSettingsByName, onRequestPinShortcut = onRequestPinShortcut, ) @@ -249,13 +241,13 @@ internal fun AppSettingsScreen( AppSettingsBottomAppBar( onRefreshIconClick = onRevertAppSettings, onSettingsIconClick = { - appSettingDialogState.updateShowDialog(true) + showAppSettingDialog = true }, onShortcutIconClick = { - shortcutDialogState.updateShowDialog(true) + showShortcutDialog = true }, onSettingsSuggestIconClick = { - templateDialogState.updateShowDialog(true) + showTemplateDialog = true }, onFloatingActionButtonClick = onApplyAppSettings, ) @@ -299,10 +291,7 @@ internal fun AppSettingsScreen( private fun AppSettingsLaunchedEffects( appSettingsRouteData: AppSettingsRouteData, snackbarHostState: SnackbarHostState, - appSettingDialogState: AppSettingDialogState, - shortcutDialogState: ShortcutDialogState, activityIcon: ByteArray?, - secureSettings: List, addAppSettingResult: AddAppSettingResult?, applyAppSettingsResult: AppSettingsResult?, revertAppSettingsResult: AppSettingsResult?, @@ -311,7 +300,6 @@ private fun AppSettingsLaunchedEffects( onResetRevertAppSettingsResult: () -> Unit, onResetRequestPinShortcutResult: () -> Unit, onResetAddAppSettingResult: () -> Unit, - onGetSecureSettingsByName: (SettingType, String) -> Unit, ) { val context = LocalContext.current @@ -487,81 +475,51 @@ private fun AppSettingsLaunchedEffects( onResetAddAppSettingResult() } - - LaunchedEffect( - key1 = appSettingDialogState.key, - ) { - val settingType = SettingType.entries[appSettingDialogState.selectedRadioOptionIndex] - - snapshotFlow { appSettingDialogState.key }.debounce(500).distinctUntilChanged().onEach { - onGetSecureSettingsByName( - settingType, - appSettingDialogState.key, - ) - }.collect() - } - - LaunchedEffect( - key1 = appSettingDialogState.selectedRadioOptionIndex, - ) { - val settingType = SettingType.entries[appSettingDialogState.selectedRadioOptionIndex] - - snapshotFlow { appSettingDialogState.selectedRadioOptionIndex }.debounce(500) - .distinctUntilChanged().onEach { - onGetSecureSettingsByName( - settingType, - appSettingDialogState.key, - ) - }.collect() - } - - LaunchedEffect(key1 = secureSettings) { - appSettingDialogState.updateSecureSettings(secureSettings) - } - - LaunchedEffect(key1 = activityIcon) { - activityIcon?.let { - shortcutDialogState.updateIcon(it) - } - } } @Composable private fun AppSettingsDialogs( - appSettingDialogState: AppSettingDialogState, - shortcutDialogState: ShortcutDialogState, appSettingTemplates: List, - templateDialogState: TemplateDialogState, - onAddAppSetting: ( - id: Int, - enabled: Boolean, + componentName: String, + icon: ByteArray?, + secureSettings: List, + showAppSettingDialog: Boolean, + showShortcutDialog: Boolean, + showTemplateDialog: Boolean, + onAddAppSetting: (AppSetting) -> Unit, + onDismissAppSettingDialog: () -> Unit, + onDismissShortcutDialog: () -> Unit, + onDismissTemplateDialog: () -> Unit, + onGetSecureSettingsByName: ( settingType: SettingType, - label: String, - key: String, - valueOnLaunch: String, - valueOnRevert: String, + text: String, ) -> Unit, onRequestPinShortcut: (ByteArray?, String, String) -> Unit, ) { - if (appSettingDialogState.showDialog) { + if (showAppSettingDialog) { AppSettingDialog( - appSettingDialogState = appSettingDialogState, + componentName = componentName, + secureSettings = secureSettings, onAddAppSetting = onAddAppSetting, + onDismissRequest = onDismissAppSettingDialog, + onGetSecureSettingsByName = onGetSecureSettingsByName, ) } - if (shortcutDialogState.showDialog) { + if (showShortcutDialog) { ShortcutDialog( - shortcutDialogState = shortcutDialogState, + icon = icon, + onDismissRequest = onDismissShortcutDialog, onRequestPinShortcut = onRequestPinShortcut, ) } - if (templateDialogState.showDialog) { + if (showTemplateDialog) { TemplateDialog( appSettingTemplates = appSettingTemplates, - templateDialogState = templateDialogState, + componentName = componentName, onAddAppSetting = onAddAppSetting, + onDismissRequest = onDismissTemplateDialog, ) } } diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsViewModel.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsViewModel.kt index 2115a86b9..4318ff0a8 100644 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsViewModel.kt +++ b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/AppSettingsViewModel.kt @@ -124,27 +124,10 @@ class AppSettingsViewModel @Inject constructor( } } - fun addAppSetting( - id: Int, - enabled: Boolean, - settingType: SettingType, - label: String, - key: String, - valueOnLaunch: String, - valueOnRevert: String, - ) { + fun addAppSetting(appSetting: AppSetting) { viewModelScope.launch { _addAppSettingsResult.update { - addAppSettingUseCase( - id = id, - componentName = componentName, - enabled = enabled, - settingType = settingType, - label = label, - key = key, - valueOnLaunch = valueOnLaunch, - valueOnRevert = valueOnRevert, - ) + addAppSettingUseCase(appSetting = appSetting) } } } diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/appsetting/AppSettingDialog.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/AppSettingDialog.kt similarity index 52% rename from feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/appsetting/AppSettingDialog.kt rename to feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/AppSettingDialog.kt index af7f9f2d1..71c25534d 100644 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/appsetting/AppSettingDialog.kt +++ b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/AppSettingDialog.kt @@ -15,7 +15,7 @@ * limitations under the License. * */ -package com.android.geto.feature.appsettings.dialog.appsetting +package com.android.geto.feature.appsettings.dialog import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -40,6 +40,13 @@ import androidx.compose.material3.RadioButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -47,49 +54,151 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import com.android.geto.designsystem.component.DialogContainer +import com.android.geto.domain.model.AppSetting +import com.android.geto.domain.model.SecureSetting import com.android.geto.domain.model.SettingType import com.android.geto.feature.appsettings.R import com.android.geto.feature.appsettings.getSettingTypeTitle +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.onEach +@OptIn(FlowPreview::class) @Composable internal fun AppSettingDialog( modifier: Modifier = Modifier, - appSettingDialogState: AppSettingDialogState, - onAddAppSetting: ( - id: Int, - enabled: Boolean, + componentName: String, + secureSettings: List, + onAddAppSetting: (AppSetting) -> Unit, + onDismissRequest: () -> Unit, + onGetSecureSettingsByName: ( settingType: SettingType, - label: String, - key: String, - valueOnLaunch: String, - valueOnRevert: String, + text: String, ) -> Unit, ) { + var selectedRadioOptionIndex by remember { mutableIntStateOf(0) } + + var label by remember { mutableStateOf("") } + + var key by remember { mutableStateOf("") } + + var valueOnLaunch by remember { mutableStateOf("") } + + var valueOnRevert by remember { mutableStateOf("") } + + var showLabelError by remember { mutableStateOf(false) } + + var showKeyError by remember { mutableStateOf(false) } + + var showKeyNotFoundError by remember { mutableStateOf(false) } + + var showValueOnLaunchError by remember { mutableStateOf(false) } + + var showValueOnRevertError by remember { mutableStateOf(false) } + + var secureSettingsExpanded by remember { mutableStateOf(false) } + + LaunchedEffect(key1 = Unit) { + snapshotFlow { key }.debounce(500).distinctUntilChanged().onEach { + onGetSecureSettingsByName( + SettingType.entries[selectedRadioOptionIndex], + key, + ) + }.collect() + } + + LaunchedEffect(key1 = Unit) { + snapshotFlow { selectedRadioOptionIndex }.debounce(500) + .distinctUntilChanged().onEach { + onGetSecureSettingsByName( + SettingType.entries[selectedRadioOptionIndex], + key, + ) + }.collect() + } + DialogContainer( modifier = modifier .padding(16.dp) .verticalScroll(rememberScrollState()), - onDismissRequest = { appSettingDialogState.updateShowDialog(false) }, + onDismissRequest = onDismissRequest, ) { - Column( - modifier = Modifier.fillMaxWidth(), - ) { + Column(modifier = Modifier.fillMaxWidth()) { AppSettingDialogTitle() AppSettingDialogRadioButtonGroup( - selected = appSettingDialogState.selectedRadioOptionIndex, - onSelect = appSettingDialogState::updateSelectedRadioOptionIndex, + selected = selectedRadioOptionIndex, + onSelect = { + selectedRadioOptionIndex = it + }, ) AppSettingDialogTextFields( - appSettingDialogState = appSettingDialogState, + key = key, + label = label, + secureSettings = secureSettings, + secureSettingsExpanded = secureSettingsExpanded, + showKeyError = showKeyError, + showKeyNotFoundError = showKeyNotFoundError, + showLabelError = showLabelError, + showValueOnLaunchError = showValueOnLaunchError, + showValueOnRevertError = showValueOnRevertError, + valueOnLaunch = valueOnLaunch, + valueOnRevert = valueOnRevert, + onUpdateKey = { + key = it + }, + onUpdateLabel = { + label = it + }, + onUpdateSecureSettingsExpanded = { + secureSettingsExpanded = it + }, + onUpdateValueOnLaunch = { + valueOnLaunch = it + }, + onUpdateValueOnRevert = { + valueOnRevert = it + }, ) AppSettingDialogButtons( - onCancelClick = { - appSettingDialogState.updateShowDialog(false) + onCancelClick = onDismissRequest, + onAddClick = { + showLabelError = label.isBlank() + + showKeyError = key.isBlank() + + showKeyNotFoundError = + key.isNotBlank() && !secureSettings.mapNotNull { it.name }.contains(key) + + showValueOnLaunchError = valueOnLaunch.isBlank() + + showValueOnRevertError = valueOnRevert.isBlank() + + if (!showLabelError && + !showKeyNotFoundError && + !showKeyError && + !showValueOnLaunchError && + !showValueOnRevertError + ) { + onAddAppSetting( + AppSetting( + enabled = true, + settingType = SettingType.entries[selectedRadioOptionIndex], + componentName = componentName, + label = label, + key = key, + valueOnLaunch = valueOnLaunch, + valueOnRevert = valueOnRevert, + ), + ) + + onDismissRequest() + } }, - onAddClick = { appSettingDialogState.getAppSetting(onAddAppSetting = onAddAppSetting) }, ) } } @@ -149,10 +258,27 @@ private fun AppSettingDialogRadioButtonGroup( @Composable private fun AppSettingDialogTextFields( - appSettingDialogState: AppSettingDialogState, + key: String, + label: String, + secureSettings: List, + secureSettingsExpanded: Boolean, + showKeyError: Boolean, + showKeyNotFoundError: Boolean, + showLabelError: Boolean, + showValueOnLaunchError: Boolean, + showValueOnRevertError: Boolean, + valueOnLaunch: String, + valueOnRevert: String, + onUpdateKey: (String) -> Unit, + onUpdateLabel: (String) -> Unit, + onUpdateSecureSettingsExpanded: (Boolean) -> Unit, + onUpdateValueOnLaunch: (String) -> Unit, + onUpdateValueOnRevert: (String) -> Unit, ) { val labelIsBlank = stringResource(id = R.string.setting_label_is_blank) + val valueOnLaunchIsBlank = stringResource(id = R.string.setting_value_on_launch_is_blank) + val valueOnRevertIsBlank = stringResource(id = R.string.setting_value_on_revert_is_blank) Spacer(modifier = Modifier.height(10.dp)) @@ -161,14 +287,14 @@ private fun AppSettingDialogTextFields( modifier = Modifier .fillMaxWidth() .padding(horizontal = 10.dp), - value = appSettingDialogState.label, - onValueChange = appSettingDialogState::updateLabel, + value = label, + onValueChange = onUpdateLabel, label = { Text(text = stringResource(R.string.setting_label)) }, - isError = appSettingDialogState.showLabelError, + isError = showLabelError, supportingText = { - if (appSettingDialogState.showLabelError) { + if (showLabelError) { Text(text = labelIsBlank) } }, @@ -177,21 +303,28 @@ private fun AppSettingDialogTextFields( ) AppSettingDialogTextFieldWithDropdownMenu( - appSettingDialogState = appSettingDialogState, + key = key, + secureSettings = secureSettings, + secureSettingsExpanded = secureSettingsExpanded, + showKeyError = showKeyError, + showKeyNotFoundError = showKeyNotFoundError, + onUpdateKey = onUpdateKey, + onUpdateSecureSettingsExpanded = onUpdateSecureSettingsExpanded, + onUpdateValueOnRevert = onUpdateValueOnRevert, ) OutlinedTextField( modifier = Modifier .fillMaxWidth() .padding(horizontal = 10.dp), - value = appSettingDialogState.valueOnLaunch, - onValueChange = appSettingDialogState::updateValueOnLaunch, + value = valueOnLaunch, + onValueChange = onUpdateValueOnLaunch, label = { Text(text = stringResource(R.string.setting_value_on_launch)) }, - isError = appSettingDialogState.showValueOnLaunchError, + isError = showValueOnLaunchError, supportingText = { - if (appSettingDialogState.showValueOnLaunchError) { + if (showValueOnLaunchError) { Text(text = valueOnLaunchIsBlank) } }, @@ -203,14 +336,14 @@ private fun AppSettingDialogTextFields( modifier = Modifier .fillMaxWidth() .padding(horizontal = 10.dp), - value = appSettingDialogState.valueOnRevert, - onValueChange = appSettingDialogState::updateValueOnRevert, + value = valueOnRevert, + onValueChange = onUpdateValueOnRevert, label = { Text(text = stringResource(R.string.setting_value_on_revert)) }, - isError = appSettingDialogState.showValueOnRevertError, + isError = showValueOnRevertError, supportingText = { - if (appSettingDialogState.showValueOnRevertError) { + if (showValueOnRevertError) { Text(text = valueOnRevertIsBlank) } }, @@ -222,14 +355,20 @@ private fun AppSettingDialogTextFields( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun AppSettingDialogTextFieldWithDropdownMenu( - appSettingDialogState: AppSettingDialogState, + modifier: Modifier = Modifier, + key: String, + secureSettings: List, + secureSettingsExpanded: Boolean, + showKeyError: Boolean, + showKeyNotFoundError: Boolean, + onUpdateKey: (String) -> Unit, + onUpdateSecureSettingsExpanded: (Boolean) -> Unit, + onUpdateValueOnRevert: (String) -> Unit, ) { - val keyIsBlank = stringResource(id = R.string.setting_key_is_blank) - val keyNotFound = stringResource(id = R.string.setting_key_not_found) - ExposedDropdownMenuBox( - expanded = appSettingDialogState.secureSettingsExpanded, - onExpandedChange = appSettingDialogState::updateSecureSettingsExpanded, + modifier = modifier.fillMaxWidth(), + expanded = secureSettingsExpanded, + onExpandedChange = onUpdateSecureSettingsExpanded, ) { OutlinedTextField( modifier = Modifier @@ -239,35 +378,35 @@ private fun AppSettingDialogTextFieldWithDropdownMenu( ) .fillMaxWidth() .padding(horizontal = 10.dp), - value = appSettingDialogState.key, - onValueChange = appSettingDialogState::updateKey, + value = key, + onValueChange = onUpdateKey, label = { Text(text = stringResource(R.string.setting_key)) }, - trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = appSettingDialogState.secureSettingsExpanded) }, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = secureSettingsExpanded) }, colors = ExposedDropdownMenuDefaults.textFieldColors(), - isError = appSettingDialogState.showKeyError || appSettingDialogState.showKeyNotFoundError, + isError = showKeyError || showKeyNotFoundError, supportingText = { - if (appSettingDialogState.showKeyError) { - Text(text = keyIsBlank) + if (showKeyError) { + Text(text = stringResource(id = R.string.setting_key_is_blank)) } - if (appSettingDialogState.showKeyNotFoundError) { - Text(text = keyNotFound) + if (showKeyNotFoundError) { + Text(text = stringResource(id = R.string.setting_key_not_found)) } }, singleLine = true, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), ) - if (appSettingDialogState.secureSettings.isNotEmpty()) { + if (secureSettings.isNotEmpty()) { ExposedDropdownMenu( - expanded = appSettingDialogState.secureSettingsExpanded, + expanded = secureSettingsExpanded, onDismissRequest = { - appSettingDialogState.updateSecureSettingsExpanded(false) + onUpdateSecureSettingsExpanded(false) }, ) { - appSettingDialogState.secureSettings.forEach { secureSetting -> + secureSettings.forEach { secureSetting -> DropdownMenuItem( text = { Text( @@ -276,17 +415,11 @@ private fun AppSettingDialogTextFieldWithDropdownMenu( ) }, onClick = { - appSettingDialogState.updateKey( - secureSetting.name ?: "null", - ) + onUpdateKey(secureSetting.name ?: "null") - appSettingDialogState.updateValueOnRevert( - secureSetting.value ?: "null", - ) + onUpdateValueOnRevert(secureSetting.value ?: "null") - appSettingDialogState.updateSecureSettingsExpanded( - false, - ) + onUpdateSecureSettingsExpanded(false) }, contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, ) diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/shortcut/ShortcutDialog.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/ShortcutDialog.kt similarity index 71% rename from feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/shortcut/ShortcutDialog.kt rename to feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/ShortcutDialog.kt index 78f9b06ca..eca413603 100644 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/shortcut/ShortcutDialog.kt +++ b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/ShortcutDialog.kt @@ -15,7 +15,7 @@ * limitations under the License. * */ -package com.android.geto.feature.appsettings.dialog.shortcut +package com.android.geto.feature.appsettings.dialog import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -33,6 +33,10 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -45,14 +49,23 @@ import com.android.geto.feature.appsettings.R @Composable internal fun ShortcutDialog( modifier: Modifier = Modifier, - shortcutDialogState: ShortcutDialogState, + icon: ByteArray?, + onDismissRequest: () -> Unit, onRequestPinShortcut: (ByteArray?, String, String) -> Unit, ) { + var shortLabel by remember { mutableStateOf("") } + + var showShortLabelError by remember { mutableStateOf(false) } + + var longLabel by remember { mutableStateOf("") } + + var showLongLabelError by remember { mutableStateOf(false) } + DialogContainer( modifier = modifier .padding(16.dp) .verticalScroll(rememberScrollState()), - onDismissRequest = { shortcutDialogState.updateShowDialog(false) }, + onDismissRequest = onDismissRequest, ) { Column( modifier = Modifier.fillMaxWidth(), @@ -63,18 +76,35 @@ internal fun ShortcutDialog( modifier = modifier .size(50.dp) .align(Alignment.CenterHorizontally), - icon = shortcutDialogState.icon, + icon = icon, ) ShortcutDialogTextFields( - shortcutDialogState = shortcutDialogState, + longLabel = longLabel, + shortLabel = shortLabel, + showLongLabelError = showLongLabelError, + showShortLabelError = showShortLabelError, + onUpdateLongLabel = { + longLabel = it + }, + onUpdateShortLabel = { + shortLabel = it + }, ) ShortcutDialogButtons( - onPositiveTextButtonClick = { shortcutDialogState.getShortcut(onRequestPinShortcut = onRequestPinShortcut) }, - onNegativeTextButtonClick = { - shortcutDialogState.updateShowDialog(false) + onPositiveTextButtonClick = { + showShortLabelError = shortLabel.isBlank() + + showLongLabelError = longLabel.isBlank() + + if (!showShortLabelError && !showLongLabelError) { + onRequestPinShortcut(icon, shortLabel, longLabel) + + onDismissRequest() + } }, + onNegativeTextButtonClick = onDismissRequest, ) } } @@ -107,7 +137,12 @@ private fun ShortcutDialogApplicationIcon( @Composable private fun ShortcutDialogTextFields( - shortcutDialogState: ShortcutDialogState, + longLabel: String, + shortLabel: String, + showLongLabelError: Boolean, + showShortLabelError: Boolean, + onUpdateLongLabel: (String) -> Unit, + onUpdateShortLabel: (String) -> Unit, ) { val shortLabelIsBlank = stringResource(id = R.string.short_label_is_blank) @@ -119,18 +154,18 @@ private fun ShortcutDialogTextFields( modifier = Modifier .fillMaxWidth() .padding(horizontal = 10.dp), - value = shortcutDialogState.shortLabel, - onValueChange = shortcutDialogState::updateShortLabel, + value = shortLabel, + onValueChange = onUpdateShortLabel, label = { Text(text = stringResource(R.string.short_label)) }, - isError = shortcutDialogState.showShortLabelError, + isError = showShortLabelError, supportingText = { - if (shortcutDialogState.showShortLabelError) { + if (showShortLabelError) { Text(text = shortLabelIsBlank) } else { Text( - text = "${shortcutDialogState.shortLabel.length}/${shortcutDialogState.shortLabelMaxLength}", + text = "${shortLabel.length}/10", modifier = Modifier.fillMaxWidth(), ) } @@ -143,18 +178,18 @@ private fun ShortcutDialogTextFields( modifier = Modifier .fillMaxWidth() .padding(horizontal = 10.dp), - value = shortcutDialogState.longLabel, - onValueChange = shortcutDialogState::updateLongLabel, + value = longLabel, + onValueChange = onUpdateLongLabel, label = { Text(text = stringResource(R.string.long_label)) }, - isError = shortcutDialogState.showLongLabelError, + isError = showLongLabelError, supportingText = { - if (shortcutDialogState.showLongLabelError) { + if (showLongLabelError) { Text(text = longLabelIsBlank) } else { Text( - text = "${shortcutDialogState.longLabel.length}/${shortcutDialogState.longLabelMaxLength}", + text = "${longLabel.length}/25", modifier = Modifier.fillMaxWidth(), ) } diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/template/TemplateDialog.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/TemplateDialog.kt similarity index 74% rename from feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/template/TemplateDialog.kt rename to feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/TemplateDialog.kt index 04a21a093..b7683f9dc 100644 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/template/TemplateDialog.kt +++ b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/TemplateDialog.kt @@ -15,7 +15,7 @@ * limitations under the License. * */ -package com.android.geto.feature.appsettings.dialog.template +package com.android.geto.feature.appsettings.dialog import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -36,8 +36,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.android.geto.designsystem.component.DialogContainer import com.android.geto.designsystem.icon.GetoIcons +import com.android.geto.domain.model.AppSetting import com.android.geto.domain.model.AppSettingTemplate -import com.android.geto.domain.model.SettingType import com.android.geto.feature.appsettings.R import com.android.geto.feature.appsettings.getSettingTypeTitle @@ -45,30 +45,29 @@ import com.android.geto.feature.appsettings.getSettingTypeTitle internal fun TemplateDialog( modifier: Modifier = Modifier, appSettingTemplates: List, - templateDialogState: TemplateDialogState, - onAddAppSetting: (Int, Boolean, SettingType, String, String, String, String) -> Unit, + componentName: String, + onAddAppSetting: (AppSetting) -> Unit, + onDismissRequest: () -> Unit, ) { DialogContainer( modifier = modifier .padding(16.dp), - onDismissRequest = { - templateDialogState.updateShowDialog(false) - }, + onDismissRequest = onDismissRequest, ) { Column( modifier = Modifier.fillMaxWidth(), ) { TemplateDialogTitle() - TemplateDialogContent( - appSettingTemplates = appSettingTemplates, - onAddClick = { appSettingTemplate -> - templateDialogState.addAppSetting( + LazyColumn(modifier = modifier.fillMaxWidth()) { + items(appSettingTemplates) { appSettingTemplate -> + AppSettingTemplateItem( appSettingTemplate = appSettingTemplate, + componentName = componentName, onAddAppSetting = onAddAppSetting, ) - }, - ) + } + } } } } @@ -84,27 +83,12 @@ private fun TemplateDialogTitle(modifier: Modifier = Modifier) { ) } -@Composable -private fun TemplateDialogContent( - modifier: Modifier = Modifier, - appSettingTemplates: List, - onAddClick: (AppSettingTemplate) -> Unit, -) { - LazyColumn(modifier = modifier.fillMaxWidth()) { - items(appSettingTemplates) { appSettingTemplate -> - AppSettingTemplateItem( - appSettingTemplate = appSettingTemplate, - onAddClick = onAddClick, - ) - } - } -} - @Composable private fun AppSettingTemplateItem( modifier: Modifier = Modifier, appSettingTemplate: AppSettingTemplate, - onAddClick: (AppSettingTemplate) -> Unit, + componentName: String, + onAddAppSetting: (AppSetting) -> Unit, ) { Row( modifier = modifier.padding(10.dp), @@ -131,7 +115,21 @@ private fun AppSettingTemplateItem( ) } - IconButton(onClick = { onAddClick(appSettingTemplate) }) { + IconButton( + onClick = { + onAddAppSetting( + AppSetting( + enabled = true, + settingType = appSettingTemplate.settingType, + componentName = componentName, + label = appSettingTemplate.label, + key = appSettingTemplate.key, + valueOnLaunch = appSettingTemplate.valueOnLaunch, + valueOnRevert = appSettingTemplate.valueOnRevert, + ), + ) + }, + ) { Icon( imageVector = GetoIcons.Add, contentDescription = null, diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/appsetting/AppSettingDialogState.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/appsetting/AppSettingDialogState.kt deleted file mode 100644 index 0802f11d3..000000000 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/appsetting/AppSettingDialogState.kt +++ /dev/null @@ -1,205 +0,0 @@ -/* - * - * Copyright 2023 Einstein Blanco - * - * Licensed under the GNU General Public License v3.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.gnu.org/licenses/gpl-3.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.android.geto.feature.appsettings.dialog.appsetting - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.listSaver -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import com.android.geto.domain.model.SecureSetting -import com.android.geto.domain.model.SettingType - -@Composable -internal fun rememberAppSettingDialogState(): AppSettingDialogState { - return rememberSaveable(saver = AppSettingDialogState.Saver) { - AppSettingDialogState() - } -} - -@Stable -internal class AppSettingDialogState { - var secureSettings by mutableStateOf>(emptyList()) - private set - - var secureSettingsExpanded by mutableStateOf(false) - private set - - var showDialog by mutableStateOf(false) - private set - - var selectedRadioOptionIndex by mutableIntStateOf(0) - private set - - var label by mutableStateOf("") - private set - - var showLabelError by mutableStateOf(false) - private set - - var key by mutableStateOf("") - private set - - var showKeyError by mutableStateOf(false) - private set - - var showKeyNotFoundError by mutableStateOf(false) - private set - - var valueOnLaunch by mutableStateOf("") - private set - - var showValueOnLaunchError by mutableStateOf(false) - private set - - var valueOnRevert by mutableStateOf("") - private set - - var showValueOnRevertError by mutableStateOf(false) - private set - - fun updateSecureSettings(value: List) { - secureSettings = value - } - - fun updateSecureSettingsExpanded(value: Boolean) { - secureSettingsExpanded = value - } - - fun updateShowDialog(value: Boolean) { - showDialog = value - } - - fun updateSelectedRadioOptionIndex(value: Int) { - selectedRadioOptionIndex = value - } - - fun updateLabel(value: String) { - label = value - } - - fun updateKey(value: String) { - key = value - } - - fun updateValueOnLaunch(value: String) { - valueOnLaunch = value - } - - fun updateValueOnRevert(value: String) { - valueOnRevert = value - } - - fun getAppSetting( - onAddAppSetting: ( - id: Int, - enabled: Boolean, - settingType: SettingType, - label: String, - key: String, - valueOnLaunch: String, - valueOnRevert: String, - ) -> Unit, - ) { - showLabelError = label.isBlank() - - showKeyError = key.isBlank() - - showKeyNotFoundError = - key.isNotBlank() && !secureSettings.mapNotNull { it.name }.contains(key) - - showValueOnLaunchError = valueOnLaunch.isBlank() - - showValueOnRevertError = valueOnRevert.isBlank() - - if (!showLabelError && - !showKeyNotFoundError && - !showKeyError && - !showValueOnLaunchError && - !showValueOnRevertError - ) { - onAddAppSetting( - 0, - true, - SettingType.entries[selectedRadioOptionIndex], - label, - key, - valueOnLaunch, - valueOnRevert, - ) - - showDialog = false - secureSettingsExpanded = false - secureSettings = emptyList() - - selectedRadioOptionIndex = 0 - key = "" - label = "" - valueOnLaunch = "" - valueOnRevert = "" - } - } - - companion object { - val Saver = listSaver( - save = { state -> - listOf( - state.showDialog, - state.selectedRadioOptionIndex, - state.label, - state.showLabelError, - state.key, - state.showKeyError, - state.showKeyNotFoundError, - state.valueOnLaunch, - state.showValueOnLaunchError, - state.valueOnRevert, - state.showValueOnRevertError, - ) - }, - restore = { - AppSettingDialogState().apply { - showDialog = it[0] as Boolean - - selectedRadioOptionIndex = it[1] as Int - - label = it[2] as String - - showLabelError = it[3] as Boolean - - key = it[4] as String - - showKeyError = it[5] as Boolean - - showKeyNotFoundError = it[6] as Boolean - - valueOnLaunch = it[7] as String - - showValueOnLaunchError = it[8] as Boolean - - valueOnRevert = it[9] as String - - showValueOnRevertError = it[10] as Boolean - } - }, - ) - } -} diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/shortcut/ShortcutDialogState.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/shortcut/ShortcutDialogState.kt deleted file mode 100644 index a3baebc1d..000000000 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/shortcut/ShortcutDialogState.kt +++ /dev/null @@ -1,119 +0,0 @@ -/* - * - * Copyright 2023 Einstein Blanco - * - * Licensed under the GNU General Public License v3.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.gnu.org/licenses/gpl-3.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.android.geto.feature.appsettings.dialog.shortcut - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.listSaver -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue - -@Composable -internal fun rememberShortcutDialogState(): ShortcutDialogState { - return rememberSaveable(saver = ShortcutDialogState.Saver) { - ShortcutDialogState() - } -} - -@Stable -internal class ShortcutDialogState { - var showDialog by mutableStateOf(false) - private set - - var icon by mutableStateOf(null) - private set - - var shortLabel by mutableStateOf("") - private set - - var showShortLabelError by mutableStateOf(false) - private set - - var longLabel by mutableStateOf("") - private set - - var showLongLabelError by mutableStateOf(false) - private set - - val shortLabelMaxLength = 10 - - val longLabelMaxLength = 25 - - fun updateShowDialog(value: Boolean) { - showDialog = value - } - - fun updateIcon(value: ByteArray?) { - icon = value - } - - fun updateShortLabel(value: String) { - if (value.length <= shortLabelMaxLength) { - shortLabel = value - } - } - - fun updateLongLabel(value: String) { - if (value.length <= longLabelMaxLength) { - longLabel = value - } - } - - fun getShortcut(onRequestPinShortcut: (icon: ByteArray?, shortLabel: String, longLabel: String) -> Unit) { - showShortLabelError = shortLabel.isBlank() - - showLongLabelError = longLabel.isBlank() - - if (!showShortLabelError && !showLongLabelError) { - onRequestPinShortcut(icon, shortLabel, longLabel) - - showDialog = false - longLabel = "" - shortLabel = "" - } - } - - companion object { - val Saver = listSaver( - save = { state -> - listOf( - state.showDialog, - state.shortLabel, - state.showShortLabelError, - state.longLabel, - state.showLongLabelError, - ) - }, - restore = { - ShortcutDialogState().apply { - showDialog = it[0] as Boolean - - shortLabel = it[1] as String - - showShortLabelError = it[2] as Boolean - - longLabel = it[3] as String - - showLongLabelError = it[4] as Boolean - } - }, - ) - } -} diff --git a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/template/TemplateDialogState.kt b/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/template/TemplateDialogState.kt deleted file mode 100644 index e1a24f2f0..000000000 --- a/feature/app-settings/src/main/kotlin/com/android/geto/feature/appsettings/dialog/template/TemplateDialogState.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * Copyright 2023 Einstein Blanco - * - * Licensed under the GNU General Public License v3.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.gnu.org/licenses/gpl-3.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.android.geto.feature.appsettings.dialog.template - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.listSaver -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import com.android.geto.domain.model.AppSettingTemplate -import com.android.geto.domain.model.SettingType - -@Composable -internal fun rememberTemplateDialogState(): TemplateDialogState { - return rememberSaveable(saver = TemplateDialogState.Saver) { - TemplateDialogState() - } -} - -@Stable -internal class TemplateDialogState { - var showDialog by mutableStateOf(false) - private set - - fun updateShowDialog(value: Boolean) { - showDialog = value - } - - fun addAppSetting( - appSettingTemplate: AppSettingTemplate, - onAddAppSetting: ( - ( - id: Int, - enabled: Boolean, - settingType: SettingType, - label: String, - key: String, - valueOnLaunch: String, - valueOnRevert: String, - ) -> Unit - ), - ) { - onAddAppSetting( - 0, - true, - appSettingTemplate.settingType, - appSettingTemplate.label, - appSettingTemplate.key, - appSettingTemplate.valueOnLaunch, - appSettingTemplate.valueOnRevert, - ) - - showDialog = false - } - - companion object { - val Saver = listSaver( - save = { state -> - listOf( - state.showDialog, - ) - }, - restore = { - TemplateDialogState().apply { - showDialog = it[0] as Boolean - } - }, - ) - } -}