diff options
| author | 2025-01-15 13:25:04 +0000 | |
|---|---|---|
| committer | 2025-01-27 17:00:41 +0000 | |
| commit | f8bb3ed5a9a8f3fea09a294f3fa65cf9f5789ae8 (patch) | |
| tree | c45a5555eec1fc843adea9e7bbcfd0e2048581ce | |
| parent | 092cb7e6cf0d6079c15b221801c8cb128c355306 (diff) | |
Added early verification of selected key combination
Test: ShortcutCustomizationViewModelTest
CustomShortcutCategoriesRepositoryTest CustomInputGesturesRepositoryTest
Flag: com.android.systemui.keyboard_shortcut_helper_shortcut_customizer
Fix: 381063978
Change-Id: I6dedfcf0ecccfeb44d6752af7cbb3202ae1ba385
6 files changed, 101 insertions, 64 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt index e5c638cbdfba..d355f761e5ae 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt @@ -32,18 +32,19 @@ import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestRe import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_RESERVED_COMBINATION import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.SUCCESS import com.android.systemui.settings.UserTracker +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withContext -import javax.inject.Inject -import kotlin.coroutines.CoroutineContext @SysUISingleton class CustomInputGesturesRepository @Inject -constructor(private val userTracker: UserTracker, - @Background private val bgCoroutineContext: CoroutineContext) -{ +constructor( + private val userTracker: UserTracker, + @Background private val bgCoroutineContext: CoroutineContext, +) { private val userContext: Context get() = userTracker.createCurrentUserContext(userTracker.userContext) @@ -55,8 +56,7 @@ constructor(private val userTracker: UserTracker, private val _customInputGesture = MutableStateFlow<List<InputGestureData>>(emptyList()) - val customInputGestures = - _customInputGesture.onStart { refreshCustomInputGestures() } + val customInputGestures = _customInputGesture.onStart { refreshCustomInputGestures() } fun refreshCustomInputGestures() { setCustomInputGestures(inputGestures = retrieveCustomInputGestures()) @@ -72,24 +72,24 @@ constructor(private val userTracker: UserTracker, } else emptyList() } - suspend fun addCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult { + suspend fun addCustomInputGesture( + inputGesture: InputGestureData + ): ShortcutCustomizationRequestResult { return withContext(bgCoroutineContext) { when (val result = inputManager.addCustomInputGesture(inputGesture)) { CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> { refreshCustomInputGestures() SUCCESS } - CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS -> - ERROR_RESERVED_COMBINATION + CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS -> ERROR_RESERVED_COMBINATION - CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE -> - ERROR_RESERVED_COMBINATION + CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE -> ERROR_RESERVED_COMBINATION else -> { Log.w( TAG, "Attempted to add inputGesture: $inputGesture " + - "but ran into an error with code: $result", + "but ran into an error with code: $result", ) ERROR_OTHER } @@ -97,11 +97,11 @@ constructor(private val userTracker: UserTracker, } } - suspend fun deleteCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult { - return withContext(bgCoroutineContext){ - when ( - val result = inputManager.removeCustomInputGesture(inputGesture) - ) { + suspend fun deleteCustomInputGesture( + inputGesture: InputGestureData + ): ShortcutCustomizationRequestResult { + return withContext(bgCoroutineContext) { + when (val result = inputManager.removeCustomInputGesture(inputGesture)) { CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> { refreshCustomInputGestures() SUCCESS @@ -110,7 +110,7 @@ constructor(private val userTracker: UserTracker, Log.w( TAG, "Attempted to delete inputGesture: $inputGesture " + - "but ran into an error with code: $result", + "but ran into an error with code: $result", ) ERROR_OTHER } @@ -134,7 +134,10 @@ constructor(private val userTracker: UserTracker, } } + suspend fun getInputGestureByTrigger(trigger: InputGestureData.Trigger): InputGestureData? = + withContext(bgCoroutineContext) { inputManager.getInputGesture(trigger) } + private companion object { private const val TAG = "CustomInputGesturesRepository" } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt index 18ca877775df..6ae948d2da2e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyboard.shortcut.data.repository import android.hardware.input.InputGestureData import android.hardware.input.InputGestureData.Builder +import android.hardware.input.InputGestureData.Trigger import android.hardware.input.InputGestureData.createKeyTrigger import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent.KeyGestureType @@ -175,6 +176,11 @@ constructor( return customInputGesturesRepository.resetAllCustomInputGestures() } + suspend fun isSelectedKeyCombinationAvailable(): Boolean { + val trigger = buildTriggerFromSelectedKeyCombination() ?: return false + return customInputGesturesRepository.getInputGestureByTrigger(trigger) == null + } + private fun Builder.addKeyGestureTypeForShortcutBeingCustomized(): Builder { val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized() @@ -222,7 +228,10 @@ constructor( ) } - private fun Builder.addTriggerFromSelectedKeyCombination(): Builder { + private fun Builder.addTriggerFromSelectedKeyCombination(): Builder = + setTrigger(buildTriggerFromSelectedKeyCombination()) + + private fun buildTriggerFromSelectedKeyCombination(): Trigger? { val selectedKeyCombination = _selectedKeyCombination.value if (selectedKeyCombination?.keyCode == null) { Log.w( @@ -230,16 +239,14 @@ constructor( "User requested to set shortcut but selected key combination is " + "$selectedKeyCombination", ) - return this + return null } - return setTrigger( - createKeyTrigger( - /* keycode = */ selectedKeyCombination.keyCode, - /* modifierState = */ shortcutCategoriesUtils.removeUnsupportedModifiers( - selectedKeyCombination.modifiers - ), - ) + return createKeyTrigger( + /* keycode= */ selectedKeyCombination.keyCode, + /* modifierState= */ shortcutCategoriesUtils.removeUnsupportedModifiers( + selectedKeyCombination.modifiers + ), ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt index ef242678a8ac..1a62517ad01d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt @@ -53,4 +53,7 @@ constructor(private val customShortcutRepository: CustomShortcutCategoriesReposi suspend fun resetAllCustomShortcuts(): ShortcutCustomizationRequestResult { return customShortcutRepository.resetAllCustomShortcuts() } + + suspend fun isSelectedKeyCombinationAvailable(): Boolean = + customShortcutRepository.isSelectedKeyCombinationAvailable() } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt index 864d02ef4a93..54e27a61ac78 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.phone.create import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch class ShortcutCustomizationDialogStarter @@ -57,20 +58,25 @@ constructor( private val viewModel = viewModelFactory.create() override suspend fun onActivated(): Nothing { - viewModel.shortcutCustomizationUiState.collect { uiState -> - when (uiState) { - is AddShortcutDialog, - is DeleteShortcutDialog, - is ResetShortcutDialog -> { - if (dialog == null) { - dialog = createDialog().also { it.show() } + coroutineScope { + launch { + viewModel.shortcutCustomizationUiState.collect { uiState -> + when (uiState) { + is AddShortcutDialog, + is DeleteShortcutDialog, + is ResetShortcutDialog -> { + if (dialog == null) { + dialog = createDialog().also { it.show() } + } + } + is ShortcutCustomizationUiState.Inactive -> { + dialog?.dismiss() + dialog = null + } } } - is ShortcutCustomizationUiState.Inactive -> { - dialog?.dismiss() - dialog = null - } } + launch { viewModel.activate() } } awaitCancellation() } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt index fa03883e2a35..ea36a10fb01a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt @@ -24,7 +24,6 @@ import android.os.UserHandle import android.provider.Settings import androidx.annotation.VisibleForTesting import androidx.compose.foundation.layout.width -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -36,6 +35,7 @@ import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelper import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelperBottomSheet import com.android.systemui.keyboard.shortcut.ui.composable.getWidth import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel +import com.android.systemui.lifecycle.rememberActivated import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory @@ -51,14 +51,13 @@ class ShortcutHelperDialogStarter constructor( @Application private val applicationScope: CoroutineScope, private val shortcutHelperViewModel: ShortcutHelperViewModel, - shortcutCustomizationDialogStarterFactory: ShortcutCustomizationDialogStarter.Factory, + private val shortcutCustomizationDialogStarterFactory: + ShortcutCustomizationDialogStarter.Factory, private val dialogFactory: SystemUIDialogFactory, private val activityStarter: ActivityStarter, ) : CoreStartable { @VisibleForTesting var dialog: Dialog? = null - private val shortcutCustomizationDialogStarter = - shortcutCustomizationDialogStarterFactory.create() override fun start() { shortcutHelperViewModel.shouldShow @@ -77,7 +76,10 @@ constructor( content = { dialog -> val shortcutsUiState by shortcutHelperViewModel.shortcutsUiState.collectAsStateWithLifecycle() - LaunchedEffect(Unit) { shortcutCustomizationDialogStarter.activate() } + val shortcutCustomizationDialogStarter = + rememberActivated(traceName = "shortcutCustomizationDialogStarter") { + shortcutCustomizationDialogStarterFactory.create() + } ShortcutHelper( modifier = Modifier.width(getWidth()), shortcutsUiState = shortcutsUiState, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt index 2a3c23d148bd..f4ba99c6a394 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt @@ -28,16 +28,17 @@ import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestRe import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomizationInteractor import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog +import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.res.R import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update class ShortcutCustomizationViewModel @@ -45,26 +46,12 @@ class ShortcutCustomizationViewModel constructor( private val context: Context, private val shortcutCustomizationInteractor: ShortcutCustomizationInteractor, -) { +) : ExclusiveActivatable() { private var keyDownEventCache: KeyEvent? = null private val _shortcutCustomizationUiState = MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive) - val shortcutCustomizationUiState = - shortcutCustomizationInteractor.pressedKeys - .map { keys -> - // Note that Action Key is excluded as it's already displayed on the UI - keys.filter { - it != shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey() - } - } - .combine(_shortcutCustomizationUiState) { keys, uiState -> - if (uiState is AddShortcutDialog) { - uiState.copy(pressedKeys = keys) - } else { - uiState - } - } + val shortcutCustomizationUiState = _shortcutCustomizationUiState.asStateFlow() fun onShortcutCustomizationRequested(requestInfo: ShortcutCustomizationRequestInfo) { shortcutCustomizationInteractor.onCustomizationRequested(requestInfo) @@ -112,7 +99,6 @@ constructor( suspend fun onSetShortcut() { val result = shortcutCustomizationInteractor.confirmAndSetShortcutCurrentlyBeingCustomized() - _shortcutCustomizationUiState.update { uiState -> when (result) { ShortcutCustomizationRequestResult.SUCCESS -> ShortcutCustomizationUiState.Inactive @@ -184,11 +170,41 @@ constructor( keyDownEventCache = null } + private suspend fun isSelectedKeyCombinationAvailable() = + shortcutCustomizationInteractor.isSelectedKeyCombinationAvailable() + @AssistedFactory interface Factory { fun create(): ShortcutCustomizationViewModel } + override suspend fun onActivated(): Nothing { + shortcutCustomizationInteractor.pressedKeys.collect { + val keys = filterDefaultCustomShortcutModifierKey(it) + val errorMessage = getErrorMessageForPressedKeys(keys) + + _shortcutCustomizationUiState.update { uiState -> + if (uiState is AddShortcutDialog) { + uiState.copy(pressedKeys = keys, errorMessage = errorMessage) + } else { + uiState + } + } + } + } + + private suspend fun getErrorMessageForPressedKeys(keys: List<ShortcutKey>): String { + return if (keys.isEmpty() or isSelectedKeyCombinationAvailable()) { + "" + } + else { + context.getString(R.string.shortcut_customizer_key_combination_in_use_error_message) + } + } + + private fun filterDefaultCustomShortcutModifierKey(keys: List<ShortcutKey>) = + keys.filter { it != shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey() } + companion object { private val SUPPORTED_MODIFIERS = listOf( |