• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.util.icons
18 
19 import android.content.Intent
20 import android.content.pm.PackageInfo
21 import android.content.pm.PackageManager
22 import android.graphics.drawable.Icon
23 import android.os.RemoteException
24 import android.util.Log
25 import com.android.systemui.assist.AssistManager
26 import com.android.systemui.dagger.qualifiers.Background
27 import com.android.systemui.shared.system.PackageManagerWrapper
28 import javax.inject.Inject
29 import kotlinx.coroutines.CoroutineDispatcher
30 import kotlinx.coroutines.withContext
31 
32 interface AppCategoryIconProvider {
33     /** Returns the [Icon] of the default assistant app, if it exists. */
assistantAppIconnull34     suspend fun assistantAppIcon(): Icon?
35 
36     /**
37      * Returns the [Icon] of the default app of [category], if it exists. Category can be for
38      * example [Intent.CATEGORY_APP_EMAIL] or [Intent.CATEGORY_APP_CALCULATOR].
39      */
40     suspend fun categoryAppIcon(category: String): Icon?
41 }
42 
43 class AppCategoryIconProviderImpl
44 @Inject
45 constructor(
46     @Background private val backgroundDispatcher: CoroutineDispatcher,
47     private val assistManager: AssistManager,
48     private val packageManager: PackageManager,
49     private val packageManagerWrapper: PackageManagerWrapper,
50 ) : AppCategoryIconProvider {
51 
52     override suspend fun assistantAppIcon(): Icon? {
53         val assistInfo = assistManager.assistInfo ?: return null
54         return getPackageIcon(assistInfo.packageName)
55     }
56 
57     override suspend fun categoryAppIcon(category: String): Icon? {
58         val intent = Intent(Intent.ACTION_MAIN).apply { addCategory(category) }
59         val packageInfo = getPackageInfo(intent) ?: return null
60         return getPackageIcon(packageInfo.packageName)
61     }
62 
63     private suspend fun getPackageInfo(intent: Intent): PackageInfo? =
64         withContext(backgroundDispatcher) {
65             val packageName =
66                 packageManagerWrapper
67                     .resolveActivity(/* intent= */ intent, /* flags= */ 0)
68                     ?.activityInfo
69                     ?.packageName ?: return@withContext null
70             return@withContext getPackageInfo(packageName)
71         }
72 
73     private suspend fun getPackageIcon(packageName: String): Icon? {
74         val appInfo = getPackageInfo(packageName)?.applicationInfo ?: return null
75         return if (appInfo.icon != 0) {
76             Icon.createWithResource(appInfo.packageName, appInfo.icon)
77         } else {
78             null
79         }
80     }
81 
82     private suspend fun getPackageInfo(packageName: String): PackageInfo? =
83         withContext(backgroundDispatcher) {
84             try {
85                 return@withContext packageManager.getPackageInfo(packageName, /* flags= */ 0)
86             } catch (e: RemoteException) {
87                 Log.e(TAG, "Failed to retrieve package info for $packageName")
88                 return@withContext null
89             }
90         }
91 
92     companion object {
93         private const val TAG = "DefaultAppsIconProvider"
94     }
95 }
96