From fdeb19e02ce4a9e26c44cab711e6bc9405322878 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 5 Jan 2024 09:29:06 -0800 Subject: [PATCH] Simplifying pending widget inflation path Instead of adding a PendingView in appWidgetHost, attaching a listener in PendingView for widget-updates. This moves the view inflation as the last stage of widget binding. Eventually we can separate the model update and view inflation to different threads Bug: 318539160 Test: atest TaplBinderTests Flag: None Change-Id: I124edaa9622fa367302b80969036d128a978396e --- .../uioverrides/QuickstepAppWidgetHost.java | 6 +- .../uioverrides/QuickstepWidgetHolder.java | 94 ++++++-------- src/com/android/launcher3/Launcher.java | 15 +-- .../widget/LauncherAppWidgetHost.java | 77 ++++++++---- .../widget/LauncherWidgetHolder.java | 115 ++++++++---------- .../widget/PendingAppWidgetHostView.java | 59 +++++++-- .../widget/WidgetHostViewLoader.java | 3 +- 7 files changed, 198 insertions(+), 171 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java index 6659fa0ee7..45813ce52e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java @@ -25,6 +25,7 @@ import android.os.Looper; import androidx.annotation.NonNull; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.util.Executors; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.LauncherWidgetHolder; @@ -55,7 +56,10 @@ final class QuickstepAppWidgetHost extends AppWidgetHost { @Override public void onAppWidgetRemoved(int appWidgetId) { - mAppWidgetRemovedCallback.accept(appWidgetId); + // Route the call via model thread, in case it comes while a loader-bind is in progress + Executors.MODEL_EXECUTOR.execute( + () -> Executors.MAIN_EXECUTOR.execute( + () -> mAppWidgetRemovedCallback.accept(appWidgetId))); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java index 1a75535e82..0fb2b17eef 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java @@ -34,10 +34,10 @@ import androidx.annotation.WorkerThread; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.LauncherWidgetHolder; -import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.ArrayList; import java.util.Collections; @@ -67,11 +67,11 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { private static AppWidgetHost sWidgetHost = null; + private final UpdateHandler mUpdateHandler = this::onWidgetUpdate; private final @Nullable RemoteViews.InteractionHandler mInteractionHandler; private final @NonNull IntConsumer mAppWidgetRemovedCallback; - private final ArrayList mProviderChangedListeners = new ArrayList<>(); // Map to all pending updated keyed with appWidgetId; private final SparseArray mPendingUpdateMap = new SparseArray<>(); @@ -175,7 +175,10 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { @Override public void destroy() { try { - MAIN_EXECUTOR.submit(() -> sHolders.remove(this)).get(); + MAIN_EXECUTOR.submit(() -> { + clearViews(); + sHolders.remove(this); + }).get(); } catch (Exception e) { Log.e(TAG, "Failed to remove self from holder list", e); } @@ -187,26 +190,6 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { == (FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED); } - /** - * Add a listener that is triggered when the providers of the widgets are changed - * @param listener The listener that notifies when the providers changed - */ - @Override - public void addProviderChangeListener( - @NonNull LauncherWidgetHolder.ProviderChangedListener listener) { - MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.add(listener)); - } - - /** - * Remove the specified listener from the host - * @param listener The listener that is to be removed from the host - */ - @Override - public void removeProviderChangeListener( - LauncherWidgetHolder.ProviderChangedListener listener) { - MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.remove(listener)); - } - /** * Stop the host from updating the widget views */ @@ -220,44 +203,41 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { setListeningFlag(false); } - /** - * Create a view for the specified app widget - * @param context The activity context for which the view is created - * @param appWidgetId The ID of the widget - * @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget - * @return A view for the widget - */ + @Override + public SafeCloseable addOnUpdateListener(int appWidgetId, + LauncherAppWidgetProviderInfo appWidget, Runnable callback) { + UpdateHandler handler = new UpdateHandler() { + @Override + public void onWidgetUpdate(int widgetId, UpdateKey key, T data) { + if (KEY_VIEWS_UPDATE == key) { + callback.run(); + } + } + }; + QuickstepWidgetHolderListener holderListener = getHolderListener(appWidgetId); + holderListener.addHolder(handler); + return () -> holderListener.mListeningHolders.remove(handler); + } + @NonNull @Override - public LauncherAppWidgetHostView createView(@NonNull Context context, int appWidgetId, - @NonNull LauncherAppWidgetProviderInfo appWidget) { - if (appWidget.isCustomWidget()) { - LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context); - lahv.setAppWidget(appWidgetId, appWidget); - CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv); - return lahv; - } - - LauncherAppWidgetHostView widgetView = getPendingView(appWidgetId); - if (widgetView != null) { - removePendingView(appWidgetId); - } else { - widgetView = new LauncherAppWidgetHostView(context); - } + protected LauncherAppWidgetHostView createViewInternal( + int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) { + LauncherAppWidgetHostView widgetView = new LauncherAppWidgetHostView(mContext); widgetView.setInteractionHandler(mInteractionHandler); widgetView.setAppWidget(appWidgetId, appWidget); - mViews.put(appWidgetId, widgetView); + widgetView.updateAppWidget(getHolderListener(appWidgetId).addHolder(mUpdateHandler)); + return widgetView; + } + private static QuickstepWidgetHolderListener getHolderListener(int appWidgetId) { QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId); if (listener == null) { listener = new QuickstepWidgetHolderListener(appWidgetId); sWidgetHost.setListener(appWidgetId, listener); sListeners.put(appWidgetId, listener); } - RemoteViews remoteViews = listener.addHolder(this); - widgetView.updateAppWidget(remoteViews); - - return widgetView; + return listener; } /** @@ -267,7 +247,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { public void clearViews() { mViews.clear(); for (int i = sListeners.size() - 1; i >= 0; i--) { - sListeners.valueAt(i).mListeningHolders.remove(this); + sListeners.valueAt(i).mListeningHolders.remove(mUpdateHandler); } } @@ -275,7 +255,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { implements AppWidgetHost.AppWidgetHostListener { // Static listeners should use a set that is backed by WeakHashMap to avoid memory leak - private final Set mListeningHolders = Collections.newSetFromMap( + private final Set mListeningHolders = Collections.newSetFromMap( new WeakHashMap<>()); private final int mWidgetId; @@ -288,7 +268,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { @UiThread @Nullable - public RemoteViews addHolder(@NonNull QuickstepWidgetHolder holder) { + public RemoteViews addHolder(@NonNull UpdateHandler holder) { mListeningHolders.add(holder); return mRemoteViews; } @@ -359,11 +339,15 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { } } + private interface UpdateKey extends BiConsumer { } + + private interface UpdateHandler { + void onWidgetUpdate(int widgetId, UpdateKey key, T data); + } + private static class PendingUpdate { public final IntSet changedViews = new IntSet(); public AppWidgetProviderInfo providerInfo; public RemoteViews remoteViews; } - - private interface UpdateKey extends BiConsumer { } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index e0e35a4b8b..5c49b89b00 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1011,7 +1011,7 @@ public class Launcher extends StatefulActivity AppWidgetHostView boundWidget = null; if (resultCode == RESULT_OK) { animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION; - final AppWidgetHostView layout = mAppWidgetHolder.createView(this, appWidgetId, + final AppWidgetHostView layout = mAppWidgetHolder.createView(appWidgetId, requestArgs.getWidgetHandler().getProviderInfo(this)); boundWidget = layout; onCompleteRunnable = () -> { @@ -1464,7 +1464,7 @@ public class Launcher extends StatefulActivity if (hostView == null) { // Perform actual inflation because we're live - hostView = mAppWidgetHolder.createView(this, appWidgetId, appWidgetInfo); + hostView = mAppWidgetHolder.createView(appWidgetId, appWidgetInfo); } LauncherAppWidgetInfo launcherInfo; @@ -2319,7 +2319,7 @@ public class Launcher extends StatefulActivity } final AppWidgetHostView view; if (mIsSafeModeEnabled) { - view = new PendingAppWidgetHostView(this, item, mIconCache, true); + view = new PendingAppWidgetHostView(this, item, null, true); prepareAppWidget(view, item); return view; } @@ -2450,14 +2450,9 @@ public class Launcher extends StatefulActivity item.minSpanX = appWidgetInfo.minSpanX; item.minSpanY = appWidgetInfo.minSpanY; - view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo); - } else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) - && appWidgetInfo != null) { - mAppWidgetHolder.addPendingView(item.appWidgetId, - new PendingAppWidgetHostView(this, item, mIconCache, false)); - view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo); + view = mAppWidgetHolder.createView(item.appWidgetId, appWidgetInfo); } else { - view = new PendingAppWidgetHostView(this, item, mIconCache, false); + view = new PendingAppWidgetHostView(this, item, appWidgetInfo, false); } prepareAppWidget(view, item); } finally { diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java index 739e2045dd..b1c477cbda 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java @@ -21,13 +21,22 @@ import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_I import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.RemoteViews; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.util.Executors; +import com.android.launcher3.util.SafeCloseable; +import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; import java.util.function.IntConsumer; /** @@ -37,8 +46,7 @@ import java.util.function.IntConsumer; */ class LauncherAppWidgetHost extends AppWidgetHost { @NonNull - private final ArrayList - mProviderChangeListeners = new ArrayList<>(); + private final List mProviderChangeListeners; @NonNull private final Context mContext; @@ -46,33 +54,13 @@ class LauncherAppWidgetHost extends AppWidgetHost { @Nullable private final IntConsumer mAppWidgetRemovedCallback; - @NonNull - private final LauncherWidgetHolder mHolder; - public LauncherAppWidgetHost(@NonNull Context context, - @Nullable IntConsumer appWidgetRemovedCallback, @NonNull LauncherWidgetHolder holder) { + @Nullable IntConsumer appWidgetRemovedCallback, + List providerChangeListeners) { super(context, APPWIDGET_HOST_ID); mContext = context; mAppWidgetRemovedCallback = appWidgetRemovedCallback; - mHolder = holder; - } - - /** - * Add a listener that is triggered when the providers of the widgets are changed - * @param listener The listener that notifies when the providers changed - */ - public void addProviderChangeListener( - @NonNull LauncherWidgetHolder.ProviderChangedListener listener) { - mProviderChangeListeners.add(listener); - } - - /** - * Remove the specified listener from the host - * @param listener The listener that is to be removed from the host - */ - public void removeProviderChangeListener( - LauncherWidgetHolder.ProviderChangedListener listener) { - mProviderChangeListeners.remove(listener); + mProviderChangeListeners = providerChangeListeners; } @Override @@ -89,7 +77,7 @@ class LauncherAppWidgetHost extends AppWidgetHost { @NonNull public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { - return mHolder.onCreateView(context, appWidgetId); + return new ListenableHostView(context); } /** @@ -115,7 +103,10 @@ class LauncherAppWidgetHost extends AppWidgetHost { if (mAppWidgetRemovedCallback == null) { return; } - mAppWidgetRemovedCallback.accept(appWidgetId); + // Route the call via model thread, in case it comes while a loader-bind is in progress + Executors.MODEL_EXECUTOR.execute( + () -> Executors.MAIN_EXECUTOR.execute( + () -> mAppWidgetRemovedCallback.accept(appWidgetId))); } /** @@ -126,4 +117,36 @@ class LauncherAppWidgetHost extends AppWidgetHost { super.clearViews(); } + public static class ListenableHostView extends LauncherAppWidgetHostView { + + private Set mUpdateListeners = Collections.EMPTY_SET; + + ListenableHostView(Context context) { + super(context); + } + + @Override + public void updateAppWidget(RemoteViews remoteViews) { + super.updateAppWidget(remoteViews); + mUpdateListeners.forEach(Runnable::run); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(LauncherAppWidgetHostView.class.getName()); + } + + /** + * Adds a callback to be run everytime the provided app widget updates. + * @return a closable to remove this callback + */ + public SafeCloseable addUpdateListener(Runnable callback) { + if (mUpdateListeners == Collections.EMPTY_SET) { + mUpdateListeners = Collections.newSetFromMap(new WeakHashMap<>()); + } + mUpdateListeners.add(callback); + return () -> mUpdateListeners.remove(callback); + } + } } diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java index fbd48cf094..23127b3e4d 100644 --- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java +++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java @@ -42,8 +42,12 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.ResourceBasedOverride; +import com.android.launcher3.util.SafeCloseable; +import com.android.launcher3.widget.LauncherAppWidgetHost.ListenableHostView; import com.android.launcher3.widget.custom.CustomWidgetManager; +import java.util.ArrayList; +import java.util.List; import java.util.function.IntConsumer; /** @@ -61,15 +65,14 @@ public class LauncherWidgetHolder { FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED; @NonNull - private final Context mContext; + protected final Context mContext; @NonNull private final AppWidgetHost mWidgetHost; @NonNull protected final SparseArray mViews = new SparseArray<>(); - @NonNull - private final SparseArray mPendingViews = new SparseArray<>(); + protected final List mProviderChangedListeners = new ArrayList<>(); protected int mFlags = FLAG_STATE_IS_NORMAL; @@ -86,7 +89,8 @@ public class LauncherWidgetHolder { protected AppWidgetHost createHost( Context context, @Nullable IntConsumer appWidgetRemovedCallback) { - return new LauncherAppWidgetHost(context, appWidgetRemovedCallback, this); + return new LauncherAppWidgetHost( + context, appWidgetRemovedCallback, mProviderChangedListeners); } /** @@ -157,28 +161,6 @@ public class LauncherWidgetHolder { mViews.remove(appWidgetId); } - /** - * Add the pending view to the host for complete configuration in further steps - * @param appWidgetId The ID of the specified app widget - * @param view The {@link PendingAppWidgetHostView} of the app widget - */ - public void addPendingView(int appWidgetId, @NonNull PendingAppWidgetHostView view) { - mPendingViews.put(appWidgetId, view); - } - - /** - * @param appWidgetId The app widget id of the specified widget - * @return The {@link PendingAppWidgetHostView} of the widget if it exists, null otherwise - */ - @Nullable - protected PendingAppWidgetHostView getPendingView(int appWidgetId) { - return mPendingViews.get(appWidgetId); - } - - protected void removePendingView(int appWidgetId) { - mPendingViews.remove(appWidgetId); - } - /** * Called when the launcher is destroyed */ @@ -201,18 +183,18 @@ public class LauncherWidgetHolder { * Add a listener that is triggered when the providers of the widgets are changed * @param listener The listener that notifies when the providers changed */ - public void addProviderChangeListener(@NonNull ProviderChangedListener listener) { - LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost; - tempHost.addProviderChangeListener(listener); + public void addProviderChangeListener( + @NonNull LauncherWidgetHolder.ProviderChangedListener listener) { + MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.add(listener)); } /** * Remove the specified listener from the host * @param listener The listener that is to be removed from the host */ - public void removeProviderChangeListener(ProviderChangedListener listener) { - LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost; - tempHost.removeProviderChangeListener(listener); + public void removeProviderChangeListener( + LauncherWidgetHolder.ProviderChangedListener listener) { + MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.remove(listener)); } /** @@ -315,33 +297,52 @@ public class LauncherWidgetHolder { return mWidgetHost.getAppWidgetIds(); } + /** + * Adds a callback to be run everytime the provided app widget updates. + * @return a closable to remove this callback + */ + public SafeCloseable addOnUpdateListener( + int appWidgetId, LauncherAppWidgetProviderInfo appWidget, Runnable callback) { + if (createView(appWidgetId, appWidget) instanceof ListenableHostView lhv) { + return lhv.addUpdateListener(callback); + } + return () -> { }; + } + /** * Create a view for the specified app widget - * @param context The activity context for which the view is created + * * @param appWidgetId The ID of the widget - * @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget + * @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget * @return A view for the widget */ @NonNull - public AppWidgetHostView createView(@NonNull Context context, int appWidgetId, - @NonNull LauncherAppWidgetProviderInfo appWidget) { - + public AppWidgetHostView createView( + int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) { if (appWidget.isCustomWidget()) { - LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context); + LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(mContext); lahv.setAppWidget(0, appWidget); - CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv); + CustomWidgetManager.INSTANCE.get(mContext).onViewCreated(lahv); return lahv; - } else if ((mFlags & FLAG_LISTENING) == 0) { + } + + LauncherAppWidgetHostView view = createViewInternal(appWidgetId, appWidget); + mViews.put(appWidgetId, view); + return view; + } + + @NonNull + protected LauncherAppWidgetHostView createViewInternal( + int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) { + if ((mFlags & FLAG_LISTENING) == 0) { // Since the launcher hasn't started listening to widget updates, we can't simply call - // super.createView here because the later will make a binder call to retrieve + // host.createView here because the later will make a binder call to retrieve // RemoteViews from system process. - LauncherAppWidgetHostView view = - new PendingAppWidgetHostView(context, appWidgetId, appWidget); - mViews.put(appWidgetId, view); - return view; + return new PendingAppWidgetHostView(mContext, appWidgetId, appWidget); } else { try { - return mWidgetHost.createView(context, appWidgetId, appWidget); + return (LauncherAppWidgetHostView) mWidgetHost.createView( + mContext, appWidgetId, appWidget); } catch (Exception e) { if (!Utilities.isBinderSizeError(e)) { throw new RuntimeException(e); @@ -352,7 +353,7 @@ public class LauncherWidgetHolder { // will update. LauncherAppWidgetHostView view = mViews.get(appWidgetId); if (view == null) { - view = onCreateView(mContext, appWidgetId); + view = new ListenableHostView(mContext); } view.setAppWidget(appWidgetId, appWidget); view.switchToErrorView(); @@ -371,26 +372,6 @@ public class LauncherWidgetHolder { void notifyWidgetProvidersChanged(); } - /** - * Called to return a proper view when creating a view - * - * @param context The context for which the widget view is created - * @param appWidgetId The ID of the added widget - * @return A view for the specified app widget - */ - @NonNull - public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId) { - final LauncherAppWidgetHostView view; - if (getPendingView(appWidgetId) != null) { - view = getPendingView(appWidgetId); - removePendingView(appWidgetId); - } else { - view = new LauncherAppWidgetHostView(context); - } - mViews.put(appWidgetId, view); - return view; - } - /** * Clears all the views from the host */ diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java index 2bd4c7e843..6ad913e695 100644 --- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java @@ -18,6 +18,7 @@ package com.android.launcher3.widget; import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon; import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.content.Context; import android.graphics.Canvas; @@ -38,16 +39,18 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.RemoteViews; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.icons.FastBitmapDrawable; -import com.android.launcher3.icons.IconCache; 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.SafeCloseable; import com.android.launcher3.util.Themes; import java.util.List; @@ -64,12 +67,16 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView private static final int DEFERRED_ALPHA = 0x77; private final Rect mRect = new Rect(); - private OnClickListener mClickListener; + + private final LauncherAppWidgetProviderInfo mAppwidget; private final LauncherAppWidgetInfo mInfo; private final int mStartState; private final boolean mDisabledForSafeMode; private final CharSequence mLabel; + private OnClickListener mClickListener; + private SafeCloseable mOnDetachCleanup; + private int mDragFlags; private Drawable mCenterDrawable; @@ -81,8 +88,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView private Layout mSetupTextLayout; public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, - IconCache cache, boolean disabledForSafeMode) { - this(context, info, disabledForSafeMode, + @Nullable LauncherAppWidgetProviderInfo appWidget, boolean disabledForSafeMode) { + this(context, info, disabledForSafeMode, appWidget, context.getResources().getText(R.string.gadget_complete_setup_text)); super.updateAppWidget(null); @@ -91,16 +98,17 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView if (info.pendingItemInfo == null) { info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName(), info.user); - cache.updateIconInBackground(this, info.pendingItemInfo); + LauncherAppState.getInstance(context).getIconCache() + .updateIconInBackground(this, info.pendingItemInfo); } else { reapplyItemInfo(info.pendingItemInfo); } } public PendingAppWidgetHostView( - Context context, int appWidgetId, LauncherAppWidgetProviderInfo appWidget) { + Context context, int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) { this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider), false, - appWidget.label); + appWidget, appWidget.label); getBackground().mutate().setAlpha(DEFERRED_ALPHA); mCenterDrawable = new ColorDrawable(Color.TRANSPARENT); @@ -109,9 +117,11 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView } private PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, - boolean disabledForSafeMode, CharSequence label) { + boolean disabledForSafeMode, LauncherAppWidgetProviderInfo appwidget, + CharSequence label) { super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme)); + mAppwidget = appwidget; mInfo = info; mStartState = info.restoreStatus; mDisabledForSafeMode = disabledForSafeMode; @@ -128,9 +138,40 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView @Override public void updateAppWidget(RemoteViews remoteViews) { + checkIfRestored(); + } + + private void checkIfRestored() { WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(getContext()); if (widgetManagerHelper.isAppWidgetRestored(mInfo.appWidgetId)) { - reInflate(); + MAIN_EXECUTOR.getHandler().post(this::reInflate); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + 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. + if (mOnDetachCleanup != null) { + mOnDetachCleanup.close(); + } + mOnDetachCleanup = mLauncher.getAppWidgetHolder() + .addOnUpdateListener(mInfo.appWidgetId, mAppwidget, this::checkIfRestored); + checkIfRestored(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mOnDetachCleanup != null) { + mOnDetachCleanup.close(); + mOnDetachCleanup = null; } } diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index b18cd471cb..1cc00ef6cb 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -1,7 +1,6 @@ package com.android.launcher3.widget; import android.appwidget.AppWidgetHostView; -import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.util.Log; @@ -117,7 +116,7 @@ public class WidgetHostViewLoader implements DragController.DragListener { return; } AppWidgetHostView hostView = mLauncher.getAppWidgetHolder().createView( - (Context) mLauncher, mWidgetLoadingId, pInfo); + mWidgetLoadingId, pInfo); mInfo.boundWidget = hostView; // We used up the widget Id in binding the above view.