Removing BaseDraggingActivity
> Merging come methods to BaseActivtiy
> Separating wallpaper theme implementation to an independent class

Bug: 393703968
Flag: EXEMPT refactor
Test: Presubmit
Change-Id: I8cdb6d1520beff3f8226b62c5b7eafd48b2c674f
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 690dec4..33f7d0c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -459,7 +459,7 @@
 
     protected void onItemClicked(View view) {
         if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) {
-            QuickstepLauncher.super.getItemOnClickListener().onClick(view);
+            super.getItemOnClickListener().onClick(view);
         }
     }
 
@@ -731,6 +731,9 @@
         final boolean ret = super.initDeviceProfile(idp);
         mDeviceProfile.isPredictiveBackSwipe =
                 getApplicationInfo().isOnBackInvokedCallbackEnabled();
+        if (ret) {
+            SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
+        }
         return ret;
     }
 
@@ -1237,6 +1240,7 @@
         }
     }
 
+    @NonNull
     @Override
     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
         ActivityOptionsWrapper activityOptions = mAppTransitionManager.getActivityLaunchOptions(
@@ -1367,12 +1371,6 @@
     }
 
     @Override
-    protected void onDeviceProfileInitiated() {
-        super.onDeviceProfileInitiated();
-        SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
-    }
-
-    @Override
     public void dispatchDeviceProfileChanged() {
         super.dispatchDeviceProfileChanged();
         Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged",
@@ -1508,4 +1506,9 @@
     public void setCanShowAllAppsEducationView(boolean canShowAllAppsEducationView) {
         mCanShowAllAppsEducationView = canShowAllAppsEducationView;
     }
+
+    @Override
+    public void returnToHomescreen() {
+        getStateManager().goToState(LauncherState.NORMAL);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 5e8ea37..fca67c3 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme;
 import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -247,7 +248,6 @@
 
     @Override
     public void returnToHomescreen() {
-        super.returnToHomescreen();
         // TODO(b/137318995) This should go home, but doing so removes freeform windows
     }
 
@@ -261,6 +261,7 @@
         }
     }
 
+    @NonNull
     @Override
     public ActivityOptionsWrapper getActivityLaunchOptions(final View v, @Nullable ItemInfo item) {
         if (!(v instanceof TaskView)) {
@@ -371,6 +372,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setWallpaperDependentTheme(this);
 
         mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER);
 
@@ -431,7 +433,6 @@
      */
     private void initDeviceProfile() {
         mDeviceProfile = createDeviceProfile();
-        onDeviceProfileInitiated();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 42e8694..be47df9 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -24,11 +24,10 @@
 import android.graphics.PointF;
 import android.view.MotionEvent;
 
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationDeviceState;
@@ -80,7 +79,7 @@
     @Override
     public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
         startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null, TAG);
-        BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
+        ActivityContext activity = ActivityContext.lookupContext(mContext);
         int state = (mGestureState != null && mGestureState.getEndTarget() != null)
                 ? mGestureState.getEndTarget().containerType
                 : LAUNCHER_STATE_HOME;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index b1a4808..11e7d2c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -63,11 +63,6 @@
     <T extends View> T getOverviewPanel();
 
     /**
-     * Returns the RootView
-     */
-    View getRootView();
-
-    /**
      * Dispatches a generic motion event to the view hierarchy.
      * Returns the current RecentsViewContainer as context
      */
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 2e75261..6277e41 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
 import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
@@ -29,17 +30,27 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.ActionMode;
+import android.view.View;
 import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.logging.StatsLogManager;
+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.ActivityOptionsWrapper;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WindowBounds;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.ScrimView;
 
@@ -52,7 +63,8 @@
 /**
  * Launcher BaseActivity
  */
-public abstract class BaseActivity extends Activity implements ActivityContext {
+public abstract class BaseActivity extends Activity implements ActivityContext,
+        DisplayInfoChangeListener {
 
     private static final String TAG = "BaseActivity";
     static final boolean DEBUG = false;
@@ -126,6 +138,10 @@
     public @interface ActivityFlags {
     }
 
+    // When starting an action mode, setting this tag will cause the action mode to be cancelled
+    // automatically when user interacts with the launcher.
+    public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+
     /** Returns a human-readable string for the specified {@link ActivityFlags}. */
     public static String getActivityStateString(@ActivityFlags int flags) {
         StringJoiner result = new StringJoiner("|");
@@ -160,6 +176,8 @@
     private final RunnableList[] mEventCallbacks =
             {new RunnableList(), new RunnableList(), new RunnableList(), new RunnableList()};
 
+    private ActionMode mCurrentActionMode;
+
     @Override
     public ViewCache getViewCache() {
         return mViewCache;
@@ -206,6 +224,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         registerBackDispatcher();
+        DisplayController.INSTANCE.get(this).addChangeListener(this);
     }
 
     @Override
@@ -253,6 +272,7 @@
     protected void onDestroy() {
         super.onDestroy();
         mEventCallbacks[EVENT_DESTROYED].executeAllAndClear();
+        DisplayController.INSTANCE.get(this).removeChangeListener(this);
     }
 
     @Override
@@ -403,6 +423,61 @@
         writer.println(prefix + "mForceInvisible: " + mForceInvisible);
     }
 
+
+    @Override
+    public void onActionModeStarted(ActionMode mode) {
+        super.onActionModeStarted(mode);
+        mCurrentActionMode = mode;
+    }
+
+    @Override
+    public void onActionModeFinished(ActionMode mode) {
+        super.onActionModeFinished(mode);
+        mCurrentActionMode = null;
+    }
+
+    protected boolean isInAutoCancelActionMode() {
+        return mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag();
+    }
+
+    @Override
+    public boolean finishAutoCancelActionMode() {
+        if (isInAutoCancelActionMode()) {
+            mCurrentActionMode.finish();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    @NonNull
+    public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+        ActivityOptionsWrapper wrapper = ActivityContext.super.getActivityLaunchOptions(v, item);
+        addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
+        return wrapper;
+    }
+
+    @Override
+    public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+        ActivityOptionsWrapper wrapper =
+                ActivityContext.super.makeDefaultActivityOptions(splashScreenStyle);
+        addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
+        return wrapper;
+    }
+
+    protected WindowBounds getMultiWindowDisplaySize() {
+        return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
+    }
+
+    @Override
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.isVerticalBarLayout()) {
+            reapplyUi();
+        }
+    }
+
+    protected void reapplyUi() {}
+
     public static <T extends BaseActivity> T fromContext(Context context) {
         if (context instanceof BaseActivity) {
             return (T) context;
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
deleted file mode 100644
index 3b93cf4..0000000
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.view.ActionMode;
-import android.view.View;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
-import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.OnColorHintListener;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.WallpaperColorHints;
-import com.android.launcher3.util.WindowBounds;
-
-/**
- * Extension of BaseActivity allowing support for drag-n-drop
- */
-@SuppressWarnings("NewApi")
-public abstract class BaseDraggingActivity extends BaseActivity
-        implements OnColorHintListener, DisplayInfoChangeListener {
-
-    // When starting an action mode, setting this tag will cause the action mode to be cancelled
-    // automatically when user interacts with the launcher.
-    public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
-
-    private ActionMode mCurrentActionMode;
-
-    private int mThemeRes = R.style.AppTheme;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        DisplayController.INSTANCE.get(this).addChangeListener(this);
-
-        // Update theme
-        WallpaperColorHints.get(this).registerOnColorHintsChangedListener(this);
-        int themeRes = Themes.getActivityThemeRes(this);
-        if (themeRes != mThemeRes) {
-            mThemeRes = themeRes;
-            setTheme(themeRes);
-        }
-    }
-
-    @MainThread
-    @Override
-    public void onColorHintsChanged(int colorHints) {
-        updateTheme();
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        updateTheme();
-    }
-
-    private void updateTheme() {
-        if (mThemeRes != Themes.getActivityThemeRes(this)) {
-            recreateToUpdateTheme();
-        }
-    }
-
-    protected void recreateToUpdateTheme() {
-        recreate();
-    }
-
-    @Override
-    public void onActionModeStarted(ActionMode mode) {
-        super.onActionModeStarted(mode);
-        mCurrentActionMode = mode;
-    }
-
-    @Override
-    public void onActionModeFinished(ActionMode mode) {
-        super.onActionModeFinished(mode);
-        mCurrentActionMode = null;
-    }
-
-    protected boolean isInAutoCancelActionMode() {
-        return mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag();
-    }
-
-    @Override
-    public boolean finishAutoCancelActionMode() {
-        if (isInAutoCancelActionMode()) {
-            mCurrentActionMode.finish();
-            return true;
-        }
-        return false;
-    }
-
-    public abstract View getRootView();
-
-    public void returnToHomescreen() {
-        // no-op
-    }
-
-    @Override
-    @NonNull
-    public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
-        ActivityOptionsWrapper wrapper = super.getActivityLaunchOptions(v, item);
-        addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
-        return wrapper;
-    }
-
-    @Override
-    public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
-        ActivityOptionsWrapper wrapper = super.makeDefaultActivityOptions(splashScreenStyle);
-        addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
-        return wrapper;
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        DisplayController.INSTANCE.get(this).removeChangeListener(this);
-        WallpaperColorHints.get(this).unregisterOnColorsChangedListener(this);
-    }
-
-    protected void onDeviceProfileInitiated() {
-    }
-
-    @Override
-    public void onDisplayInfoChanged(Context context, Info info, int flags) {
-        if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.isVerticalBarLayout()) {
-            reapplyUi();
-        }
-    }
-
-    @Override
-    public View.OnClickListener getItemOnClickListener() {
-        return ItemClickHandler.INSTANCE;
-    }
-
-    protected abstract void reapplyUi();
-
-    protected WindowBounds getMultiWindowDisplaySize() {
-        return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
-    }
-
-    @Override
-    public boolean isAppBlockedForSafeMode() {
-        return LauncherAppState.getInstance(this).isSafeModeEnabled();
-    }
-}
diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt
index 66c948a..0cc7fc7 100644
--- a/src/com/android/launcher3/DropTargetHandler.kt
+++ b/src/com/android/launcher3/DropTargetHandler.kt
@@ -2,7 +2,7 @@
 
 import android.content.ComponentName
 import android.view.View
-import com.android.launcher3.BaseDraggingActivity.EVENT_RESUMED
+import com.android.launcher3.BaseActivity.EVENT_RESUMED
 import com.android.launcher3.DropTarget.DragObject
 import com.android.launcher3.LauncherConstants.ActivityCodes
 import com.android.launcher3.SecondaryDropTarget.DeferredOnComplete
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 647d2ad..a7a68d1 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -102,6 +102,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
 import static com.android.launcher3.util.SettingsCache.TOUCHPAD_NATURAL_SCROLLING;
+import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -144,6 +145,7 @@
 import android.view.Menu;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.WindowInsets;
@@ -222,6 +224,7 @@
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.AllAppsSwipeController;
+import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.BackPressHandler;
@@ -509,6 +512,7 @@
         }
 
         super.onCreate(savedInstanceState);
+        setWallpaperDependentTheme(this);
 
         LauncherAppState app = LauncherAppState.getInstance(this);
         mModel = app.getModel();
@@ -819,7 +823,6 @@
                     this, getMultiWindowDisplaySize());
         }
 
-        onDeviceProfileInitiated();
         if (FOLDABLE_SINGLE_PAGE.get() && mDeviceProfile.isTwoPanels) {
             mCellPosMapper = new TwoPanelCellPosMapper(mDeviceProfile.inv.numColumns);
         } else {
@@ -2840,12 +2843,6 @@
         // Overridden
     }
 
-    @Override
-    public void returnToHomescreen() {
-        super.returnToHomescreen();
-        getStateManager().goToState(LauncherState.NORMAL);
-    }
-
     public void closeOpenViews() {
         closeOpenViews(true);
     }
@@ -3170,5 +3167,10 @@
         return findViewById(R.id.popup_container);
     }
 
+    @Override
+    public OnClickListener getItemOnClickListener() {
+        return ItemClickHandler.INSTANCE;
+    }
+
     // End of Getters and Setters
 }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 7d5e481..79e9bd2 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -345,7 +345,7 @@
     public final  <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
             float getDepth(DEVICE_PROFILE_CONTEXT context) {
         return getDepth(context,
-                BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
+                ActivityContext.lookupContext(context).getDeviceProfile().isMultiWindowMode);
     }
 
     /**
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 1c9db17..e52ca6d 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -40,7 +40,6 @@
 import androidx.annotation.LayoutRes;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
@@ -579,7 +578,7 @@
     /**
      * Dismisses the popup if it is no longer valid
      */
-    public static void dismissInvalidPopup(BaseDraggingActivity activity) {
+    public static <T extends Context & ActivityContext> void dismissInvalidPopup(T activity) {
         PopupContainerWithArrow popup = getOpen(activity);
         if (popup != null && (!popup.mOriginalIcon.isAttachedToWindow()
                 || !ShortcutUtil.supportsShortcuts((ItemInfo) popup.mOriginalIcon.getTag()))) {
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 9b3292d..df27b54 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.secondarydisplay;
 
+import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Intent;
@@ -29,7 +31,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
@@ -59,7 +61,6 @@
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -67,7 +68,7 @@
 /**
  * Launcher activity for secondary displays
  */
-public class SecondaryDisplayLauncher extends BaseDraggingActivity
+public class SecondaryDisplayLauncher extends BaseActivity
         implements BgDataModel.Callbacks, DragController.DragListener {
 
     private LauncherModel mModel;
@@ -77,7 +78,6 @@
     private View mAppsButton;
 
     private PopupDataProvider mPopupDataProvider;
-    private WidgetPickerDataProvider mWidgetPickerDataProvider;
 
     private boolean mAppDrawerShown = false;
 
@@ -90,6 +90,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setWallpaperDependentTheme(this);
         mModel = LauncherAppState.getInstance(this).getModel();
         mDragController = new SecondaryDragController(this);
         mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
@@ -202,14 +203,6 @@
     }
 
     @Override
-    public View getRootView() {
-        return mDragLayer;
-    }
-
-    @Override
-    protected void reapplyUi() { }
-
-    @Override
     public BaseDragLayer getDragLayer() {
         return mDragLayer;
     }
@@ -317,11 +310,6 @@
     }
 
     @Override
-    public WidgetPickerDataProvider getWidgetPickerDataProvider() {
-        return mWidgetPickerDataProvider;
-    }
-
-    @Override
     public OnClickListener getItemOnClickListener() {
         return this::onIconClicked;
     }
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index f21e5da..15190f6 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -18,7 +18,6 @@
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 
-import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_RECREATE_TO_UPDATE_THEME;
 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 
 import android.content.Context;
@@ -30,9 +29,8 @@
 import android.view.View;
 
 import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
 
-import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.LauncherRootView;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -46,7 +44,7 @@
  * @param <STATE_TYPE> Type of state object
  */
 public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
-        extends BaseDraggingActivity implements StatefulContainer<STATE_TYPE> {
+        extends BaseActivity implements StatefulContainer<STATE_TYPE> {
 
     public final Handler mHandler = new Handler();
     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
@@ -56,7 +54,6 @@
 
     protected Configuration mOldConfig;
     private int mOldRotation;
-    private boolean mRecreateToUpdateTheme = false;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -66,18 +63,6 @@
         mOldRotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
     }
 
-    @Override
-    protected void onSaveInstanceState(@NonNull Bundle outState) {
-        outState.putBoolean(RUNTIME_STATE_RECREATE_TO_UPDATE_THEME, mRecreateToUpdateTheme);
-        super.onSaveInstanceState(outState);
-    }
-
-    @Override
-    protected void recreateToUpdateTheme() {
-        mRecreateToUpdateTheme = true;
-        super.recreateToUpdateTheme();
-    }
-
     /**
      * Create handlers to control the property changes for this activity
      */
diff --git a/src/com/android/launcher3/util/WallpaperThemeManager.kt b/src/com/android/launcher3/util/WallpaperThemeManager.kt
new file mode 100644
index 0000000..c48ef07
--- /dev/null
+++ b/src/com/android/launcher3/util/WallpaperThemeManager.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.app.Activity
+import android.content.ComponentCallbacks
+import android.content.res.Configuration
+import android.os.Bundle
+import com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_RECREATE_TO_UPDATE_THEME
+import com.android.launcher3.R
+
+/** Utility class to manage activity's theme in case it is wallpaper dependent */
+class WallpaperThemeManager private constructor(private val activity: Activity) :
+    OnColorHintListener, ActivityLifecycleCallbacksAdapter, ComponentCallbacks {
+
+    private var themeRes: Int = R.style.AppTheme
+
+    private var recreateToUpdateTheme = false
+
+    init {
+        // Update theme
+        WallpaperColorHints.get(activity).registerOnColorHintsChangedListener(this)
+        val expectedTheme = Themes.getActivityThemeRes(activity)
+        if (expectedTheme != themeRes) {
+            themeRes = expectedTheme
+            activity.setTheme(expectedTheme)
+        }
+
+        activity.registerActivityLifecycleCallbacks(this)
+        activity.registerComponentCallbacks(this)
+    }
+
+    override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) =
+        bundle.putBoolean(RUNTIME_STATE_RECREATE_TO_UPDATE_THEME, recreateToUpdateTheme)
+
+    override fun onActivityDestroyed(unused: Activity) =
+        WallpaperColorHints.get(activity).unregisterOnColorsChangedListener(this)
+
+    override fun onConfigurationChanged(config: Configuration) = updateTheme()
+
+    override fun onLowMemory() {}
+
+    override fun onColorHintsChanged(colorHints: Int) = updateTheme()
+
+    private fun updateTheme() {
+        if (themeRes != Themes.getActivityThemeRes(activity)) {
+            recreateToUpdateTheme = true
+            activity.recreate()
+        }
+    }
+
+    companion object {
+
+        /**
+         * Sets a wallpaper dependent theme on this activity. The activity is automatically
+         * recreated when a wallpaper change can potentially change the theme.
+         */
+        @JvmStatic
+        fun Activity.setWallpaperDependentTheme() {
+            if (!isDestroyed) {
+                WallpaperThemeManager(this)
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index b164b7f..c9acca7 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -62,6 +62,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.DropTargetHandler;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -166,6 +167,11 @@
         return false;
     }
 
+    /** Returns the RootView */
+    default View getRootView() {
+        return getDragLayer();
+    }
+
     /**
      * The root view to support drag-and-drop and popup support.
      */
@@ -410,7 +416,8 @@
             View v, Intent intent, @Nullable ItemInfo item) {
         Preconditions.assertUIThread();
         Context context = (Context) this;
-        if (isAppBlockedForSafeMode() && !new ApplicationInfoWrapper(context, intent).isSystem()) {
+        if (LauncherAppState.getInstance(context).isSafeModeEnabled()
+                && !new ApplicationInfoWrapper(context, intent).isSystem()) {
             Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
             return null;
         }
@@ -456,11 +463,6 @@
         return null;
     }
 
-    /** Returns {@code true} if an app launch is blocked due to safe mode. */
-    default boolean isAppBlockedForSafeMode() {
-        return false;
-    }
-
     /**
      * Creates and logs a new app launch event.
      */
@@ -476,6 +478,7 @@
      * @param v View initiating a launch.
      * @param item Item associated with the view.
      */
+    @NonNull
     default ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
         int left = 0, top = 0;
         int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index f499fca..78197e2 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -41,7 +41,6 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.ItemInfo;
@@ -50,6 +49,7 @@
 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.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
@@ -218,8 +218,7 @@
      * @param widgetId The ID of the widget
      * @param requestCode The request code
      */
-    public void startConfigActivity(@NonNull BaseDraggingActivity activity, int widgetId,
-            int requestCode) {
+    public void startConfigActivity(@NonNull BaseActivity activity, int widgetId, int requestCode) {
         if (!WIDGETS_ENABLED) {
             sendActionCancelled(activity, requestCode);
             return;
@@ -245,7 +244,7 @@
      * the configuration of the {@code widgetId} app widget, or null of options cannot be produced.
      */
     @Nullable
-    protected Bundle getConfigurationActivityOptions(@NonNull BaseDraggingActivity activity,
+    protected Bundle getConfigurationActivityOptions(@NonNull ActivityContext activity,
             int widgetId) {
         LauncherAppWidgetHostView view = mViews.get(widgetId);
         if (view == null) {
diff --git a/tests/src/com/android/launcher3/util/WallpaperThemeManagerTest.kt b/tests/src/com/android/launcher3/util/WallpaperThemeManagerTest.kt
new file mode 100644
index 0000000..4c8fd8a
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/WallpaperThemeManagerTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.app.Activity
+import android.content.ComponentCallbacks
+import android.content.res.Configuration
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.any
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.eq
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.ExtendedMockito.times
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.launcher3.util.WallpaperThemeManager.Companion.setWallpaperDependentTheme
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.never
+import org.mockito.kotlin.whenever
+
+/** Tests for WallpaperThemeManager */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WallpaperThemeManagerTest {
+
+    @get:Rule val context = SandboxApplication()
+
+    @Mock lateinit var activity: Activity
+    @Captor lateinit var callbacksCaptor: ArgumentCaptor<ComponentCallbacks>
+
+    private lateinit var mockSession: StaticMockitoSession
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        mockSession = mockitoSession().spyStatic(Themes::class.java).startMocking()
+
+        doReturn(1).`when`<Int> { Themes.getActivityThemeRes(any()) }
+        doReturn(context).whenever(activity).applicationContext
+    }
+
+    @After
+    fun tearDown() {
+        mockSession.finishMocking()
+    }
+
+    @Test
+    fun `correct theme set on activity create`() {
+        activity.setWallpaperDependentTheme()
+        verify(activity, times(1)).setTheme(eq(1))
+    }
+
+    @Test
+    fun `ignores update if theme does not change`() {
+        activity.setWallpaperDependentTheme()
+        verify(activity).registerComponentCallbacks(callbacksCaptor.capture())
+        callbacksCaptor.value.onConfigurationChanged(Configuration())
+        verify(activity, never()).recreate()
+    }
+
+    @Test
+    fun `activity recreated if theme changes`() {
+        activity.setWallpaperDependentTheme()
+        verify(activity).registerComponentCallbacks(callbacksCaptor.capture())
+
+        doReturn(3).`when`<Int> { Themes.getActivityThemeRes(any()) }
+        callbacksCaptor.value.onConfigurationChanged(Configuration())
+        verify(activity, times(1)).recreate()
+    }
+}