diff options
author | 2025-03-13 23:20:29 -0700 | |
---|---|---|
committer | 2025-03-14 14:19:29 -0700 | |
commit | 66916cda10669dd0517615039b6d160bbf11c62a (patch) | |
tree | 55027bb3e12f73b72146a31345b163bdd21cec58 | |
parent | 0c762ac84f78c7d700a6c20540bd7bef571c0364 (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
20 files changed, 288 insertions, 371 deletions
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml index 1ce28cf995..49cee0fa86 100644 --- a/quickstep/res/values/config.xml +++ b/quickstep/res/values/config.xml @@ -23,7 +23,6 @@ <string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string> <string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string> - <string name="widget_holder_factory_class" translatable="false">com.android.launcher3.uioverrides.QuickstepWidgetHolder$QuickstepHolderFactory</string> <string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string> <string name="app_launch_tracker_class" translatable="false">com.android.launcher3.appprediction.PredictionAppTracker</string> <string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string> diff --git a/quickstep/src/com/android/launcher3/dagger/Modules.kt b/quickstep/src/com/android/launcher3/dagger/Modules.kt index 52be4132f2..7671a82869 100644 --- a/quickstep/src/com/android/launcher3/dagger/Modules.kt +++ b/quickstep/src/com/android/launcher3/dagger/Modules.kt @@ -16,11 +16,13 @@ package com.android.launcher3.dagger +import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepWidgetHolderFactory import com.android.launcher3.uioverrides.SystemApiWrapper import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl import com.android.launcher3.util.ApiWrapper import com.android.launcher3.util.PluginManagerWrapper import com.android.launcher3.util.window.WindowManagerProxy +import com.android.launcher3.widget.LauncherWidgetHolder.WidgetHolderFactory import com.android.quickstep.util.GestureExclusionManager import com.android.quickstep.util.SystemWindowManagerProxy import dagger.Binds @@ -40,6 +42,13 @@ abstract class ApiWrapperModule { } @Module +abstract class WidgetModule { + + @Binds + abstract fun bindWidgetHolderFactory(factor: QuickstepWidgetHolderFactory): WidgetHolderFactory +} + +@Module abstract class PluginManagerWrapperModule { @Binds abstract fun bindPluginManagerWrapper(impl: PluginManagerWrapperImpl): PluginManagerWrapper diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java deleted file mode 100644 index 45813ce52e..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2022 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.uioverrides; - -import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID; - -import android.appwidget.AppWidgetHost; -import android.appwidget.AppWidgetProviderInfo; -import android.content.Context; -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; - -import java.util.function.IntConsumer; - -/** - * {@link AppWidgetHost} that is used to receive the changes to the widgets without - * storing any {@code Activity} info like that of the launcher. - */ -final class QuickstepAppWidgetHost extends AppWidgetHost { - private final @NonNull Context mContext; - private final @NonNull IntConsumer mAppWidgetRemovedCallback; - private final @NonNull LauncherWidgetHolder.ProviderChangedListener mProvidersChangedListener; - - QuickstepAppWidgetHost(@NonNull Context context, @NonNull IntConsumer appWidgetRemovedCallback, - @NonNull LauncherWidgetHolder.ProviderChangedListener listener, - @NonNull Looper looper) { - super(context, APPWIDGET_HOST_ID, null, looper); - mContext = context; - mAppWidgetRemovedCallback = appWidgetRemovedCallback; - mProvidersChangedListener = listener; - } - - @Override - protected void onProvidersChanged() { - mProvidersChangedListener.notifyWidgetProvidersChanged(); - } - - @Override - public void onAppWidgetRemoved(int 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 - 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)); - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHostProvider.kt b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHostProvider.kt new file mode 100644 index 0000000000..1387cb7fce --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHostProvider.kt @@ -0,0 +1,48 @@ +/* + * 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.uioverrides + +import android.app.ActivityThread +import android.content.Context +import android.content.ContextWrapper +import com.android.launcher3.BuildConfig +import com.android.launcher3.util.LooperExecutor +import com.android.launcher3.widget.LauncherWidgetHolder +import com.android.launcher3.widget.ListenableAppWidgetHost + +object QuickstepAppWidgetHostProvider { + + /** Static widget host which is always listening and is lazily created */ + @JvmStatic + val staticQuickstepHost: ListenableAppWidgetHost by lazy { + ListenableAppWidgetHost( + LooperContext( + ActivityThread.currentApplication(), + ListenableAppWidgetHost.widgetHolderExecutor, + ), + LauncherWidgetHolder.APPWIDGET_HOST_ID, + ) + .apply { if (BuildConfig.WIDGETS_ENABLED) startListening() } + } + + private class LooperContext(ctx: Context, val executor: LooperExecutor) : ContextWrapper(ctx) { + + override fun getMainLooper() = executor.looper + + override fun getMainExecutor() = executor + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java index 26a1322ea6..2f61eab845 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java @@ -34,8 +34,11 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.widget.LauncherAppWidgetHostView; +import java.util.function.Consumer; + /** Provides a Quickstep specific animation when launching an activity from an app widget. */ -class QuickstepInteractionHandler implements RemoteViews.InteractionHandler { +class QuickstepInteractionHandler implements RemoteViews.InteractionHandler, + Consumer<LauncherAppWidgetHostView> { private static final String TAG = "QuickstepInteractionHandler"; @@ -45,6 +48,11 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler { mLauncher = launcher; } + @Override + public void accept(LauncherAppWidgetHostView host) { + host.setInteractionHandler(this); + } + @SuppressWarnings("NewApi") @Override public boolean onInteraction(View view, PendingIntent pendingIntent, diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 806b8ab1cc..605fd312d0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -143,7 +143,6 @@ import com.android.launcher3.taskbar.TaskbarManager; import com.android.launcher3.taskbar.TaskbarUIController; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; -import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory; import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory; import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController; @@ -172,7 +171,6 @@ import com.android.launcher3.util.StableViewInfo; import com.android.launcher3.util.StartActivityParams; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.FloatingIconView; -import com.android.launcher3.widget.LauncherWidgetHolder; import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.OverviewComponentObserver; import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener; @@ -298,6 +296,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, @Override protected void setupViews() { + getAppWidgetHolder().setOnViewCreationCallback(new QuickstepInteractionHandler(this)); super.setupViews(); mActionsView = findViewById(R.id.overview_actions_view); @@ -726,15 +725,6 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, } @Override - protected LauncherWidgetHolder createAppWidgetHolder() { - final QuickstepHolderFactory factory = - (QuickstepHolderFactory) LauncherWidgetHolder.HolderFactory.newFactory(this); - return factory.newInstance(this, - appWidgetId -> getWorkspace().removeWidget(appWidgetId), - new QuickstepInteractionHandler(this)); - } - - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java index 56fc4d1c0b..9970a7dcf8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java @@ -16,13 +16,14 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED; +import static com.android.launcher3.uioverrides.QuickstepAppWidgetHostProvider.getStaticQuickstepHost; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.widget.ListenableAppWidgetHost.getWidgetHolderExecutor; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; -import android.util.Log; import android.util.SparseArray; import android.widget.RemoteViews; @@ -31,28 +32,26 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import com.android.launcher3.config.FeatureFlags; 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 java.util.ArrayList; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.WeakHashMap; import java.util.function.BiConsumer; -import java.util.function.IntConsumer; /** * {@link LauncherWidgetHolder} that puts the app widget host in the background */ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { - private static final String TAG = "QuickstepWidgetHolder"; - private static final UpdateKey<AppWidgetProviderInfo> KEY_PROVIDER_UPDATE = AppWidgetHostView::onUpdateProviderInfo; private static final UpdateKey<RemoteViews> KEY_VIEWS_UPDATE = @@ -60,51 +59,17 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { private static final UpdateKey<Integer> KEY_VIEW_DATA_CHANGED = AppWidgetHostView::onViewDataChanged; - private static final List<QuickstepWidgetHolder> sHolders = new ArrayList<>(); private static final SparseArray<QuickstepWidgetHolderListener> sListeners = new SparseArray<>(); - private static AppWidgetHost sWidgetHost = null; - private final UpdateHandler mUpdateHandler = this::onWidgetUpdate; - private final @Nullable RemoteViews.InteractionHandler mInteractionHandler; - - private final @NonNull IntConsumer mAppWidgetRemovedCallback; // Map to all pending updated keyed with appWidgetId; private final SparseArray<PendingUpdate> mPendingUpdateMap = new SparseArray<>(); - private QuickstepWidgetHolder(@NonNull Context context, - @Nullable IntConsumer appWidgetRemovedCallback, - @Nullable RemoteViews.InteractionHandler interactionHandler) { - super(context, appWidgetRemovedCallback); - mAppWidgetRemovedCallback = appWidgetRemovedCallback != null ? appWidgetRemovedCallback - : i -> {}; - mInteractionHandler = interactionHandler; - MAIN_EXECUTOR.execute(() -> sHolders.add(this)); - } - - @Override - @NonNull - protected AppWidgetHost createHost(@NonNull Context context, - @Nullable IntConsumer appWidgetRemovedCallback) { - if (sWidgetHost == null) { - sWidgetHost = new QuickstepAppWidgetHost(context.getApplicationContext(), - i -> MAIN_EXECUTOR.execute(() -> - sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))), - () -> MAIN_EXECUTOR.execute(() -> - sHolders.forEach(h -> - // Listeners might remove themselves from the list during the - // iteration. Creating a copy of the list to avoid exceptions - // for concurrent modification. - new ArrayList<>(h.mProviderChangedListeners).forEach( - ProviderChangedListener::notifyWidgetProvidersChanged))), - getWidgetHolderExecutor().getLooper()); - if (WIDGETS_ENABLED) { - sWidgetHost.startListening(); - } - } - return sWidgetHost; + @AssistedInject + public QuickstepWidgetHolder(@Assisted("UI_CONTEXT") @NonNull Context context) { + super(context, getStaticQuickstepHost()); } @Override @@ -168,21 +133,6 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { sListeners.remove(appWidgetId); } - /** - * Called when the launcher is destroyed - */ - @Override - public void destroy() { - try { - MAIN_EXECUTOR.submit(() -> { - clearViews(); - sHolders.remove(this); - }).get(); - } catch (Exception e) { - Log.e(TAG, "Failed to remove self from holder list", e); - } - } - @Override protected boolean shouldListen(int flags) { return (flags & (FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED)) @@ -199,7 +149,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { } getWidgetHolderExecutor().execute(() -> { - sWidgetHost.setAppWidgetHidden(); + mWidgetHost.setAppWidgetHidden(); setListeningFlag(false); }); } @@ -239,7 +189,6 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { protected LauncherAppWidgetHostView createViewInternal( int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) { LauncherAppWidgetHostView widgetView = new LauncherAppWidgetHostView(mContext); - widgetView.setInteractionHandler(mInteractionHandler); widgetView.setAppWidget(appWidgetId, appWidget); widgetView.updateAppWidget(getHolderListener(appWidgetId).addHolder(mUpdateHandler)); return widgetView; @@ -249,7 +198,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId); if (listener == null) { listener = new QuickstepWidgetHolderListener(appWidgetId); - sWidgetHost.setListener(appWidgetId, listener); + getStaticQuickstepHost().setListener(appWidgetId, listener); sListeners.put(appWidgetId, listener); } return listener; @@ -322,44 +271,13 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { } } - /** - * {@code HolderFactory} subclass that takes an interaction handler as one of the parameters - * when creating a new instance. - */ - public static class QuickstepHolderFactory extends HolderFactory { - @SuppressWarnings("unused") - public QuickstepHolderFactory(Context context) { } + /** A factory that generates new instances of {@code LauncherWidgetHolder} */ + @AssistedFactory + public interface QuickstepWidgetHolderFactory extends WidgetHolderFactory { @Override - public LauncherWidgetHolder newInstance(@NonNull Context context, - @Nullable IntConsumer appWidgetRemovedCallback) { - return newInstance(context, appWidgetRemovedCallback, null); - } - - /** - * @param context The context of the caller - * @param appWidgetRemovedCallback The callback that is called when widgets are removed - * @param interactionHandler The interaction handler when the widgets are clicked - * @return A new {@link LauncherWidgetHolder} instance - */ - public LauncherWidgetHolder newInstance(@NonNull Context context, - @Nullable IntConsumer appWidgetRemovedCallback, - @Nullable RemoteViews.InteractionHandler interactionHandler) { - - if (!FeatureFlags.ENABLE_WIDGET_HOST_IN_BACKGROUND.get()) { - return new LauncherWidgetHolder(context, appWidgetRemovedCallback) { - @Override - protected AppWidgetHost createHost(Context context, - @Nullable IntConsumer appWidgetRemovedCallback) { - AppWidgetHost host = super.createHost(context, appWidgetRemovedCallback); - host.setInteractionHandler(interactionHandler); - return host; - } - }; - } - return new QuickstepWidgetHolder(context, appWidgetRemovedCallback, interactionHandler); - } + QuickstepWidgetHolder newInstance(@Assisted("UI_CONTEXT") @NonNull Context context); } private interface UpdateKey<T> extends BiConsumer<AppWidgetHostView, T> { } diff --git a/res/values/config.xml b/res/values/config.xml index 3b48c9eddd..fc636a5055 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -68,7 +68,6 @@ <string name="app_launch_tracker_class" translatable="false"></string> <string name="test_information_handler_class" translatable="false"></string> <string name="secondary_display_predictions_class" translatable="false"></string> - <string name="widget_holder_factory_class" translatable="false"></string> <string name="taskbar_search_session_controller_class" translatable="false"></string> <string name="taskbar_model_callbacks_factory_class" translatable="false"></string> <string name="taskbar_view_callbacks_factory_class" translatable="false"></string> 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; diff --git a/src_no_quickstep/com/android/launcher3/dagger/Modules.kt b/src_no_quickstep/com/android/launcher3/dagger/Modules.kt index c3bf7c532b..7dbe9c3119 100644 --- a/src_no_quickstep/com/android/launcher3/dagger/Modules.kt +++ b/src_no_quickstep/com/android/launcher3/dagger/Modules.kt @@ -16,6 +16,9 @@ package com.android.launcher3.dagger +import com.android.launcher3.widget.LauncherWidgetHolder.WidgetHolderFactory +import com.android.launcher3.widget.LauncherWidgetHolder.WidgetHolderFactoryImpl +import dagger.Binds import dagger.Module private object Modules {} @@ -24,6 +27,12 @@ private object Modules {} @Module abstract class ApiWrapperModule {} +@Module +abstract class WidgetModule { + @Binds + abstract fun bindWidgetHolderFactory(factor: WidgetHolderFactoryImpl): WidgetHolderFactory +} + @Module abstract class PluginManagerWrapperModule {} @Module object StaticObjectModule {} diff --git a/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt b/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt index b66a9d3330..a76ccf015e 100644 --- a/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt +++ b/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt @@ -21,6 +21,7 @@ import com.android.launcher3.LauncherPrefs import com.android.launcher3.dagger.ApiWrapperModule import com.android.launcher3.dagger.AppModule import com.android.launcher3.dagger.StaticObjectModule +import com.android.launcher3.dagger.WidgetModule import com.android.launcher3.dagger.WindowManagerProxyModule import dagger.Binds import dagger.Module @@ -39,15 +40,27 @@ abstract class FakePrefsModule { ApiWrapperModule::class, WindowManagerProxyModule::class, StaticObjectModule::class, + WidgetModule::class, AppModule::class, ] ) class AllModulesForTest /** All modules except the WMProxy */ -@Module(includes = [ApiWrapperModule::class, StaticObjectModule::class, AppModule::class]) +@Module( + includes = + [ApiWrapperModule::class, StaticObjectModule::class, AppModule::class, WidgetModule::class] +) class AllModulesMinusWMProxy /** All modules except the ApiWrapper */ -@Module(includes = [WindowManagerProxyModule::class, StaticObjectModule::class, AppModule::class]) +@Module( + includes = + [ + WindowManagerProxyModule::class, + StaticObjectModule::class, + AppModule::class, + WidgetModule::class, + ] +) class AllModulesMinusApiWrapper diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/LauncherAppWidgetHostTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherAppWidgetHostTest.kt index 79b493a364..d5d1f4af93 100644 --- a/tests/multivalentTests/src/com/android/launcher3/widget/LauncherAppWidgetHostTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherAppWidgetHostTest.kt @@ -21,32 +21,22 @@ import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.launcher3.util.ActivityContextWrapper import com.android.launcher3.util.Executors +import com.android.launcher3.util.TestUtil import java.util.function.IntConsumer import org.junit.Assert.assertNotSame import org.junit.Assert.assertNull import org.junit.Assert.assertSame -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class LauncherAppWidgetHostTest { - @Mock private lateinit var onAppWidgetRemovedCallback: IntConsumer - private val context = ActivityContextWrapper(getInstrumentation().targetContext) - private lateinit var underTest: LauncherAppWidgetHost - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - underTest = LauncherAppWidgetHost(context, onAppWidgetRemovedCallback, emptyList()) - } + private var underTest = LauncherAppWidgetHost(context, HOST_ID) @Test fun `Host set view to recycle`() { @@ -74,15 +64,20 @@ class LauncherAppWidgetHostTest { @Test fun `Runnable called when app widget removed`() { + val holder = LauncherWidgetHolder(context, underTest) + holder.setAppWidgetRemovedCallback(mock(IntConsumer::class.java)) + TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {} + underTest.onAppWidgetRemoved(WIDGET_ID) Executors.MODEL_EXECUTOR.submit {}.get() getInstrumentation().waitForIdleSync() - verify(onAppWidgetRemovedCallback).accept(WIDGET_ID) + verify(holder.mAppWidgetRemovedCallback!!).accept(WIDGET_ID) } companion object { + const val HOST_ID = 2233 const val WIDGET_ID = 10001 } } diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt index 1a659e2b56..44f29d67e2 100644 --- a/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt @@ -47,7 +47,7 @@ class LauncherWidgetHolderTest { fun setUp() { assertTrue(WIDGETS_ENABLED) widgetHolder = - LauncherWidgetHolder(ActivityContextWrapper(getInstrumentation().targetContext)) {} + LauncherWidgetHolder(ActivityContextWrapper(getInstrumentation().targetContext)) } @After @@ -62,7 +62,7 @@ class LauncherWidgetHolderTest { widgetHolder.setListeningFlag(false) assertFalse(widgetHolder.isListening) widgetHolder.startListening() - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() getInstrumentation().waitForIdleSync() assertTrue(widgetHolder.isListening) verify(testView, times(1)).reInflate() @@ -73,10 +73,10 @@ class LauncherWidgetHolderTest { fun holder_start_listening_after_activity_start() { widgetHolder.setShouldListenFlag(FLAG_STATE_IS_NORMAL or FLAG_ACTIVITY_RESUMED, true) widgetHolder.setActivityStarted(false) - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get())) widgetHolder.setActivityStarted(true) - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get())) } @@ -84,10 +84,10 @@ class LauncherWidgetHolderTest { fun holder_start_listening_after_activity_resume() { widgetHolder.setShouldListenFlag(FLAG_STATE_IS_NORMAL or FLAG_ACTIVITY_STARTED, true) widgetHolder.setActivityResumed(false) - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get())) widgetHolder.setActivityResumed(true) - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get())) } @@ -95,10 +95,10 @@ class LauncherWidgetHolderTest { fun holder_start_listening_after_state_normal() { widgetHolder.setShouldListenFlag(FLAG_ACTIVITY_RESUMED or FLAG_ACTIVITY_STARTED, true) widgetHolder.setStateIsNormal(false) - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get())) widgetHolder.setStateIsNormal(true) - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get())) } @@ -117,7 +117,7 @@ class LauncherWidgetHolderTest { @Test fun holder_add_provider_change_listener() { - val listener = LauncherWidgetHolder.ProviderChangedListener {} + val listener = ListenableAppWidgetHost.ProviderChangedListener {} widgetHolder.addProviderChangeListener(listener) getInstrumentation().waitForIdleSync() assertEquals(1, widgetHolder.mProviderChangedListeners.size) @@ -127,7 +127,7 @@ class LauncherWidgetHolderTest { @Test fun holder_remove_provider_change_listener() { - val listener = LauncherWidgetHolder.ProviderChangedListener {} + val listener = ListenableAppWidgetHost.ProviderChangedListener {} widgetHolder.addProviderChangeListener(listener) widgetHolder.removeProviderChangeListener(listener) getInstrumentation().waitForIdleSync() @@ -139,7 +139,7 @@ class LauncherWidgetHolderTest { widgetHolder.setListeningFlag(true) assertTrue(widgetHolder.isListening) widgetHolder.stopListening() - widgetHolder.widgetHolderExecutor.submit {}.get() + ListenableAppWidgetHost.widgetHolderExecutor.submit {}.get() assertFalse(widgetHolder.isListening) } |