summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author Sunny Goyal <sunnygoyal@google.com> 2025-03-13 23:20:29 -0700
committer Sunny Goyal <sunnygoyal@google.com> 2025-03-14 14:19:29 -0700
commit66916cda10669dd0517615039b6d160bbf11c62a (patch)
tree55027bb3e12f73b72146a31345b163bdd21cec58 /src
parent0c762ac84f78c7d700a6c20540bd7bef571c0364 (diff)
Moving LauncherAppWidgetHolder to dagger
This would allow customizing the widget holder in LauncherPreview Bug: 361850561 Test: Updated tests and presubmit Flag: EXEMPT dagger Change-Id: I32491169188992453693048986c57cb780fdf1d8
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/Launcher.java12
-rw-r--r--src/com/android/launcher3/config/FeatureFlags.java5
-rw-r--r--src/com/android/launcher3/dagger/LauncherAppModule.java1
-rw-r--r--src/com/android/launcher3/dagger/LauncherBaseAppComponent.java2
-rw-r--r--src/com/android/launcher3/widget/LauncherAppWidgetHost.java68
-rw-r--r--src/com/android/launcher3/widget/LauncherWidgetHolder.java161
-rw-r--r--src/com/android/launcher3/widget/ListenableAppWidgetHost.kt72
-rw-r--r--src/com/android/launcher3/widget/PendingAppWidgetHostView.java2
8 files changed, 163 insertions, 160 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5c9392d69d..d5b3ed5645 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -532,11 +532,14 @@ public class Launcher extends StatefulActivity<LauncherState>
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
+ mAppWidgetManager = new WidgetManagerHelper(this);
+ mAppWidgetHolder = LauncherWidgetHolder.newInstance(this);
+ mAppWidgetHolder.setAppWidgetRemovedCallback(
+ appWidgetId -> getWorkspace().removeWidget(appWidgetId));
+
setupViews();
updateDisallowBack();
- mAppWidgetManager = new WidgetManagerHelper(this);
- mAppWidgetHolder = createAppWidgetHolder();
mAppWidgetHolder.startListening();
mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null));
mItemInflater = new ItemInflater<>(this, mAppWidgetHolder, getItemOnClickListener(),
@@ -1614,11 +1617,6 @@ public class Launcher extends StatefulActivity<LauncherState>
return instance;
}
- protected LauncherWidgetHolder createAppWidgetHolder() {
- return LauncherWidgetHolder.HolderFactory.newFactory(this).newInstance(
- this, appWidgetId -> getWorkspace().removeWidget(appWidgetId));
- }
-
@Override
protected void onNewIntent(Intent intent) {
if (Utilities.isRunningInTestHarness()) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 44dcc06791..d987841410 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -163,11 +163,6 @@ public final class FeatureFlags {
"ENABLE_WIDGET_TRANSITION_FOR_RESIZING", DISABLED,
"Enable widget transition animation when resizing the widgets");
- // TODO(Block 25): Clean up flags
- public static final BooleanFlag ENABLE_WIDGET_HOST_IN_BACKGROUND = getDebugFlag(270394384,
- "ENABLE_WIDGET_HOST_IN_BACKGROUND", ENABLED,
- "Enable background widget updates listening for widget holder");
-
// TODO(Block 27): Clean up flags
public static final BooleanFlag ENABLE_OVERLAY_CONNECTION_OPTIM = getDebugFlag(270392629,
"ENABLE_OVERLAY_CONNECTION_OPTIM", DISABLED,
diff --git a/src/com/android/launcher3/dagger/LauncherAppModule.java b/src/com/android/launcher3/dagger/LauncherAppModule.java
index c58a414304..0fd32190c4 100644
--- a/src/com/android/launcher3/dagger/LauncherAppModule.java
+++ b/src/com/android/launcher3/dagger/LauncherAppModule.java
@@ -23,6 +23,7 @@ import dagger.Module;
ApiWrapperModule.class,
PluginManagerWrapperModule.class,
StaticObjectModule.class,
+ WidgetModule.class,
AppModule.class
})
public class LauncherAppModule {
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index c49909772e..f86772e5f5 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -46,6 +46,7 @@ import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WallpaperColorHints;
import com.android.launcher3.util.window.RefreshRateTracker;
import com.android.launcher3.util.window.WindowManagerProxy;
+import com.android.launcher3.widget.LauncherWidgetHolder.WidgetHolderFactory;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import dagger.BindsInstance;
@@ -89,6 +90,7 @@ public interface LauncherBaseAppComponent {
WidgetsFilterDataProvider getWidgetsFilterDataProvider();
LoaderCursorFactory getLoaderCursorFactory();
+ WidgetHolderFactory getWidgetHolderFactory();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 91b899c2ef..63d2954e70 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -16,8 +16,6 @@
package com.android.launcher3.widget;
-import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
-
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
@@ -26,49 +24,18 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.util.Executors;
-import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.IntConsumer;
-
/**
* Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
* which correctly captures all long-press events. This ensures that users can
* always pick up and move widgets.
*/
-class LauncherAppWidgetHost extends AppWidgetHost {
- @NonNull
- private final List<ProviderChangedListener> mProviderChangeListeners;
-
- @NonNull
- private final Context mContext;
-
- @Nullable
- private final IntConsumer mAppWidgetRemovedCallback;
+class LauncherAppWidgetHost extends ListenableAppWidgetHost {
@Nullable
private ListenableHostView mViewToRecycle;
- public LauncherAppWidgetHost(@NonNull Context context,
- @Nullable IntConsumer appWidgetRemovedCallback,
- List<ProviderChangedListener> providerChangeListeners) {
- super(context, APPWIDGET_HOST_ID);
- mContext = context;
- mAppWidgetRemovedCallback = appWidgetRemovedCallback;
- mProviderChangeListeners = providerChangeListeners;
- }
-
- @Override
- protected void onProvidersChanged() {
- if (!mProviderChangeListeners.isEmpty()) {
- for (LauncherWidgetHolder.ProviderChangedListener callback :
- new ArrayList<>(mProviderChangeListeners)) {
- callback.notifyWidgetProvidersChanged();
- }
- }
+ LauncherAppWidgetHost(@NonNull Context context, int appWidgetId) {
+ super(context, appWidgetId);
}
/**
@@ -94,35 +61,6 @@ class LauncherAppWidgetHost extends AppWidgetHost {
}
/**
- * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
- */
- @Override
- protected void onProviderChanged(int appWidgetId, @NonNull AppWidgetProviderInfo appWidget) {
- LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
- mContext, appWidget);
- super.onProviderChanged(appWidgetId, info);
- // The super method updates the dimensions of the providerInfo. Update the
- // launcher spans accordingly.
- info.initSpans(mContext, LauncherAppState.getIDP(mContext));
- }
-
- /**
- * Called on an appWidget is removed for a widgetId
- *
- * @param appWidgetId TODO: make this override when SDK is updated
- */
- @Override
- public void onAppWidgetRemoved(int appWidgetId) {
- if (mAppWidgetRemovedCallback == null) {
- return;
- }
- // 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)));
- }
-
- /**
* The same as super.clearViews(), except with the scope exposed
*/
@Override
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 78197e2dd0..642f35abcd 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -20,10 +20,9 @@ import static android.app.Activity.RESULT_CANCELED;
import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
import static com.android.launcher3.Flags.enableWorkspaceInflation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
+import static com.android.launcher3.widget.ListenableAppWidgetHost.getWidgetHolderExecutor;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -32,6 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Looper;
+import android.util.Log;
import android.util.SparseArray;
import android.widget.Toast;
@@ -43,18 +43,23 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.dagger.LauncherComponentProvider;
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.LooperExecutor;
-import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.ListenableAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.widget.custom.CustomWidgetManager;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
import java.util.function.IntConsumer;
/**
@@ -62,51 +67,57 @@ import java.util.function.IntConsumer;
* background.
*/
public class LauncherWidgetHolder {
+
+ private static final String TAG = "LauncherWidgetHolder";
+
public static final int APPWIDGET_HOST_ID = 1024;
protected static final int FLAG_LISTENING = 1;
protected static final int FLAG_STATE_IS_NORMAL = 1 << 1;
protected static final int FLAG_ACTIVITY_STARTED = 1 << 2;
protected static final int FLAG_ACTIVITY_RESUMED = 1 << 3;
+
private static final int FLAGS_SHOULD_LISTEN =
FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
+ // TODO(b/191735836): Replace with ActivityOptions.KEY_SPLASH_SCREEN_STYLE when un-hidden
+ private static final String KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle";
+ // TODO(b/191735836): Replace with SplashScreen.SPLASH_SCREEN_STYLE_EMPTY when un-hidden
+ private static final int SPLASH_SCREEN_STYLE_EMPTY = 0;
+
@NonNull
protected final Context mContext;
@NonNull
- private final AppWidgetHost mWidgetHost;
+ protected final ListenableAppWidgetHost mWidgetHost;
@NonNull
protected final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
- protected final List<ProviderChangedListener> mProviderChangedListeners = new ArrayList<>();
+
+ /** package visibility */
+ final List<ProviderChangedListener> mProviderChangedListeners = new ArrayList<>();
protected AtomicInteger mFlags = new AtomicInteger(FLAG_STATE_IS_NORMAL);
- // TODO(b/191735836): Replace with ActivityOptions.KEY_SPLASH_SCREEN_STYLE when un-hidden
- private static final String KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle";
- // TODO(b/191735836): Replace with SplashScreen.SPLASH_SCREEN_STYLE_EMPTY when un-hidden
- private static final int SPLASH_SCREEN_STYLE_EMPTY = 0;
+ @Nullable
+ private Consumer<LauncherAppWidgetHostView> mOnViewCreationCallback;
- protected LauncherWidgetHolder(@NonNull Context context,
- @Nullable IntConsumer appWidgetRemovedCallback) {
- mContext = context;
- mWidgetHost = createHost(context, appWidgetRemovedCallback);
- }
+ /** package visibility */
+ @Nullable IntConsumer mAppWidgetRemovedCallback;
- protected AppWidgetHost createHost(
- Context context, @Nullable IntConsumer appWidgetRemovedCallback) {
- return new LauncherAppWidgetHost(
- context, appWidgetRemovedCallback, mProviderChangedListeners);
+ @AssistedInject
+ protected LauncherWidgetHolder(@Assisted("UI_CONTEXT") @NonNull Context context) {
+ this(context, new LauncherAppWidgetHost(context, APPWIDGET_HOST_ID));
}
- protected LooperExecutor getWidgetHolderExecutor() {
- return UI_HELPER_EXECUTOR;
+ protected LauncherWidgetHolder(
+ @NonNull Context context, @NonNull ListenableAppWidgetHost appWidgetHost) {
+ mContext = context;
+ mWidgetHost = appWidgetHost;
+ MAIN_EXECUTOR.execute(() -> mWidgetHost.getHolders().add(this));
}
- /**
- * Starts listening to the widget updates from the server side
- */
+ /** Starts listening to the widget updates from the server side */
public void startListening() {
if (!WIDGETS_ENABLED) {
return;
@@ -127,13 +138,11 @@ public class LauncherWidgetHolder {
// TODO: Investigate why widgetHost.startListening() always return non-empty updates
setListeningFlag(true);
- MAIN_EXECUTOR.execute(() -> updateDeferredView());
+ MAIN_EXECUTOR.execute(this::updateDeferredView);
});
}
- /**
- * Update any views which have been deferred because the host was not listening.
- */
+ /** Update any views which have been deferred because the host was not listening */
protected void updateDeferredView() {
// Update any views which have been deferred because the host was not listening.
// We go in reverse order and inflate any deferred or cached widget
@@ -180,7 +189,14 @@ public class LauncherWidgetHolder {
* Called when the launcher is destroyed
*/
public void destroy() {
- // No-op
+ try {
+ MAIN_EXECUTOR.submit(() -> {
+ clearViews();
+ mWidgetHost.getHolders().remove(this);
+ }).get();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to remove self from holder list", e);
+ }
}
/**
@@ -198,8 +214,7 @@ 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 LauncherWidgetHolder.ProviderChangedListener listener) {
+ public void addProviderChangeListener(@NonNull ProviderChangedListener listener) {
MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.add(listener));
}
@@ -207,12 +222,23 @@ public class LauncherWidgetHolder {
* 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) {
+ public void removeProviderChangeListener(ProviderChangedListener listener) {
MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.remove(listener));
}
/**
+ * Sets a callbacks for whenever a widget view is created
+ */
+ public void setOnViewCreationCallback(@Nullable Consumer<LauncherAppWidgetHostView> callback) {
+ mOnViewCreationCallback = callback;
+ }
+
+ /** Sets a callback for listening app widget removals */
+ public void setAppWidgetRemovedCallback(@Nullable IntConsumer callback) {
+ mAppWidgetRemovedCallback = callback;
+ }
+
+ /**
* Starts the configuration activity for the widget
* @param activity The activity in which to start the configuration page
* @param widgetId The ID of the widget
@@ -284,9 +310,7 @@ public class LauncherWidgetHolder {
activity.startActivityForResult(intent, requestCode);
}
- /**
- * Stop the host from listening to the widget updates
- */
+ /** Stop the host from listening to the widget updates */
public void stopListening() {
if (!WIDGETS_ENABLED) {
return;
@@ -298,8 +322,8 @@ public class LauncherWidgetHolder {
}
/**
- * Update {@link FLAG_LISTENING} on {@link mFlags} after making binder calls from
- * {@link sWidgetHost}.
+ * Update {@link #FLAG_LISTENING} on {@link #mFlags} after making binder calls from
+ * {@link #mWidgetHost}.
*/
@WorkerThread
protected void setListeningFlag(final boolean isListening) {
@@ -350,6 +374,7 @@ public class LauncherWidgetHolder {
}
LauncherAppWidgetHostView view = createViewInternal(appWidgetId, appWidget);
+ if (mOnViewCreationCallback != null) mOnViewCreationCallback.accept(view);
// Do not update mViews on a background thread call, as the holder is not thread safe.
if (!enableWorkspaceInflation() || Looper.myLooper() == Looper.getMainLooper()) {
mViews.put(appWidgetId, view);
@@ -368,8 +393,8 @@ public class LauncherWidgetHolder {
// Binder can also inflate placeholder widgets in case of backup-restore. Skip
// attaching such widgets
- boolean isRealWidget = ((view instanceof PendingAppWidgetHostView pw)
- ? pw.isDeferredWidget() : true)
+ boolean isRealWidget = (!(view instanceof PendingAppWidgetHostView pw)
+ || pw.isDeferredWidget())
&& view.getAppWidgetInfo() != null;
if (isRealWidget && mViews.get(view.getAppWidgetId()) != view) {
view = recycleExistingView(view);
@@ -446,28 +471,13 @@ public class LauncherWidgetHolder {
}
}
- /**
- * Listener for getting notifications on provider changes.
- */
- public interface ProviderChangedListener {
- /**
- * Notify the listener that the providers have changed
- */
- void notifyWidgetProvidersChanged();
- }
-
- /**
- * Clears all the views from the host
- */
+ /** Clears all the views from the host */
public void clearViews() {
- LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
- tempHost.clearViews();
+ ((LauncherAppWidgetHost) mWidgetHost).clearViews();
mViews.clear();
}
- /**
- * Clears all the internal widget views
- */
+ /** Clears all the internal widget views */
public void clearWidgetViews() {
clearViews();
}
@@ -514,32 +524,19 @@ public class LauncherWidgetHolder {
* Returns the new LauncherWidgetHolder instance
*/
public static LauncherWidgetHolder newInstance(Context context) {
- return HolderFactory.newFactory(context).newInstance(context, null);
+ return LauncherComponentProvider.get(context).getWidgetHolderFactory().newInstance(context);
}
- /**
- * A factory class that generates new instances of {@code LauncherWidgetHolder}
- */
- public static class HolderFactory implements ResourceBasedOverride {
-
- /**
- * @param context The context of the caller
- * @param appWidgetRemovedCallback The callback that is called when widgets are removed
- * @return A new instance of {@code LauncherWidgetHolder}
- */
- public LauncherWidgetHolder newInstance(@NonNull Context context,
- @Nullable IntConsumer appWidgetRemovedCallback) {
- return new LauncherWidgetHolder(context, appWidgetRemovedCallback);
- }
+ /** A factory that generates new instances of {@code LauncherWidgetHolder} */
+ public interface WidgetHolderFactory {
- /**
- * @param context The context of the caller
- * @return A new instance of factory class for widget holders. If not specified, returning
- * {@code HolderFactory} by default.
- */
- public static HolderFactory newFactory(Context context) {
- return Overrides.getObject(
- HolderFactory.class, context, R.string.widget_holder_factory_class);
- }
+ LauncherWidgetHolder newInstance(@NonNull Context context);
+ }
+
+ /** A factory that generates new instances of {@code LauncherWidgetHolder} */
+ @AssistedFactory
+ public interface WidgetHolderFactoryImpl extends WidgetHolderFactory {
+
+ LauncherWidgetHolder newInstance(@Assisted("UI_CONTEXT") @NonNull Context context);
}
}
diff --git a/src/com/android/launcher3/widget/ListenableAppWidgetHost.kt b/src/com/android/launcher3/widget/ListenableAppWidgetHost.kt
new file mode 100644
index 0000000000..58bf0aa869
--- /dev/null
+++ b/src/com/android/launcher3/widget/ListenableAppWidgetHost.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.widget
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Context
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.LooperExecutor
+
+open class ListenableAppWidgetHost(private val ctx: Context, hostId: Int) :
+ AppWidgetHost(ctx, hostId) {
+
+ protected val holders = mutableListOf<LauncherWidgetHolder>()
+
+ override fun onProvidersChanged() {
+ MAIN_EXECUTOR.execute {
+ holders.forEach { holder ->
+ // Listeners might remove themselves from the list during the iteration.
+ // Creating a copy of the list to avoid exceptions for concurrent modification.
+ holder.mProviderChangedListeners.toList().forEach {
+ it.notifyWidgetProvidersChanged()
+ }
+ }
+ }
+ }
+
+ override fun onAppWidgetRemoved(appWidgetId: Int) {
+ // Route the call via model thread, in case it comes while a loader-bind is in progress
+ MODEL_EXECUTOR.execute {
+ MAIN_EXECUTOR.execute {
+ holders.forEach { it.mAppWidgetRemovedCallback?.accept(appWidgetId) }
+ }
+ }
+ }
+
+ override fun onProviderChanged(appWidgetId: Int, appWidget: AppWidgetProviderInfo) {
+ val info = LauncherAppWidgetProviderInfo.fromProviderInfo(ctx, appWidget)
+ super.onProviderChanged(appWidgetId, info)
+ // The super method updates the dimensions of the providerInfo. Update the
+ // launcher spans accordingly.
+ info.initSpans(ctx, InvariantDeviceProfile.INSTANCE.get(ctx))
+ }
+
+ /** Listener for getting notifications on provider changes. */
+ fun interface ProviderChangedListener {
+ /** Notify the listener that the providers have changed */
+ fun notifyWidgetProvidersChanged()
+ }
+
+ companion object {
+
+ @JvmStatic val widgetHolderExecutor: LooperExecutor = Executors.UI_HELPER_EXECUTOR
+ }
+}
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index cd8e4571e5..1c29f8907f 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -66,7 +66,7 @@ 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 com.android.launcher3.widget.ListenableAppWidgetHost.ProviderChangedListener;
import java.util.List;