From 72f9943f642dd9f57ff9d25deb12dc84cf3e7bc9 Mon Sep 17 00:00:00 2001 From: "Pechetty Sravani (xWF)" Date: Wed, 29 Jan 2025 22:46:49 -0800 Subject: [PATCH] Revert "Unifying various model update callbacks into one" Revert submission 31445615-model-callbacks Reason for revert: Reverted changes: /q/submissionid:31445615-model-callbacks Change-Id: If6893a125756e5abfbb215af377576429bb1c91b --- .../taskbar/TaskbarModelCallbacks.java | 13 +- .../uioverrides/PredictedAppIcon.java | 2 +- src/com/android/launcher3/BubbleTextView.java | 131 +++++++-------- src/com/android/launcher3/Launcher.java | 19 ++- src/com/android/launcher3/ModelCallbacks.kt | 24 ++- src/com/android/launcher3/Workspace.java | 102 ++++++++++++ .../launcher3/allapps/AllAppsStore.java | 7 +- .../graphics/PreloadIconDrawable.java | 24 ++- .../android/launcher3/model/BgDataModel.java | 8 +- .../launcher3/model/CacheDataUpdatedTask.java | 24 +-- .../launcher3/model/ModelTaskController.kt | 10 +- .../model/PackageInstallStateChangedTask.java | 3 +- .../launcher3/model/PackageUpdatedTask.java | 11 +- .../launcher3/touch/ItemClickHandler.java | 15 +- .../android/launcher3/util/ApiWrapper.java | 16 -- .../util/LauncherBindableItemsContainer.java | 64 ++++++-- .../widget/PendingAppWidgetHostView.java | 154 ++++++------------ .../launcher3/ui/BubbleTextViewTest.java | 46 ------ .../LauncherBindableItemsContainerTest.kt | 137 ---------------- 19 files changed, 369 insertions(+), 441 deletions(-) delete mode 100644 tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 6815f97e16..f905c5f4fc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -25,6 +25,7 @@ import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; @@ -38,9 +39,9 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.function.Predicate; /** @@ -113,9 +114,15 @@ public class TaskbarModelCallbacks implements return modified; } + @Override - public void bindItemsUpdated(Set updates) { - updateContainerItems(updates, mContext); + public void bindWorkspaceItemsChanged(List updated) { + updateWorkspaceItems(updated, mContext); + } + + @Override + public void bindRestoreItemsChange(HashSet updates) { + updateRestoreItems(updates, mContext); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 9b7bb1c9cd..a85e5e0049 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -214,7 +214,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { boolean animate = shouldAnimateIconChange(info); Drawable oldIcon = getIcon(); int oldPlateColor = mPlateColor.currentColor; - applyFromWorkspaceItem(info); + applyFromWorkspaceItem(info, null); setContentDescription( mIsPinned ? info.contentDescription : diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index d3684b25e6..315096c841 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -29,7 +29,6 @@ import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; -import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -64,7 +63,6 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.TextView; import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; @@ -368,6 +366,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotScaleAnim.start(); } + @UiThread + public void applyFromWorkspaceItem(WorkspaceItemInfo info) { + applyFromWorkspaceItem(info, null); + } + @Override public void setAccessibilityDelegate(AccessibilityDelegate delegate) { if (delegate instanceof BaseAccessibilityDelegate) { @@ -381,10 +384,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } @UiThread - public void applyFromWorkspaceItem(WorkspaceItemInfo info) { + public void applyFromWorkspaceItem(WorkspaceItemInfo info, PreloadIconDrawable icon) { applyIconAndLabel(info); setItemInfo(info); - + applyLoadingState(icon); applyDotState(info, false /* animate */); setDownloadStateContentDescription(info, info.getProgressLevel()); } @@ -392,11 +395,17 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @UiThread public void applyFromApplicationInfo(AppInfo info) { applyIconAndLabel(info); + + // We don't need to check the info since it's not a WorkspaceItemInfo setItemInfo(info); + // Verify high res immediately verifyHighRes(); + if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) { + applyProgressLevel(); + } applyDotState(info, false /* animate */); setDownloadStateContentDescription(info, info.getProgressLevel()); } @@ -440,50 +449,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @VisibleForTesting @UiThread public void applyIconAndLabel(ItemInfoWithIcon info) { - FastBitmapDrawable oldIcon = mIcon; - if (!canReuseIcon(info)) { - setNonPendingIcon(info); - } - applyLabel(info); - maybeApplyProgressLevel(info, oldIcon); - } - - /** - * Check if we can reuse icon so that any animation is preserved - */ - private boolean canReuseIcon(ItemInfoWithIcon info) { - return mIcon instanceof PreloadIconDrawable p - && p.hasNotCompleted() && p.isSameInfo(info.bitmap); - } - - /** - * Apply progress level to the icon if necessary - */ - private void maybeApplyProgressLevel(ItemInfoWithIcon info, FastBitmapDrawable oldIcon) { - if (!shouldApplyProgressLevel(info, oldIcon)) { - return; - } - PreloadIconDrawable pendingIcon = applyProgressLevel(info); - boolean isNoLongerPending = info instanceof WorkspaceItemInfo wii - ? !wii.hasPromiseIconUi() : !info.isArchived(); - if (isNoLongerPending && info.getProgressLevel() == 100 && pendingIcon != null) { - pendingIcon.maybePerformFinishedAnimation( - (oldIcon instanceof PreloadIconDrawable p) ? p : pendingIcon, - () -> setNonPendingIcon( - (getTag() instanceof ItemInfoWithIcon iiwi) ? iiwi : info)); - } - } - - /** - * Check if progress level should be applied to the icon - */ - private boolean shouldApplyProgressLevel(ItemInfoWithIcon info, FastBitmapDrawable oldIcon) { - return (info.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0 - || (info instanceof WorkspaceItemInfo wii && wii.hasPromiseIconUi()) - || (oldIcon instanceof PreloadIconDrawable p && p.hasNotCompleted()); - } - - private void setNonPendingIcon(ItemInfoWithIcon info) { ThemeManager themeManager = ThemeManager.INSTANCE.get(getContext()); int flags = (shouldUseTheme() && themeManager.isMonoThemeEnabled()) ? FLAG_THEMED : 0; @@ -498,6 +463,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotParams.appColor = iconDrawable.getIconColor(); mDotParams.dotColor = Themes.getAttrColor(getContext(), R.attr.notificationDotColor); setIcon(iconDrawable); + applyLabel(info); } protected boolean shouldUseTheme() { @@ -1104,10 +1070,38 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mLongPressHelper.cancelLongPress(); } + /** + * Applies the loading progress value to the progress bar. + * + * If this app is installing, the progress bar will be updated with the installation progress. + * If this app is installed and downloading incrementally, the progress bar will be updated + * with the total download progress. + */ + public void applyLoadingState(PreloadIconDrawable icon) { + if (getTag() instanceof ItemInfoWithIcon) { + WorkspaceItemInfo info = (WorkspaceItemInfo) getTag(); + if ((info.runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0 + || info.hasPromiseIconUi() + || (info.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0 + || (icon != null)) { + updateProgressBarUi(info.getProgressLevel() == 100 ? icon : null); + } + } + } + + private void updateProgressBarUi(PreloadIconDrawable oldIcon) { + FastBitmapDrawable originalIcon = mIcon; + PreloadIconDrawable preloadDrawable = applyProgressLevel(); + if (preloadDrawable != null && oldIcon != null) { + preloadDrawable.maybePerformFinishedAnimation(oldIcon, () -> setIcon(originalIcon)); + } + } + /** Applies the given progress level to the this icon's progress bar. */ @Nullable - private PreloadIconDrawable applyProgressLevel(ItemInfoWithIcon info) { - if (info.isInactiveArchive()) { + public PreloadIconDrawable applyProgressLevel() { + if (!(getTag() instanceof ItemInfoWithIcon info) + || ((ItemInfoWithIcon) getTag()).isInactiveArchive()) { return null; } @@ -1121,16 +1115,23 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setContentDescription(getContext() .getString(R.string.app_waiting_download_title, info.title)); } - PreloadIconDrawable pid; - if (mIcon instanceof PreloadIconDrawable p) { - pid = p; - pid.setLevel(progressLevel); - pid.setIsDisabled(isIconDisabled(info)); - } else { - pid = makePreloadIcon(info); - setIcon(pid); + if (mIcon != null) { + PreloadIconDrawable preloadIconDrawable; + if (mIcon instanceof PreloadIconDrawable) { + preloadIconDrawable = (PreloadIconDrawable) mIcon; + preloadIconDrawable.setLevel(progressLevel); + preloadIconDrawable.setIsDisabled(isIconDisabled(info)); + } else { + preloadIconDrawable = makePreloadIcon(); + setIcon(preloadIconDrawable); + if (info.isArchived() && Flags.useNewIconForArchivedApps()) { + // reapply text without cloud icon as soon as unarchiving is triggered + applyLabel(info); + } + } + return preloadIconDrawable; } - return pid; + return null; } /** @@ -1139,11 +1140,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, */ @Nullable public PreloadIconDrawable makePreloadIcon() { - return getTag() instanceof ItemInfoWithIcon info ? makePreloadIcon(info) : null; - } + if (!(getTag() instanceof ItemInfoWithIcon)) { + return null; + } - @NonNull - private PreloadIconDrawable makePreloadIcon(ItemInfoWithIcon info) { + ItemInfoWithIcon info = (ItemInfoWithIcon) getTag(); int progressLevel = info.getProgressLevel(); final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info); @@ -1162,7 +1163,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public void applyDotState(ItemInfo itemInfo, boolean animate) { - if (mIcon != null) { + if (mIcon instanceof FastBitmapDrawable) { boolean wasDotted = mDotInfo != null; mDotInfo = mActivity.getDotInfoForItem(itemInfo); boolean isDotted = mDotInfo != null; @@ -1211,7 +1212,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setContentDescription(getContext().getString( R.string.app_archived_title, info.title)); } - } else if ((info.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) + } else if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) { String percentageString = NumberFormat.getPercentInstance() .format(progressLevel * 0.01); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 647d2ad97b..7df4014fc0 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -277,11 +277,11 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; @@ -2598,12 +2598,25 @@ public class Launcher extends StatefulActivity mModelCallbacks.bindIncrementalDownloadProgressUpdated(app); } + @Override + public void bindWidgetsRestored(ArrayList widgets) { + mModelCallbacks.bindWidgetsRestored(widgets); + } + /** * See {@code LauncherBindingDelegate} */ @Override - public void bindItemsUpdated(Set updates) { - mModelCallbacks.bindItemsUpdated(updates); + public void bindWorkspaceItemsChanged(List updated) { + mModelCallbacks.bindWorkspaceItemsChanged(updated); + } + + /** + * See {@code LauncherBindingDelegate} + */ + @Override + public void bindRestoreItemsChange(HashSet updates) { + mModelCallbacks.bindRestoreItemsChange(updates); } /** diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt index 5338fb435f..5d3252514e 100644 --- a/src/com/android/launcher3/ModelCallbacks.kt +++ b/src/com/android/launcher3/ModelCallbacks.kt @@ -17,6 +17,8 @@ import com.android.launcher3.model.BgDataModel import com.android.launcher3.model.StringCache import com.android.launcher3.model.data.AppInfo import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.model.data.LauncherAppWidgetInfo +import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.popup.PopupContainerWithArrow import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.IntArray as LIntArray @@ -213,13 +215,29 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { launcher.appsView.appsStore.updateProgressBar(app) } + override fun bindWidgetsRestored(widgets: ArrayList?) { + launcher.workspace.widgetsRestored(widgets) + } + + /** + * Some shortcuts were updated in the background. Implementation of the method from + * LauncherModel.Callbacks. + * + * @param updated list of shortcuts which have changed. + */ + override fun bindWorkspaceItemsChanged(updated: List) { + if (updated.isNotEmpty()) { + launcher.workspace.updateWorkspaceItems(updated, launcher) + PopupContainerWithArrow.dismissInvalidPopup(launcher) + } + } + /** * Update the state of a package, typically related to install state. Implementation of the * method from LauncherModel.Callbacks. */ - override fun bindItemsUpdated(updates: Set) { - launcher.workspace.updateContainerItems(updates, launcher) - PopupContainerWithArrow.dismissInvalidPopup(launcher) + override fun bindRestoreItemsChange(updates: HashSet?) { + launcher.workspace.updateRestoreItems(updates, launcher) } /** diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 7d82179d43..86c49d0e47 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -53,6 +53,8 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; @@ -123,9 +125,13 @@ import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperOffsetInterpolator; import com.android.launcher3.widget.LauncherAppWidgetHostView; +import com.android.launcher3.widget.LauncherWidgetHolder; +import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener; import com.android.launcher3.widget.NavigableAppWidgetHostView; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; +import com.android.launcher3.widget.PendingAppWidgetHostView; +import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.util.WidgetSizes; import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks; import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayTouchProxy; @@ -658,6 +664,9 @@ public class Workspace extends PagedView bindAndInitFirstWorkspaceScreen(); } + // Remove any deferred refresh callbacks + mLauncher.mHandler.removeCallbacksAndMessages(DeferredWidgetRefresh.class); + // Re-enable the layout transitions enableLayoutTransitions(); } @@ -3456,6 +3465,43 @@ public class Workspace extends PagedView removeItemsByMatcher(matcher); } + public void widgetsRestored(final ArrayList changedInfo) { + if (!changedInfo.isEmpty()) { + DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo, + mLauncher.getAppWidgetHolder()); + + LauncherAppWidgetInfo item = changedInfo.get(0); + final AppWidgetProviderInfo widgetInfo; + WidgetManagerHelper widgetHelper = new WidgetManagerHelper(getContext()); + if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { + widgetInfo = widgetHelper.findProvider(item.providerName, item.user); + } else { + widgetInfo = widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId, + item.getTargetComponent()); + } + + if (widgetInfo != null) { + // Re-inflate the widgets which have changed status + widgetRefresh.run(); + } else { + // widgetRefresh will automatically run when the packages are updated. + // For now just update the progress bars + mapOverItems(new ItemOperator() { + @Override + public boolean evaluate(ItemInfo info, View view) { + if (view instanceof PendingAppWidgetHostView + && changedInfo.contains(info)) { + ((LauncherAppWidgetInfo) info).installProgress = 100; + ((PendingAppWidgetHostView) view).applyState(); + } + // process all the shortcuts + return false; + } + }); + } + } + } + public boolean isOverlayShown() { return mOverlayShown; } @@ -3562,6 +3608,62 @@ public class Workspace extends PagedView return mLauncher.getCellPosMapper(); } + /** + * Used as a workaround to ensure that the AppWidgetService receives the + * PACKAGE_ADDED broadcast before updating widgets. + */ + private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener { + private final ArrayList mInfos; + private final LauncherWidgetHolder mWidgetHolder; + private final Handler mHandler; + + private boolean mRefreshPending; + + DeferredWidgetRefresh(ArrayList infos, + LauncherWidgetHolder holder) { + mInfos = infos; + mWidgetHolder = holder; + mHandler = mLauncher.mHandler; + mRefreshPending = true; + + mWidgetHolder.addProviderChangeListener(this); + // Force refresh after 10 seconds, if we don't get the provider changed event. + // This could happen when the provider is no longer available in the app. + Message msg = Message.obtain(mHandler, this); + msg.obj = DeferredWidgetRefresh.class; + mHandler.sendMessageDelayed(msg, 10000); + } + + @Override + public void run() { + mWidgetHolder.removeProviderChangeListener(this); + mHandler.removeCallbacks(this); + + if (!mRefreshPending) { + return; + } + + mRefreshPending = false; + + ArrayList views = new ArrayList<>(mInfos.size()); + mapOverItems((info, view) -> { + if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) { + views.add((PendingAppWidgetHostView) view); + } + // process all children + return false; + }); + for (PendingAppWidgetHostView view : views) { + view.reInflate(); + } + } + + @Override + public void notifyWidgetProvidersChanged() { + run(); + } + } + private class StateTransitionListener extends AnimatorListenerAdapter implements AnimatorUpdateListener { diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java index d5a4022792..9afe06c6f7 100644 --- a/src/com/android/launcher3/allapps/AllAppsStore.java +++ b/src/com/android/launcher3/allapps/AllAppsStore.java @@ -17,6 +17,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; +import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK; import android.content.Context; import android.os.UserHandle; @@ -228,7 +229,11 @@ public class AllAppsStore { public void updateProgressBar(AppInfo app) { updateAllIcons((child) -> { if (child.getTag() == app) { - child.applyFromApplicationInfo(app); + if ((app.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) == 0) { + child.applyFromApplicationInfo(app); + } else { + child.applyProgressLevel(); + } } }); } diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java index f1891821b7..3464e9bdb6 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -24,6 +24,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; @@ -32,14 +33,12 @@ import android.graphics.PathMeasure; import android.graphics.Rect; import android.util.Property; -import androidx.annotation.VisibleForTesting; import androidx.core.graphics.ColorUtils; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorListeners; -import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.util.Themes; @@ -64,6 +63,8 @@ public class PreloadIconDrawable extends FastBitmapDrawable { private static final int DEFAULT_PATH_SIZE = 100; private static final int MAX_PAINT_ALPHA = 255; + private static final int TRACK_ALPHA = (int) (0.27f * MAX_PAINT_ALPHA); + private static final int DISABLED_ICON_ALPHA = (int) (0.6f * MAX_PAINT_ALPHA); private static final long DURATION_SCALE = 500; private static final long SCALE_AND_ALPHA_ANIM_DURATION = 500; @@ -283,25 +284,20 @@ public class PreloadIconDrawable extends FastBitmapDrawable { (long) ((finalProgress - mInternalStateProgress) * DURATION_SCALE)); mCurrentAnim.setInterpolator(LINEAR); if (isFinish) { + if (onFinishCallback != null) { + mCurrentAnim.addListener(AnimatorListeners.forEndCallback(onFinishCallback)); + } mCurrentAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mRanFinishAnimation = true; } }); - if (onFinishCallback != null) { - mCurrentAnim.addListener(AnimatorListeners.forEndCallback(onFinishCallback)); - } } mCurrentAnim.start(); } } - @VisibleForTesting - public ObjectAnimator getActiveAnimation() { - return mCurrentAnim; - } - /** * Sets the internal progress and updates the UI accordingly * for progress <= 0: @@ -362,7 +358,8 @@ public class PreloadIconDrawable extends FastBitmapDrawable { @Override public FastBitmapConstantState newConstantState() { return new PreloadIconConstantState( - mBitmapInfo, + mBitmap, + mIconColor, mItem, mIndicatorColor, new int[] {mSystemAccentColor, mSystemBackgroundColor}, @@ -380,13 +377,14 @@ public class PreloadIconDrawable extends FastBitmapDrawable { private final Path mShapePath; public PreloadIconConstantState( - BitmapInfo bitmapInfo, + Bitmap bitmap, + int iconColor, ItemInfoWithIcon info, int indicatorColor, int[] preloadColors, boolean isDarkMode, Path shapePath) { - super(bitmapInfo); + super(bitmap, iconColor); mInfo = info; mIndicatorColor = indicatorColor; mPreloadColors = preloadColors; diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index ddc775dd9c..a04cbfb27b 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -49,6 +49,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.CollectionInfo; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutKey; @@ -69,6 +70,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -417,9 +419,9 @@ public class BgDataModel { * Binds updated incremental download progress */ default void bindIncrementalDownloadProgressUpdated(AppInfo app) { } - - /** Called when a runtime property of the ItemInfo is updated due to some system event */ - default void bindItemsUpdated(Set updates) { } + default void bindWorkspaceItemsChanged(List updated) { } + default void bindWidgetsRestored(ArrayList widgets) { } + default void bindRestoreItemsChange(HashSet updates) { } default void bindWorkspaceComponentsRemoved(Predicate matcher) { } /** diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java index 48934e2c11..b544b91382 100644 --- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java +++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java @@ -15,9 +15,6 @@ */ package com.android.launcher3.model; -import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG; -import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER; - import android.content.ComponentName; import android.os.UserHandle; @@ -26,8 +23,6 @@ import androidx.annotation.NonNull; import com.android.launcher3.LauncherModel.ModelUpdateTask; import com.android.launcher3.LauncherSettings; import com.android.launcher3.icons.IconCache; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import java.util.ArrayList; @@ -60,7 +55,7 @@ public class CacheDataUpdatedTask implements ModelUpdateTask { public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel, @NonNull AllAppsList apps) { IconCache iconCache = taskController.getApp().getIconCache(); - ArrayList updatedItems = new ArrayList<>(); + ArrayList updatedShortcuts = new ArrayList<>(); synchronized (dataModel) { dataModel.forAllWorkspaceItemInfos(mUser, si -> { @@ -69,25 +64,12 @@ public class CacheDataUpdatedTask implements ModelUpdateTask { && isValidShortcut(si) && cn != null && mPackages.contains(cn.getPackageName())) { iconCache.getTitleAndIcon(si, si.getMatchingLookupFlag()); - updatedItems.add(si); + updatedShortcuts.add(si); } }); - - dataModel.itemsIdMap.stream() - .filter(WIDGET_FILTER) - .filter(item -> mUser.equals(item.user)) - .map(item -> (LauncherAppWidgetInfo) item) - .filter(widget -> mPackages.contains(widget.providerName.getPackageName()) - && widget.pendingItemInfo != null) - .forEach(widget -> { - iconCache.getTitleAndIconForApp( - widget.pendingItemInfo, DEFAULT_LOOKUP_FLAG); - updatedItems.add(widget); - }); - apps.updateIconsAndLabels(mPackages, mUser); } - taskController.bindUpdatedWorkspaceItems(updatedItems); + taskController.bindUpdatedWorkspaceItems(updatedShortcuts); taskController.bindApplicationsIfNeeded(); } diff --git a/src/com/android/launcher3/model/ModelTaskController.kt b/src/com/android/launcher3/model/ModelTaskController.kt index 40ea17d437..fc5334341b 100644 --- a/src/com/android/launcher3/model/ModelTaskController.kt +++ b/src/com/android/launcher3/model/ModelTaskController.kt @@ -22,6 +22,7 @@ import com.android.launcher3.LauncherModel.CallbackTask import com.android.launcher3.celllayout.CellPosMapper import com.android.launcher3.model.BgDataModel.FixedContainerItems import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.util.PackageUserKey import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder import java.util.Objects @@ -50,17 +51,18 @@ class ModelTaskController( */ fun getModelWriter() = model.getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null) - fun bindUpdatedWorkspaceItems(allUpdates: Collection) { + fun bindUpdatedWorkspaceItems(allUpdates: List) { // Bind workspace items - val workspaceUpdates = allUpdates.filter { it.id != ItemInfo.NO_ID }.toSet() + val workspaceUpdates = + allUpdates.stream().filter { info -> info.id != ItemInfo.NO_ID }.toList() if (workspaceUpdates.isNotEmpty()) { - scheduleCallbackTask { it.bindItemsUpdated(workspaceUpdates) } + scheduleCallbackTask { it.bindWorkspaceItemsChanged(workspaceUpdates) } } // Bind extra items if any allUpdates .stream() - .mapToInt { it.container } + .mapToInt { info: WorkspaceItemInfo -> info.container } .distinct() .mapToObj { dataModel.extraItems.get(it) } .filter { Objects.nonNull(it) } diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java index a2160422b2..4103937445 100644 --- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java +++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java @@ -99,7 +99,8 @@ public class PackageInstallStateChangedTask implements ModelUpdateTask { }); if (!updates.isEmpty()) { - taskController.bindUpdatedWorkspaceItems(updates); + taskController.scheduleCallbackTask( + callbacks -> callbacks.bindRestoreItemsChange(updates)); } } } diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 6bef292841..1153f487a0 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -214,7 +214,8 @@ public class PackageUpdatedTask implements ModelUpdateTask { // Update shortcut infos if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) { - final ArrayList updatedWorkspaceItems = new ArrayList<>(); + final ArrayList updatedWorkspaceItems = new ArrayList<>(); + final ArrayList widgets = new ArrayList<>(); // For system apps, package manager send OP_UPDATE when an app is enabled. final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE; @@ -363,8 +364,8 @@ public class PackageUpdatedTask implements ModelUpdateTask { // if the widget has a config activity. In case there is no config // activity, it will be marked as 'restored' during bind. widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; - widgetInfo.installProgress = 100; - updatedWorkspaceItems.add(widgetInfo); + + widgets.add(widgetInfo); taskController.getModelWriter().updateItemInDatabase(widgetInfo); }); } @@ -376,6 +377,10 @@ public class PackageUpdatedTask implements ModelUpdateTask { "removing shortcuts with invalid target components." + " ids=" + removedShortcuts); } + + if (!widgets.isEmpty()) { + taskController.scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets)); + } } final HashSet removedPackages = new HashSet<>(); diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index 381d17accb..78709b84c8 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -228,9 +228,10 @@ public class ItemClickHandler { private static void onClickPendingAppItem(View v, Launcher launcher, String packageName, boolean downloadStarted) { ItemInfo item = (ItemInfo) v.getTag(); - CompletableFuture siFuture = CompletableFuture.supplyAsync(() -> - InstallSessionHelper.INSTANCE.get(launcher) - .getActiveSessionInfo(item.user, packageName), + CompletableFuture siFuture; + siFuture = CompletableFuture.supplyAsync(() -> + InstallSessionHelper.INSTANCE.get(launcher) + .getActiveSessionInfo(item.user, packageName), UI_HELPER_EXECUTOR); Consumer marketLaunchAction = sessionInfo -> { if (sessionInfo != null) { @@ -244,8 +245,8 @@ public class ItemClickHandler { } } // Fallback to using custom market intent. - Intent intent = ApiWrapper.INSTANCE.get(launcher).getMarketSearchIntent( - packageName, item.user); + Intent intent = ApiWrapper.INSTANCE.get(launcher).getAppMarketActivityIntent( + packageName, Process.myUserHandle()); launcher.startActivitySafely(v, intent, item); }; @@ -357,7 +358,9 @@ public class ItemClickHandler { // Check for abandoned promise if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi() && (!Flags.enableSupportForArchiving() || !shortcut.isArchived())) { - String packageName = shortcut.getTargetPackage(); + String packageName = shortcut.getIntent().getComponent() != null + ? shortcut.getIntent().getComponent().getPackageName() + : shortcut.getIntent().getPackage(); if (!TextUtils.isEmpty(packageName)) { onClickPendingAppItem( v, diff --git a/src/com/android/launcher3/util/ApiWrapper.java b/src/com/android/launcher3/util/ApiWrapper.java index d24d084a29..467a7ec54f 100644 --- a/src/com/android/launcher3/util/ApiWrapper.java +++ b/src/com/android/launcher3/util/ApiWrapper.java @@ -28,7 +28,6 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.ShortcutInfo; import android.graphics.drawable.ColorDrawable; import android.net.Uri; -import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; @@ -121,21 +120,6 @@ public class ApiWrapper { * Activity). */ public Intent getAppMarketActivityIntent(String packageName, UserHandle user) { - return createMarketIntent(packageName); - } - - /** - * Returns an intent which can be used to start a search for a package on app market - */ - public Intent getMarketSearchIntent(String packageName, UserHandle user) { - // If we are search for the current user, just launch the market directly as the - // system won't have the installer details either - return (Process.myUserHandle().equals(user)) - ? createMarketIntent(packageName) - : getAppMarketActivityIntent(packageName, user); - } - - private static Intent createMarketIntent(String packageName) { return new Intent(Intent.ACTION_VIEW) .setData(new Uri.Builder() .scheme("market") diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java index 20e3eafcfb..02779ce596 100644 --- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java +++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java @@ -15,20 +15,24 @@ */ package com.android.launcher3.util; +import android.graphics.drawable.Drawable; import android.view.View; import com.android.launcher3.BubbleTextView; import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.model.data.AppPairInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.widget.PendingAppWidgetHostView; -import java.util.Set; +import java.util.HashSet; +import java.util.List; /** * Interface representing a container which can bind Launcher items with some utility methods @@ -37,22 +41,27 @@ public interface LauncherBindableItemsContainer { /** * Called to update workspace items as a result of - * {@link com.android.launcher3.model.BgDataModel.Callbacks#bindItemsUpdated(Set)} + * {@link com.android.launcher3.model.BgDataModel.Callbacks#bindWorkspaceItemsChanged(List)} */ - default void updateContainerItems(Set updates, ActivityContext context) { + default void updateWorkspaceItems(List shortcuts, ActivityContext context) { + final HashSet updates = new HashSet<>(shortcuts); ItemOperator op = (info, v) -> { - if (v instanceof BubbleTextView shortcut - && info instanceof WorkspaceItemInfo wii - && updates.contains(info)) { - shortcut.applyFromWorkspaceItem(wii); - } else if (info instanceof FolderInfo && v instanceof FolderIcon folderIcon) { - folderIcon.updatePreviewItems(updates::contains); + if (v instanceof BubbleTextView && updates.contains(info)) { + WorkspaceItemInfo si = (WorkspaceItemInfo) info; + BubbleTextView shortcut = (BubbleTextView) v; + Drawable oldIcon = shortcut.getIcon(); + boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable) + && ((PreloadIconDrawable) oldIcon).hasNotCompleted(); + shortcut.applyFromWorkspaceItem( + si, + si.isPromise() != oldPromiseState + && oldIcon instanceof PreloadIconDrawable + ? (PreloadIconDrawable) oldIcon + : null); + } else if (info instanceof FolderInfo && v instanceof FolderIcon) { + ((FolderIcon) v).updatePreviewItems(updates::contains); } else if (info instanceof AppPairInfo && v instanceof AppPairIcon appPairIcon) { appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains); - } else if (v instanceof PendingAppWidgetHostView pendingView - && updates.contains(info)) { - pendingView.applyState(); - pendingView.postProviderAvailabilityCheck(); } // Iterate all items @@ -66,6 +75,35 @@ public interface LauncherBindableItemsContainer { } } + /** + * Called to update restored items as a result of + * {@link com.android.launcher3.model.BgDataModel.Callbacks#bindRestoreItemsChange(HashSet)}} + */ + default void updateRestoreItems(final HashSet updates, ActivityContext context) { + ItemOperator op = (info, v) -> { + if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView + && updates.contains(info)) { + ((BubbleTextView) v).applyLoadingState(null); + } else if (v instanceof PendingAppWidgetHostView + && info instanceof LauncherAppWidgetInfo + && updates.contains(info)) { + ((PendingAppWidgetHostView) v).applyState(); + } else if (v instanceof FolderIcon && info instanceof FolderInfo) { + ((FolderIcon) v).updatePreviewItems(updates::contains); + } else if (info instanceof AppPairInfo && v instanceof AppPairIcon appPairIcon) { + appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains); + } + // process all the shortcuts + return false; + }; + + mapOverItems(op); + Folder folder = Folder.getOpen(context); + if (folder != null) { + folder.iterateOverItems(op); + } + } + /** * Map the operator over the shortcuts and widgets. * diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java index cd8e4571e5..9c9b80d80f 100644 --- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java @@ -21,7 +21,7 @@ import static android.graphics.Paint.DITHER_FLAG; import static android.graphics.Paint.FILTER_BITMAP_FLAG; import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon; -import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; +import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.appwidget.AppWidgetProviderInfo; @@ -37,9 +37,6 @@ import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; @@ -63,10 +60,8 @@ import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.PackageItemInfo; -import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.Themes; -import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener; import java.util.List; @@ -86,8 +81,6 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView private final Matrix mMatrix = new Matrix(); private final RectF mPreviewBitmapRect = new RectF(); private final RectF mCanvasRect = new RectF(); - private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final RunnableList mOnDetachCleanup = new RunnableList(); private final LauncherWidgetHolder mWidgetHolder; private final LauncherAppWidgetProviderInfo mAppwidget; @@ -97,6 +90,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView private final CharSequence mLabel; private OnClickListener mClickListener; + private SafeCloseable mOnDetachCleanup; private int mDragFlags; @@ -216,15 +210,16 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView protected void onAttachedToWindow() { super.onAttachedToWindow(); - mOnDetachCleanup.executeAllAndClear(); if ((mAppwidget != null) && !mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) && mInfo.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED) { // If the widget is not completely restored, but has a valid ID, then listen of // updates from provider app for potential restore complete. - SafeCloseable updateCleanup = mWidgetHolder.addOnUpdateListener( + if (mOnDetachCleanup != null) { + mOnDetachCleanup.close(); + } + mOnDetachCleanup = mWidgetHolder.addOnUpdateListener( mInfo.appWidgetId, mAppwidget, this::checkIfRestored); - mOnDetachCleanup.add(updateCleanup::close); checkIfRestored(); } } @@ -232,7 +227,10 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - mOnDetachCleanup.executeAllAndClear(); + if (mOnDetachCleanup != null) { + mOnDetachCleanup.close(); + mOnDetachCleanup = null; + } } /** @@ -297,30 +295,43 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView mCenterDrawable.setCallback(null); mCenterDrawable = null; } - mDragFlags = FLAG_DRAW_ICON; + mDragFlags = 0; + if (info.bitmap.icon != null) { + mDragFlags = FLAG_DRAW_ICON; - // The view displays three modes, - // 1) App icon in the center - // 2) Preload icon in the center - // 3) App icon in the center with a setup icon on the top left corner. - if (mDisabledForSafeMode) { - FastBitmapDrawable disabledIcon = info.newIcon(getContext()); - disabledIcon.setIsDisabled(true); - mCenterDrawable = disabledIcon; - mSettingIconDrawable = null; - } else if (isReadyForClickSetup()) { - mCenterDrawable = info.newIcon(getContext()); - mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate(); - updateSettingColor(info.bitmap.color); + Drawable widgetCategoryIcon = getWidgetCategoryIcon(); + // The view displays three modes, + // 1) App icon in the center + // 2) Preload icon in the center + // 3) App icon in the center with a setup icon on the top left corner. + if (mDisabledForSafeMode) { + if (widgetCategoryIcon == null) { + FastBitmapDrawable disabledIcon = info.newIcon(getContext()); + disabledIcon.setIsDisabled(true); + mCenterDrawable = disabledIcon; + } else { + widgetCategoryIcon.setColorFilter(getDisabledColorFilter()); + mCenterDrawable = widgetCategoryIcon; + } + mSettingIconDrawable = null; + } else if (isReadyForClickSetup()) { + mCenterDrawable = widgetCategoryIcon == null + ? info.newIcon(getContext()) + : widgetCategoryIcon; + mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate(); + updateSettingColor(info.bitmap.color); - mDragFlags |= FLAG_DRAW_SETTINGS | FLAG_DRAW_LABEL; - } else { - mCenterDrawable = newPendingIcon(getContext(), info); - mSettingIconDrawable = null; - applyState(); + mDragFlags |= FLAG_DRAW_SETTINGS | FLAG_DRAW_LABEL; + } else { + mCenterDrawable = widgetCategoryIcon == null + ? newPendingIcon(getContext(), info) + : widgetCategoryIcon; + mSettingIconDrawable = null; + applyState(); + } + mCenterDrawable.setCallback(this); + mDrawableSizeChanged = true; } - mCenterDrawable.setCallback(this); - mDrawableSizeChanged = true; invalidate(); } @@ -339,11 +350,6 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView } public void applyState() { - if (mCenterDrawable instanceof FastBitmapDrawable fb - && mInfo.pendingItemInfo != null - && !fb.isSameInfo(mInfo.pendingItemInfo.bitmap)) { - reapplyItemInfo(mInfo.pendingItemInfo); - } if (mCenterDrawable != null) { mCenterDrawable.setLevel(Math.max(mInfo.installProgress, 0)); } @@ -480,72 +486,16 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView } /** - * Creates a runnable runnable which tries to refresh the widget if it is restored - */ - public void postProviderAvailabilityCheck() { - if (!mInfo.hasRestoreFlag(FLAG_PROVIDER_NOT_READY) && getAppWidgetInfo() == null) { - // If the info state suggests that the provider is ready, but there is no - // provider info attached on this pending view, recreate when the provider is available - DeferredWidgetRefresh restoreRunnable = new DeferredWidgetRefresh(); - mOnDetachCleanup.add(restoreRunnable::cleanup); - mHandler.post(restoreRunnable::notifyWidgetProvidersChanged); - } - } - - /** - * Used as a workaround to ensure that the AppWidgetService receives the - * PACKAGE_ADDED broadcast before updating widgets. + * Returns the widget category icon for {@link #mInfo}. * - * This class will periodically check for the availability of the WidgetProvider as a result - * of providerChanged callback from the host. When the provider is available or a timeout of - * 10-sec is reached, it reinflates the pending-widget which in-turn goes through the process - * of re-evaluating the pending state of the widget, + *

If {@link #mInfo}'s category is {@code PackageItemInfo#NO_CATEGORY} or unknown, returns + * {@code null}. */ - private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener { - private boolean mRefreshPending = true; - - DeferredWidgetRefresh() { - mWidgetHolder.addProviderChangeListener(this); - // Force refresh after 10 seconds, if we don't get the provider changed event. - // This could happen when the provider is no longer available in the app. - Message msg = Message.obtain(getHandler(), this); - msg.obj = DeferredWidgetRefresh.class; - mHandler.sendMessageDelayed(msg, 10000); - } - - /** - * Reinflate the widget if it is still attached. - */ - @Override - public void run() { - cleanup(); - if (mRefreshPending) { - reInflate(); - mRefreshPending = false; - } - } - - @Override - public void notifyWidgetProvidersChanged() { - final AppWidgetProviderInfo widgetInfo; - WidgetManagerHelper widgetHelper = new WidgetManagerHelper(getContext()); - if (mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { - widgetInfo = widgetHelper.findProvider(mInfo.providerName, mInfo.user); - } else { - widgetInfo = widgetHelper.getLauncherAppWidgetInfo(mInfo.appWidgetId, - mInfo.getTargetComponent()); - } - if (widgetInfo != null) { - run(); - } - } - - /** - * Removes any scheduled callbacks and change listeners, no-op if nothing is scheduled - */ - public void cleanup() { - mWidgetHolder.removeProviderChangeListener(this); - mHandler.removeCallbacks(this); + @Nullable + private Drawable getWidgetCategoryIcon() { + if (mInfo.pendingItemInfo.widgetCategory == WidgetSections.NO_CATEGORY) { + return null; } + return mInfo.pendingItemInfo.newIcon(getContext()); } } diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java index faf6b91b1e..f51871b647 100644 --- a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java @@ -27,9 +27,7 @@ import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT_SMALL; import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING; import static com.android.launcher3.Flags.FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS; import static com.android.launcher3.LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE; -import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.google.common.truth.Truth.assertThat; @@ -41,8 +39,6 @@ import static org.mockito.Mockito.verify; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; import android.graphics.Typeface; import android.os.Build; import android.os.UserHandle; @@ -61,17 +57,13 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.Flags; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.Utilities; -import com.android.launcher3.graphics.PreloadIconDrawable; -import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; -import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.search.StringMatcherUtility; import com.android.launcher3.util.ActivityContextWrapper; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext; -import com.android.launcher3.util.TestUtil; import com.android.launcher3.views.BaseDragLayer; import org.junit.After; @@ -81,8 +73,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; -import java.util.concurrent.CountDownLatch; - /** * Unit tests for testing modifyTitleToSupportMultiLine() in BubbleTextView.java * This class tests a couple of strings and uses the getMaxLines() to determine if the test passes. @@ -495,40 +485,4 @@ public class BubbleTextViewTest { assertThat(mBubbleTextView.getIcon().hasBadge()).isEqualTo(true); } - - @Test - public void applyingPendingIcon_preserves_last_icon() throws Exception { - mItemInfoWithIcon.bitmap = - BitmapInfo.fromBitmap(Bitmap.createBitmap(100, 100, Config.ARGB_8888)); - mItemInfoWithIcon.setProgressLevel(30, PackageInstallInfo.STATUS_INSTALLING); - - TestUtil.runOnExecutorSync(MAIN_EXECUTOR, - () -> mBubbleTextView.applyIconAndLabel(mItemInfoWithIcon)); - assertThat(mBubbleTextView.getIcon()).isInstanceOf(PreloadIconDrawable.class); - assertThat(mBubbleTextView.getIcon().getLevel()).isEqualTo(30); - PreloadIconDrawable oldIcon = (PreloadIconDrawable) mBubbleTextView.getIcon(); - - // Same icon is used when progress changes - mItemInfoWithIcon.setProgressLevel(50, PackageInstallInfo.STATUS_INSTALLING); - TestUtil.runOnExecutorSync(MAIN_EXECUTOR, - () -> mBubbleTextView.applyIconAndLabel(mItemInfoWithIcon)); - assertThat(mBubbleTextView.getIcon()).isSameInstanceAs(oldIcon); - assertThat(mBubbleTextView.getIcon().getLevel()).isEqualTo(50); - - // Icon is replaced with a non pending icon when download finishes - mItemInfoWithIcon.setProgressLevel(100, PackageInstallInfo.STATUS_INSTALLED); - - CountDownLatch animWait = new CountDownLatch(1); - TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> { - mBubbleTextView.applyIconAndLabel(mItemInfoWithIcon); - assertThat(mBubbleTextView.getIcon()).isSameInstanceAs(oldIcon); - assertThat(oldIcon.getActiveAnimation()).isNotNull(); - oldIcon.getActiveAnimation().addListener(forEndCallback(animWait::countDown)); - }); - animWait.await(); - - // Assert that the icon is replaced with a non-pending icon - assertThat(mBubbleTextView.getIcon()).isNotInstanceOf(PreloadIconDrawable.class); - } - } diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt deleted file mode 100644 index 93be5f5125..0000000000 --- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2025 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.util - -import android.content.ComponentName -import android.content.pm.LauncherApps -import android.graphics.Bitmap -import android.graphics.Bitmap.Config.ARGB_8888 -import android.os.Process.myUserHandle -import android.platform.uiautomatorhelpers.DeviceHelpers.context -import android.view.View -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.launcher3.BubbleTextView -import com.android.launcher3.graphics.PreloadIconDrawable -import com.android.launcher3.icons.BitmapInfo -import com.android.launcher3.icons.FastBitmapDrawable -import com.android.launcher3.icons.PlaceHolderIconDrawable -import com.android.launcher3.model.data.AppInfo -import com.android.launcher3.model.data.AppInfo.makeLaunchIntent -import com.android.launcher3.model.data.ItemInfo -import com.android.launcher3.model.data.WorkspaceItemInfo -import com.android.launcher3.pm.PackageInstallInfo -import com.android.launcher3.util.Executors.MAIN_EXECUTOR -import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator -import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY -import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2 -import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3 -import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class LauncherBindableItemsContainerTest { - - private val icon1 by lazy { getLAI(TEST_ACTIVITY) } - private val icon2 by lazy { getLAI(TEST_ACTIVITY2) } - private val icon3 by lazy { getLAI(TEST_ACTIVITY3) } - - private val container = TestContainer() - - @Test - fun `icon bitmap is updated`() { - container.addIcon(icon1) - container.addIcon(icon2) - container.addIcon(icon3) - - assertThat(container.getAppIcon(icon1).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - assertThat(container.getAppIcon(icon2).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - assertThat(container.getAppIcon(icon3).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - - icon2.bitmap = BitmapInfo.fromBitmap(Bitmap.createBitmap(200, 200, ARGB_8888)) - TestUtil.runOnExecutorSync(MAIN_EXECUTOR) { - container.updateContainerItems(setOf(icon2), container) - } - - assertThat(container.getAppIcon(icon1).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - assertThat(container.getAppIcon(icon3).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - assertThat(container.getAppIcon(icon2).icon) - .isNotInstanceOf(PlaceHolderIconDrawable::class.java) - assertThat(container.getAppIcon(icon2).icon).isInstanceOf(FastBitmapDrawable::class.java) - } - - @Test - fun `icon download progress updated`() { - container.addIcon(icon1) - container.addIcon(icon2) - assertThat(container.getAppIcon(icon1).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - assertThat(container.getAppIcon(icon2).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - - icon1.status = WorkspaceItemInfo.FLAG_RESTORED_ICON - icon1.bitmap = BitmapInfo.fromBitmap(Bitmap.createBitmap(200, 200, ARGB_8888)) - icon1.setProgressLevel(30, PackageInstallInfo.STATUS_INSTALLING) - TestUtil.runOnExecutorSync(MAIN_EXECUTOR) { - container.updateContainerItems(setOf(icon1), container) - } - - assertThat(container.getAppIcon(icon2).icon) - .isInstanceOf(PlaceHolderIconDrawable::class.java) - assertThat(container.getAppIcon(icon1).icon).isInstanceOf(PreloadIconDrawable::class.java) - val oldIcon = container.getAppIcon(icon1).icon as PreloadIconDrawable - assertThat(oldIcon.level).isEqualTo(30) - } - - private fun getLAI(className: String): WorkspaceItemInfo = - AppInfo( - context, - context - .getSystemService(LauncherApps::class.java)!! - .resolveActivity( - makeLaunchIntent(ComponentName(TEST_PACKAGE, className)), - myUserHandle(), - )!!, - myUserHandle(), - ) - .makeWorkspaceItem(context) - - class TestContainer : ActivityContextWrapper(context), LauncherBindableItemsContainer { - - val items = mutableMapOf() - - override fun mapOverItems(op: ItemOperator) { - items.forEach { (item, view) -> if (op.evaluate(item, view)) return@forEach } - } - - fun addIcon(info: WorkspaceItemInfo) { - val btv = BubbleTextView(this) - btv.applyFromWorkspaceItem(info) - items[info] = btv - } - - fun getAppIcon(info: WorkspaceItemInfo) = items[info] as BubbleTextView - } -}