Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c3dcb27 authored by Joshua Mokut's avatar Joshua Mokut Committed by Android (Google) Code Review
Browse files

Merge "Pressing enter should correspond to default button action" into main

parents 4b10d202 312ecf91
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -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
@@ -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",
@@ -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,
+23 −23
Original line number Diff line number Diff line
@@ -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 =
@@ -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()
        }
    }

@@ -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()
        }
    }

@@ -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)
@@ -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)
@@ -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()
    }
+5 −3
Original line number Diff line number Diff line
@@ -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 = {
+53 −39
Original line number Diff line number Diff line
@@ -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
@@ -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,
@@ -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)
@@ -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 =
@@ -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(
@@ -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),
        )
    }
}
@@ -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,
@@ -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,
@@ -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)
@@ -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,
@@ -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()
@@ -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,
+18 −5
Original line number Diff line number Diff line
@@ -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)

@@ -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
@@ -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