From a1cebfa0c017eca7f114043b7601cd1a76115f01 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 20 Sep 2024 10:16:58 -0700 Subject: [PATCH] Updating IconProvider API to use a single API to load icons Bug: 366237794 Test: atest IconProviderTest Flag: EXEMPT refactor, changes in underlying API Change-Id: If8c6a4f13de7e7d262ee0b6d0c6897981f75e639 --- .../com/android/quickstep/TaskIconCache.java | 2 +- src/com/android/launcher3/Utilities.java | 3 +- .../android/launcher3/icons/IconCache.java | 5 - .../pm/ShortcutConfigActivityInfo.java | 2 +- .../widget/DatabaseWidgetPreviewLoader.java | 4 +- .../widget/LauncherAppWidgetProviderInfo.java | 2 +- tests/AndroidManifest-common.xml | 30 +++ tests/res/drawable/test_app_info_icon.xml | 22 ++ .../drawable/test_different_activity_icon.xml | 22 ++ .../res/drawable/test_wrong_activity_icon.xml | 22 ++ .../launcher3/icons/IconProviderTest.kt | 207 ++++++++++++++++++ 11 files changed, 309 insertions(+), 12 deletions(-) create mode 100644 tests/res/drawable/test_app_info_icon.xml create mode 100644 tests/res/drawable/test_different_activity_icon.xml create mode 100644 tests/res/drawable/test_wrong_activity_icon.xml create mode 100644 tests/src/com/android/launcher3/icons/IconProviderTest.kt diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java index 1f6c02c7aa..91fa72da2d 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -226,7 +226,7 @@ public class TaskIconCache implements TaskIconDataSource, DisplayInfoChangeListe synchronized (mDefaultIcons) { if (mDefaultIconBase == null) { try (BaseIconFactory bif = getIconFactory()) { - mDefaultIconBase = bif.makeDefaultIcon(); + mDefaultIconBase = bif.makeDefaultIcon(mIconProvider); } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index f8ac48a7df..fa47449ad2 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -626,8 +626,7 @@ public final class Utilities { if (activityInfo == null) { return null; } - mainIcon = appState.getIconProvider().getIcon( - activityInfo, appState.getInvariantDeviceProfile().fillResIconDpi); + mainIcon = appState.getIconCache().getFullResIcon(activityInfo.getActivityInfo()); } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { List siList = ShortcutKey.fromItemInfo(info) .buildRequest(context) diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index e41b03dd8d..825881cb6f 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -36,7 +36,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ShortcutInfo; import android.database.Cursor; import android.database.sqlite.SQLiteException; -import android.graphics.drawable.Drawable; import android.os.Looper; import android.os.Process; import android.os.Trace; @@ -589,10 +588,6 @@ public class IconCache extends BaseIconCache { info.bitmap = packageEntry.bitmap; } - public Drawable getFullResIcon(LauncherActivityInfo info) { - return mIconProvider.getIcon(info, mIconDpi); - } - public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) { cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(), info.getAppLabel()); diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java index 351ebce087..02938d0609 100644 --- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java +++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java @@ -131,7 +131,7 @@ public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAn @Override public Drawable getFullResIcon(IconCache cache) { - return cache.getFullResIcon(mInfo); + return cache.getFullResIcon(mInfo.getActivityInfo()); } @Override diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java index ab42839e20..e1001577f4 100644 --- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java +++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java @@ -213,8 +213,8 @@ public class DatabaseWidgetPreviewLoader { // Draw icon in the center. try { - Drawable icon = LauncherAppState.getInstance(mContext).getIconCache() - .getFullResIcon(info.provider.getPackageName(), info.icon); + Drawable icon = info.getFullResIcon( + LauncherAppState.getInstance(mContext).getIconCache()); if (icon != null) { int appIconSize = dp.iconSizePx; int iconSize = (int) Math.min(appIconSize * scale, diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java index e77ba249bd..3559554607 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java @@ -223,6 +223,6 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo @Override public Drawable getFullResIcon(IconCache cache) { - return cache.getFullResIcon(provider.getPackageName(), icon); + return cache.getFullResIcon(getActivityInfo()); } } \ No newline at end of file diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index 2553cf9442..68e493d86a 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -420,6 +420,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/res/drawable/test_different_activity_icon.xml b/tests/res/drawable/test_different_activity_icon.xml new file mode 100644 index 0000000000..43d3611900 --- /dev/null +++ b/tests/res/drawable/test_different_activity_icon.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/tests/res/drawable/test_wrong_activity_icon.xml b/tests/res/drawable/test_wrong_activity_icon.xml new file mode 100644 index 0000000000..c3ae9f0a30 --- /dev/null +++ b/tests/res/drawable/test_wrong_activity_icon.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/tests/src/com/android/launcher3/icons/IconProviderTest.kt b/tests/src/com/android/launcher3/icons/IconProviderTest.kt new file mode 100644 index 0000000000..5517fce944 --- /dev/null +++ b/tests/src/com/android/launcher3/icons/IconProviderTest.kt @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2024 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.launcher3.icons + +import android.content.ComponentName +import android.content.Context +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.content.pm.LauncherActivityInfo +import android.content.pm.LauncherApps +import android.content.pm.PackageItemInfo +import android.content.pm.PackageManager +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.Drawable +import android.os.Parcel +import android.os.Parcelable.Creator +import android.os.Process +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.model.data.AppInfo +import com.android.launcher3.widget.WidgetManagerHelper +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests for IconProvider */ +@LargeTest +@RunWith(AndroidJUnit4::class) +class IconProviderTest { + + lateinit var context: Context + lateinit var pm: PackageManager + lateinit var iconProvider: IconProvider + + lateinit var testContext: Context + + @Before + fun setup() { + context = InstrumentationRegistry.getInstrumentation().targetContext + pm = context.packageManager + iconProvider = IconProvider(context) + + testContext = InstrumentationRegistry.getInstrumentation().context + } + + @Test + fun launcherActivityInfo_activity_icon() { + val icon = iconProvider.getIcon(getLauncherActivityInfo(DiffIconActivity).activityInfo) + assertNotNull(icon) + verifyIconResName(icon, ICON_DIFFERENT_ACTIVITY) + } + + @Test + fun packageActivityInfo_activity_icon() { + val icon = iconProvider.getIcon(getPackageActivityInfo(DiffIconActivity)) + assertNotNull(icon) + verifyIconResName(icon, ICON_DIFFERENT_ACTIVITY) + } + + @Test + fun launcherActivityInfo_wrong_icon() { + val ai = + getLauncherActivityInfo(WrongIconActivity) + .activityInfo + .overrideAppIcon(ActivityInfo.CREATOR) + assertEquals(ai.icon.toResourceName(), ICON_WRONG_DRAWABLE) + val icon = iconProvider.getIcon(ai) + assertNotNull(icon) + // App icon is loaded if the drawable is not found + verifyIconResName(icon, ICON_APP_INFO) + } + + @Test + fun packageActivityInfo_wrong_icon() { + val ai = getPackageActivityInfo(WrongIconActivity) + assertEquals(ai.icon.toResourceName(), ICON_WRONG_DRAWABLE) + assertNotEquals(ai.icon, 0) + val icon = iconProvider.getIcon(ai) + assertNotNull(icon) + // App icon is loaded if the drawable is not found + verifyIconResName(icon, ICON_APP_INFO) + } + + @Test + fun launcherActivityInfo_fallback_to_icon() { + val ai = + getLauncherActivityInfo(AppIconActivity) + .activityInfo + .overrideAppIcon(ActivityInfo.CREATOR) + assertEquals(ai.icon, 0) + val icon = iconProvider.getIcon(ai) + assertNotNull(icon) + // App icon is loaded if component icon is not defined + verifyIconResName(icon, ICON_APP_INFO) + } + + @Test + fun packageActivityInfo_fallback_to_icon() { + val ai = getPackageActivityInfo(AppIconActivity) + assertEquals(ai.icon, 0) + val icon = iconProvider.getIcon(ai) + assertNotNull(icon) + // App icon is loaded if component icon is not defined + verifyIconResName(icon, ICON_APP_INFO) + } + + @Test + fun applicationInfo_icon() { + val appInfo = + getLauncherActivityInfo(AppIconActivity) + .applicationInfo + .overrideAppIcon(ApplicationInfo.CREATOR) + val icon = iconProvider.getIcon(appInfo) + assertNotNull(icon) + verifyIconResName(icon, ICON_APP_INFO) + } + + @Test + fun applicationInfo_wrong_icon() { + val appInfo = + getLauncherActivityInfo(AppIconActivity) + .applicationInfo + .overrideAppIcon(ApplicationInfo.CREATOR) + appInfo.icon = 0 + + val icon = iconProvider.getIcon(appInfo) + assertNotNull(icon) + // Fallback is loaded if the drawable is defined + assertTrue(pm.isDefaultApplicationIcon(icon)) + } + + @Test + fun appwidgetProviderInfo_icon() { + val widgetInfo = + WidgetManagerHelper(context) + .findProvider(ComponentName(testContext, AppWidgetNoConfig), Process.myUserHandle()) + assertNotNull(widgetInfo) + + val icon = iconProvider.getIcon(widgetInfo.activityInfo) + assertNotNull(icon) + verifyIconResName(icon, ICON_WIDGET_NO_CONFIG) + } + + private fun verifyIconResName(icon: Drawable, resName: String) { + assertTrue(icon is AdaptiveIconDrawable) + assertEquals(resName, (icon as AdaptiveIconDrawable).sourceDrawableResId.toResourceName()) + } + + private fun Int.toResourceName() = testContext.resources.getResourceEntryName(this) + + private fun getLauncherActivityInfo(className: String): LauncherActivityInfo = + context + .getSystemService(LauncherApps::class.java)!! + .resolveActivity(getActivityIntent(className), Process.myUserHandle()) + + private fun getPackageActivityInfo(className: String): ActivityInfo = + pm.resolveActivity(getActivityIntent(className), 0)!! + .activityInfo + .overrideAppIcon(ActivityInfo.CREATOR) + + private fun PackageItemInfo.overrideAppIcon(creator: Creator): T { + // Clone the obj since it may have been cached by the system + val p = Parcel.obtain() + writeToParcel(p, 0) + p.setDataPosition(0) + val result = creator.createFromParcel(p) + p.recycle() + result.applicationInfo.icon = + testContext.resources.getIdentifier(ICON_APP_INFO, "drawable", testContext.packageName) + return result + } + + private fun getActivityIntent(className: String) = + AppInfo.makeLaunchIntent(ComponentName(testContext, className)) + + companion object { + private const val AppIconActivity = "com.android.launcher3.tests.AppIconActivity" + private const val DiffIconActivity = "com.android.launcher3.tests.DiffIconActivity" + private const val WrongIconActivity = "com.android.launcher3.tests.WrongIconActivity" + private const val AppWidgetNoConfig = + "com.android.launcher3.testcomponent.AppWidgetNoConfig" + + private const val ICON_DIFFERENT_ACTIVITY = "test_different_activity_icon" + private const val ICON_APP_INFO = "test_app_info_icon" + private const val ICON_WRONG_DRAWABLE = "test_wrong_activity_icon" + private const val ICON_WIDGET_NO_CONFIG = "test_widget_no_config_icon" + } +}