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.car.carlauncher.datasources
18 
19 import android.content.BroadcastReceiver
20 import android.content.Context
21 import android.content.Intent
22 import android.content.IntentFilter
23 import android.content.pm.LauncherActivityInfo
24 import android.content.pm.LauncherApps
25 import android.content.res.Resources
26 import android.os.UserHandle
27 import com.android.car.carlauncher.R
28 import kotlinx.coroutines.CoroutineDispatcher
29 import kotlinx.coroutines.Dispatchers
30 import kotlinx.coroutines.channels.awaitClose
31 import kotlinx.coroutines.flow.Flow
32 import kotlinx.coroutines.flow.callbackFlow
33 import kotlinx.coroutines.flow.conflate
34 import kotlinx.coroutines.flow.flowOn
35 import kotlinx.coroutines.withContext
36 
37 interface LauncherActivitiesDataSource {
38 
39     /**
40      * Gets all the Launchable activities for the user.
41      */
getAllLauncherActivitiesnull42     suspend fun getAllLauncherActivities(): List<LauncherActivityInfo>
43 
44     /**
45      * Flow notifying changes if packages are changed.
46      */
47     fun getOnPackagesChanged(): Flow<String>
48 
49     /**
50      * Get packages to hide explicitly
51      */
52     fun getAppsToHide(): List<String>
53 
54     companion object {
55         val TAG: String = LauncherActivitiesDataSource::class.java.simpleName
56     }
57 }
58 
59 /**
60  * Impl of [LauncherActivitiesDataSource] to surface all the launcher activities apis.
61  * All the operations in this class are non blocking.
62  *
63  * @property [launcherApps] Used to fetch launcher activities.
64  * @property [registerReceiverFunction] Function to register the broadcast receiver.
65  * Should be provided by the Android Component owning the [Context]
66  * @property [unregisterReceiverFunction] Function to unregister the broadcast receiver.
67  * Should be provided by the Android Component owning the [Context]
68  * @property [userHandle] Specified user's handle to fetch launcher activities.
69  * @param [resources] Application resources, not bound to activity's configuration changes.
70  * @property [bgDispatcher] Executes all the operations on this background coroutine dispatcher.
71  */
72 class LauncherActivitiesDataSourceImpl(
73     private val launcherApps: LauncherApps,
74     private val registerReceiverFunction: (BroadcastReceiver, IntentFilter) -> Unit,
75     private val unregisterReceiverFunction: (BroadcastReceiver) -> Unit,
76     private val userHandle: UserHandle,
77     val resources: Resources,
78     private val bgDispatcher: CoroutineDispatcher = Dispatchers.Default
79 ) : LauncherActivitiesDataSource {
80 
81     private val listOfApps = resources.getStringArray(R.array.hidden_apps).toList()
82 
83     /**
84      * Gets all launcherActivities for a user with [userHandle]
85      */
getAllLauncherActivitiesnull86     override suspend fun getAllLauncherActivities(): List<LauncherActivityInfo> {
87         return withContext(bgDispatcher) {
88             launcherApps.getActivityList(
89                 /* packageName = */
90                 null,
91                 userHandle
92             )
93         }
94     }
95 
96     /**
97      * Gets a flow Producer which report changes in the packages with following actions:
98      * [Intent.ACTION_PACKAGE_ADDED], [Intent.ACTION_PACKAGE_CHANGED],
99      * [Intent.ACTION_PACKAGE_REPLACED] or [Intent.ACTION_PACKAGE_REMOVED].
100      *
101      * Note: The producer sends an `Empty String` initially. This immediately tells the collector
102      * that there are no changes as of now with packages.
103      *
104      * When the scope in which this flow is collected is closed/canceled
105      * [unregisterReceiverFunction] is triggered.
106      */
getOnPackagesChangednull107     override fun getOnPackagesChanged(): Flow<String> {
108         return callbackFlow {
109             trySend("")
110             val filter = IntentFilter()
111             filter.addAction(Intent.ACTION_PACKAGE_ADDED)
112             filter.addAction(Intent.ACTION_PACKAGE_CHANGED)
113             filter.addAction(Intent.ACTION_PACKAGE_REPLACED)
114             filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
115             filter.addDataScheme("package")
116             val receiver = object : BroadcastReceiver() {
117                 override fun onReceive(context: Context?, intent: Intent?) {
118                     val packageName = intent?.data?.schemeSpecificPart
119                     if (packageName.isNullOrBlank()) {
120                         return
121                     }
122                     trySend(packageName)
123                 }
124             }
125             registerReceiverFunction(receiver, filter)
126             awaitClose {
127                 unregisterReceiverFunction(receiver)
128             }
129         }.flowOn(bgDispatcher).conflate()
130     }
131 
132     /**
133      * Gets packages that are explicitly required to be hidden.
134      *
135      * * Note: This packages are defined in [Resources] by name __hidden_apps__
136      */
getAppsToHidenull137     override fun getAppsToHide(): List<String> {
138         return listOfApps
139     }
140 }
141