Merge "Loading generated preview on-demand instead of keeping everything in memory" into main

This commit is contained in:
Treehugger Robot
2024-12-09 17:26:02 +00:00
committed by Android (Google) Code Review
7 changed files with 126 additions and 207 deletions
@@ -1,19 +1,8 @@
package com.android.launcher3.model;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.util.SparseArray;
import android.widget.RemoteViews;
import androidx.core.os.BuildCompat;
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo;
@@ -21,7 +10,6 @@ import com.android.launcher3.icons.IconCache;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
/**
* An wrapper over various items displayed in a widget picker,
@@ -37,11 +25,9 @@ public class WidgetItem extends ComponentKey {
public final String label;
public final CharSequence description;
public final int spanX, spanY;
public final SparseArray<RemoteViews> generatedPreviews;
public WidgetItem(LauncherAppWidgetProviderInfo info,
InvariantDeviceProfile idp, IconCache iconCache, Context context,
WidgetManagerHelper helper) {
InvariantDeviceProfile idp, IconCache iconCache, Context context) {
super(info.provider, info.getProfile());
label = iconCache.getTitleNoCache(info);
@@ -51,27 +37,6 @@ public class WidgetItem extends ComponentKey {
spanX = Math.min(info.spanX, idp.numColumns);
spanY = Math.min(info.spanY, idp.numRows);
if (BuildCompat.isAtLeastV() && Flags.enableGeneratedPreviews()) {
generatedPreviews = new SparseArray<>(3);
for (int widgetCategory : new int[] {
WIDGET_CATEGORY_HOME_SCREEN,
WIDGET_CATEGORY_KEYGUARD,
WIDGET_CATEGORY_SEARCHBOX,
}) {
if ((widgetCategory & widgetInfo.generatedPreviewCategories) != 0) {
generatedPreviews.put(widgetCategory,
helper.loadGeneratedPreview(widgetInfo, widgetCategory));
}
}
} else {
generatedPreviews = null;
}
}
public WidgetItem(LauncherAppWidgetProviderInfo info,
InvariantDeviceProfile idp, IconCache iconCache, Context context) {
this(info, idp, iconCache, context, new WidgetManagerHelper(context));
}
public WidgetItem(ShortcutConfigActivityInfo info, IconCache iconCache) {
@@ -82,7 +47,6 @@ public class WidgetItem extends ComponentKey {
widgetInfo = null;
activityInfo = info;
spanX = spanY = 1;
generatedPreviews = null;
}
/**
@@ -101,26 +65,8 @@ public class WidgetItem extends ComponentKey {
return false;
}
/** Returns whether this {@link WidgetItem} has a preview layout that can be used. */
@SuppressLint("NewApi") // Already added API check.
public boolean hasPreviewLayout() {
return widgetInfo != null && widgetInfo.previewLayout != Resources.ID_NULL;
}
/** Returns whether this {@link WidgetItem} is for a shortcut rather than an app widget. */
public boolean isShortcut() {
return activityInfo != null;
}
/**
* Returns whether this {@link WidgetItem} has a generated preview for the given widget
* category.
*/
public boolean hasGeneratedPreview(int widgetCategory) {
if (!Flags.enableGeneratedPreviews() || generatedPreviews == null) {
return false;
}
return generatedPreviews.contains(widgetCategory)
&& generatedPreviews.get(widgetCategory) != null;
}
}
@@ -149,8 +149,7 @@ public class WidgetsModel {
LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
widgetsAndShortcuts.add(new WidgetItem(
launcherWidgetInfo, idp, app.getIconCache(), app.getContext(),
widgetManager));
launcherWidgetInfo, idp, app.getIconCache(), app.getContext()));
updatedItems.add(launcherWidgetInfo);
}
@@ -213,7 +212,6 @@ public class WidgetsModel {
if (!WIDGETS_ENABLED) {
return;
}
WidgetManagerHelper widgetManager = new WidgetManagerHelper(app.getContext());
for (Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsByPackageItem.entrySet()) {
if (packageNames.contains(entry.getKey().packageName)) {
List<WidgetItem> items = entry.getValue();
@@ -226,7 +224,7 @@ public class WidgetsModel {
} else {
items.set(i, new WidgetItem(item.widgetInfo,
app.getInvariantDeviceProfile(), app.getIconCache(),
app.getContext(), widgetManager));
app.getContext()));
}
}
}
@@ -15,12 +15,15 @@
*/
package com.android.launcher3.widget;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
@@ -30,17 +33,19 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.Log;
import android.util.Size;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.core.os.BuildCompat;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShadowGenerator;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.CancellableTask;
@@ -52,20 +57,19 @@ import com.android.launcher3.widget.util.WidgetSizes;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
/** Utility class to load widget previews */
/**
* Utility class to generate widget previews
*
* Note that it no longer uses database, all previews are freshly generated
*/
public class DatabaseWidgetPreviewLoader {
private static final String TAG = "WidgetPreviewLoader";
private final Context mContext;
private final float mPreviewBoxCornerRadius;
public DatabaseWidgetPreviewLoader(Context context) {
mContext = context;
float previewCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
mPreviewBoxCornerRadius = previewCornerRadius > 0
? previewCornerRadius
: mContext.getResources().getDimension(R.dimen.widget_preview_corner_radius);
}
/**
@@ -77,10 +81,10 @@ public class DatabaseWidgetPreviewLoader {
public CancellableTask loadPreview(
@NonNull WidgetItem item,
@NonNull Size previewSize,
@NonNull Consumer<Bitmap> callback) {
@NonNull Consumer<WidgetPreviewInfo> callback) {
Handler handler = getLoaderExecutor().getHandler();
CancellableTask<Bitmap> request = new CancellableTask<>(
() -> generatePreview(item, previewSize.getWidth(), previewSize.getHeight()),
CancellableTask<WidgetPreviewInfo> request = new CancellableTask<>(
() -> generatePreviewInfoBg(item, previewSize.getWidth(), previewSize.getHeight()),
MAIN_EXECUTOR,
callback);
Utilities.postAsyncCallback(handler, request);
@@ -93,6 +97,39 @@ public class DatabaseWidgetPreviewLoader {
return Executors.UI_HELPER_EXECUTOR;
}
/** Generated the preview object. This method must be called on a background thread */
@VisibleForTesting
@NonNull
public WidgetPreviewInfo generatePreviewInfoBg(
WidgetItem item, int previewWidth, int previewHeight) {
WidgetPreviewInfo result = new WidgetPreviewInfo();
AppWidgetProviderInfo widgetInfo = item.widgetInfo;
if (BuildCompat.isAtLeastV() && Flags.enableGeneratedPreviews() && widgetInfo != null
&& ((widgetInfo.generatedPreviewCategories & WIDGET_CATEGORY_HOME_SCREEN) != 0)) {
result.remoteViews = new WidgetManagerHelper(mContext)
.loadGeneratedPreview(widgetInfo, WIDGET_CATEGORY_HOME_SCREEN);
if (result.remoteViews != null) {
result.providerInfo = widgetInfo;
}
}
if (result.providerInfo == null && widgetInfo != null
&& widgetInfo.previewLayout != Resources.ID_NULL) {
result.providerInfo = fromProviderInfo(mContext, widgetInfo.clone());
// A hack to force the initial layout to be the preview layout since there is no API for
// rendering a preview layout for work profile apps yet. For non-work profile layout, a
// proper solution is to use RemoteViews(PackageName, LayoutId).
result.providerInfo.initialLayout = item.widgetInfo.previewLayout;
}
if (result.providerInfo == null) {
// fallback to bitmap preview
result.previewBitmap = generatePreview(item, previewWidth, previewHeight);
}
return result;
}
/**
* Returns a generated preview for a widget and if the preview should be saved in persistent
* storage.
@@ -232,21 +269,6 @@ public class DatabaseWidgetPreviewLoader {
});
}
private RectF drawBoxWithShadow(Canvas c, int width, int height) {
Resources res = mContext.getResources();
ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.WHITE);
builder.shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
builder.radius = mPreviewBoxCornerRadius;
builder.keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
builder.bounds.set(builder.shadowBlur, builder.shadowBlur,
width - builder.shadowBlur,
height - builder.shadowBlur - builder.keyShadowDistance);
builder.drawShadow(c);
return builder.bounds;
}
private Bitmap generateShortcutPreview(
ShortcutConfigActivityInfo info, int maxWidth, int maxHeight) {
int iconSize = ActivityContext.lookupContext(mContext).getDeviceProfile().allAppsIconSizePx;
@@ -280,4 +302,15 @@ public class DatabaseWidgetPreviewLoader {
throw new RuntimeException(e);
}
}
/**
* Simple class to hold preview information
*/
public static class WidgetPreviewInfo {
public AppWidgetProviderInfo providerInfo;
public RemoteViews remoteViews;
public Bitmap previewBitmap;
}
}
@@ -123,7 +123,7 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
@Override
public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
super.setAppWidget(appWidgetId, info);
if (!mTrackingWidgetUpdate) {
if (!mTrackingWidgetUpdate && appWidgetId != -1) {
mTrackingWidgetUpdate = true;
Trace.beginAsyncSection(TRACE_METHOD_NAME + info.provider, appWidgetId);
Log.i(TAG, "App widget created with id: " + appWidgetId);
@@ -254,16 +254,6 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
mLongPressHelper.cancelLongPress();
}
@Override
public AppWidgetProviderInfo getAppWidgetInfo() {
AppWidgetProviderInfo info = super.getAppWidgetInfo();
if (info != null && !(info instanceof LauncherAppWidgetProviderInfo)) {
throw new IllegalStateException("Launcher widget must have"
+ " LauncherAppWidgetProviderInfo");
}
return info;
}
@Override
public void getFocusedRect(Rect r) {
super.getFocusedRect(r);
@@ -16,18 +16,17 @@
package com.android.launcher3.widget;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -48,13 +47,10 @@ import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedPropertySetter;
@@ -65,11 +61,10 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.DatabaseWidgetPreviewLoader.WidgetPreviewInfo;
import com.android.launcher3.widget.picker.util.WidgetPreviewContainerSize;
import com.android.launcher3.widget.util.WidgetSizes;
import java.util.function.Consumer;
/**
* Represents the individual cell of the widget inside the widget tray. The preview is drawn
* horizontally centered, and scaled down if needed.
@@ -242,17 +237,6 @@ public class WidgetCell extends LinearLayout {
* Applies the item to this view
*/
public void applyFromCellItem(WidgetItem item) {
applyFromCellItem(item, this::applyPreview, /*cachedPreview=*/null);
}
/**
* Applies the item to this view
* @param item item to apply
* @param callback callback when preview is loaded in case the preview is being loaded or cached
* @param cachedPreview previously cached preview bitmap is present
*/
public void applyFromCellItem(WidgetItem item, @NonNull Consumer<Bitmap> callback,
@Nullable Bitmap cachedPreview) {
Context context = getContext();
mItem = item;
mWidgetSize = getWidgetItemSizePx(getContext(), mActivity.getDeviceProfile(), mItem);
@@ -283,37 +267,28 @@ public class WidgetCell extends LinearLayout {
}
if (mRemoteViewsPreview != null) {
mAppWidgetHostViewPreview = createAppWidgetHostView(context);
setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, item.widgetInfo,
mRemoteViewsPreview);
} else if (Flags.enableGeneratedPreviews()
&& item.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)) {
mAppWidgetHostViewPreview = createAppWidgetHostView(context);
setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, item.widgetInfo,
item.generatedPreviews.get(WIDGET_CATEGORY_HOME_SCREEN));
} else if (item.hasPreviewLayout()) {
// If the context is a Launcher activity, DragView will show mAppWidgetHostViewPreview
// as a preview during drag & drop. And thus, we should use LauncherAppWidgetHostView,
// which supports applying local color extraction during drag & drop.
mAppWidgetHostViewPreview = isLauncherContext(context)
? new LauncherAppWidgetHostView(context)
: createAppWidgetHostView(context);
LauncherAppWidgetProviderInfo providerInfo =
fromProviderInfo(context, item.widgetInfo.clone());
// A hack to force the initial layout to be the preview layout since there is no API for
// rendering a preview layout for work profile apps yet. For non-work profile layout, a
// proper solution is to use RemoteViews(PackageName, LayoutId).
providerInfo.initialLayout = item.widgetInfo.previewLayout;
setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, providerInfo, null);
} else if (cachedPreview != null) {
applyPreview(cachedPreview);
WidgetPreviewInfo previewInfo = new WidgetPreviewInfo();
previewInfo.providerInfo = item.widgetInfo;
previewInfo.remoteViews = mRemoteViewsPreview;
applyPreview(previewInfo);
} else {
if (mActiveRequest == null) {
mActiveRequest = mWidgetPreviewLoader.loadPreview(mItem, mWidgetSize, callback);
mActiveRequest = mWidgetPreviewLoader.loadPreview(
mItem, mWidgetSize, this::applyPreview);
}
}
}
private void applyPreview(WidgetPreviewInfo previewInfo) {
if (previewInfo.providerInfo != null) {
mAppWidgetHostViewPreview = createAppWidgetHostView(getContext());
setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, previewInfo.providerInfo,
previewInfo.remoteViews);
} else {
applyBitmapPreview(previewInfo.previewBitmap);
}
}
private void initPreviewContainerSizeAndScale() {
WidgetPreviewContainerSize previewSize = WidgetPreviewContainerSize.Companion.forItem(mItem,
mActivity.getDeviceProfile());
@@ -337,7 +312,7 @@ public class WidgetCell extends LinearLayout {
private void setAppWidgetHostViewPreview(
NavigableAppWidgetHostView appWidgetHostViewPreview,
LauncherAppWidgetProviderInfo providerInfo,
AppWidgetProviderInfo providerInfo,
@Nullable RemoteViews remoteViews) {
appWidgetHostViewPreview.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
appWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, providerInfo);
@@ -349,7 +324,7 @@ public class WidgetCell extends LinearLayout {
mWidgetSize.getWidth(), mWidgetSize.getHeight(), Gravity.CENTER);
mWidgetImageContainer.addView(appWidgetHostViewPreview, /* index= */ 0, widgetHostLP);
mWidgetImage.setVisibility(View.GONE);
applyPreview(null);
applyBitmapPreview(null);
appWidgetHostViewPreview.addOnLayoutChangeListener(
(v, l, t, r, b, ol, ot, or, ob) ->
@@ -407,7 +382,7 @@ public class WidgetCell extends LinearLayout {
mAnimatePreview = shouldAnimate;
}
private void applyPreview(Bitmap bitmap) {
private void applyBitmapPreview(Bitmap bitmap) {
if (bitmap != null) {
Drawable drawable = new RoundDrawableWrapper(
new FastBitmapDrawable(bitmap), mEnforcedCornerRadius);
@@ -496,8 +471,8 @@ public class WidgetCell extends LinearLayout {
mLongPressHelper.cancelLongPress();
}
private static NavigableAppWidgetHostView createAppWidgetHostView(Context context) {
return new NavigableAppWidgetHostView(context) {
private static LauncherAppWidgetHostView createAppWidgetHostView(Context context) {
return new LauncherAppWidgetHostView(context) {
@Override
protected boolean shouldAllowDirectClick() {
return false;
@@ -505,10 +480,6 @@ public class WidgetCell extends LinearLayout {
};
}
private static boolean isLauncherContext(Context context) {
return ActivityContext.lookupContext(context) instanceof Launcher;
}
@Override
public CharSequence getAccessibilityClassName() {
return WidgetCell.class.getName();
@@ -1,9 +1,8 @@
package com.android.launcher3.widget
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
import android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
import android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX
import android.content.ComponentName
import android.content.Context
import android.content.pm.ActivityInfo
@@ -14,23 +13,30 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.widget.RemoteViews
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.Flags.FLAG_ENABLE_GENERATED_PREVIEWS
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.R
import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.IconProvider
import com.android.launcher3.model.WidgetItem
import com.android.launcher3.util.ActivityContextWrapper
import com.android.launcher3.util.Executors
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -46,28 +52,25 @@ class GeneratedPreviewTest {
getInstrumentation().context.run {
resources.getIdentifier("test_layout_appwidget_blue", "layout", packageName)
}
private lateinit var context: Context
private lateinit var context: SandboxModelContext
private lateinit var uiContext: Context
private lateinit var generatedPreview: RemoteViews
private lateinit var widgetCell: WidgetCell
private lateinit var helper: WidgetManagerHelper
private lateinit var appWidgetProviderInfo: LauncherAppWidgetProviderInfo
private lateinit var widgetItem: WidgetItem
private lateinit var iconCache: IconCache
@Mock lateinit var iconCache: IconCache
@Before
fun setup() {
context = getApplicationContext()
MockitoAnnotations.initMocks(this)
context = SandboxModelContext()
generatedPreview = RemoteViews(context.packageName, generatedPreviewLayout)
uiContext =
ActivityContextWrapper(ContextThemeWrapper(context, R.style.WidgetContainerTheme))
widgetCell =
LayoutInflater.from(
ActivityContextWrapper(
ContextThemeWrapper(
context,
com.android.launcher3.R.style.WidgetContainerTheme,
)
)
)
.inflate(com.android.launcher3.R.layout.widget_cell, null) as WidgetCell
LayoutInflater.from(uiContext).inflate(R.layout.widget_cell, null) as WidgetCell
appWidgetProviderInfo =
AppWidgetProviderInfo()
.apply {
@@ -76,72 +79,52 @@ class GeneratedPreviewTest {
providerInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
}
.let { LauncherAppWidgetProviderInfo.fromProviderInfo(context, it) }
helper =
object : WidgetManagerHelper(context) {
override fun loadGeneratedPreview(
info: AppWidgetProviderInfo,
widgetCategory: Int,
) =
generatedPreview.takeIf {
info === appWidgetProviderInfo &&
widgetCategory == WIDGET_CATEGORY_HOME_SCREEN
}
val widgetManager = context.spyService(AppWidgetManager::class.java)
doAnswer { i ->
generatedPreview.takeIf {
i.arguments[0] == appWidgetProviderInfo.provider &&
i.arguments[1] == appWidgetProviderInfo.user &&
i.arguments[2] == WIDGET_CATEGORY_HOME_SCREEN
}
}
.whenever(widgetManager)
.getWidgetPreview(any(), any(), any())
createWidgetItem()
}
@After
fun tearDown() {
iconCache.close()
context.destroy()
}
private fun createWidgetItem() {
Executors.MODEL_EXECUTOR.submit {
val idp = InvariantDeviceProfile()
if (::iconCache.isInitialized) iconCache.close()
iconCache = IconCache(context, idp, null, IconProvider(context))
widgetItem = WidgetItem(appWidgetProviderInfo, idp, iconCache, context, helper)
widgetItem = WidgetItem(appWidgetProviderInfo, idp, iconCache, context)
}
.get()
}
@Test
fun widgetItem_hasGeneratedPreview() {
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isTrue()
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
}
@Test
fun widgetItem_hasGeneratedPreview_noPreview() {
appWidgetProviderInfo.generatedPreviewCategories = 0
createWidgetItem()
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isFalse()
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
}
@Test
fun widgetItem_hasGeneratedPreview_nullPreview() {
appWidgetProviderInfo.generatedPreviewCategories =
WIDGET_CATEGORY_HOME_SCREEN or WIDGET_CATEGORY_KEYGUARD
createWidgetItem()
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isTrue()
// loadGeneratedPreview returns null for KEYGUARD, so this should still be false.
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
val preview = DatabaseWidgetPreviewLoader(uiContext).generatePreviewInfoBg(widgetItem, 1, 1)
assertThat(preview.remoteViews).isNull()
}
@Test
fun widgetItem_getGeneratedPreview() {
val preview = widgetItem.generatedPreviews.get(WIDGET_CATEGORY_HOME_SCREEN)
assertThat(preview).isEqualTo(generatedPreview)
val preview = DatabaseWidgetPreviewLoader(uiContext).generatePreviewInfoBg(widgetItem, 1, 1)
assertThat(preview.remoteViews).isEqualTo(generatedPreview)
}
@Test
fun widgetCell_showGeneratedPreview() {
widgetCell.applyFromCellItem(widgetItem)
DatabaseWidgetPreviewLoader.getLoaderExecutor().submit {}.get()
TestUtil.runOnExecutorSync(DatabaseWidgetPreviewLoader.getLoaderExecutor()) {}
TestUtil.runOnExecutorSync(MAIN_EXECUTOR) {}
assertThat(widgetCell.appWidgetHostViewPreview).isNotNull()
assertThat(widgetCell.appWidgetHostViewPreview?.appWidgetInfo)
.isEqualTo(appWidgetProviderInfo)
@@ -54,7 +54,6 @@ import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetManagerHelper;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import org.junit.Before;
@@ -143,7 +142,6 @@ public final class WidgetsListTableViewHolderBinderTest {
}
private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
WidgetManagerHelper widgetManager = new WidgetManagerHelper(mContext);
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
for (int i = 0; i < numOfWidgets; i++) {
ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
@@ -151,7 +149,7 @@ public final class WidgetsListTableViewHolderBinderTest {
widgetItems.add(new WidgetItem(
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
mTestProfile, mIconCache, mContext, widgetManager));
mTestProfile, mIconCache, mContext));
}
return widgetItems;
}