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