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

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

Merge changes Ibac3af87,Id9775b25,I891ab972 into main

* changes:
  Added repository to expose app shortcuts for user visible apps
  Shortcut Helper Font updates
  use shortcut command as key for retrieving shortcut to be deleted
parents d3af2e14 9308ef59
Loading
Loading
Loading
Loading
+156 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.keyboard.shortcut.data.repository

import android.content.pm.UserInfo
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.appsShortcutCategoryRepository
import com.android.systemui.keyboard.shortcut.fakeLauncherApps
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
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.ShortcutIcon
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class AppsShortcutCategoryRepositoryTest : SysuiTestCase() {

    private val kosmos = testKosmos().useUnconfinedTestDispatcher()

    private val repo = kosmos.appsShortcutCategoryRepository
    private val fakeLauncherApps = kosmos.fakeLauncherApps
    private val userTracker = kosmos.fakeUserTracker
    private val testScope = kosmos.testScope

    @Before
    fun setup() {
        userTracker.set(
            userInfos =
                listOf(
                    UserInfo(/* id= */ PRIMARY_USER_ID, /* name= */ "Primary User", /* flags= */ 0)
                ),
            selectedUserIndex = PRIMARY_USER_INDEX,
        )

        fakeLauncherApps.installPackageForUser(
            TEST_PACKAGE_1,
            TEST_CLASS_1,
            UserHandle(/* userId= */ PRIMARY_USER_ID),
            ICON_RES_ID_1,
            TEST_PACKAGE_LABEL_1,
        )

        fakeLauncherApps.installPackageForUser(
            TEST_PACKAGE_2,
            TEST_CLASS_2,
            UserHandle(/* userId= */ PRIMARY_USER_ID),
            ICON_RES_ID_2,
            TEST_PACKAGE_LABEL_2,
        )
    }

    @Test
    fun categories_emitsCorrectShortcutCategoryWithAllInstalledApps() {
        testScope.runTest {
            val categories by collectLastValue(repo.categories)

            assertThat(categories).containsExactly(expectedShortcutCategoryWithBothAppShortcuts)
        }
    }

    @Test
    fun categories_emitsEmptyListWhenAlUserVisibleAppsAreUninstalled() {
        testScope.runTest {
            val categories by collectLastValue(repo.categories)

            fakeLauncherApps.uninstallPackageForUser(
                TEST_PACKAGE_1,
                TEST_CLASS_1,
                UserHandle(/* userId= */ PRIMARY_USER_ID),
            )

            fakeLauncherApps.uninstallPackageForUser(
                TEST_PACKAGE_2,
                TEST_CLASS_2,
                UserHandle(/* userId= */ PRIMARY_USER_ID),
            )

            assertThat(categories).isEmpty()
        }
    }

    private val expectedShortcutCategoryWithBothAppShortcuts =
        ShortcutCategory(
            ShortcutCategoryType.AppCategories,
            ShortcutSubCategory(
                label = context.getString(R.string.keyboard_shortcut_group_applications),
                shortcuts =
                    listOf(
                        Shortcut(
                            label = TEST_PACKAGE_LABEL_1,
                            commands = emptyList(),
                            icon =
                                ShortcutIcon(
                                    packageName = TEST_PACKAGE_1,
                                    resourceId = ICON_RES_ID_1,
                                ),
                            pkgName = TEST_PACKAGE_1,
                            className = TEST_CLASS_1,
                        ),
                        Shortcut(
                            label = TEST_PACKAGE_LABEL_2,
                            commands = emptyList(),
                            icon =
                                ShortcutIcon(
                                    packageName = TEST_PACKAGE_2,
                                    resourceId = ICON_RES_ID_2,
                                ),
                            pkgName = TEST_PACKAGE_2,
                            className = TEST_CLASS_2,
                        ),
                    ),
            ),
        )

    private companion object {
        const val TEST_PACKAGE_1 = "test.package.one"
        const val TEST_PACKAGE_2 = "test.package.two"
        const val TEST_CLASS_1 = "TestClassOne"
        const val TEST_CLASS_2 = "TestClassTwo"
        const val PRIMARY_USER_ID = 10
        const val PRIMARY_USER_INDEX = 0
        const val ICON_RES_ID_1 = 1
        const val ICON_RES_ID_2 = 2
        const val TEST_PACKAGE_LABEL_1 = "ApplicationOne"
        const val TEST_PACKAGE_LABEL_2 = "ApplicationTwo"
    }
}
+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
@@ -145,7 +145,7 @@ class UserVisibleAppsRepositoryTest : SysuiTestCase() {
        )
    }

    companion object {
    private companion object {
        const val TEST_PACKAGE_1 = "test.package.one"
        const val TEST_PACKAGE_2 = "test.package.two"
        const val TEST_CLASS_1 = "TestClassOne"
+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")
+72 −5
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.appsShortcutCategoryRepository
import com.android.systemui.keyboard.shortcut.data.repository.FakeAppsShortcutCategoryRepository
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations
@@ -34,24 +36,28 @@ import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customIn
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.groupWithGoHomeShortcutInfo
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.systemCategoryWithCustomHomeShortcut
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.systemCategoryWithMergedGoHomeShortcut
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
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.ShortcutCategoryType.InputMethodEditor
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesInteractor
import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperCustomizationModeInteractor
import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -69,8 +75,8 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {
    private val multitaskingShortcutsSource = FakeKeyboardShortcutGroupsSource()

    private val kosmos =
        testKosmos().also {
            it.testDispatcher = UnconfinedTestDispatcher()
        testKosmos().useUnconfinedTestDispatcher().also {
            it.appsShortcutCategoryRepository = FakeAppsShortcutCategoryRepository()
            it.shortcutHelperSystemShortcutsSource = systemShortcutsSource
            it.shortcutHelperMultiTaskingShortcutsSource = multitaskingShortcutsSource
            it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
@@ -83,7 +89,9 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {
    private val testScope = kosmos.testScope
    private lateinit var interactor: ShortcutHelperCategoriesInteractor
    private val helper = kosmos.shortcutHelperTestHelper
    private val inter by lazy { kosmos.shortcutHelperCategoriesInteractor }
    private val customizationModeInteractor = kosmos.shortcutHelperCustomizationModeInteractor
    private val fakeAppsShortcutCategoryRepo: FakeAppsShortcutCategoryRepository =
        kosmos.appsShortcutCategoryRepository as FakeAppsShortcutCategoryRepository

    @Before
    fun setShortcuts() {
@@ -383,8 +391,67 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {
            assertThat(shortcutKeyCount).containsExactly(1, 2, 3).inOrder()
        }

    @Test
    @DisableFlags(Flags.FLAG_EXTENDED_APPS_SHORTCUT_CATEGORY)
    fun categories_extendedAppsCategoryFlagOff_allCustomizationModes_doesNotEmit3PAppsShortcut() =
        testScope.runTest {
            setExtendedAppsShortcutCategory()
            val categories by collectLastValue(interactor.shortcutCategories)
            helper.showFromActivity()

            customizationModeInteractor.toggleCustomizationMode(isCustomizing = false)
            assertThat(categories).doesNotContain(TestAppsShortcutCategory)

            customizationModeInteractor.toggleCustomizationMode(isCustomizing = true)
            assertThat(categories).doesNotContain(TestAppsShortcutCategory)
        }

    @Test
    @EnableFlags(Flags.FLAG_EXTENDED_APPS_SHORTCUT_CATEGORY)
    fun categories_extendedAppsCategoryFlagOn_customizationOn_emitsExtendedAppShortcutCategory() =
        testScope.runTest {
            setExtendedAppsShortcutCategory()
            val categories by collectLastValue(interactor.shortcutCategories)
            helper.showFromActivity()

            customizationModeInteractor.toggleCustomizationMode(isCustomizing = true)
            assertThat(categories).contains(TestAppsShortcutCategory)
        }

    @Test
    @EnableFlags(Flags.FLAG_EXTENDED_APPS_SHORTCUT_CATEGORY)
    fun categories_extendedAppsCategoryFlagOn_customizationOff_doesNotEmitExtendedAppShortcutCategory() =
        testScope.runTest {
            setExtendedAppsShortcutCategory()
            val categories by collectLastValue(interactor.shortcutCategories)
            helper.showFromActivity()

            customizationModeInteractor.toggleCustomizationMode(isCustomizing = false)
            assertThat(categories).doesNotContain(TestAppsShortcutCategory)
        }

    private fun setExtendedAppsShortcutCategory() {
        fakeAppsShortcutCategoryRepo.setFakeAppsShortcutCategories(listOf(TestAppsShortcutCategory))
    }

    private fun setCustomInputGestures(customInputGestures: List<InputGestureData>) {
        whenever(fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()))
            .thenReturn(customInputGestures)
    }

    private val TestAppsShortcutCategory =
        ShortcutCategory(
            type = ShortcutCategoryType.AppCategories,
            ShortcutSubCategory(
                label = context.getString(R.string.keyboard_shortcut_group_applications),
                shortcuts =
                    listOf(
                        Shortcut(
                            label = "TestApp",
                            commands = emptyList(),
                            contentDescription = "TestApp, Press key",
                        )
                    ),
            ),
        )
}
Loading