summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Josh <jmokut@google.com> 2025-01-15 13:25:04 +0000
committer Josh <jmokut@google.com> 2025-01-27 17:00:41 +0000
commitf8bb3ed5a9a8f3fea09a294f3fa65cf9f5789ae8 (patch)
treec45a5555eec1fc843adea9e7bbcfd0e2048581ce
parent092cb7e6cf0d6079c15b221801c8cb128c355306 (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
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt54
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(