Loading packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt +3 −6 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME import android.os.SystemClock import android.view.KeyEvent import android.view.KeyEvent.ACTION_DOWN import android.view.KeyEvent.ACTION_UP import android.view.KeyEvent.KEYCODE_A import android.view.KeyEvent.META_ALT_ON import android.view.KeyEvent.META_CTRL_ON Loading Loading @@ -540,11 +541,7 @@ object TestShortcuts { simpleShortcutCategory(System, "System apps", "Take a note"), simpleShortcutCategory(System, "System controls", "Take screenshot"), simpleShortcutCategory(System, "System controls", "Go back"), simpleShortcutCategory( MultiTasking, "Split screen", "Switch to full screen", ), simpleShortcutCategory(MultiTasking, "Split screen", "Switch to full screen"), simpleShortcutCategory( MultiTasking, "Split screen", Loading Loading @@ -704,7 +701,7 @@ object TestShortcuts { android.view.KeyEvent( /* downTime = */ SystemClock.uptimeMillis(), /* eventTime = */ SystemClock.uptimeMillis(), /* action = */ ACTION_DOWN, /* action = */ ACTION_UP, /* code = */ KEYCODE_A, /* repeat = */ 0, /* metaState = */ 0, Loading packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt +23 −23 Original line number Diff line number Diff line Loading @@ -92,7 +92,8 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) assertThat(uiState).isEqualTo( assertThat(uiState) .isEqualTo( AddShortcutDialog( shortcutLabel = "Standard shortcut", defaultCustomShortcutModifierKey = Loading Loading @@ -137,8 +138,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) assertThat((uiState as AddShortcutDialog).pressedKeys) .isEmpty() assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty() } } Loading @@ -161,8 +161,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest) assertThat((uiState as AddShortcutDialog).errorMessage) .isEmpty() assertThat((uiState as AddShortcutDialog).errorMessage).isEmpty() } } Loading Loading @@ -244,32 +243,34 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { } @Test fun onKeyPressed_handlesKeyEvents_whereActionKeyIsAlsoPressed() { fun onShortcutKeyCombinationSelected_handlesKeyEvents_whereActionKeyIsAlsoPressed() { testScope.runTest { viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) val isHandled = viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) val isHandled = viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) assertThat(isHandled).isTrue() } } @Test fun onKeyPressed_doesNotHandleKeyEvents_whenActionKeyIsNotAlsoPressed() { fun onShortcutKeyCombinationSelected_doesNotHandleKeyEvents_whenActionKeyIsNotAlsoPressed() { testScope.runTest { viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) val isHandled = viewModel.onKeyPressed(keyDownEventWithoutActionKeyPressed) val isHandled = viewModel.onShortcutKeyCombinationSelected(keyDownEventWithoutActionKeyPressed) assertThat(isHandled).isFalse() } } @Test fun onKeyPressed_convertsKeyEventsAndUpdatesUiStatesPressedKey() { fun onShortcutKeyCombinationSelected_convertsKeyEventsAndUpdatesUiStatesPressedKey() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) viewModel.onKeyPressed(keyUpEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed) // Note that Action Key is excluded as it's already displayed on the UI assertThat((uiState as AddShortcutDialog).pressedKeys) Loading @@ -282,8 +283,8 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) viewModel.onKeyPressed(keyUpEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed) // Note that Action Key is excluded as it's already displayed on the UI assertThat((uiState as AddShortcutDialog).pressedKeys) Loading @@ -292,16 +293,15 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { // Close the dialog and show it again viewModel.onDialogDismissed() viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) assertThat((uiState as AddShortcutDialog).pressedKeys) .isEmpty() assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty() } } private suspend fun openAddShortcutDialogAndSetShortcut() { viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest) viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) viewModel.onKeyPressed(keyUpEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed) viewModel.onSetShortcut() } Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt +5 −3 Original line number Diff line number Diff line Loading @@ -85,7 +85,9 @@ constructor( ShortcutCustomizationDialog( uiState = uiState, modifier = Modifier.width(364.dp).wrapContentHeight().padding(vertical = 24.dp), onKeyPress = { viewModel.onKeyPressed(it) }, onShortcutKeyCombinationSelected = { viewModel.onShortcutKeyCombinationSelected(it) }, onCancel = { dialog.dismiss() }, onConfirmSetShortcut = { coroutineScope.launch { viewModel.onSetShortcut() } }, onConfirmDeleteShortcut = { Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt +53 −39 Original line number Diff line number Diff line Loading @@ -49,8 +49,12 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight Loading @@ -65,7 +69,7 @@ import com.android.systemui.res.R fun ShortcutCustomizationDialog( uiState: ShortcutCustomizationUiState, modifier: Modifier = Modifier, onKeyPress: (KeyEvent) -> Boolean, onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean, onCancel: () -> Unit, onConfirmSetShortcut: () -> Unit, onConfirmDeleteShortcut: () -> Unit, Loading @@ -73,7 +77,13 @@ fun ShortcutCustomizationDialog( ) { when (uiState) { is ShortcutCustomizationUiState.AddShortcutDialog -> { AddShortcutDialog(modifier, uiState, onKeyPress, onCancel, onConfirmSetShortcut) AddShortcutDialog( modifier, uiState, onShortcutKeyCombinationSelected, onCancel, onConfirmSetShortcut, ) } is ShortcutCustomizationUiState.DeleteShortcutDialog -> { DeleteShortcutDialog(modifier, onCancel, onConfirmDeleteShortcut) Loading @@ -91,17 +101,14 @@ fun ShortcutCustomizationDialog( private fun AddShortcutDialog( modifier: Modifier, uiState: ShortcutCustomizationUiState.AddShortcutDialog, onKeyPress: (KeyEvent) -> Boolean, onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean, onCancel: () -> Unit, onConfirmSetShortcut: () -> Unit onConfirmSetShortcut: () -> Unit, ) { Column(modifier = modifier) { Title(uiState.shortcutLabel) Description( text = stringResource( id = R.string.shortcut_customize_mode_add_shortcut_description ) text = stringResource(id = R.string.shortcut_customize_mode_add_shortcut_description) ) PromptShortcutModifier( modifier = Loading @@ -112,8 +119,9 @@ private fun AddShortcutDialog( ) SelectedKeyCombinationContainer( shouldShowError = uiState.errorMessage.isNotEmpty(), onKeyPress = onKeyPress, onShortcutKeyCombinationSelected = onShortcutKeyCombinationSelected, pressedKeys = uiState.pressedKeys, onConfirmSetShortcut = onConfirmSetShortcut, ) ErrorMessageContainer(uiState.errorMessage) DialogButtons( Loading @@ -121,9 +129,7 @@ private fun AddShortcutDialog( isConfirmButtonEnabled = uiState.pressedKeys.isNotEmpty(), onConfirm = onConfirmSetShortcut, confirmButtonText = stringResource( R.string.shortcut_helper_customize_dialog_set_shortcut_button_label ), stringResource(R.string.shortcut_helper_customize_dialog_set_shortcut_button_label), ) } } Loading @@ -132,18 +138,13 @@ private fun AddShortcutDialog( private fun DeleteShortcutDialog( modifier: Modifier, onCancel: () -> Unit, onConfirmDeleteShortcut: () -> Unit onConfirmDeleteShortcut: () -> Unit, ) { ConfirmationDialog( modifier = modifier, title = stringResource( id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title ), title = stringResource(id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title), description = stringResource( id = R.string.shortcut_customize_mode_remove_shortcut_description ), stringResource(id = R.string.shortcut_customize_mode_remove_shortcut_description), confirmButtonText = stringResource(R.string.shortcut_helper_customize_dialog_remove_button_label), onCancel = onCancel, Loading @@ -155,18 +156,13 @@ private fun DeleteShortcutDialog( private fun ResetShortcutDialog( modifier: Modifier, onCancel: () -> Unit, onConfirmResetShortcut: () -> Unit onConfirmResetShortcut: () -> Unit, ) { ConfirmationDialog( modifier = modifier, title = stringResource( id = R.string.shortcut_customize_mode_reset_shortcut_dialog_title ), title = stringResource(id = R.string.shortcut_customize_mode_reset_shortcut_dialog_title), description = stringResource( id = R.string.shortcut_customize_mode_reset_shortcut_description ), stringResource(id = R.string.shortcut_customize_mode_reset_shortcut_description), confirmButtonText = stringResource(R.string.shortcut_helper_customize_dialog_reset_button_label), onCancel = onCancel, Loading Loading @@ -201,6 +197,9 @@ private fun DialogButtons( onConfirm: () -> Unit, confirmButtonText: String, ) { val focusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { focusRequester.requestFocus() } Row( modifier = Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp) Loading @@ -218,6 +217,10 @@ private fun DialogButtons( ) Spacer(modifier = Modifier.width(8.dp)) ShortcutHelperButton( modifier = Modifier.focusRequester(focusRequester).focusProperties { canFocus = true }, // enable focus on touch/click mode onClick = onConfirm, color = MaterialTheme.colorScheme.primary, width = 116.dp, Loading Loading @@ -248,8 +251,9 @@ private fun ErrorMessageContainer(errorMessage: String) { @Composable private fun SelectedKeyCombinationContainer( shouldShowError: Boolean, onKeyPress: (KeyEvent) -> Boolean, onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean, pressedKeys: List<ShortcutKey>, onConfirmSetShortcut: () -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() Loading @@ -269,7 +273,17 @@ private fun SelectedKeyCombinationContainer( Modifier.padding(all = 16.dp) .sizeIn(minWidth = 332.dp, minHeight = 56.dp) .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp)) .onKeyEvent { onKeyPress(it) } .onPreviewKeyEvent { keyEvent -> val keyEventProcessed = onShortcutKeyCombinationSelected(keyEvent) if ( !keyEventProcessed && keyEvent.key == Key.Enter && keyEvent.type == KeyEventType.KeyUp ) { onConfirmSetShortcut() true } else keyEventProcessed } .focusProperties { canFocus = true } // enables keyboard focus when in touch mode .focusRequester(focusRequester), interactionSource = interactionSource, Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt +18 −5 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ constructor( private val context: Context, private val shortcutCustomizationInteractor: ShortcutCustomizationInteractor, ) { private var keyDownEventCache: KeyEvent? = null private val _shortcutCustomizationUiState = MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive) Loading Loading @@ -94,9 +95,16 @@ constructor( shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null) } fun onKeyPressed(keyEvent: KeyEvent): Boolean { if ((keyEvent.isMetaPressed && keyEvent.type == KeyEventType.KeyDown)) { updatePressedKeys(keyEvent) fun onShortcutKeyCombinationSelected(keyEvent: KeyEvent): Boolean { if (isModifier(keyEvent)) { return false } if (keyEvent.isMetaPressed && keyEvent.type == KeyEventType.KeyDown) { keyDownEventCache = keyEvent return true } else if (keyEvent.type == KeyEventType.KeyUp && keyEvent.key == keyDownEventCache?.key) { updatePressedKeys(keyDownEventCache!!) clearKeyDownEventCache() return true } return false Loading Loading @@ -157,16 +165,21 @@ constructor( return (uiState as? AddShortcutDialog)?.copy(errorMessage = errorMessage) ?: uiState } private fun isModifier(keyEvent: KeyEvent) = SUPPORTED_MODIFIERS.contains(keyEvent.key) private fun updatePressedKeys(keyEvent: KeyEvent) { val isModifier = SUPPORTED_MODIFIERS.contains(keyEvent.key) val keyCombination = KeyCombination( modifiers = keyEvent.nativeKeyEvent.modifiers, keyCode = if (!isModifier) keyEvent.key.nativeKeyCode else null, keyCode = if (!isModifier(keyEvent)) keyEvent.key.nativeKeyCode else null, ) shortcutCustomizationInteractor.updateUserSelectedKeyCombination(keyCombination) } private fun clearKeyDownEventCache() { keyDownEventCache = null } @AssistedFactory interface Factory { fun create(): ShortcutCustomizationViewModel Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt +3 −6 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME import android.os.SystemClock import android.view.KeyEvent import android.view.KeyEvent.ACTION_DOWN import android.view.KeyEvent.ACTION_UP import android.view.KeyEvent.KEYCODE_A import android.view.KeyEvent.META_ALT_ON import android.view.KeyEvent.META_CTRL_ON Loading Loading @@ -540,11 +541,7 @@ object TestShortcuts { simpleShortcutCategory(System, "System apps", "Take a note"), simpleShortcutCategory(System, "System controls", "Take screenshot"), simpleShortcutCategory(System, "System controls", "Go back"), simpleShortcutCategory( MultiTasking, "Split screen", "Switch to full screen", ), simpleShortcutCategory(MultiTasking, "Split screen", "Switch to full screen"), simpleShortcutCategory( MultiTasking, "Split screen", Loading Loading @@ -704,7 +701,7 @@ object TestShortcuts { android.view.KeyEvent( /* downTime = */ SystemClock.uptimeMillis(), /* eventTime = */ SystemClock.uptimeMillis(), /* action = */ ACTION_DOWN, /* action = */ ACTION_UP, /* code = */ KEYCODE_A, /* repeat = */ 0, /* metaState = */ 0, Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt +23 −23 Original line number Diff line number Diff line Loading @@ -92,7 +92,8 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) assertThat(uiState).isEqualTo( assertThat(uiState) .isEqualTo( AddShortcutDialog( shortcutLabel = "Standard shortcut", defaultCustomShortcutModifierKey = Loading Loading @@ -137,8 +138,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) assertThat((uiState as AddShortcutDialog).pressedKeys) .isEmpty() assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty() } } Loading @@ -161,8 +161,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest) assertThat((uiState as AddShortcutDialog).errorMessage) .isEmpty() assertThat((uiState as AddShortcutDialog).errorMessage).isEmpty() } } Loading Loading @@ -244,32 +243,34 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { } @Test fun onKeyPressed_handlesKeyEvents_whereActionKeyIsAlsoPressed() { fun onShortcutKeyCombinationSelected_handlesKeyEvents_whereActionKeyIsAlsoPressed() { testScope.runTest { viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) val isHandled = viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) val isHandled = viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) assertThat(isHandled).isTrue() } } @Test fun onKeyPressed_doesNotHandleKeyEvents_whenActionKeyIsNotAlsoPressed() { fun onShortcutKeyCombinationSelected_doesNotHandleKeyEvents_whenActionKeyIsNotAlsoPressed() { testScope.runTest { viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) val isHandled = viewModel.onKeyPressed(keyDownEventWithoutActionKeyPressed) val isHandled = viewModel.onShortcutKeyCombinationSelected(keyDownEventWithoutActionKeyPressed) assertThat(isHandled).isFalse() } } @Test fun onKeyPressed_convertsKeyEventsAndUpdatesUiStatesPressedKey() { fun onShortcutKeyCombinationSelected_convertsKeyEventsAndUpdatesUiStatesPressedKey() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) viewModel.onKeyPressed(keyUpEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed) // Note that Action Key is excluded as it's already displayed on the UI assertThat((uiState as AddShortcutDialog).pressedKeys) Loading @@ -282,8 +283,8 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) viewModel.onKeyPressed(keyUpEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed) // Note that Action Key is excluded as it's already displayed on the UI assertThat((uiState as AddShortcutDialog).pressedKeys) Loading @@ -292,16 +293,15 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { // Close the dialog and show it again viewModel.onDialogDismissed() viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) assertThat((uiState as AddShortcutDialog).pressedKeys) .isEmpty() assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty() } } private suspend fun openAddShortcutDialogAndSetShortcut() { viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest) viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) viewModel.onKeyPressed(keyUpEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed) viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed) viewModel.onSetShortcut() } Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt +5 −3 Original line number Diff line number Diff line Loading @@ -85,7 +85,9 @@ constructor( ShortcutCustomizationDialog( uiState = uiState, modifier = Modifier.width(364.dp).wrapContentHeight().padding(vertical = 24.dp), onKeyPress = { viewModel.onKeyPressed(it) }, onShortcutKeyCombinationSelected = { viewModel.onShortcutKeyCombinationSelected(it) }, onCancel = { dialog.dismiss() }, onConfirmSetShortcut = { coroutineScope.launch { viewModel.onSetShortcut() } }, onConfirmDeleteShortcut = { Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt +53 −39 Original line number Diff line number Diff line Loading @@ -49,8 +49,12 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight Loading @@ -65,7 +69,7 @@ import com.android.systemui.res.R fun ShortcutCustomizationDialog( uiState: ShortcutCustomizationUiState, modifier: Modifier = Modifier, onKeyPress: (KeyEvent) -> Boolean, onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean, onCancel: () -> Unit, onConfirmSetShortcut: () -> Unit, onConfirmDeleteShortcut: () -> Unit, Loading @@ -73,7 +77,13 @@ fun ShortcutCustomizationDialog( ) { when (uiState) { is ShortcutCustomizationUiState.AddShortcutDialog -> { AddShortcutDialog(modifier, uiState, onKeyPress, onCancel, onConfirmSetShortcut) AddShortcutDialog( modifier, uiState, onShortcutKeyCombinationSelected, onCancel, onConfirmSetShortcut, ) } is ShortcutCustomizationUiState.DeleteShortcutDialog -> { DeleteShortcutDialog(modifier, onCancel, onConfirmDeleteShortcut) Loading @@ -91,17 +101,14 @@ fun ShortcutCustomizationDialog( private fun AddShortcutDialog( modifier: Modifier, uiState: ShortcutCustomizationUiState.AddShortcutDialog, onKeyPress: (KeyEvent) -> Boolean, onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean, onCancel: () -> Unit, onConfirmSetShortcut: () -> Unit onConfirmSetShortcut: () -> Unit, ) { Column(modifier = modifier) { Title(uiState.shortcutLabel) Description( text = stringResource( id = R.string.shortcut_customize_mode_add_shortcut_description ) text = stringResource(id = R.string.shortcut_customize_mode_add_shortcut_description) ) PromptShortcutModifier( modifier = Loading @@ -112,8 +119,9 @@ private fun AddShortcutDialog( ) SelectedKeyCombinationContainer( shouldShowError = uiState.errorMessage.isNotEmpty(), onKeyPress = onKeyPress, onShortcutKeyCombinationSelected = onShortcutKeyCombinationSelected, pressedKeys = uiState.pressedKeys, onConfirmSetShortcut = onConfirmSetShortcut, ) ErrorMessageContainer(uiState.errorMessage) DialogButtons( Loading @@ -121,9 +129,7 @@ private fun AddShortcutDialog( isConfirmButtonEnabled = uiState.pressedKeys.isNotEmpty(), onConfirm = onConfirmSetShortcut, confirmButtonText = stringResource( R.string.shortcut_helper_customize_dialog_set_shortcut_button_label ), stringResource(R.string.shortcut_helper_customize_dialog_set_shortcut_button_label), ) } } Loading @@ -132,18 +138,13 @@ private fun AddShortcutDialog( private fun DeleteShortcutDialog( modifier: Modifier, onCancel: () -> Unit, onConfirmDeleteShortcut: () -> Unit onConfirmDeleteShortcut: () -> Unit, ) { ConfirmationDialog( modifier = modifier, title = stringResource( id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title ), title = stringResource(id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title), description = stringResource( id = R.string.shortcut_customize_mode_remove_shortcut_description ), stringResource(id = R.string.shortcut_customize_mode_remove_shortcut_description), confirmButtonText = stringResource(R.string.shortcut_helper_customize_dialog_remove_button_label), onCancel = onCancel, Loading @@ -155,18 +156,13 @@ private fun DeleteShortcutDialog( private fun ResetShortcutDialog( modifier: Modifier, onCancel: () -> Unit, onConfirmResetShortcut: () -> Unit onConfirmResetShortcut: () -> Unit, ) { ConfirmationDialog( modifier = modifier, title = stringResource( id = R.string.shortcut_customize_mode_reset_shortcut_dialog_title ), title = stringResource(id = R.string.shortcut_customize_mode_reset_shortcut_dialog_title), description = stringResource( id = R.string.shortcut_customize_mode_reset_shortcut_description ), stringResource(id = R.string.shortcut_customize_mode_reset_shortcut_description), confirmButtonText = stringResource(R.string.shortcut_helper_customize_dialog_reset_button_label), onCancel = onCancel, Loading Loading @@ -201,6 +197,9 @@ private fun DialogButtons( onConfirm: () -> Unit, confirmButtonText: String, ) { val focusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { focusRequester.requestFocus() } Row( modifier = Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp) Loading @@ -218,6 +217,10 @@ private fun DialogButtons( ) Spacer(modifier = Modifier.width(8.dp)) ShortcutHelperButton( modifier = Modifier.focusRequester(focusRequester).focusProperties { canFocus = true }, // enable focus on touch/click mode onClick = onConfirm, color = MaterialTheme.colorScheme.primary, width = 116.dp, Loading Loading @@ -248,8 +251,9 @@ private fun ErrorMessageContainer(errorMessage: String) { @Composable private fun SelectedKeyCombinationContainer( shouldShowError: Boolean, onKeyPress: (KeyEvent) -> Boolean, onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean, pressedKeys: List<ShortcutKey>, onConfirmSetShortcut: () -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() Loading @@ -269,7 +273,17 @@ private fun SelectedKeyCombinationContainer( Modifier.padding(all = 16.dp) .sizeIn(minWidth = 332.dp, minHeight = 56.dp) .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp)) .onKeyEvent { onKeyPress(it) } .onPreviewKeyEvent { keyEvent -> val keyEventProcessed = onShortcutKeyCombinationSelected(keyEvent) if ( !keyEventProcessed && keyEvent.key == Key.Enter && keyEvent.type == KeyEventType.KeyUp ) { onConfirmSetShortcut() true } else keyEventProcessed } .focusProperties { canFocus = true } // enables keyboard focus when in touch mode .focusRequester(focusRequester), interactionSource = interactionSource, Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt +18 −5 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ constructor( private val context: Context, private val shortcutCustomizationInteractor: ShortcutCustomizationInteractor, ) { private var keyDownEventCache: KeyEvent? = null private val _shortcutCustomizationUiState = MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive) Loading Loading @@ -94,9 +95,16 @@ constructor( shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null) } fun onKeyPressed(keyEvent: KeyEvent): Boolean { if ((keyEvent.isMetaPressed && keyEvent.type == KeyEventType.KeyDown)) { updatePressedKeys(keyEvent) fun onShortcutKeyCombinationSelected(keyEvent: KeyEvent): Boolean { if (isModifier(keyEvent)) { return false } if (keyEvent.isMetaPressed && keyEvent.type == KeyEventType.KeyDown) { keyDownEventCache = keyEvent return true } else if (keyEvent.type == KeyEventType.KeyUp && keyEvent.key == keyDownEventCache?.key) { updatePressedKeys(keyDownEventCache!!) clearKeyDownEventCache() return true } return false Loading Loading @@ -157,16 +165,21 @@ constructor( return (uiState as? AddShortcutDialog)?.copy(errorMessage = errorMessage) ?: uiState } private fun isModifier(keyEvent: KeyEvent) = SUPPORTED_MODIFIERS.contains(keyEvent.key) private fun updatePressedKeys(keyEvent: KeyEvent) { val isModifier = SUPPORTED_MODIFIERS.contains(keyEvent.key) val keyCombination = KeyCombination( modifiers = keyEvent.nativeKeyEvent.modifiers, keyCode = if (!isModifier) keyEvent.key.nativeKeyCode else null, keyCode = if (!isModifier(keyEvent)) keyEvent.key.nativeKeyCode else null, ) shortcutCustomizationInteractor.updateUserSelectedKeyCombination(keyCombination) } private fun clearKeyDownEventCache() { keyDownEventCache = null } @AssistedFactory interface Factory { fun create(): ShortcutCustomizationViewModel Loading