diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt index 7fd100fd13980..6c41caab38a84 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt @@ -19,6 +19,7 @@ package com.android.systemui.mediaprojection.appselector import android.app.Activity import android.content.ComponentName import android.content.Context +import com.android.launcher3.icons.IconFactory import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.MediaProjectionAppSelectorActivity import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader @@ -91,6 +92,11 @@ interface MediaProjectionAppSelectorModule { activity: MediaProjectionAppSelectorActivity ): ConfigurationController = ConfigurationControllerImpl(activity) + @Provides + fun bindIconFactory( + context: Context + ): IconFactory = IconFactory.obtain(context) + @Provides @MediaProjectionAppSelector @MediaProjectionAppSelectorScope diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt index 0927f3b007244..b85d6285c35bf 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt @@ -19,13 +19,14 @@ package com.android.systemui.mediaprojection.appselector.data import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager -import android.content.pm.PackageManager.ComponentInfoFlags import android.graphics.drawable.Drawable import android.os.UserHandle import com.android.launcher3.icons.BaseIconFactory.IconOptions import com.android.launcher3.icons.IconFactory import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.shared.system.PackageManagerWrapper import javax.inject.Inject +import javax.inject.Provider import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext @@ -38,14 +39,18 @@ class IconLoaderLibAppIconLoader constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, private val context: Context, - private val packageManager: PackageManager + // Use wrapper to access hidden API that allows to get ActivityInfo for any user id + private val packageManagerWrapper: PackageManagerWrapper, + private val packageManager: PackageManager, + private val iconFactoryProvider: Provider ) : AppIconLoader { override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? = withContext(backgroundDispatcher) { - IconFactory.obtain(context).use { iconFactory -> - val activityInfo = packageManager - .getActivityInfo(component, ComponentInfoFlags.of(0)) + iconFactoryProvider.get().use { iconFactory -> + val activityInfo = + packageManagerWrapper.getActivityInfo(component, userId) + ?: return@withContext null val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null val userHandler = UserHandle.of(userId) val options = IconOptions().apply { setUser(userHandler) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt new file mode 100644 index 0000000000000..9b346d0120efe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector.data + +import android.content.ComponentName +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import androidx.test.filters.SmallTest +import com.android.launcher3.icons.BitmapInfo +import com.android.launcher3.icons.FastBitmapDrawable +import com.android.launcher3.icons.IconFactory +import com.android.systemui.SysuiTestCase +import com.android.systemui.shared.system.PackageManagerWrapper +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class IconLoaderLibAppIconLoaderTest : SysuiTestCase() { + + private val iconFactory: IconFactory = mock() + private val packageManagerWrapper: PackageManagerWrapper = mock() + private val packageManager: PackageManager = mock() + private val dispatcher = Dispatchers.Unconfined + + private val appIconLoader = + IconLoaderLibAppIconLoader( + backgroundDispatcher = dispatcher, + context = context, + packageManagerWrapper = packageManagerWrapper, + packageManager = packageManager, + iconFactoryProvider = { iconFactory } + ) + + @Test + fun loadIcon_loadsIconUsingTheSameUserId() { + val icon = createIcon() + val component = ComponentName("com.test", "TestApplication") + givenIcon(component, userId = 123, icon = icon) + + val loadedIcon = runBlocking { appIconLoader.loadIcon(userId = 123, component = component) } + + assertThat(loadedIcon).isEqualTo(icon) + } + + private fun givenIcon(component: ComponentName, userId: Int, icon: FastBitmapDrawable) { + val activityInfo = mock() + whenever(packageManagerWrapper.getActivityInfo(component, userId)).thenReturn(activityInfo) + val rawIcon = mock() + whenever(activityInfo.loadIcon(packageManager)).thenReturn(rawIcon) + + val bitmapInfo = mock() + whenever(iconFactory.createBadgedIconBitmap(eq(rawIcon), any())).thenReturn(bitmapInfo) + whenever(bitmapInfo.newIcon(context)).thenReturn(icon) + } + + private fun createIcon(): FastBitmapDrawable = + FastBitmapDrawable(Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)) +}