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

Commit 45f3acfb authored by Josh's avatar Josh
Browse files

use shortcut command as key for retrieving shortcut to be deleted

Shortcut commands can uniquely identify key Input Gestures to be
deleted, while Key_gesture_type may not be unique, E.g For Application
shortcuts they all share the same key_gesture_type

Flag: com.android.systemui.extended_apps_shortcut_category
Test: customShortcutCategoriesRepositoryTest
Fix: 405058325
Change-Id: I891ab972456e0799700bc1bbaff29a4b9c6faed5
parent be38c2b2
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.hardware.input.fakeInputManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.KeyEvent.KEYCODE_A
import android.view.KeyEvent.KEYCODE_B
import android.view.KeyEvent.KEYCODE_SLASH
import android.view.KeyEvent.META_ALT_ON
import android.view.KeyEvent.META_CAPS_LOCK_ON
@@ -55,9 +56,12 @@ import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeIn
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.launchCalendarShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardKeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.shortcutCommand
import com.android.systemui.keyboard.shortcut.shortcutHelperInputDeviceRepository
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -291,6 +295,32 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
        }
    }

    @Test
    @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
    fun removeAppCategoryShortcut_successfullyRetrievesGestureDataAndDeletesTheCorrectShortcut() {
        testScope.runTest {
            // We are collecting this because the flow is a cold flow but we need its value as a
            // stateflow when deleting a custom shortcut.
            // TODO remove when refactoring test - use Fakes Instead. b/405358441
            collectLastValue(kosmos.shortcutHelperInputDeviceRepository.activeInputDevice)
            var customInputGestures = listOf(ctrlAltAShortcut, ctrlAltBShortcut)
            whenever(inputManager.getCustomInputGestures(anyOrNull())).then {
                return@then customInputGestures
            }
            whenever(inputManager.removeCustomInputGesture(any())).then {
                val inputGestureToRemove = it.getArgument<InputGestureData>(0)
                val containsGesture = customInputGestures.contains(inputGestureToRemove)
                customInputGestures = customInputGestures - inputGestureToRemove
                return@then if (containsGesture) CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
                else CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
            }
            helper.toggle(deviceId = 123)

            customizeShortcut(customizationRequest = ctrlAltAShortcutDeleteRequest)
            assertThat(customInputGestures).containsExactly(ctrlAltBShortcut)
        }
    }

    @Test
    @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
    fun categories_isUpdatedAfterCustomShortcutIsDeleted() {
@@ -436,4 +466,18 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
            .setAppLaunchData(appLaunchData)
            .build()
    }

    private val ctrlAltAShortcut = simpleInputGestureDataForAppLaunchShortcut()
    private val ctrlAltBShortcut = simpleInputGestureDataForAppLaunchShortcut(keyCode = KEYCODE_B)
    private val ctrlAltAShortcutDeleteRequest =
        SingleShortcutCustomization.Delete(
            categoryType = ShortcutCategoryType.AppCategories,
            subCategoryLabel = context.getString(R.string.keyboard_shortcut_group_applications),
            customShortcutCommand =
                shortcutCommand {
                    key("Ctrl")
                    key("Alt")
                    key("A")
                },
        )
}
+1 −1
Original line number Diff line number Diff line
@@ -603,7 +603,7 @@ object TestShortcuts {
            label = "Calendar",
            categoryType = ShortcutCategoryType.AppCategories,
            subCategoryLabel = "Applications",
            shortcutCommand =
            defaultShortcutCommand =
                shortcutCommand {
                    key("Ctrl")
                    key("Alt")
+65 −13
Original line number Diff line number Diff line
@@ -18,13 +18,15 @@ package com.android.systemui.keyboard.shortcut.data.repository

import android.hardware.input.InputGestureData
import android.hardware.input.InputGestureData.Builder
import android.hardware.input.InputGestureData.KeyTrigger
import android.hardware.input.InputGestureData.Trigger
import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
import android.hardware.input.KeyGestureEvent.KeyGestureType
import android.hardware.input.KeyGlyphMap
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.mutableStateOf
import com.android.systemui.Flags.shortcutHelperKeyGlyph
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -34,20 +36,21 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject

@SysUISingleton
class CustomShortcutCategoriesRepository
@Inject
constructor(
    inputDeviceRepository: ShortcutHelperInputDeviceRepository,
    private val inputDeviceRepository: ShortcutHelperInputDeviceRepository,
    @Background private val backgroundScope: CoroutineScope,
    private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
    private val inputGestureDataAdapter: InputGestureDataAdapter,
@@ -57,7 +60,7 @@ constructor(
) : ShortcutCategoriesRepository {

    private val _selectedKeyCombination = MutableStateFlow<KeyCombination?>(null)
    private val _shortcutBeingCustomized = mutableStateOf<ShortcutCustomizationRequestInfo?>(null)
    private val _shortcutBeingCustomized = MutableStateFlow<ShortcutCustomizationRequestInfo?>(null)

    val pressedKeys =
        _selectedKeyCombination
@@ -65,10 +68,7 @@ constructor(
                if (inputDevice == null || keyCombination == null) {
                    return@combine emptyList()
                } else {
                    val keyGlyphMap =
                        if (shortcutHelperKeyGlyph()) {
                            inputManager.getKeyGlyphMap(inputDevice.id)
                        } else null
                    val keyGlyphMap = getKeyGlyphMap(inputDevice.id)
                    val modifiers =
                        shortcutCategoriesUtils.toShortcutModifierKeys(
                            keyCombination.modifiers,
@@ -150,9 +150,27 @@ constructor(
    }

    private fun retrieveInputGestureDataForShortcutBeingDeleted(): InputGestureData? {
        val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()
        return customInputGesturesRepository.retrieveCustomInputGestures().firstOrNull {
            it.action.keyGestureType() == keyGestureType
        val keyGestureTypeForShortcutBeingDeleted = getKeyGestureTypeForShortcutBeingCustomized()
        val inputGesturesMatchingKeyGestureType =
            customInputGesturesRepository.retrieveCustomInputGestures().filter {
                it.action.keyGestureType() == keyGestureTypeForShortcutBeingDeleted
            }

        return if (keyGestureTypeForShortcutBeingDeleted == KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
            val shortcutBeingDeleted = getShortcutBeingCustomized() as Delete
            if (shortcutBeingDeleted.customShortcutCommand == null){
                Log.w(TAG, "Requested to delete custom shortcut but customShortcutCommand was null")
                return null
            }

            inputGesturesMatchingKeyGestureType.firstOrNull {
                checkShortcutKeyTriggerEquality(
                    it.trigger,
                    shortcutBeingDeleted.customShortcutCommand.keys,
                ) ?: false
            }
        } else {
            inputGesturesMatchingKeyGestureType.firstOrNull()
        }
    }

@@ -181,6 +199,41 @@ constructor(
        return customInputGesturesRepository.getInputGestureByTrigger(trigger) == null
    }

    private fun checkShortcutKeyTriggerEquality(
        trigger: Trigger,
        keys: List<ShortcutKey>,
    ): Boolean? {
        return getConvertedKeyTrigger(trigger)?.containsAll(keys)
    }

    private fun getConvertedKeyTrigger(trigger: Trigger): List<ShortcutKey>? {
        if (trigger is KeyTrigger) {
            val inputDevice = inputDeviceRepository.activeInputDevice.value ?: return null

            val modifierKeys =
                shortcutCategoriesUtils.toShortcutModifierKeys(
                    keyGlyphMap = getKeyGlyphMap(inputDevice.id),
                    modifiers = trigger.modifierState,
                ) ?: return null

            val keyCodeShortcutKey =
                shortcutCategoriesUtils.toShortcutKey(
                    keyGlyphMap = getKeyGlyphMap(inputDevice.id),
                    keyCharacterMap = inputDevice.keyCharacterMap,
                    keyCode = trigger.keycode,
                ) ?: return null

            return modifierKeys + keyCodeShortcutKey
        }
        return null
    }

    private fun getKeyGlyphMap(deviceId: Int): KeyGlyphMap? {
        return if (shortcutHelperKeyGlyph()) {
            inputManager.getKeyGlyphMap(deviceId)
        } else null
    }

    private fun Builder.addKeyGestureTypeForShortcutBeingCustomized(): Builder {
        val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()

@@ -202,8 +255,7 @@ constructor(
            return this
        }

        val defaultShortcutCommand = shortcutBeingCustomized.shortcutCommand

        val defaultShortcutCommand = shortcutBeingCustomized.defaultShortcutCommand ?: return this
        val appLaunchData =
            appLaunchDataRepository.getAppLaunchDataForShortcutWithCommand(defaultShortcutCommand)

+5 −4
Original line number Diff line number Diff line
@@ -22,20 +22,21 @@ sealed interface ShortcutCustomizationRequestInfo {
        val label: String
        val categoryType: ShortcutCategoryType
        val subCategoryLabel: String
        val shortcutCommand: ShortcutCommand
        val defaultShortcutCommand: ShortcutCommand?

        data class Add(
            override val label: String = "",
            override val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
            override val subCategoryLabel: String = "",
            override val shortcutCommand: ShortcutCommand = ShortcutCommand(),
            override val defaultShortcutCommand: ShortcutCommand? = null,
        ) : SingleShortcutCustomization

        data class Delete(
            override val label: String = "",
            override val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
            override val subCategoryLabel: String = "",
            override val shortcutCommand: ShortcutCommand = ShortcutCommand(),
            override val defaultShortcutCommand: ShortcutCommand? = null,
            val customShortcutCommand: ShortcutCommand? = null,
        ) : SingleShortcutCustomization
    }

+3 −2
Original line number Diff line number Diff line
@@ -667,7 +667,7 @@ private fun Shortcut(
                onShortcutCustomizationRequested(
                    ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add(
                        label = shortcut.label,
                        shortcutCommand = shortcut.commands.first(),
                        defaultShortcutCommand = shortcut.commands.firstOrNull { !it.isCustom },
                    )
                )
            },
@@ -675,7 +675,8 @@ private fun Shortcut(
                onShortcutCustomizationRequested(
                    ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete(
                        label = shortcut.label,
                        shortcutCommand = shortcut.commands.first(),
                        defaultShortcutCommand = shortcut.commands.firstOrNull { !it.isCustom },
                        customShortcutCommand = shortcut.commands.firstOrNull { it.isCustom },
                    )
                )
            },