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

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

Merge changes I79008ba1,Iac6718b5 into main

* changes:
  Minor UI Changes
  Capturing user's selected custom key combination for shortcuts
parents b98adbf0 482cb781
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3827,6 +3827,10 @@
         assigning a new custom key combination to a shortcut in shortcut helper. The helper is a
         component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
    <string name="shortcut_helper_customize_dialog_error_message">Key combination already in use. Try another key.</string>
    <!-- Plus sign, used in keyboard shortcut helper to combine keys for shortcut. E.g. Ctrl + A
         The helper is a component that shows the user which keyboard shortcuts they can use.
         [CHAR LIMIT=NONE] -->
    <string name="shortcut_helper_plus_symbol">+</string>


    <!-- Keyboard touchpad tutorial scheduler-->
+13 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.keyboard.shortcut.ui

import android.app.Dialog
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
@@ -33,6 +34,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import com.android.systemui.statusbar.phone.create
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation

class ShortcutCustomizationDialogStarter
@AssistedInject
@@ -57,6 +59,7 @@ constructor(
                dialog = null
            }
        }
        awaitCancellation()
    }

    fun onShortcutCustomizationRequested(requestInfo: ShortcutCustomizationRequestInfo) {
@@ -66,14 +69,21 @@ constructor(
    private fun createAddShortcutDialog(): Dialog {
        return dialogFactory.create(dialogDelegate = ShortcutCustomizationDialogDelegate()) { dialog
            ->
            val uiState by viewModel.shortcutCustomizationUiState.collectAsStateWithLifecycle()
            val uiState by
                viewModel.shortcutCustomizationUiState.collectAsStateWithLifecycle(
                    initialValue = ShortcutCustomizationUiState.Inactive
                )
            AssignNewShortcutDialog(
                uiState = uiState,
                modifier = Modifier.width(364.dp).wrapContentHeight().padding(vertical = 24.dp),
                onKeyPress = { viewModel.onKeyPressed(it) },
                onCancel = { dialog.dismiss() },
            )
            dialog.setOnDismissListener { viewModel.onAddShortcutDialogDismissed() }
            dialog.setOnDismissListener { viewModel.onDialogDismissed() }

            // By default, apps cannot intercept action key. The system always handles it. This
            // flag is needed to enable customisation dialog window to intercept action key
            dialog.window?.addPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS)
        }
    }

+82 −18
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -39,18 +40,22 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
import com.android.systemui.res.R
@@ -81,15 +86,16 @@ fun AssignNewShortcutDialog(
            SelectedKeyCombinationContainer(
                shouldShowErrorMessage = uiState.shouldShowErrorMessage,
                onKeyPress = onKeyPress,
                pressedKeys = uiState.pressedKeys,
            )
            KeyCombinationAlreadyInUseErrorMessage(uiState.shouldShowErrorMessage)
            DialogButtons(onCancel, isValidKeyCombination = uiState.isValidKeyCombination)
            DialogButtons(onCancel, isSetShortcutButtonEnabled = uiState.pressedKeys.isNotEmpty())
        }
    }
}

@Composable
fun DialogButtons(onCancel: () -> Unit, isValidKeyCombination: Boolean) {
fun DialogButtons(onCancel: () -> Unit, isSetShortcutButtonEnabled: Boolean) {
    Row(
        modifier =
            Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp)
@@ -113,7 +119,7 @@ fun DialogButtons(onCancel: () -> Unit, isValidKeyCombination: Boolean) {
            contentColor = MaterialTheme.colorScheme.onPrimary,
            text =
                stringResource(R.string.shortcut_helper_customize_dialog_set_shortcut_button_label),
            enabled = isValidKeyCombination,
            enabled = isSetShortcutButtonEnabled,
        )
    }
}
@@ -137,10 +143,9 @@ fun KeyCombinationAlreadyInUseErrorMessage(shouldShowErrorMessage: Boolean) {

@Composable
fun SelectedKeyCombinationContainer(
    keyCombination: String =
        stringResource(R.string.shortcut_helper_add_shortcut_dialog_placeholder),
    shouldShowErrorMessage: Boolean,
    onKeyPress: (KeyEvent) -> Boolean,
    pressedKeys: List<ShortcutKey>,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isFocused by interactionSource.collectIsFocusedAsState()
@@ -148,6 +153,9 @@ fun SelectedKeyCombinationContainer(
        if (!isFocused) MaterialTheme.colorScheme.outline
        else if (shouldShowErrorMessage) MaterialTheme.colorScheme.error
        else MaterialTheme.colorScheme.primary
    val focusRequester = remember { FocusRequester() }

    LaunchedEffect(Unit) { focusRequester.requestFocus() }

    ClickableShortcutSurface(
        onClick = {},
@@ -157,22 +165,19 @@ fun SelectedKeyCombinationContainer(
            Modifier.padding(all = 16.dp)
                .sizeIn(minWidth = 332.dp, minHeight = 56.dp)
                .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp))
                .onPreviewKeyEvent { onKeyPress(it) },
                .onKeyEvent { onKeyPress(it) }
                .focusRequester(focusRequester),
        interactionSource = interactionSource,
    ) {
        Row(
            modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 16.dp, bottom = 16.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Text(
                text = keyCombination,
                style = MaterialTheme.typography.headlineSmall,
                fontSize = 16.sp,
                lineHeight = 24.sp,
                fontWeight = FontWeight.W500,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.width(252.dp),
            )
            if (pressedKeys.isEmpty()) {
                PressKeyPrompt()
            } else {
                PressedKeysTextContainer(pressedKeys)
            }
            Spacer(modifier = Modifier.weight(1f))
            if (shouldShowErrorMessage) {
                Icon(
@@ -186,6 +191,67 @@ fun SelectedKeyCombinationContainer(
    }
}

@Composable
private fun RowScope.PressedKeysTextContainer(pressedKeys: List<ShortcutKey>) {
    pressedKeys.forEachIndexed { keyIndex, key ->
        if (keyIndex > 0) {
            ShortcutKeySeparator()
        }
        if (key is ShortcutKey.Text) {
            ShortcutTextKey(key)
        } else if (key is ShortcutKey.Icon) {
            ShortcutIconKey(key)
        }
    }
}

@Composable
private fun ShortcutKeySeparator() {
    Text(
        text = stringResource(id = R.string.shortcut_helper_plus_symbol),
        style = MaterialTheme.typography.titleSmall,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        color = MaterialTheme.colorScheme.onSurfaceVariant,
    )
}

@Composable
private fun RowScope.ShortcutIconKey(key: ShortcutKey.Icon) {
    Icon(
        painter =
            when (key) {
                is ShortcutKey.Icon.ResIdIcon -> painterResource(key.drawableResId)
                is ShortcutKey.Icon.DrawableIcon -> rememberDrawablePainter(drawable = key.drawable)
            },
        contentDescription = null,
        modifier = Modifier.align(Alignment.CenterVertically).height(24.dp),
        tint = MaterialTheme.colorScheme.onSurfaceVariant,
    )
}

@Composable
private fun PressKeyPrompt() {
    Text(
        text = stringResource(id = R.string.shortcut_helper_add_shortcut_dialog_placeholder),
        style = MaterialTheme.typography.titleSmall,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        color = MaterialTheme.colorScheme.onSurfaceVariant,
    )
}

@Composable
private fun ShortcutTextKey(key: ShortcutKey.Text) {
    Text(
        text = key.value,
        style = MaterialTheme.typography.titleSmall,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        color = MaterialTheme.colorScheme.onSurfaceVariant,
    )
}

@Composable
private fun Title(title: String, modifier: Modifier = Modifier) {
    Text(
@@ -203,8 +269,6 @@ private fun Description(modifier: Modifier = Modifier) {
    Text(
        text = stringResource(id = R.string.shortcut_helper_customize_mode_sub_title),
        style = MaterialTheme.typography.bodyMedium,
        fontSize = 14.sp,
        lineHeight = 20.sp,
        modifier = modifier.wrapContentSize(Alignment.Center),
        color = MaterialTheme.colorScheme.onSurfaceVariant,
    )
+1 −1
Original line number Diff line number Diff line
@@ -22,9 +22,9 @@ sealed interface ShortcutCustomizationUiState {
    data class AddShortcutDialog(
        val shortcutLabel: String,
        val shouldShowErrorMessage: Boolean,
        val isValidKeyCombination: Boolean,
        val defaultCustomShortcutModifierKey: ShortcutKey.Icon.ResIdIcon,
        val isDialogShowing: Boolean,
        val pressedKeys: List<ShortcutKey> = emptyList(),
    ) : ShortcutCustomizationUiState

    data object Inactive : ShortcutCustomizationUiState
+57 −5
Original line number Diff line number Diff line
@@ -17,14 +17,22 @@
package com.android.systemui.keyboard.shortcut.ui.viewmodel

import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.isMetaPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.nativeKeyCode
import androidx.compose.ui.input.key.type
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.ui.model.ShortcutCustomizationUiState
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update

class ShortcutCustomizationViewModel
@@ -35,7 +43,21 @@ constructor(private val shortcutCustomizationInteractor: ShortcutCustomizationIn
    private val _shortcutCustomizationUiState =
        MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive)

    val shortcutCustomizationUiState = _shortcutCustomizationUiState.asStateFlow()
    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 ShortcutCustomizationUiState.AddShortcutDialog) {
                    uiState.copy(pressedKeys = keys)
                } else {
                    uiState
                }
            }

    fun onShortcutCustomizationRequested(requestInfo: ShortcutCustomizationRequestInfo) {
        when (requestInfo) {
@@ -44,10 +66,10 @@ constructor(private val shortcutCustomizationInteractor: ShortcutCustomizationIn
                    ShortcutCustomizationUiState.AddShortcutDialog(
                        shortcutLabel = requestInfo.label,
                        shouldShowErrorMessage = false,
                        isValidKeyCombination = false,
                        defaultCustomShortcutModifierKey =
                            shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey(),
                        isDialogShowing = false,
                        pressedKeys = emptyList(),
                    )
                _shortcutBeingCustomized.value = requestInfo
            }
@@ -62,18 +84,48 @@ constructor(private val shortcutCustomizationInteractor: ShortcutCustomizationIn
        }
    }

    fun onAddShortcutDialogDismissed() {
    fun onDialogDismissed() {
        _shortcutBeingCustomized.value = null
        _shortcutCustomizationUiState.value = ShortcutCustomizationUiState.Inactive
        shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null)
    }

    fun onKeyPressed(keyEvent: KeyEvent): Boolean {
        // TODO Not yet implemented b/373638584
        if ((keyEvent.isMetaPressed && keyEvent.type == KeyEventType.KeyDown)) {
            updatePressedKeys(keyEvent)
            return true
        }
        return false
    }

    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,
            )
        shortcutCustomizationInteractor.updateUserSelectedKeyCombination(keyCombination)
    }

    @AssistedFactory
    interface Factory {
        fun create(): ShortcutCustomizationViewModel
    }

    companion object {
        private val SUPPORTED_MODIFIERS =
            listOf(
                Key.MetaLeft,
                Key.MetaRight,
                Key.CtrlRight,
                Key.CtrlLeft,
                Key.AltLeft,
                Key.AltRight,
                Key.ShiftLeft,
                Key.ShiftRight,
                Key.Function,
                Key.Symbol,
            )
    }
}
Loading