Removing dependency on PackageInfo in IconCache
Bug: 363324203
Flag: EXEMPT bugfix
Test: atest IconCacheTest
atest IconCacheUpdateHandler
Change-Id: I85005ef1069960a17a0f3e7265749a8ef3004172
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
index 0469636..f542b8c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -23,6 +23,7 @@
import android.content.IIntentSender
import android.content.Intent
import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.pm.ShortcutInfo
@@ -76,7 +77,7 @@
UserManager.USER_TYPE_PROFILE_PRIVATE -> UserIconInfo.TYPE_PRIVATE
else -> UserIconInfo.TYPE_MAIN
},
- userSerialNumber.toLong()
+ userSerialNumber.toLong(),
)
}
}
@@ -110,7 +111,7 @@
)
.toBundle()
requireActivityResult = false
- }
+ },
)
else super.getAppMarketActivityIntent(packageName, user)
@@ -131,7 +132,7 @@
)
.toBundle()
requireActivityResult = false
- }
+ },
)
else null
@@ -160,7 +161,7 @@
allowlistToken: IBinder?,
finishedReceiver: IIntentReceiver?,
requiredPermission: String?,
- options: Bundle?
+ options: Bundle?,
) {
if (code != -1) {
Executors.MAIN_EXECUTOR.execute {
@@ -168,9 +169,9 @@
context,
context.getString(
R.string.set_default_home_app,
- context.getString(R.string.derived_app_name)
+ context.getString(R.string.derived_app_name),
),
- Toast.LENGTH_LONG
+ Toast.LENGTH_LONG,
)
.show()
}
@@ -183,4 +184,7 @@
context.startActivity(ProxyActivityStarter.getLaunchIntent(context, params))
}
}
+
+ override fun getApplicationInfoHash(appInfo: ApplicationInfo): String =
+ (appInfo.sourceDir?.hashCode() ?: 0).toString() + " " + appInfo.longVersionCode
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 8121e53..76dc770 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -220,7 +220,7 @@
private CancellableTask mIconLoadRequest;
- private boolean mEnableIconUpdateAnimation = false;
+ private boolean mHighResUpdateInProgress = false;
public BubbleTextView(Context context) {
this(context, null, 0);
@@ -1195,10 +1195,6 @@
}
}
- protected boolean iconUpdateAnimationEnabled() {
- return mEnableIconUpdateAnimation;
- }
-
protected void applyCompoundDrawables(Drawable icon) {
if (icon == null) {
// Icon can be null when we use the BubbleTextView for text only.
@@ -1216,7 +1212,7 @@
// If the current icon is a placeholder color, animate its update.
if (mIcon != null
&& mIcon instanceof PlaceHolderIconDrawable
- && iconUpdateAnimationEnabled()) {
+ && mHighResUpdateInProgress) {
((PlaceHolderIconDrawable) mIcon).animateIconUpdate(icon);
}
@@ -1238,7 +1234,7 @@
if (getTag() == info) {
mIconLoadRequest = null;
mDisableRelayout = true;
- mEnableIconUpdateAnimation = true;
+ mHighResUpdateInProgress = true;
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
info.bitmap.icon.prepareToDraw();
@@ -1253,7 +1249,7 @@
}
mDisableRelayout = false;
- mEnableIconUpdateAnimation = false;
+ mHighResUpdateInProgress = false;
}
}
@@ -1265,7 +1261,7 @@
mIconLoadRequest.cancel();
mIconLoadRequest = null;
}
- if (getTag() instanceof ItemInfoWithIcon) {
+ if (getTag() instanceof ItemInfoWithIcon && !mHighResUpdateInProgress) {
ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
if (info.usingLowResIcon()) {
mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
diff --git a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
index c673bb3..f8a5552 100644
--- a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
+++ b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
@@ -20,11 +20,9 @@
import android.content.Context
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
-import android.content.pm.PackageInfo
import android.content.pm.ShortcutInfo
import android.graphics.drawable.Drawable
import android.os.UserHandle
-import android.text.TextUtils
import android.util.Log
import com.android.launcher3.BuildConfig
import com.android.launcher3.LauncherAppState
@@ -35,7 +33,6 @@
import com.android.launcher3.util.ApplicationInfoWrapper
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.util.Themes
-import kotlin.math.max
/** Wrapper over ShortcutInfo to provide extra information related to ShortcutInfo */
class CacheableShortcutInfo(val shortcutInfo: ShortcutInfo, val appInfo: ApplicationInfoWrapper) {
@@ -103,13 +100,6 @@
override fun getLabel(info: CacheableShortcutInfo): CharSequence? = info.shortcutInfo.shortLabel
- override fun getDescription(info: CacheableShortcutInfo, fallback: CharSequence): CharSequence =
- info.shortcutInfo.longLabel.let { if (TextUtils.isEmpty(it)) fallback else it!! }
-
- override fun getLastUpdatedTime(info: CacheableShortcutInfo?, packageInfo: PackageInfo) =
- info?.let { max(info.shortcutInfo.lastChangedTimestamp, packageInfo.lastUpdateTime) }
- ?: packageInfo.lastUpdateTime
-
override fun getApplicationInfo(info: CacheableShortcutInfo) = info.appInfo.getInfo()
override fun loadIcon(context: Context, cache: BaseIconCache, info: CacheableShortcutInfo) =
@@ -126,4 +116,12 @@
)
} ?: BitmapInfo.LOW_RES_INFO
}
+
+ override fun getFreshnessIdentifier(
+ item: CacheableShortcutInfo,
+ provider: IconProvider,
+ ): String? =
+ item.shortcutInfo.lastChangedTimestamp.toString() +
+ "-" +
+ provider.getStateForApp(getApplicationInfo(item))
}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index ffed1e8..b6fe66a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -29,10 +29,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@@ -66,6 +63,7 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.CancellableTask;
+import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetSections;
@@ -143,15 +141,9 @@
public synchronized void updateIconsForPkg(@NonNull final String packageName,
@NonNull final UserHandle user) {
removeIconsForPkg(packageName, user);
- try {
- PackageInfo info = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- long userSerial = mUserManager.getSerialNumberForUser(user);
- for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
- addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, info, userSerial);
- }
- } catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found", e);
+ long userSerial = mUserManager.getSerialNumberForUser(user);
+ for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
+ addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, userSerial);
}
}
@@ -602,6 +594,11 @@
info.getAppLabel());
}
+ @VisibleForTesting
+ synchronized boolean isItemInDb(ComponentKey cacheKey) {
+ return getEntryFromDBLocked(cacheKey, new CacheEntry(), false);
+ }
+
/**
* Interface for receiving itemInfo with high-res icon.
*/
diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java
index c4d5f2b..78a3128 100644
--- a/src/com/android/launcher3/icons/LauncherIconProvider.java
+++ b/src/com/android/launcher3/icons/LauncherIconProvider.java
@@ -16,14 +16,18 @@
package com.android.launcher3.icons;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.Themes;
import org.xmlpull.v1.XmlPullParser;
@@ -70,6 +74,11 @@
return super.getSystemIconState() + (mSupportsIconTheme ? ",with-theme" : ",no-theme");
}
+ @Override
+ protected String getApplicationInfoHash(@NonNull ApplicationInfo appInfo) {
+ return ApiWrapper.INSTANCE.get(mContext).getApplicationInfoHash(appInfo);
+ }
+
private Map<String, ThemeData> getThemedIconMap() {
if (mThemedIconMap != null) {
return mThemedIconMap;
diff --git a/src/com/android/launcher3/util/ApiWrapper.java b/src/com/android/launcher3/util/ApiWrapper.java
index 095518c..21f91acd 100644
--- a/src/com/android/launcher3/util/ApiWrapper.java
+++ b/src/com/android/launcher3/util/ApiWrapper.java
@@ -24,6 +24,7 @@
import android.app.role.RoleManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.ColorDrawable;
@@ -32,6 +33,7 @@
import android.os.UserManager;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BuildConfig;
@@ -156,6 +158,14 @@
}
}
+ /**
+ * Returns a hash to uniquely identify a particular version of appInfo
+ */
+ public String getApplicationInfoHash(@NonNull ApplicationInfo appInfo) {
+ // The hashString in source dir changes with every install
+ return appInfo.sourceDir;
+ }
+
@Override
public void close() { }
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
index 1f0e750..519108d 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
@@ -210,6 +211,70 @@
() -> assertNull(mIconCache.getInMemoryEntryLocked(cacheKey)));
}
+ @Test
+ public void item_kept_in_db_if_nothing_changes() {
+ RoboApiWrapper.INSTANCE.initialize();
+ ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+ UserHandle user = myUserHandle();
+
+ LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+ .resolveActivity(makeLaunchIntent(cn), user);
+ assertNotNull(lai);
+
+ // Since this is a new update, there should not be any update
+ Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
+ assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
+
+ // Another update should not cause any changes
+ Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
+ assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
+ }
+
+ @Test
+ public void item_updated_in_db_if_appInfo_changes() {
+ RoboApiWrapper.INSTANCE.initialize();
+ ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+ UserHandle user = myUserHandle();
+
+ LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+ .resolveActivity(makeLaunchIntent(cn), user);
+ assertNotNull(lai);
+
+ // Since this is a new update, there should not be any update
+ Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
+ assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
+
+ // Another update should trigger an update
+ lai.getApplicationInfo().sourceDir = "some-random-source-dir";
+ Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE))
+ .containsExactly(new PackageUserKey(TEST_PACKAGE, user));
+ assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
+ }
+
+ @Test
+ public void item_removed_in_db_if_item_removed() {
+ RoboApiWrapper.INSTANCE.initialize();
+ ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+ UserHandle user = myUserHandle();
+
+ LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+ .resolveActivity(makeLaunchIntent(cn), user);
+ assertNotNull(lai);
+
+ // Since this is a new update, there should not be any update
+ Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
+ assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
+
+ // Another update should trigger an update
+ ComponentName cn2 = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY2);
+ LauncherActivityInfo lai2 = mContext.getSystemService(LauncherApps.class)
+ .resolveActivity(makeLaunchIntent(cn2), user);
+
+ Truth.assertThat(executeIconUpdate(lai2, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
+ assertFalse(mIconCache.isItemInDb(new ComponentKey(cn, user)));
+ assertTrue(mIconCache.isItemInDb(new ComponentKey(cn2, user)));
+ }
+
/**
* Executes the icon update for the provided entry and returns the updated packages
*/
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
index b54636c..8e54c94 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
@@ -17,77 +17,109 @@
package com.android.launcher3.icons
import android.content.ComponentName
-import android.content.pm.PackageInfo
-import android.database.Cursor
-import android.os.UserHandle
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.database.MatrixCursor
+import android.os.Process.myUserHandle
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.icons.cache.BaseIconCache
-import com.android.launcher3.icons.cache.CachingLogic
+import com.android.launcher3.icons.cache.BaseIconCache.IconDB
+import com.android.launcher3.icons.cache.CachedObject
+import com.android.launcher3.icons.cache.CachedObjectCachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
import com.android.launcher3.util.RoboApiWrapper
+import com.google.common.truth.Truth.assertThat
import java.util.concurrent.FutureTask
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class IconCacheUpdateHandlerTest {
- @Mock private lateinit var cursor: Cursor
- @Mock private lateinit var user: UserHandle
- @Mock private lateinit var cachingLogic: CachingLogic<String>
+ @Mock private lateinit var iconProvider: IconProvider
@Mock private lateinit var baseIconCache: BaseIconCache
- private var componentMap: HashMap<ComponentName, String> = hashMapOf()
- private var ignorePackages: Set<String> = setOf()
- private var packageInfoMap: HashMap<String, PackageInfo> = hashMapOf()
-
- private val dummyRowData =
- IconCacheRowData(
- "com.android.fake/.FakeActivity",
- System.currentTimeMillis(),
- 1,
- 1.0.toLong(),
- "stateOfConfusion",
- )
+ private var cursor: MatrixCursor? = null
+ private var cachingLogic = CachedObjectCachingLogic<BaseIconCache>(getApplicationContext())
@Before
fun setup() {
-
MockitoAnnotations.initMocks(this)
- // Load in a specific row to the database
- doReturn(0).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_COMPONENT)
- doReturn(1).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_LAST_UPDATED)
- doReturn(2).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_VERSION)
- doReturn(3).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_ROWID)
- doReturn(4).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_SYSTEM_STATE)
- doReturn(dummyRowData.component).`when`(cursor).getString(0)
- doReturn(dummyRowData.lastUpdated).`when`(cursor).getLong(1)
- doReturn(dummyRowData.version).`when`(cursor).getInt(2)
- doReturn(dummyRowData.row).`when`(cursor).getLong(3)
- doReturn(dummyRowData.systemState).`when`(cursor).getString(4)
+ doReturn(iconProvider).whenever(baseIconCache).iconProvider
+ }
+
+ @After
+ fun tearDown() {
+ cursor?.close()
}
@Test
fun `IconCacheUpdateHandler returns null if the component name is malformed`() {
- val updateHandlerUnderTest = IconCacheUpdateHandler(packageInfoMap, baseIconCache)
+ val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
+ val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
val result =
updateHandlerUnderTest.updateOrDeleteIcon(
- cursor,
- componentMap,
- ignorePackages,
- user,
+ createCursor(1, cn.flattenToString() + "#", "freshId-old"),
+ hashMapOf(cn to TestCachedObject(cn, "freshId")),
+ setOf(),
+ myUserHandle(),
cachingLogic,
)
-
- assert(result == null)
+ assertThat(result).isNull()
}
+
+ @Test
+ fun `IconCacheUpdateHandler returns null if the freshId match`() {
+ val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
+ val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
+
+ val result =
+ updateHandlerUnderTest.updateOrDeleteIcon(
+ createCursor(1, cn.flattenToString(), "freshId"),
+ hashMapOf(cn to TestCachedObject(cn, "freshId")),
+ setOf(),
+ myUserHandle(),
+ cachingLogic,
+ )
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun `IconCacheUpdateHandler returns non-null if the freshId do not match`() {
+ val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
+ val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
+ val testObj = TestCachedObject(cn, "freshId")
+
+ val result =
+ updateHandlerUnderTest.updateOrDeleteIcon(
+ createCursor(1, cn.flattenToString(), "freshId-old"),
+ hashMapOf(cn to testObj),
+ setOf(),
+ myUserHandle(),
+ cachingLogic,
+ )
+ assertThat(result).isEqualTo(testObj)
+ }
+
+ private fun createCursor(row: Long, component: String, appState: String) =
+ MatrixCursor(
+ arrayOf(IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, IconDB.COLUMN_FRESHNESS_ID)
+ )
+ .apply { addRow(arrayOf(row, component, appState)) }
+ .apply {
+ cursor = this
+ moveToNext()
+ }
}
/** Utility method to wait for the icon update handler to finish */
@@ -105,10 +137,16 @@
}
}
-data class IconCacheRowData(
- val component: String,
- val lastUpdated: Long,
- val version: Int,
- val row: Long,
- val systemState: String,
-)
+class TestCachedObject(val cn: ComponentName, val freshnessId: String) :
+ CachedObject<BaseIconCache> {
+
+ override fun getComponent() = cn
+
+ override fun getUser() = myUserHandle()
+
+ override fun getLabel(pm: PackageManager?): CharSequence? = null
+
+ override fun getApplicationInfo(): ApplicationInfo? = null
+
+ override fun getFreshnessIdentifier(iconProvider: IconProvider): String? = freshnessId
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
index ec83b8b..7484bce 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
@@ -38,8 +38,8 @@
@get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private val providerName =
ComponentName(
- "com.android.launcher3.tests",
- "com.android.launcher3.testcomponent.AppWidgetNoConfig"
+ getInstrumentation().getContext().getPackageName(),
+ "com.android.launcher3.testcomponent.AppWidgetNoConfig",
)
private val generatedPreviewLayout =
getInstrumentation().context.run {
@@ -61,7 +61,7 @@
ActivityContextWrapper(
ContextThemeWrapper(
context,
- com.android.launcher3.R.style.WidgetContainerTheme
+ com.android.launcher3.R.style.WidgetContainerTheme,
)
)
)
@@ -78,7 +78,7 @@
object : WidgetManagerHelper(context) {
override fun loadGeneratedPreview(
info: AppWidgetProviderInfo,
- widgetCategory: Int
+ widgetCategory: Int,
) =
generatedPreview.takeIf {
info === appWidgetProviderInfo &&