1 /* 2 * Copyright (C) 2025 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.data.repository 18 19 import android.content.Context 20 import android.content.pm.LauncherActivityInfo 21 import android.content.pm.LauncherApps 22 import android.os.Handler 23 import android.os.UserHandle 24 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 25 import com.android.systemui.dagger.SysUISingleton 26 import com.android.systemui.dagger.qualifiers.Background 27 import com.android.systemui.settings.UserTracker 28 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow 29 import java.util.concurrent.Executor 30 import javax.inject.Inject 31 import kotlinx.coroutines.channels.awaitClose 32 import kotlinx.coroutines.flow.Flow 33 34 @SysUISingleton 35 class UserVisibleAppsRepository 36 @Inject 37 constructor( 38 private val userTracker: UserTracker, 39 @Background private val bgExecutor: Executor, 40 @Background private val bgHandler: Handler, 41 private val launcherApps: LauncherApps, 42 ) { 43 44 val userVisibleApps: Flow<List<LauncherActivityInfo>> <lambda>null45 get() = conflatedCallbackFlow { 46 val packageChangeCallback: LauncherApps.Callback = 47 object : LauncherApps.Callback() { 48 override fun onPackageAdded(packageName: String, userHandle: UserHandle) { 49 trySendWithFailureLogging( 50 element = retrieveLauncherApps(), 51 loggingTag = TAG, 52 elementDescription = ON_PACKAGE_ADDED, 53 ) 54 } 55 56 override fun onPackageChanged(packageName: String, userHandle: UserHandle) { 57 trySendWithFailureLogging( 58 element = retrieveLauncherApps(), 59 loggingTag = TAG, 60 elementDescription = ON_PACKAGE_CHANGED, 61 ) 62 } 63 64 override fun onPackageRemoved(packageName: String, userHandle: UserHandle) { 65 trySendWithFailureLogging( 66 element = retrieveLauncherApps(), 67 loggingTag = TAG, 68 elementDescription = ON_PACKAGE_REMOVED, 69 ) 70 } 71 72 override fun onPackagesAvailable( 73 packages: Array<out String>, 74 userHandle: UserHandle, 75 replacing: Boolean, 76 ) { 77 trySendWithFailureLogging( 78 element = retrieveLauncherApps(), 79 loggingTag = TAG, 80 elementDescription = ON_PACKAGES_AVAILABLE, 81 ) 82 } 83 84 override fun onPackagesUnavailable( 85 packages: Array<out String>, 86 userHandle: UserHandle, 87 replacing: Boolean, 88 ) { 89 trySendWithFailureLogging( 90 element = retrieveLauncherApps(), 91 loggingTag = TAG, 92 elementDescription = ON_PACKAGES_UNAVAILABLE, 93 ) 94 } 95 } 96 97 val userChangeCallback = 98 object : UserTracker.Callback { 99 override fun onUserChanged(newUser: Int, userContext: Context) { 100 trySendWithFailureLogging( 101 element = retrieveLauncherApps(), 102 loggingTag = TAG, 103 elementDescription = ON_USER_CHANGED, 104 ) 105 } 106 } 107 108 userTracker.addCallback(userChangeCallback, bgExecutor) 109 launcherApps.registerCallback(packageChangeCallback, bgHandler) 110 111 trySendWithFailureLogging( 112 element = retrieveLauncherApps(), 113 loggingTag = TAG, 114 elementDescription = INITIAL_VALUE, 115 ) 116 117 awaitClose { 118 userTracker.removeCallback(userChangeCallback) 119 launcherApps.unregisterCallback(packageChangeCallback) 120 } 121 } 122 retrieveLauncherAppsnull123 private fun retrieveLauncherApps(): List<LauncherActivityInfo> { 124 return launcherApps.getActivityList(/* packageName= */ null, userTracker.userHandle) 125 } 126 127 private companion object { 128 const val TAG = "UserVisibleAppsRepository" 129 const val ON_PACKAGE_ADDED = "onPackageAdded" 130 const val ON_PACKAGE_CHANGED = "onPackageChanged" 131 const val ON_PACKAGE_REMOVED = "onPackageRemoved" 132 const val ON_PACKAGES_AVAILABLE = "onPackagesAvailable" 133 const val ON_PACKAGES_UNAVAILABLE = "onPackagesUnavailable" 134 const val ON_USER_CHANGED = "onUserChanged" 135 const val INITIAL_VALUE = "InitialValue" 136 } 137 } 138