• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.keyboard.shortcut.domain.interactor
18 
19 import android.content.Context
20 import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Application
23 import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository
24 import com.android.systemui.keyboard.shortcut.extensions.toContentDescription
25 import com.android.systemui.keyboard.shortcut.qualifiers.CustomShortcutCategories
26 import com.android.systemui.keyboard.shortcut.qualifiers.DefaultShortcutCategories
27 import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
28 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
29 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
30 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
31 import com.android.systemui.res.R
32 import dagger.Lazy
33 import javax.inject.Inject
34 import kotlinx.coroutines.flow.Flow
35 import kotlinx.coroutines.flow.combine
36 import kotlinx.coroutines.flow.flowOf
37 
38 @SysUISingleton
39 class ShortcutHelperCategoriesInteractor
40 @Inject
41 constructor(
42     @Application private val context: Context,
43     @DefaultShortcutCategories defaultCategoriesRepository: ShortcutCategoriesRepository,
44     @CustomShortcutCategories customCategoriesRepositoryLazy: Lazy<ShortcutCategoriesRepository>,
45 ) {
46     val shortcutCategories: Flow<List<ShortcutCategory>> =
47         defaultCategoriesRepository.categories.combine(
48             if (keyboardShortcutHelperShortcutCustomizer()) {
49                 customCategoriesRepositoryLazy.get().categories
50             } else {
51                 flowOf(emptyList())
52             }
53         ) { defaultShortcutCategories, customShortcutCategories ->
54             groupCategories(defaultShortcutCategories + customShortcutCategories)
55         }
56 
57     private fun groupCategories(
58         shortcutCategories: List<ShortcutCategory>
59     ): List<ShortcutCategory> {
60         return shortcutCategories
61             .groupBy { it.type }
62             .entries
63             .map { (categoryType, groupedCategories) ->
64                 ShortcutCategory(
65                     type = categoryType,
66                     subCategories =
67                         groupSubCategories(groupedCategories.flatMap { it.subCategories }),
68                 )
69             }
70     }
71 
72     private fun groupSubCategories(
73         subCategories: List<ShortcutSubCategory>
74     ): List<ShortcutSubCategory> {
75         return subCategories
76             .groupBy { it.label }
77             .entries
78             .map { (label, groupedSubcategories) ->
79                 ShortcutSubCategory(
80                     label = label,
81                     shortcuts =
82                         groupShortcutsInSubcategory(groupedSubcategories.flatMap { it.shortcuts }),
83                 )
84             }
85     }
86 
87     private fun groupShortcutsInSubcategory(shortcuts: List<Shortcut>) =
88         shortcuts
89             .groupBy { it.label }
90             .entries
91             .map { (commonLabel, groupedShortcuts) ->
92                 groupedShortcuts[0].copy(
93                     commands = groupedShortcuts.flatMap { it.commands }.sortedBy { it.keys.size },
94                     contentDescription =
95                         toContentDescription(commonLabel, groupedShortcuts.flatMap { it.commands }),
96                 )
97             }
98 
99     private fun toContentDescription(label: String, commands: List<ShortcutCommand>): String {
100         val pressKey = context.getString(R.string.shortcut_helper_add_shortcut_dialog_placeholder)
101         val andConjunction =
102             context.getString(R.string.shortcut_helper_key_combinations_and_conjunction)
103         val orConjunction =
104             context.getString(R.string.shortcut_helper_key_combinations_or_separator)
105         return buildString {
106             append("$label, $pressKey")
107             commands.forEachIndexed { i, shortcutCommand ->
108                 if (i > 0) {
109                     append(", $orConjunction")
110                 }
111                 shortcutCommand.keys.forEachIndexed { j, shortcutKey ->
112                     if (j > 0) {
113                         append(" $andConjunction")
114                     }
115                     shortcutKey.toContentDescription(context)?.let { append(" $it") }
116                 }
117             }
118         }
119     }
120 }
121