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

Commit 75314ea1 authored by Josh's avatar Josh
Browse files

Added logic for limiting extra apps shortcut customization

When user customizes 10 app shortcuts, we no longer allow further custom
app shortcut customization.

Test: ShortcutHelperViewModelTest
Flag: com.android.systemui.extended_apps_shortcut_category
Fix: 405985619
Change-Id: I7483b9a80bf3841d4d833187e56f3e9239305903
parent 6a98f299
Loading
Loading
Loading
Loading
+99 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.systemui.keyboard.shortcut.defaultShortcutCategoriesRepositor
import com.android.systemui.keyboard.shortcut.fakeCustomShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.fakeDefaultShortcutCategoriesRepository
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.CurrentApp
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
@@ -46,6 +47,7 @@ import com.android.systemui.keyboard.shortcut.shortcutHelperViewModel
import com.android.systemui.keyboard.shortcut.ui.model.IconSource
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel.Companion.EXTENDED_APPS_SHORTCUT_CUSTOMIZATION_LIMIT
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.model.sysUiState
@@ -468,6 +470,78 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
        }
    }

    @Test
    fun allowExtendedAppShortcutsCustomization_true_WhenExtraAppsShortcutsCustomizedIsBelowLimit() {
        testScope.runTest {
            setupShortcutHelperWithExtendedAppsShortcutCustomizations(
                numberOfDefaultAppsShortcuts = 3,
                numberOfCustomShortcutsForDefaultApps = 3,
                numberOfCustomShortcutsForExtendedApps = 3,
            )

            underTest.toggleCustomizationMode(true)
            val uiState by collectLastValue(underTest.shortcutsUiState)

            val activeUiState = uiState as ShortcutsUiState.Active

            assertThat(activeUiState.allowExtendedAppShortcutsCustomization).isTrue()
        }
    }

    @Test
    fun allowExtendedAppShortcutsCustomization_false_WhenExtraAppsShortcutsCustomizedIsAtLimit() {
        testScope.runTest {
            setupShortcutHelperWithExtendedAppsShortcutCustomizations(
                numberOfDefaultAppsShortcuts = 3,
                numberOfCustomShortcutsForDefaultApps = 3,
                numberOfCustomShortcutsForExtendedApps = EXTENDED_APPS_SHORTCUT_CUSTOMIZATION_LIMIT,
            )

            underTest.toggleCustomizationMode(true)
            val uiState by collectLastValue(underTest.shortcutsUiState)

            val activeUiState = uiState as ShortcutsUiState.Active

            assertThat(activeUiState.allowExtendedAppShortcutsCustomization).isFalse()
        }
    }

    @Test
    fun allowExtendedAppShortcutsCustomization_false_WhenExtraAppsShortcutsCustomizedIsAboveLimit() {
        testScope.runTest {
            setupShortcutHelperWithExtendedAppsShortcutCustomizations(
                numberOfDefaultAppsShortcuts = 3,
                numberOfCustomShortcutsForDefaultApps = 3,
                numberOfCustomShortcutsForExtendedApps =
                    EXTENDED_APPS_SHORTCUT_CUSTOMIZATION_LIMIT + 3,
            )

            underTest.toggleCustomizationMode(true)
            val uiState by collectLastValue(underTest.shortcutsUiState)

            val activeUiState = uiState as ShortcutsUiState.Active

            assertThat(activeUiState.allowExtendedAppShortcutsCustomization).isFalse()
        }
    }

    private fun setupShortcutHelperWithExtendedAppsShortcutCustomizations(
        numberOfDefaultAppsShortcuts: Int,
        numberOfCustomShortcutsForDefaultApps: Int,
        numberOfCustomShortcutsForExtendedApps: Int,
    ) {
        testHelper.showFromActivity()
        fakeDefaultShortcutCategoriesRepository.setShortcutCategories(
            buildAppShortcutsCategory(numberOfDefaultAppsShortcuts)
        )
        fakeCustomShortcutCategoriesRepository.setShortcutCategories(
            buildAppShortcutsCategory(
                numberOfCustomShortcutsForDefaultApps,
                numberOfCustomShortcutsForExtendedApps,
            )
        )
    }

    private fun openHelperAndSearchForFooString() {
        testHelper.showFromActivity()
        underTest.onSearchQueryChanged("foo")
@@ -488,6 +562,18 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
            shortcuts = shortcutLabels.map { simpleShortcut(it, isCustomShortcut) },
        )

    private fun subCategoryWithShortcutLabels(
        defaultShortcutLabels: List<String>,
        customShortcutLabels: List<String>,
        subCategoryLabel: String = FIRST_SIMPLE_GROUP_LABEL,
    ) =
        ShortcutSubCategory(
            label = subCategoryLabel,
            shortcuts =
                defaultShortcutLabels.map { simpleShortcut(it) } +
                    customShortcutLabels.map { simpleShortcut(it, isCustom = true) },
        )

    private fun setShortcutsCategoriesForSearchQuery() {
        fakeDefaultShortcutCategoriesRepository.setShortcutCategories(
            ShortcutCategory(
@@ -516,6 +602,19 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
            contentDescription { "$label, Press key Ctrl plus A" }
        }

    private fun buildAppShortcutsCategory(
        numberOfDefaultAppsShortcuts: Int = 0,
        numberOfCustomAppsShortcuts: Int = 0,
    ): ShortcutCategory {
        val defaultAppLabels = (1..numberOfDefaultAppsShortcuts).map { "default app $it" }
        val customAppLabels = (1..numberOfCustomAppsShortcuts).map { "custom app $it" }

        return ShortcutCategory(
            type = ShortcutCategoryType.AppCategories,
            subCategories = listOf(subCategoryWithShortcutLabels(defaultAppLabels, customAppLabels)),
        )
    }

    private companion object {
        const val FIRST_SIMPLE_GROUP_LABEL = "simple group 1"
        const val SECOND_SIMPLE_GROUP_LABEL = "simple group 2"
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ data class Shortcut(
    val className: String = "",
) {
    val containsCustomShortcutCommands: Boolean = commands.any { it.isCustom }
    val containsDefaultShortcutCommands: Boolean = commands.any { !it.isCustom }
}

class ShortcutBuilder(private val label: String) {
+16 −3
Original line number Diff line number Diff line
@@ -198,6 +198,7 @@ private fun ActiveShortcutHelper(
            shortcutsUiState.isCustomizationModeEnabled,
            onCustomizationModeToggled,
            shortcutsUiState.isExtendedAppCategoryFlagEnabled,
            shortcutsUiState.allowExtendedAppShortcutsCustomization,
            modifier,
            onShortcutCustomizationRequested,
        )
@@ -392,6 +393,7 @@ private fun ShortcutHelperTwoPane(
    isCustomizationModeEnabled: Boolean,
    onCustomizationModeToggled: (isCustomizing: Boolean) -> Unit,
    isExtendedAppCategoryFlagEnabled: Boolean,
    allowExtendedAppShortcutsCustomization: Boolean,
    modifier: Modifier = Modifier,
    onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
) {
@@ -442,8 +444,9 @@ private fun ShortcutHelperTwoPane(
                selectedCategory,
                isCustomizing = isCustomizationModeEnabled,
                isExtendedAppCategoryFlagEnabled,
                allowExtendedAppShortcutsCustomization,
                Modifier.fillMaxSize().padding(top = 8.dp).semantics { isTraversalGroup = true },
                onShortcutCustomizationRequested = onShortcutCustomizationRequested,
                onShortcutCustomizationRequested,
            )
        }
    }
@@ -514,6 +517,7 @@ private fun EndSidePanel(
    category: ShortcutCategoryUi?,
    isCustomizing: Boolean,
    isExtendedAppCategoryFlagEnabled: Boolean,
    allowExtendedAppShortcutsCustomization: Boolean,
    modifier: Modifier = Modifier,
    onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
) {
@@ -549,13 +553,15 @@ private fun EndSidePanel(
                            onShortcutCustomizationRequested(requestInfo)
                    }
                },
                allowExtendedAppShortcutsCustomization = allowExtendedAppShortcutsCustomization,
            )
            Spacer(modifier = Modifier.height(8.dp))
        }
        if (
            category.type == ShortcutCategoryType.AppCategories &&
                !isCustomizationModeEnabled &&
                isExtendedAppCategoryFlagEnabled
                isExtendedAppCategoryFlagEnabled &&
                allowExtendedAppShortcutsCustomization
        ) {
            item {
                ShortcutHelperButton(
@@ -595,6 +601,7 @@ private fun SubCategoryContainerDualPane(
    subCategory: ShortcutSubCategory,
    isCustomizing: Boolean,
    onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit,
    allowExtendedAppShortcutsCustomization: Boolean,
) {
    Surface(
        modifier = Modifier.fillMaxWidth(),
@@ -632,6 +639,7 @@ private fun SubCategoryContainerDualPane(
                                onShortcutCustomizationRequested(requestInfo)
                        }
                    },
                    allowExtendedAppShortcutsCustomization = allowExtendedAppShortcutsCustomization,
                )
            }
        }
@@ -652,6 +660,7 @@ private fun Shortcut(
    modifier: Modifier,
    searchQuery: String,
    shortcut: ShortcutModel,
    allowExtendedAppShortcutsCustomization: Boolean = true,
    isCustomizing: Boolean = false,
    onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
) {
@@ -692,6 +701,7 @@ private fun Shortcut(
            modifier = Modifier.weight(.666f).semantics { hideFromAccessibility() },
            shortcut = shortcut,
            isCustomizing = isCustomizing,
            allowExtendedAppShortcutsCustomization = allowExtendedAppShortcutsCustomization,
            onAddShortcutRequested = {
                onShortcutCustomizationRequested(
                    ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add(
@@ -740,6 +750,7 @@ private fun ShortcutKeyCombinations(
    modifier: Modifier = Modifier,
    shortcut: ShortcutModel,
    isCustomizing: Boolean = false,
    allowExtendedAppShortcutsCustomization: Boolean,
    onAddShortcutRequested: () -> Unit = {},
    onDeleteShortcutRequested: () -> Unit = {},
) {
@@ -761,7 +772,9 @@ private fun ShortcutKeyCombinations(
        AnimatedVisibility(visible = isCustomizing) {
            if (shortcut.containsCustomShortcutCommands) {
                DeleteShortcutButton(onDeleteShortcutRequested)
            } else {
            } else if (
                shortcut.containsDefaultShortcutCommands || allowExtendedAppShortcutsCustomization
            ) {
                AddShortcutButton(onAddShortcutRequested)
            }
        }
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ sealed interface ShortcutsUiState {
        val isExtendedAppCategoryFlagEnabled: Boolean = false,
        val shouldShowResetButton: Boolean = false,
        val isCustomizationModeEnabled: Boolean = false,
        val allowExtendedAppShortcutsCustomization: Boolean = true,
    ) : ShortcutsUiState

    data object Inactive : ShortcutsUiState
+43 −18
Original line number Diff line number Diff line
@@ -37,7 +37,12 @@ import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperSt
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.Accessibility
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp
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.ui.model.IconSource
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi
@@ -102,6 +107,10 @@ constructor(
                        isExtendedAppCategoryFlagEnabled = extendedAppsShortcutCategory(),
                        shouldShowResetButton = shouldShowResetButton(shortcutCategoriesUi),
                        isCustomizationModeEnabled = isCustomizationModeEnabled,
                        allowExtendedAppShortcutsCustomization =
                            !isExtendedAppsShortcutCustomizationLimitReached(
                                categoriesWithLauncherExcluded
                            ),
                    )
                }
            }
@@ -111,6 +120,26 @@ constructor(
                initialValue = ShortcutsUiState.Inactive,
            )

    private fun isExtendedAppsShortcutCustomizationLimitReached(
        shortcutCategories: List<ShortcutCategory>
    ): Boolean {
        val appShortcutCategory = shortcutCategories.firstOrNull { it.type == AppCategories }
        if (appShortcutCategory == null) {
            return false
        }

        return getExtendedAppsShortcutCustomizationCount(appShortcutCategory) >=
            EXTENDED_APPS_SHORTCUT_CUSTOMIZATION_LIMIT
    }

    private fun getExtendedAppsShortcutCustomizationCount(
        appsShortcutCategory: ShortcutCategory
    ): Int {
        return appsShortcutCategory.subCategories
            .flatMap { it.shortcuts }
            .count { !it.containsDefaultShortcutCommands && it.containsCustomShortcutCommands }
    }

    private fun shouldShowResetButton(categoriesUi: List<ShortcutCategoryUi>): Boolean {
        return categoriesUi.any { it.containsCustomShortcuts }
    }
@@ -129,12 +158,10 @@ constructor(

    private fun getShortcutCategoryIcon(type: ShortcutCategoryType): IconSource {
        return when (type) {
            ShortcutCategoryType.System -> IconSource(imageVector = Icons.Default.Tv)
            ShortcutCategoryType.MultiTasking ->
                IconSource(imageVector = Icons.Default.VerticalSplit)
            ShortcutCategoryType.InputMethodEditor ->
                IconSource(imageVector = Icons.Default.Keyboard)
            ShortcutCategoryType.AppCategories -> IconSource(imageVector = Icons.Default.Apps)
            System -> IconSource(imageVector = Icons.Default.Tv)
            MultiTasking -> IconSource(imageVector = Icons.Default.VerticalSplit)
            InputMethodEditor -> IconSource(imageVector = Icons.Default.Keyboard)
            AppCategories -> IconSource(imageVector = Icons.Default.Apps)
            is CurrentApp -> {
                try {
                    val iconDrawable =
@@ -149,24 +176,18 @@ constructor(
                }
            }

            ShortcutCategoryType.Accessibility ->
                IconSource(imageVector = Icons.Default.AccessibilityNew)
            Accessibility -> IconSource(imageVector = Icons.Default.AccessibilityNew)
        }
    }

    private fun getShortcutCategoryLabel(type: ShortcutCategoryType): String =
        when (type) {
            ShortcutCategoryType.System ->
                context.getString(R.string.shortcut_helper_category_system)
            ShortcutCategoryType.MultiTasking ->
                context.getString(R.string.shortcut_helper_category_multitasking)
            ShortcutCategoryType.InputMethodEditor ->
                context.getString(R.string.shortcut_helper_category_input)
            ShortcutCategoryType.AppCategories ->
                context.getString(R.string.shortcut_helper_category_app_shortcuts)
            System -> context.getString(R.string.shortcut_helper_category_system)
            MultiTasking -> context.getString(R.string.shortcut_helper_category_multitasking)
            InputMethodEditor -> context.getString(R.string.shortcut_helper_category_input)
            AppCategories -> context.getString(R.string.shortcut_helper_category_app_shortcuts)
            is CurrentApp -> getApplicationLabelForCurrentApp(type)
            ShortcutCategoryType.Accessibility ->
                context.getString(R.string.shortcutHelper_category_accessibility)
            Accessibility -> context.getString(R.string.shortcutHelper_category_accessibility)
        }

    private fun getApplicationLabelForCurrentApp(type: CurrentApp): String {
@@ -273,4 +294,8 @@ constructor(
    private fun resetCustomizationMode() {
        customizationModeInteractor.toggleCustomizationMode(false)
    }

    companion object {
        const val EXTENDED_APPS_SHORTCUT_CUSTOMIZATION_LIMIT = 10
    }
}