1 /* <lambda>null2 * Copyright (C) 2023 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.accessibility.data.repository 18 19 import android.annotation.SuppressLint 20 import android.content.ComponentName 21 import android.content.Context 22 import android.text.TextUtils 23 import android.util.SparseArray 24 import android.view.accessibility.AccessibilityManager 25 import androidx.annotation.GuardedBy 26 import com.android.internal.accessibility.AccessibilityShortcutController 27 import com.android.systemui.dagger.SysUISingleton 28 import com.android.systemui.dagger.qualifiers.Background 29 import com.android.systemui.qs.pipeline.shared.TileSpec 30 import com.android.systemui.qs.tiles.ColorCorrectionTile 31 import com.android.systemui.qs.tiles.ColorInversionTile 32 import com.android.systemui.qs.tiles.FontScalingTile 33 import com.android.systemui.qs.tiles.HearingDevicesTile 34 import com.android.systemui.qs.tiles.OneHandedModeTile 35 import com.android.systemui.qs.tiles.ReduceBrightColorsTile 36 import javax.inject.Inject 37 import kotlinx.coroutines.CoroutineDispatcher 38 import kotlinx.coroutines.Deferred 39 import kotlinx.coroutines.async 40 import kotlinx.coroutines.coroutineScope 41 import kotlinx.coroutines.flow.SharedFlow 42 import kotlinx.coroutines.withContext 43 44 /** Provides data related to accessibility quick setting shortcut option. */ 45 interface AccessibilityQsShortcutsRepository { 46 /** 47 * Observable for the a11y features the user chooses in the Settings app to use the quick 48 * setting option. 49 */ 50 fun a11yQsShortcutTargets(userId: Int): SharedFlow<Set<String>> 51 52 /** Notify accessibility manager that there are changes to the accessibility tiles */ 53 suspend fun notifyAccessibilityManagerTilesChanged(userContext: Context, tiles: List<TileSpec>) 54 } 55 56 @SysUISingleton 57 class AccessibilityQsShortcutsRepositoryImpl 58 @Inject 59 constructor( 60 private val manager: AccessibilityManager, 61 private val userA11yQsShortcutsRepositoryFactory: UserA11yQsShortcutsRepository.Factory, 62 @Background private val backgroundDispatcher: CoroutineDispatcher, 63 ) : AccessibilityQsShortcutsRepository { 64 companion object { 65 val TILE_SPEC_TO_COMPONENT_MAPPING = 66 mapOf( 67 ColorCorrectionTile.TILE_SPEC to 68 AccessibilityShortcutController.DALTONIZER_TILE_COMPONENT_NAME, 69 ColorInversionTile.TILE_SPEC to 70 AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME, 71 OneHandedModeTile.TILE_SPEC to 72 AccessibilityShortcutController.ONE_HANDED_TILE_COMPONENT_NAME, 73 ReduceBrightColorsTile.TILE_SPEC to 74 AccessibilityShortcutController 75 .REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME, 76 FontScalingTile.TILE_SPEC to 77 AccessibilityShortcutController.FONT_SIZE_TILE_COMPONENT_NAME, 78 HearingDevicesTile.TILE_SPEC to 79 AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME 80 ) 81 } 82 83 @GuardedBy("userA11yQsShortcutsRepositories") 84 private val userA11yQsShortcutsRepositories = SparseArray<UserA11yQsShortcutsRepository>() 85 a11yQsShortcutTargetsnull86 override fun a11yQsShortcutTargets(userId: Int): SharedFlow<Set<String>> { 87 return synchronized(userA11yQsShortcutsRepositories) { 88 if (userId !in userA11yQsShortcutsRepositories) { 89 val userA11yQsShortcutsRepository = 90 userA11yQsShortcutsRepositoryFactory.create(userId) 91 userA11yQsShortcutsRepositories.put(userId, userA11yQsShortcutsRepository) 92 } 93 userA11yQsShortcutsRepositories.get(userId).targets 94 } 95 } 96 97 @SuppressLint("MissingPermission") // android.permission.STATUS_BAR_SERVICE notifyAccessibilityManagerTilesChangednull98 override suspend fun notifyAccessibilityManagerTilesChanged( 99 userContext: Context, 100 tiles: List<TileSpec> 101 ) { 102 val newTiles = mutableListOf<ComponentName>() 103 val accessibilityTileServices = getAccessibilityTileServices(userContext) 104 tiles.forEach { tileSpec -> 105 when (tileSpec) { 106 is TileSpec.CustomTileSpec -> { 107 if (accessibilityTileServices.contains(tileSpec.componentName)) { 108 newTiles.add(tileSpec.componentName) 109 } 110 } 111 is TileSpec.PlatformTileSpec -> { 112 if (TILE_SPEC_TO_COMPONENT_MAPPING.containsKey(tileSpec.spec)) { 113 newTiles.add(TILE_SPEC_TO_COMPONENT_MAPPING[tileSpec.spec]!!) 114 } 115 } 116 TileSpec.Invalid -> { 117 // do nothing 118 } 119 } 120 } 121 122 withContext(backgroundDispatcher) { 123 manager.notifyQuickSettingsTilesChanged(userContext.userId, newTiles) 124 } 125 } 126 getAccessibilityTileServicesnull127 private suspend fun getAccessibilityTileServices(context: Context): Set<ComponentName> = 128 coroutineScope { 129 val a11yServiceTileServices: Deferred<Set<ComponentName>> = 130 async(backgroundDispatcher) { 131 manager.installedAccessibilityServiceList 132 .mapNotNull { 133 val packageName = it.resolveInfo.serviceInfo.packageName 134 val tileServiceClass = it.tileServiceName 135 136 if (!TextUtils.isEmpty(tileServiceClass)) { 137 ComponentName(packageName, tileServiceClass!!) 138 } else { 139 null 140 } 141 } 142 .toSet() 143 } 144 145 val a11yShortcutInfoTileServices: Deferred<Set<ComponentName>> = 146 async(backgroundDispatcher) { 147 manager 148 .getInstalledAccessibilityShortcutListAsUser(context, context.userId) 149 .mapNotNull { 150 val packageName = it.componentName.packageName 151 val tileServiceClass = it.tileServiceName 152 153 if (!TextUtils.isEmpty(tileServiceClass)) { 154 ComponentName(packageName, tileServiceClass!!) 155 } else { 156 null 157 } 158 } 159 .toSet() 160 } 161 162 a11yServiceTileServices.await() + a11yShortcutInfoTileServices.await() 163 } 164 } 165