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