Limited recents in window introductory cl
Test: Built and tested locally
Flag: com.android.launcher3.enable_fallback_overview_in_window
Bug:292269949
Change-Id: I5352ba0b6c5bc196fbd1322d435a7e27e884f7b5
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index a6d651c..22174df 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -45,6 +45,7 @@
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
+import static com.android.quickstep.BaseContainerInterface.AnimationFactory;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -103,6 +104,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -112,6 +114,7 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.taskbar.TaskbarThresholdUtils;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -120,9 +123,9 @@
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds;
-import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
@@ -156,8 +159,6 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.startingsurface.SplashScreenExitAnimationUtils;
-import kotlin.Unit;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -167,15 +168,16 @@
import java.util.OptionalInt;
import java.util.function.Consumer;
+import kotlin.Unit;
+
/**
* Handles the navigation gestures when Launcher is the default home activity.
*/
public abstract class AbsSwipeUpHandler<
- RECENTS_CONTAINER extends Context & RecentsViewContainer,
- RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>,
- STATE extends BaseState<STATE>>
- extends SwipeUpAnimationLogic
- implements OnApplyWindowInsetsListener, RecentsAnimationCallbacks.RecentsAnimationListener {
+ RECENTS_CONTAINER extends Context & RecentsViewContainer & StatefulContainer<STATE>,
+ RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>, STATE extends BaseState<STATE>>
+ extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
+ RecentsAnimationCallbacks.RecentsAnimationListener {
private static final String TAG = "AbsSwipeUpHandler";
private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
@@ -356,6 +358,10 @@
InputConsumerController inputConsumer) {
super(context, deviceState, gestureState);
mContainerInterface = gestureState.getContainerInterface();
+ if (Flags.enableFallbackOverviewInWindow()) {
+ RecentsWindowManager.Companion.getInstanceOrNull()
+ .registerInitListener(this::onActivityInit);
+ }
mActivityInitListener =
mContainerInterface.createActivityInitListener(this::onActivityInit);
mInputConsumerProxy =
@@ -819,7 +825,7 @@
return;
}
initTransitionEndpoints(mContainer.getDeviceProfile());
- mAnimationFactory.createActivityInterface(mTransitionDragLength);
+ mAnimationFactory.createContainerInterface(mTransitionDragLength);
}
/**
@@ -863,10 +869,13 @@
}
}
+ public Intent getHomeIntent() {
+ return mGestureState.getHomeIntent();
+ }
+
public Intent getLaunchIntent() {
return mGestureState.getOverviewIntent();
}
-
/**
* Called when the value of {@link #mCurrentShift} changes
*/
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 91379b1..1e755eb 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -61,17 +61,14 @@
public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE> & RecentsViewContainer> extends
BaseContainerInterface<STATE_TYPE, ACTIVITY_TYPE> {
- private final STATE_TYPE mBackgroundState;
private STATE_TYPE mTargetState;
- @Nullable private Runnable mOnInitBackgroundStateUICallback = null;
-
protected BaseActivityInterface(boolean rotationSupportedByActivity,
STATE_TYPE overviewState, STATE_TYPE backgroundState) {
+ super(backgroundState);
this.rotationSupportedByActivity = rotationSupportedByActivity;
mTargetState = overviewState;
- mBackgroundState = backgroundState;
}
/**
@@ -155,49 +152,6 @@
recentsView.switchToScreenshot(thumbnailDatas, runnable);
}
-
- protected void runOnInitBackgroundStateUI(Runnable callback) {
- ACTIVITY_TYPE activity = getCreatedContainer();
- if (activity != null && activity.getStateManager().getState() == mBackgroundState) {
- callback.run();
- onInitBackgroundStateUI();
- return;
- }
- mOnInitBackgroundStateUICallback = callback;
- }
-
- private void onInitBackgroundStateUI() {
- if (mOnInitBackgroundStateUICallback != null) {
- mOnInitBackgroundStateUICallback.run();
- mOnInitBackgroundStateUICallback = null;
- }
- }
-
- public interface AnimationFactory {
-
- void createActivityInterface(long transitionLength);
-
- /**
- * @param attached Whether to show RecentsView alongside the app window. If false, recents
- * will be hidden by some property we can animate, e.g. alpha.
- * @param animate Whether to animate recents to/from its new attached state.
- * @param updateRunningTaskAlpha Whether to update the running task's attached alpha
- */
- default void setRecentsAttachedToAppWindow(
- boolean attached, boolean animate, boolean updateRunningTaskAlpha) { }
-
- default boolean isRecentsAttachedToAppWindow() {
- return false;
- }
-
- default boolean hasRecentsEverAttachedToAppWindow() {
- return false;
- }
-
- /** Called when the gesture ends and we know what state it is going towards */
- default void setEndTarget(GestureState.GestureEndTarget endTarget) { }
- }
-
class DefaultAnimationFactory implements AnimationFactory {
protected final ACTIVITY_TYPE mActivity;
@@ -226,7 +180,7 @@
}
@Override
- public void createActivityInterface(long transitionLength) {
+ public void createContainerInterface(long transitionLength) {
PendingAnimation pa = new PendingAnimation(transitionLength * 2);
createBackgroundToOverviewAnim(mActivity, pa);
AnimatorPlaybackController controller = pa.createPlaybackController();
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index 6447775..14c2cc4 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -41,10 +41,12 @@
import com.android.launcher3.R;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -58,10 +60,14 @@
import java.util.function.Predicate;
public abstract class BaseContainerInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
- CONTAINER_TYPE extends RecentsViewContainer> {
-
+ CONTAINER_TYPE extends RecentsViewContainer & StatefulContainer<STATE_TYPE>> {
public boolean rotationSupportedByActivity = false;
+ protected final STATE_TYPE mBackgroundState;
+
+ protected BaseContainerInterface(STATE_TYPE backgroundState) {
+ mBackgroundState = backgroundState;
+ }
@UiThread
@Nullable
@@ -73,6 +79,9 @@
@Nullable
public abstract CONTAINER_TYPE getCreatedContainer();
+ @Nullable
+ protected Runnable mOnInitBackgroundStateUICallback = null;
+
public abstract boolean isInLiveTileMode();
public abstract void onAssistantVisibilityChanged(float assistantVisibility);
@@ -97,7 +106,32 @@
@Nullable
public abstract TaskbarUIController getTaskbarController();
- public abstract BaseActivityInterface.AnimationFactory prepareRecentsUI(
+ public interface AnimationFactory {
+
+ void createContainerInterface(long transitionLength);
+
+ /**
+ * @param attached Whether to show RecentsView alongside the app window. If false, recents
+ * will be hidden by some property we can animate, e.g. alpha.
+ * @param animate Whether to animate recents to/from its new attached state.
+ * @param updateRunningTaskAlpha Whether to update the running task's attached alpha
+ */
+ default void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, boolean updateRunningTaskAlpha) { }
+
+ default boolean isRecentsAttachedToAppWindow() {
+ return false;
+ }
+
+ default boolean hasRecentsEverAttachedToAppWindow() {
+ return false;
+ }
+
+ /** Called when the gesture ends and we know what state it is going towards */
+ default void setEndTarget(GestureState.GestureEndTarget endTarget) { }
+ }
+
+ public abstract BaseContainerInterface.AnimationFactory prepareRecentsUI(
RecentsAnimationDeviceState deviceState, boolean activityVisible,
Consumer<AnimatorControllerWithResistance> callback);
@@ -135,7 +169,16 @@
return false;
}
- abstract void runOnInitBackgroundStateUI(Runnable callback);
+ public void runOnInitBackgroundStateUI(Runnable callback) {
+ StatefulContainer container = getCreatedContainer();
+ if (container != null
+ && container.getStateManager().getState() == mBackgroundState) {
+ callback.run();
+ onInitBackgroundStateUI();
+ return;
+ }
+ mOnInitBackgroundStateUICallback = callback;
+ }
@Nullable
public DesktopVisibilityController getDesktopVisibilityController() {
@@ -414,4 +457,11 @@
outRect,
orientationHandler);
}
+
+ protected void onInitBackgroundStateUI() {
+ if (mOnInitBackgroundStateUICallback != null) {
+ mOnInitBackgroundStateUICallback.run();
+ mOnInitBackgroundStateUICallback = null;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/BaseWindowInterface.java b/quickstep/src/com/android/quickstep/BaseWindowInterface.java
new file mode 100644
index 0000000..9d6e61d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/BaseWindowInterface.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 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.quickstep;
+
+import static com.android.app.animation.Interpolators.ACCELERATE_2;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
+import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.taskbar.TaskbarUIController;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.NavigationMode;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Temporary utility class in place for differences needed between
+ * Recents in Window in Launcher vs Fallback
+ */
+public abstract class BaseWindowInterface extends
+ BaseContainerInterface<RecentsState, RecentsWindowManager> {
+
+ final String TAG = "BaseWindowInterface";
+ private RecentsState mTargetState;
+
+
+ protected BaseWindowInterface(RecentsState overviewState, RecentsState backgroundState) {
+ super(backgroundState);
+ mTargetState = overviewState;
+ }
+
+ @Nullable
+ public abstract RecentsWindowManager getCreatedContainer();
+
+ @Nullable
+ public DepthController getDepthController() {
+ return null;
+ }
+
+ public final boolean isResumed() {
+ return isStarted();
+ }
+
+ public final boolean isStarted() {
+ RecentsWindowManager windowManager = getCreatedContainer();
+ return windowManager != null && windowManager.isStarted();
+ }
+
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ TaskbarUIController controller = getTaskbarController();
+ boolean isEventOverBubbleBarStashHandle =
+ controller != null && controller.isEventOverBubbleBarViews(ev);
+ return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons()
+ || isTrackpadMultiFingerSwipe(ev) || isEventOverBubbleBarStashHandle;
+ }
+
+ /**
+ * Closes any overlays.
+ */
+ public void closeOverlay() {
+ Optional.ofNullable(getTaskbarController()).ifPresent(
+ TaskbarUIController::hideOverlayWindow);
+ }
+
+ public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,
+ Runnable runnable) {
+ RecentsWindowManager windowManager = getCreatedContainer();
+ if (windowManager == null) {
+ return;
+ }
+ RecentsView recentsView = windowManager.getOverviewPanel();
+ if (recentsView == null) {
+ if (runnable != null) {
+ runnable.run();
+ }
+ return;
+ }
+ recentsView.switchToScreenshot(thumbnailDatas, runnable);
+ }
+
+ /**
+ * todo: Create an abstract animation factory to handle both activity and window implementations
+ * todo: move new factory into BaseContainerInterface and cleanup.
+ */
+
+ class DefaultAnimationFactory implements AnimationFactory {
+
+ protected final RecentsWindowManager mRecentsWindowManager;
+ private final RecentsState mStartState;
+ private final Consumer<AnimatorControllerWithResistance> mCallback;
+
+ private boolean mIsAttachedToWindow;
+ private boolean mHasEverAttachedToWindow;
+
+ DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
+ mCallback = callback;
+
+ mRecentsWindowManager = getCreatedContainer();
+ mStartState = mRecentsWindowManager.getStateManager().getState();
+ }
+
+ protected RecentsWindowManager initBackgroundStateUI() {
+ RecentsState resetState = mStartState;
+ if (mStartState.shouldDisableRestore()) {
+ resetState = mRecentsWindowManager.getStateManager().getRestState();
+ }
+ mRecentsWindowManager.getStateManager().setRestState(resetState);
+ mRecentsWindowManager.getStateManager().goToState(mBackgroundState, false);
+ onInitBackgroundStateUI();
+ return mRecentsWindowManager;
+ }
+
+ @Override
+ public void createContainerInterface(long transitionLength) {
+ PendingAnimation pa = new PendingAnimation(transitionLength * 2);
+ createBackgroundToOverviewAnim(mRecentsWindowManager, pa);
+ AnimatorPlaybackController controller = pa.createPlaybackController();
+ mRecentsWindowManager.getStateManager().setCurrentUserControlledAnimation(controller);
+
+ // Since we are changing the start position of the UI, reapply the state, at the end
+ controller.setEndAction(() -> {
+ mRecentsWindowManager.getStateManager().goToState(
+ controller.getInterpolatedProgress() > 0.5 ? mTargetState
+ : mBackgroundState,
+ /* animated= */ false);
+ });
+
+ RecentsView recentsView = mRecentsWindowManager.getOverviewPanel();
+ AnimatorControllerWithResistance controllerWithResistance =
+ AnimatorControllerWithResistance.createForRecents(controller,
+ mRecentsWindowManager, recentsView.getPagedViewOrientedState(),
+ mRecentsWindowManager.getDeviceProfile(), recentsView,
+ RECENTS_SCALE_PROPERTY, recentsView, TASK_SECONDARY_TRANSLATION);
+ mCallback.accept(controllerWithResistance);
+
+ // Creating the activity controller animation sometimes reapplies the launcher state
+ // (because we set the animation as the current state animation), so we reapply the
+ // attached state here as well to ensure recents is shown/hidden appropriately.
+ if (DisplayController.getNavigationMode(mRecentsWindowManager)
+ == NavigationMode.NO_BUTTON) {
+ setRecentsAttachedToAppWindow(mIsAttachedToWindow, false, false);
+ }
+ }
+
+ @Override
+ public void setRecentsAttachedToAppWindow(boolean attached, boolean animate,
+ boolean updateRunningTaskAlpha) {
+
+ if (mIsAttachedToWindow == attached && animate) {
+ return;
+ }
+ mRecentsWindowManager.getStateManager()
+ .cancelStateElementAnimation(INDEX_RECENTS_FADE_ANIM);
+ mRecentsWindowManager.getStateManager()
+ .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mIsAttachedToWindow = attached;
+ if (attached) {
+ mHasEverAttachedToWindow = true;
+ }
+ }});
+
+ long animationDuration = animate ? RECENTS_ATTACH_DURATION : 0;
+ Animator fadeAnim = mRecentsWindowManager.getStateManager()
+ .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
+ fadeAnim.setInterpolator(attached ? INSTANT : ACCELERATE_2);
+ fadeAnim.setDuration(animationDuration);
+ animatorSet.play(fadeAnim);
+
+ float fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(
+ mRecentsWindowManager.getOverviewPanel());
+ float toTranslation = attached ? 0 : 1;
+
+ Animator translationAnimator =
+ mRecentsWindowManager.getStateManager().createStateElementAnimation(
+ INDEX_RECENTS_TRANSLATE_X_ANIM, fromTranslation, toTranslation);
+ translationAnimator.setDuration(animationDuration);
+ animatorSet.play(translationAnimator);
+ animatorSet.start();
+ }
+
+ @Override
+ public boolean isRecentsAttachedToAppWindow() {
+ return mIsAttachedToWindow;
+ }
+
+ @Override
+ public boolean hasRecentsEverAttachedToAppWindow() {
+ return mHasEverAttachedToWindow;
+ }
+
+ @Override
+ public void setEndTarget(GestureState.GestureEndTarget endTarget) {
+ mTargetState = stateFromGestureEndTarget(endTarget);
+ }
+
+ protected void createBackgroundToOverviewAnim(RecentsWindowManager container,
+ PendingAnimation pa) {
+ // Scale down recents from being full screen to being in overview.
+ RecentsView recentsView = container.getOverviewPanel();
+ pa.addFloat(recentsView, RECENTS_SCALE_PROPERTY,
+ recentsView.getMaxScaleForFullScreen(), 1, LINEAR);
+ pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
+
+ pa.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ TaskbarUIController taskbarUIController = getTaskbarController();
+ if (taskbarUIController != null) {
+ taskbarUIController.setSystemGestureInProgress(true);
+ }
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
index ca0c88a..bea62a8 100644
--- a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
@@ -45,35 +45,16 @@
import java.util.function.Predicate;
/**
- * {@link BaseActivityInterface} for recents when the default launcher is different than the
- * currently running one and apps should interact with the {@link RecentsActivity} as opposed
+ * {@link BaseWindowInterface} for recents when the default launcher is different than the
+ * currently running one and apps should interact with the {@link RecentsWindowManager} as opposed
* to the in-launcher one.
*/
public final class FallbackWindowInterface extends BaseWindowInterface{
- private static FallbackWindowInterface INSTANCE;
+ public static final FallbackWindowInterface INSTANCE = new FallbackWindowInterface();
- private final RecentsWindowManager mRecentsWindowManager;
-
- @Nullable
- public static FallbackWindowInterface getInstance(){
- return INSTANCE;
- }
-
- public static FallbackWindowInterface init(RecentsWindowManager recentsWindowManager) {
- if (INSTANCE == null) {
- INSTANCE = new FallbackWindowInterface(recentsWindowManager);
- }
- return INSTANCE;
- }
-
- private FallbackWindowInterface(RecentsWindowManager recentsWindowManager) {
- super(DEFAULT, BACKGROUND_APP);
- mRecentsWindowManager = recentsWindowManager;
- }
-
- public void destroy() {
- INSTANCE = null;
+ private FallbackWindowInterface() {
+ super( DEFAULT, BACKGROUND_APP);
}
/** 2 */
@@ -119,7 +100,7 @@
@Nullable
@Override
public RecentsWindowManager getCreatedContainer() {
- return mRecentsWindowManager;
+ return RecentsWindowManager.Companion.getInstanceOrNull();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 3bd87ce..22967cb 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -38,6 +38,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
@@ -269,8 +270,8 @@
/**
* @return the interface to the activity handing the UI updates for this gesture.
*/
- public <S extends BaseState<S>, T extends RecentsViewContainer>
- BaseContainerInterface<S, T> getContainerInterface() {
+ public <S extends BaseState<S>, T extends RecentsViewContainer & StatefulContainer<S>>
+ BaseContainerInterface getContainerInterface() {
return mContainerInterface;
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index e474a81..bdab0e0 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -39,6 +39,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.util.ActiveGestureLog;
@@ -178,7 +179,11 @@
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- mContainerInterface = FallbackActivityInterface.INSTANCE;
+ if (Flags.enableFallbackOverviewInWindow()) {
+ mContainerInterface = FallbackWindowInterface.INSTANCE;
+ } else {
+ mContainerInterface = FallbackActivityInterface.INSTANCE;
+ }
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
mCurrentHomeIntent.setComponent(defaultHome);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 464ebc1..0e63c12 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -352,7 +352,6 @@
@Override
protected void onStop() {
super.onStop();
-
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
mFallbackRecentsView.updateLocusId();
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 289a2c1..3b93efb 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -43,10 +43,12 @@
import androidx.annotation.UiThread;
import com.android.internal.util.ArrayUtils;
+import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.SystemUiFlagUtils;
import com.android.quickstep.views.RecentsView;
@@ -64,6 +66,7 @@
SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
private final Context mCtx;
+ private RecentsWindowManager mRecentsWindowsManager;
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
private RecentsAnimationTargets mTargets;
@@ -98,10 +101,10 @@
}
};
- TaskAnimationManager(Context ctx) {
+ TaskAnimationManager(Context ctx, RecentsWindowManager manager) {
mCtx = ctx;
+ mRecentsWindowsManager = manager;
}
-
SystemUiProxy getSystemUiProxy() {
return SystemUiProxy.INSTANCE.get(mCtx);
}
@@ -299,34 +302,43 @@
mCallbacks.addListener(listener);
final ActivityOptions options = ActivityOptions.makeBasic();
- options.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
- options.setTransientLaunch();
- options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
- // Notify taskbar that we should skip reacting to launcher visibility change to
- // avoid a jumping taskbar.
- TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
- if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
- taskbarUIController.setSkipLauncherVisibilityChange(true);
+ // TODO:(b/365777482) if flag is enabled, but on launcher it will crash.
+ if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
+ && Flags.enableFallbackOverviewInWindow()){
+ mRecentsAnimationStartPending =
+ getSystemUiProxy().startRecentsActivity(intent, options, mCallbacks);
+ mRecentsWindowsManager.startRecentsWindow();
+ } else {
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
+ options.setTransientLaunch();
+ options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
- mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
- @Override
- public void onRecentsAnimationCanceled(
- @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
- taskbarUIController.setSkipLauncherVisibilityChange(false);
- }
+ // Notify taskbar that we should skip reacting to launcher visibility change to
+ // avoid a jumping taskbar.
+ TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
+ if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
+ taskbarUIController.setSkipLauncherVisibilityChange(true);
- @Override
- public void onRecentsAnimationFinished(
- @NonNull RecentsAnimationController controller) {
- taskbarUIController.setSkipLauncherVisibilityChange(false);
- }
- });
+ mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
+ @Override
+ public void onRecentsAnimationCanceled(
+ @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
+ taskbarUIController.setSkipLauncherVisibilityChange(false);
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(
+ @NonNull RecentsAnimationController controller) {
+ taskbarUIController.setSkipLauncherVisibilityChange(false);
+ }
+ });
+ }
+
+ mRecentsAnimationStartPending = getSystemUiProxy()
+ .startRecentsActivity(intent, options, mCallbacks);
}
-
- mRecentsAnimationStartPending = getSystemUiProxy()
- .startRecentsActivity(intent, options, mCallbacks);
if (enableHandleDelayedGestureCallbacks()) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
"TaskAnimationManager.startRecentsAnimation: ")
@@ -487,6 +499,10 @@
mTargets = null;
mLastGestureState = null;
mLastAppearedTaskTargets = null;
+
+ if (Flags.enableFallbackOverviewInWindow()) {
+ RecentsWindowManager.Companion.getInstanceOrNull().cleanup();
+ }
}
@Nullable
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index f4ff4b2..8e45767 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -173,9 +173,8 @@
protected T getActionsView() {
if (mActionsView == null) {
- mActionsView = BaseActivity.fromContext(
- mTaskContainer.getTaskView().getContext()).findViewById(
- R.id.overview_actions_view);
+ mActionsView = (T) RecentsViewContainer.containerFromContext(
+ mTaskContainer.getTaskView().getContext()).getActionsView();
}
return mActionsView;
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index d8063ba..07ee479 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -59,7 +59,6 @@
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -68,6 +67,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
@@ -80,6 +80,7 @@
import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskView;
import com.android.systemui.animation.RemoteAnimationTargetCompat;
import com.android.systemui.shared.recents.model.Task;
@@ -155,8 +156,9 @@
return taskView;
}
- public static void createRecentsWindowAnimator(
- @NonNull RecentsView recentsView,
+ public static <T extends Context & RecentsViewContainer & StatefulContainer<?>>
+ void createRecentsWindowAnimator(
+ @NonNull RecentsView<T, ?> recentsView,
@NonNull TaskView v,
boolean skipViewChanges,
@NonNull RemoteAnimationTarget[] appTargets,
@@ -204,8 +206,9 @@
int taskIndex = recentsView.indexOfChild(v);
Context context = v.getContext();
- BaseActivity baseActivity = BaseActivity.fromContext(context);
- DeviceProfile dp = baseActivity.getDeviceProfile();
+
+ T container = RecentsViewContainer.containerFromContext(context);
+ DeviceProfile dp = container.getDeviceProfile();
boolean showAsGrid = dp.isTablet;
boolean parallaxCenterAndAdjacentTask =
!showAsGrid && taskIndex != recentsView.getCurrentPage();
@@ -417,7 +420,8 @@
if (depthController != null) {
out.setFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE,
- BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE);
+ BACKGROUND_APP.getDepth(container),
+ TOUCH_RESPONSE);
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 399cb0f..8218105 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -76,6 +76,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.View;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -106,6 +107,8 @@
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.OverviewCommandHelper.CommandType;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
@@ -605,6 +608,8 @@
this::createLauncherSwipeHandler;
private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
this::createFallbackSwipeHandler;
+ private final AbsSwipeUpHandler.Factory mRecentsWindowSwipeHandlerFactory =
+ this::createRecentsWindowSwipeHandler;
private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
@@ -637,6 +642,7 @@
private InputEventReceiver mInputEventReceiver;
private TaskbarManager mTaskbarManager;
+ private RecentsWindowManager mRecentsWindowManager;
private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
private AllAppsActionManager mAllAppsActionManager;
private InputManager mInputManager;
@@ -667,6 +673,9 @@
mDesktopVisibilityController = new DesktopVisibilityController(this);
mTaskbarManager = new TaskbarManager(
this, mAllAppsActionManager, mNavCallbacks, mDesktopVisibilityController);
+ if (Flags.enableFallbackOverviewInWindow()) {
+ mRecentsWindowManager = RecentsWindowManager.Companion.getOrCreateInstance(this);
+ }
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
@@ -717,7 +726,7 @@
@UiThread
public void onUserUnlocked() {
Log.d(TAG, "onUserUnlocked: userId=" + getUserId());
- mTaskAnimationManager = new TaskAnimationManager(this);
+ mTaskAnimationManager = new TaskAnimationManager(this, mRecentsWindowManager);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this,
mOverviewComponentObserver, mTaskAnimationManager);
@@ -1430,8 +1439,9 @@
}
public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
- return !mOverviewComponentObserver.isHomeAndOverviewSame()
- ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory;
+ return mOverviewComponentObserver.isHomeAndOverviewSame()
+ ? mLauncherSwipeHandlerFactory : (Flags.enableFallbackOverviewInWindow()
+ ? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
}
private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
@@ -1480,7 +1490,8 @@
.append("activity == null, trying to use default input consumer"));
}
- boolean hasWindowFocus = container.getRootView().hasWindowFocus();
+ View rootview = container.getRootView();
+ boolean hasWindowFocus = rootview != null && rootview.hasWindowFocus();
boolean isPreviousGestureAnimatingToLauncher =
previousGestureState.isRunningAnimationToLauncher()
|| mDeviceState.isPredictiveBackToHomeInProgress();
@@ -1685,4 +1696,11 @@
gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer);
}
+
+ private AbsSwipeUpHandler createRecentsWindowSwipeHandler(
+ GestureState gestureState, long touchTimeMs) {
+ return new RecentsWindowSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+ gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+ mInputConsumer);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 6dfcf15..e04e38f 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -31,6 +31,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Flags;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
@@ -41,9 +42,10 @@
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
+import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.FallbackWindowInterface;
import com.android.quickstep.GestureState;
-import com.android.quickstep.RecentsActivity;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SplitSelectStateController;
@@ -72,10 +74,16 @@
}
public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr, FallbackActivityInterface.INSTANCE);
+ super(context, attrs, defStyleAttr, getContainerInterface());
mContainer.getStateManager().addStateListener(this);
}
+ private static BaseContainerInterface<RecentsState, ?> getContainerInterface() {
+ return Flags.enableFallbackOverviewInWindow()
+ ? FallbackWindowInterface.INSTANCE
+ : FallbackActivityInterface.INSTANCE;
+ }
+
@Override
public void init(OverviewActionsView actionsView, SplitSelectStateController splitController,
@Nullable DesktopRecentsTransitionController desktopRecentsTransitionController) {
@@ -286,7 +294,8 @@
}
}
- if (isOverlayEnabled) {
+ // disabling this so app icons aren't drawn on top of recent tasks.
+ if (isOverlayEnabled && !Flags.enableFallbackOverviewInWindow()) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
new file mode 100644
index 0000000..df5a8f0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 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.quickstep.fallback.window
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.view.ContextThemeWrapper
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.util.Themes
+import com.android.launcher3.views.ActivityContext
+import com.android.launcher3.views.BaseDragLayer
+import com.android.quickstep.fallback.RecentsDragLayer
+
+/**
+ * Window context for the Overview overlays.
+ * <p>
+ * Overlays have their own window and need a window context.
+ */
+open class RecentsWindowContext(windowContext: Context) :
+ ContextThemeWrapper(windowContext, Themes.getActivityThemeRes(windowContext)), ActivityContext {
+
+ private var deviceProfile: DeviceProfile? = null
+ private var dragLayer: RecentsDragLayer<RecentsWindowManager> = RecentsDragLayer(this, null)
+ private val deviceProfileChangeListeners:
+ MutableList<DeviceProfile.OnDeviceProfileChangeListener> =
+ ArrayList()
+
+ private val windowTitle: String = "RecentsWindow"
+
+ protected var windowLayoutParams: WindowManager.LayoutParams? =
+ createDefaultWindowLayoutParams(
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, windowTitle)
+
+ override fun getDragLayer(): BaseDragLayer<RecentsWindowManager> {
+ return dragLayer
+ }
+
+ override fun getDeviceProfile(): DeviceProfile {
+ if (deviceProfile == null) {
+ deviceProfile = InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
+ .copy(this)
+ }
+ return deviceProfile!!
+ }
+
+ override fun getOnDeviceProfileChangeListeners():
+ List<DeviceProfile.OnDeviceProfileChangeListener> {
+ return deviceProfileChangeListeners
+ }
+
+ /**
+ * Creates LayoutParams for adding a view directly to WindowManager as a new window.
+ *
+ * @param type The window type to pass to the created WindowManager.LayoutParams.
+ * @param title The window title to pass to the created WindowManager.LayoutParams.
+ */
+ fun createDefaultWindowLayoutParams(type: Int, title: String): WindowManager.LayoutParams {
+ var windowFlags =
+ (WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
+ WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+
+ val windowLayoutParams =
+ WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ type,
+ windowFlags,
+ PixelFormat.TRANSLUCENT,
+ )
+
+ windowLayoutParams.title = title
+ windowLayoutParams.fitInsetsTypes = 0
+ windowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ windowLayoutParams.isSystemApplicationOverlay = true
+ windowLayoutParams.privateFlags = PRIVATE_FLAG_CONSUME_IME_INSETS
+
+ return windowLayoutParams
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
new file mode 100644
index 0000000..7f59d1c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2024 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.quickstep.fallback.window
+
+import android.animation.AnimatorSet
+import android.annotation.SuppressLint
+import android.app.ActivityOptions
+import android.content.ComponentName
+import android.content.Context
+import android.content.LocusId
+import android.os.Bundle
+import android.os.Handler
+import android.util.Log
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.RemoteAnimationAdapter
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.View
+import android.view.Window
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+import android.window.RemoteTransition
+import com.android.launcher3.BaseActivity
+import com.android.launcher3.LauncherAnimationRunner
+import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory
+import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory
+import com.android.launcher3.statemanager.StatefulContainer
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.RunnableList
+import com.android.launcher3.util.SystemUiController
+import com.android.launcher3.views.BaseDragLayer
+import com.android.launcher3.views.ScrimView
+import com.android.quickstep.OverviewComponentObserver
+import com.android.quickstep.RecentsModel
+import com.android.quickstep.RemoteAnimationTargets
+import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.TouchInteractionService
+import com.android.quickstep.fallback.FallbackRecentsStateController
+import com.android.quickstep.fallback.FallbackRecentsView
+import com.android.quickstep.fallback.RecentsDragLayer
+import com.android.quickstep.fallback.RecentsState
+import com.android.quickstep.fallback.RecentsState.BACKGROUND_APP
+import com.android.quickstep.fallback.RecentsState.BG_LAUNCHER
+import com.android.quickstep.fallback.RecentsState.DEFAULT
+import com.android.quickstep.fallback.RecentsState.HOME
+import com.android.quickstep.fallback.RecentsState.MODAL_TASK
+import com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT
+import com.android.quickstep.util.RecentsAtomicAnimationFactory
+import com.android.quickstep.util.SplitSelectStateController
+import com.android.quickstep.util.TISBindHelper
+import com.android.quickstep.views.OverviewActionsView
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import java.util.function.Predicate
+
+/**
+ * Class that will manage RecentsView lifecycle within a window and interface correctly
+ * where needed. This allows us to run RecentsView in a window where needed.
+ * todo: b/365776320, b/365777482
+ */
+class RecentsWindowManager private constructor(context: Context) :
+ RecentsWindowContext(context), RecentsViewContainer, StatefulContainer<RecentsState> {
+
+ @SuppressLint("StaticFieldLeak")
+ companion object {
+ private const val HOME_APPEAR_DURATION: Long = 250
+ private const val TAG = "RecentsWindowManager"
+ private const val DEBUG = false
+
+ @Volatile private var INSTANCE: RecentsWindowManager? = null
+
+ fun getInstanceOrNull(): RecentsWindowManager? {
+ return INSTANCE
+ }
+
+ fun getOrCreateInstance(context: Context): RecentsWindowManager {
+ return INSTANCE
+ ?: synchronized(this) {
+ INSTANCE ?: RecentsWindowManager(context).also { INSTANCE = it }
+ }
+ }
+ }
+
+ protected var recentsView: FallbackRecentsView<RecentsWindowManager>? = null
+ private val windowContext: Context = createWindowContext(TYPE_APPLICATION_OVERLAY, null)
+ private val windowManager: WindowManager =
+ windowContext.getSystemService(WindowManager::class.java)!!
+ private var layoutInflater: LayoutInflater = LayoutInflater.from(this).cloneInContext(this)
+ private var stateManager: StateManager<RecentsState, RecentsWindowManager> =
+ StateManager<RecentsState, RecentsWindowManager>(this, RecentsState.BG_LAUNCHER)
+ private var mSystemUiController: SystemUiController? = null
+
+ private var dragLayer: RecentsDragLayer<RecentsWindowManager>? = null
+ private var windowView: View? = null
+ private var actionsView: OverviewActionsView<*>? = null
+ private var scrimView: ScrimView? = null
+ private val mHandler = Handler()
+
+
+ private var isShown = false
+
+ private var tisBindHelper: TISBindHelper = TISBindHelper(this) {}
+
+ // Callback array that corresponds to events defined in @ActivityEvent
+ private val mEventCallbacks =
+ arrayOf(RunnableList(), RunnableList(), RunnableList(), RunnableList())
+ private var onInitListener: Predicate<Boolean>? = null
+
+ override fun startHome() {
+ val recentsView: RecentsView<*, *> = getOverviewPanel()
+ recentsView.switchToScreenshot {
+ recentsView.finishRecentsAnimation(true) { startHomeInternal() }
+ }
+ }
+
+ private fun startHomeInternal() {
+ val runner = LauncherAnimationRunner(mainThreadHandler, mAnimationToHomeFactory, true)
+ val options =
+ ActivityOptions.makeRemoteAnimation(
+ RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0),
+ RemoteTransition(
+ runner.toRemoteTransition(),
+ iApplicationThread,
+ "StartHomeFromRecents",
+ ),
+ )
+ OverviewComponentObserver.startHomeIntentSafely(this, options.toBundle(), TAG)
+ }
+
+ private val mAnimationToHomeFactory =
+ RemoteAnimationFactory {
+ _: Int,
+ appTargets: Array<RemoteAnimationTarget>?,
+ wallpaperTargets: Array<RemoteAnimationTarget>?,
+ nonAppTargets: Array<RemoteAnimationTarget>?,
+ result: LauncherAnimationRunner.AnimationResult? ->
+ val controller =
+ getStateManager().createAnimationToNewWorkspace(BG_LAUNCHER, HOME_APPEAR_DURATION)
+ controller.dispatchOnStart()
+ val targets =
+ RemoteAnimationTargets(
+ appTargets,
+ wallpaperTargets,
+ nonAppTargets,
+ RemoteAnimationTarget.MODE_OPENING,
+ )
+ for (app in targets.apps) {
+ SurfaceControl.Transaction().setAlpha(app.leash, 1f).apply()
+ }
+ val anim = AnimatorSet()
+ anim.play(controller.animationPlayer)
+ anim.setDuration(HOME_APPEAR_DURATION)
+ result!!.setAnimation(
+ anim,
+ this@RecentsWindowManager,
+ {
+ getStateManager().goToState(HOME, false)
+ cleanup()
+ },
+ true, /* skipFirstFrame */
+ )
+ }
+
+ fun cleanup() {
+ if (isShown) {
+ windowManager.removeViewImmediate(windowView)
+ isShown = false
+ }
+ }
+
+ fun startRecentsWindow() {
+ if (isShown) return
+ if (windowView == null) {
+ windowView = layoutInflater.inflate(R.layout.fallback_recents_activity, null)
+ }
+ windowManager.addView(windowView, windowLayoutParams)
+ isShown = true
+
+ windowView?.systemUiVisibility =
+ (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
+
+ recentsView = windowView?.findViewById(R.id.overview_panel)
+ actionsView = windowView?.findViewById(R.id.overview_actions_view)
+ scrimView = windowView?.findViewById(R.id.scrim_view)
+ val systemUiProxy = SystemUiProxy.INSTANCE[this]
+ val splitSelectStateController =
+ SplitSelectStateController(
+ this,
+ mHandler,
+ getStateManager(),
+ null /* depthController */,
+ statsLogManager,
+ systemUiProxy,
+ RecentsModel.INSTANCE[this],
+ null, /*activityBackCallback*/
+ )
+ recentsView?.init(actionsView, splitSelectStateController, null)
+ dragLayer = windowView?.findViewById(R.id.drag_layer)
+
+ actionsView?.updateDimension(getDeviceProfile(), recentsView?.lastComputedTaskSize)
+ actionsView?.updateVerticalMargin(DisplayController.getNavigationMode(this))
+
+ mSystemUiController = SystemUiController(windowView)
+ onInitListener?.test(true)
+ }
+
+ override fun canStartHomeSafely(): Boolean {
+ val overviewCommandHelper = tisBindHelper!!.overviewCommandHelper
+ return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely()
+ }
+
+ override fun getDesktopVisibilityController(): DesktopVisibilityController? {
+ return tisBindHelper.desktopVisibilityController
+ }
+
+ fun registerInitListener(onInitListener: Predicate<Boolean>) {
+ this.onInitListener = onInitListener
+ }
+
+ override fun collectStateHandlers(out: MutableList<StateManager.StateHandler<RecentsState?>>?) {
+ out!!.add(FallbackRecentsStateController(this))
+ }
+
+ override fun getStateManager(): StateManager<RecentsState, RecentsWindowManager> {
+ return this.stateManager
+ }
+
+ override fun shouldAnimateStateChange(): Boolean {
+ return true
+ }
+
+ override fun isInState(state: RecentsState?): Boolean {
+ return stateManager.state == state
+ }
+
+ override fun onStateSetStart(state: RecentsState?) {
+ super.onStateSetStart(state)
+ logState(state, "state started:")
+ }
+
+ override fun onStateSetEnd(state: RecentsState?) {
+ super.onStateSetEnd(state)
+ logState(state, "state ended:")
+ }
+
+ private fun logState(state: RecentsState?, prefix: String) {
+ if (!DEBUG) {
+ return
+ }
+ if (state != null) {
+ when (state) {
+ DEFAULT -> Log.d(TAG, prefix + "default")
+ MODAL_TASK -> {
+ Log.d(TAG, prefix + "MODAL_TASK")
+ }
+ BACKGROUND_APP -> {
+ Log.d(TAG, prefix + "BACKGROUND_APP")
+ }
+ HOME -> {
+ Log.d(TAG, prefix + "HOME")
+ }
+ BG_LAUNCHER -> {
+ Log.d(TAG, prefix + "BG_LAUNCHER")
+ }
+ OVERVIEW_SPLIT_SELECT -> {
+ Log.d(TAG, prefix + "OVERVIEW_SPLIT_SELECT")
+ }
+ }
+ }
+ }
+
+ override fun getSystemUiController(): SystemUiController? {
+ if (mSystemUiController == null) {
+ mSystemUiController = SystemUiController(rootView)
+ }
+ return mSystemUiController
+ }
+
+ override fun getContext(): Context {
+ return this
+ }
+
+ override fun getScrimView(): ScrimView? {
+ return scrimView
+ }
+
+ override fun <T : View?> getOverviewPanel(): T {
+ return recentsView as T
+ }
+
+ override fun getRootView(): View? {
+ return windowView
+ }
+
+ override fun getDragLayer(): BaseDragLayer<RecentsWindowManager> {
+ return dragLayer!!
+ }
+
+ override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
+ // TODO(b/368610710)
+ return false
+ }
+
+ override fun dispatchKeyEvent(ev: KeyEvent?): Boolean {
+ // TODO(b/368610710)
+ return false
+ }
+
+ override fun getActionsView(): OverviewActionsView<*>? {
+ return actionsView
+ }
+
+ override fun addForceInvisibleFlag(flag: Int) {}
+
+ override fun clearForceInvisibleFlag(flag: Int) {}
+
+ override fun setLocusContext(id: LocusId?, bundle: Bundle?) {
+ // no op
+ }
+
+ override fun isStarted(): Boolean {
+ return isShown
+ }
+
+ /** Adds a callback for the provided activity event */
+ override fun addEventCallback(@BaseActivity.ActivityEvent event: Int, callback: Runnable?) {
+ mEventCallbacks[event].add(callback)
+ }
+
+ /** Removes a previously added callback */
+ override fun removeEventCallback(@BaseActivity.ActivityEvent event: Int, callback: Runnable?) {
+ mEventCallbacks[event].remove(callback)
+ }
+
+ override fun runOnBindToTouchInteractionService(r: Runnable?) {
+ tisBindHelper.runOnBindToTouchInteractionService(r)
+ }
+
+ override fun addMultiWindowModeChangedListener(
+ listener: BaseActivity.MultiWindowModeChangedListener?
+ ) {
+ // TODO(b/368408838)
+ }
+
+ override fun removeMultiWindowModeChangedListener(
+ listener: BaseActivity.MultiWindowModeChangedListener?
+ ) {}
+
+ override fun returnToHomescreen() {
+ startHome()
+ }
+
+ override fun isRecentsViewVisible(): Boolean {
+ return getStateManager().state!!.isRecentsViewVisible
+ }
+
+ override fun createAtomicAnimationFactory(): AtomicAnimationFactory<RecentsState?>? {
+ return RecentsAtomicAnimationFactory<RecentsWindowManager, RecentsState>(this)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
new file mode 100644
index 0000000..d222feb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2024 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.quickstep.fallback.window;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.EXTRA_COMPONENT_NAME;
+import static android.content.Intent.EXTRA_USER;
+
+import static com.android.app.animation.Interpolators.ACCELERATE;
+import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT;
+import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
+import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
+import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK;
+import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+
+import android.animation.ObjectAnimator;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.RemoteAnimationTarget;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.AbsSwipeUpHandler;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
+import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * Handles the navigation gestures when a 3rd party launcher is the default home activity.
+ *
+ * Bugs: b/365775417
+ */
+public class RecentsWindowSwipeHandler extends AbsSwipeUpHandler<RecentsWindowManager,
+ FallbackRecentsView<RecentsWindowManager>, RecentsState> {
+
+ private static final String TAG = "RecentsWindowSwipeHandler";
+
+ /**
+ * Message used for receiving gesture nav contract information. We use a static messenger to
+ * avoid leaking too make binders in case the receiving launcher does not handle the contract
+ * properly.
+ */
+ private static StaticMessageReceiver sMessageReceiver = null;
+
+ private FallbackHomeAnimationFactory mActiveAnimationFactory;
+ private final boolean mRunningOverHome;
+
+ private final Matrix mTmpMatrix = new Matrix();
+ private float mMaxLauncherScale = 1;
+
+ private boolean mAppCanEnterPip;
+
+ public RecentsWindowSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
+ boolean continuingLastGesture, InputConsumerController inputConsumer) {
+ super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+ continuingLastGesture, inputConsumer);
+
+ mRunningOverHome = mGestureState.getRunningTask() != null
+ && mGestureState.getRunningTask().isHomeTask();
+ if (mRunningOverHome) {
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+ RecentsWindowSwipeHandler.
+ this::updateHomeActivityTransformDuringSwipeUp));
+ }
+ }
+
+ @Override
+ protected void initTransitionEndpoints(DeviceProfile dp) {
+ super.initTransitionEndpoints(dp);
+ if (mRunningOverHome) {
+ // Full screen scale should be independent of remote target handle
+ mMaxLauncherScale = 1 / mRemoteTargetHandles[0].getTaskViewSimulator()
+ .getFullScreenScale();
+ }
+ }
+
+ private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
+ Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
+ }
+
+ private void setHomeScaleAndAlpha(SurfaceProperties builder,
+ RemoteAnimationTarget app, float verticalShift, float alpha) {
+ float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
+ mTmpMatrix.setScale(scale, scale,
+ app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
+ builder.setMatrix(mTmpMatrix).setAlpha(alpha);
+ builder.setShow();
+ }
+
+ @Override
+ protected HomeAnimationFactory createHomeAnimationFactory(
+ List<IBinder> launchCookies,
+ long duration,
+ boolean isTargetTranslucent,
+ boolean appCanEnterPip,
+ RemoteAnimationTarget runningTaskTarget,
+ @Nullable TaskView targetTaskView) {
+ mAppCanEnterPip = appCanEnterPip;
+ if (appCanEnterPip) {
+ return new FallbackPipToHomeAnimationFactory();
+ }
+ mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
+ //todo: b/368410893 follow up on this as its intent focused and seems to cut immediately
+ Intent intent = new Intent(mGestureState.getHomeIntent());
+ if (mActiveAnimationFactory != null && runningTaskTarget != null) {
+ mActiveAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo);
+ }
+ return mActiveAnimationFactory;
+ }
+
+ @Override
+ protected boolean handleTaskAppeared(@NonNull RemoteAnimationTarget[] appearedTaskTargets,
+ @NonNull ActiveGestureLog.CompoundString failureReason) {
+ if (mActiveAnimationFactory != null
+ && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTargets)) {
+ mActiveAnimationFactory = null;
+ return false;
+ }
+
+ return super.handleTaskAppeared(appearedTaskTargets, failureReason);
+ }
+
+ @Override
+ protected void finishRecentsControllerToHome(Runnable callback) {
+ final Runnable recentsCallback;
+ if (mAppCanEnterPip) {
+ // Make sure Launcher is resumed after auto-enter-pip transition to actually trigger
+ // the PiP task appearing.
+ recentsCallback = () -> {
+ callback.run();
+ RecentsWindowManager.Companion.getInstanceOrNull().startHome();
+ };
+ } else {
+ recentsCallback = callback;
+ }
+ mRecentsView.cleanupRemoteTargets();
+ mRecentsAnimationController.finish(
+ mAppCanEnterPip /* toRecents */, recentsCallback, true /* sendUserLeaveHint */);
+ }
+
+ @Override
+ protected void switchToScreenshot() {
+ if (mRunningOverHome) {
+ // When the current task is home, then we don't need to capture anything
+ mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ } else {
+ super.switchToScreenshot();
+ }
+ }
+
+ @Override
+ protected void notifyGestureAnimationStartToRecents() {
+ if (mRunningOverHome) {
+ if (DisplayController.getNavigationMode(mContext).hasGestures) {
+ mRecentsView.onGestureAnimationStartOnHome(
+ mGestureState.getRunningTask().getPlaceholderTasks(),
+ mDeviceState.getRotationTouchHelper());
+ }
+ } else {
+ super.notifyGestureAnimationStartToRecents();
+ }
+ }
+
+ private class FallbackPipToHomeAnimationFactory extends HomeAnimationFactory {
+ @NonNull
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ // copied from {@link LauncherSwipeHandlerV2.LauncherHomeAnimationFactory}
+ long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ return mContainer.getStateManager().createAnimationToNewWorkspace(
+ RecentsState.HOME, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
+ }
+ }
+
+ private class FallbackHomeAnimationFactory extends HomeAnimationFactory
+ implements Consumer<Message> {
+ private final Rect mTempRect = new Rect();
+ private final TransformParams mHomeAlphaParams = new TransformParams();
+ private final AnimatedFloat mHomeAlpha;
+
+ private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat();
+ private final AnimatedFloat mRecentsAlpha = new AnimatedFloat();
+
+ private final RectF mTargetRect = new RectF();
+ private SurfaceControl mSurfaceControl;
+
+ private boolean mAnimationFinished;
+ private Message mOnFinishCallback;
+
+ private final long mDuration;
+
+ private RectFSpringAnim mSpringAnim;
+ FallbackHomeAnimationFactory(long duration) {
+ mDuration = duration;
+
+ if (mRunningOverHome) {
+ mHomeAlpha = new AnimatedFloat();
+ mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1);
+ mVerticalShiftForScale.value = mCurrentShift.value;
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+ FallbackHomeAnimationFactory.this
+ ::updateHomeActivityTransformDuringHomeAnim));
+ } else {
+ mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
+ mHomeAlpha.value = 0;
+ mHomeAlphaParams.setHomeBuilderProxy(
+ this::updateHomeActivityTransformDuringHomeAnim);
+ }
+
+ mRecentsAlpha.value = 1;
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTransformParams().setBaseBuilderProxy(
+ FallbackHomeAnimationFactory.this
+ ::updateRecentsActivityTransformDuringHomeAnim));
+ }
+
+ @NonNull
+ @Override
+ public RectF getWindowTargetRect() {
+ if (mTargetRect.isEmpty()) {
+ mTargetRect.set(super.getWindowTargetRect());
+ }
+ return mTargetRect;
+ }
+
+ private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ builder.setAlpha(mRecentsAlpha.value);
+ }
+
+ private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
+ }
+
+ @NonNull
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ PendingAnimation pa = new PendingAnimation(mDuration);
+ pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCELERATE);
+ return pa.createPlaybackController();
+ }
+
+ private void updateHomeAlpha() {
+ if (mHomeAlphaParams.getTargetSet() != null) {
+ mHomeAlphaParams.applySurfaceParams(
+ mHomeAlphaParams.createSurfaceParams(BuilderProxy.NO_OP));
+ }
+ }
+
+ public boolean handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
+ if (appearedTaskTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
+ RemoteAnimationTargets targets = new RemoteAnimationTargets(
+ new RemoteAnimationTarget[] {appearedTaskTarget},
+ new RemoteAnimationTarget[0], new RemoteAnimationTarget[0],
+ appearedTaskTarget.mode);
+ mHomeAlphaParams.setTargetSet(targets);
+ updateHomeAlpha();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void playAtomicAnimation(float velocity) {
+ ObjectAnimator alphaAnim = mHomeAlpha.animateToValue(mHomeAlpha.value, 1);
+ alphaAnim.setDuration(mDuration).setInterpolator(ACCELERATE);
+ alphaAnim.start();
+
+ if (mRunningOverHome) {
+ // Spring back launcher scale
+ new SpringAnimationBuilder(mContext)
+ .setStartValue(mVerticalShiftForScale.value)
+ .setEndValue(0)
+ .setStartVelocity(-velocity / mTransitionDragLength)
+ .setMinimumVisibleChange(1f / mDp.heightPx)
+ .setDampingRatio(0.6f)
+ .setStiffness(800)
+ .build(mVerticalShiftForScale, AnimatedFloat.VALUE)
+ .start();
+ }
+ }
+
+ @Override
+ public void setAnimation(RectFSpringAnim anim) {
+ mSpringAnim = anim;
+ mSpringAnim.addAnimatorListener(forEndCallback(this::onRectAnimationEnd));
+ }
+
+ private void onRectAnimationEnd() {
+ mAnimationFinished = true;
+ maybeSendEndMessage();
+ }
+
+ private void maybeSendEndMessage() {
+ if (mAnimationFinished && mOnFinishCallback != null) {
+ try {
+ mOnFinishCallback.replyTo.send(mOnFinishCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending icon position", e);
+ }
+ }
+ }
+
+ @Override
+ public void accept(Message msg) {
+ try {
+ Bundle data = msg.getData();
+ RectF position = data.getParcelable(EXTRA_ICON_POSITION);
+ if (!position.isEmpty()) {
+ mSurfaceControl = data.getParcelable(EXTRA_ICON_SURFACE);
+ mTargetRect.set(position);
+ if (mSpringAnim != null) {
+ mSpringAnim.onTargetPositionChanged();
+ }
+ }
+ mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK);
+ maybeSendEndMessage();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ @Override
+ public void update(RectF currentRect, float progress, float radius, int overlayAlpha) {
+ if (mSurfaceControl != null) {
+ currentRect.roundOut(mTempRect);
+ Transaction t = new Transaction();
+ try {
+ t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
+ t.apply();
+ } catch (RuntimeException e) {
+ // Ignore
+ }
+ }
+ }
+
+ private void addGestureContract(Intent intent, RunningTaskInfo runningTaskInfo) {
+ if (mRunningOverHome || runningTaskInfo == null) {
+ return;
+ }
+
+ TaskKey key = new TaskKey(runningTaskInfo);
+ if (key.getComponent() != null) {
+ if (sMessageReceiver == null) {
+ sMessageReceiver = new StaticMessageReceiver();
+ }
+
+ Bundle gestureNavContract = new Bundle();
+ gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent());
+ gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId));
+ gestureNavContract.putParcelable(
+ EXTRA_REMOTE_CALLBACK, sMessageReceiver.newCallback(this));
+ intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract);
+ }
+ }
+ }
+
+ private static class StaticMessageReceiver implements Handler.Callback {
+
+ private final Messenger mMessenger =
+ new Messenger(new Handler(Looper.getMainLooper(), this));
+
+ private ParcelUuid mCurrentUID = new ParcelUuid(UUID.randomUUID());
+ private WeakReference<Consumer<Message>> mCurrentCallback = new WeakReference<>(null);
+
+ public Message newCallback(Consumer<Message> callback) {
+ mCurrentUID = new ParcelUuid(UUID.randomUUID());
+ mCurrentCallback = new WeakReference<>(callback);
+
+ Message msg = Message.obtain();
+ msg.replyTo = mMessenger;
+ msg.obj = mCurrentUID;
+ return msg;
+ }
+
+ @Override
+ public boolean handleMessage(@NonNull Message message) {
+ if (mCurrentUID.equals(message.obj)) {
+ Consumer<Message> consumer = mCurrentCallback.get();
+ if (consumer != null) {
+ consumer.accept(message);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 69d3bc9..e19b338 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -41,6 +41,7 @@
import androidx.annotation.UiThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
@@ -424,7 +425,10 @@
mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
notifyGestureStarted(true /*isLikelyToStartNewTask*/);
} else {
- Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
+ // todo differentiate intent based on if we are on home or in app for overview in window
+ Intent intent = new Intent(Flags.enableFallbackOverviewInWindow()
+ ? mInteractionHandler.getHomeIntent()
+ : mInteractionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
mInteractionHandler);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index c61f71d..a236eca 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -28,6 +28,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
@@ -41,7 +42,8 @@
/**
* Input consumer for handling touch on the recents/Launcher activity.
*/
-public class OverviewInputConsumer<S extends BaseState<S>, T extends RecentsViewContainer>
+public class OverviewInputConsumer<S extends BaseState<S>,
+ T extends RecentsViewContainer & StatefulContainer<S>>
implements InputConsumer {
private final T mContainer;
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
index 7b97c23..c07b7fb 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
@@ -59,10 +59,10 @@
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- private val recentsViewContainer =
- RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
+ private val recentsViewContainer: RecentsViewContainer =
+ RecentsViewContainer.containerFromContext(context)
private val launcherApps: LauncherApps? = context.getSystemService(LauncherApps::class.java)
@@ -138,7 +138,7 @@
usageLimit =
launcherApps?.getAppUsageLimit(
task.topComponent.packageName,
- UserHandle.of(task.key.userId)
+ UserHandle.of(task.key.userId),
)
} catch (e: Exception) {
Log.e(TAG, "Error initializing digital well being toast", e)
@@ -162,7 +162,7 @@
task: Task,
taskView: TaskView,
snapshotView: View,
- @StagePosition stagePosition: Int
+ @StagePosition stagePosition: Int,
) {
this.task = task
this.taskView = taskView
@@ -201,7 +201,7 @@
private fun getReadableDuration(
duration: Duration,
- @StringRes durationLessThanOneMinuteStringId: Int
+ @StringRes durationLessThanOneMinuteStringId: Int,
): String {
val hours = Math.toIntExact(duration.toHours())
val minutes = Math.toIntExact(duration.minusHours(hours.toLong()).toMinutes())
@@ -211,7 +211,7 @@
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.NARROW)
.formatMeasures(
Measure(hours, MeasureUnit.HOUR),
- Measure(minutes, MeasureUnit.MINUTE)
+ Measure(minutes, MeasureUnit.MINUTE),
)
// Apply FormatWidth.WIDE if only the hour part is non-zero (unless forced).
hours > 0 ->
@@ -239,7 +239,7 @@
@VisibleForTesting
fun getBannerText(
remainingTime: Long = appRemainingTimeMs,
- forContentDesc: Boolean = false
+ forContentDesc: Boolean = false,
): String {
val duration =
Duration.ofMillis(
@@ -250,7 +250,7 @@
val readableDuration =
getReadableDuration(
duration,
- R.string.shorter_duration_less_than_one_minute /* forceFormatWidth */
+ R.string.shorter_duration_less_than_one_minute, /* forceFormatWidth */
)
val splitBannerConfig = getSplitBannerConfig()
return when {
@@ -277,7 +277,7 @@
Log.e(
TAG,
"Failed to open app usage settings for task " + task.topComponent.packageName,
- e
+ e,
)
}
}
@@ -285,13 +285,13 @@
private fun getContentDescriptionForTask(
task: Task,
appUsageLimitTimeMs: Long,
- appRemainingTimeMs: Long
+ appRemainingTimeMs: Long,
): String? =
if (appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0)
context.getString(
R.string.task_contents_description_with_remaining_time,
task.titleDescription,
- getBannerText(appRemainingTimeMs, true /* forContentDesc */)
+ getBannerText(appRemainingTimeMs, true /* forContentDesc */),
)
else task.titleDescription
@@ -310,7 +310,7 @@
recentsViewContainer.deviceProfile,
splitBounds,
taskView.layoutParams.width,
- taskView.layoutParams.height
+ taskView.layoutParams.height,
)
if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
snapshotWidth = groupedTaskSize.first.x
@@ -327,7 +327,7 @@
recentsViewContainer.deviceProfile,
snapshotWidth,
snapshotHeight,
- this
+ this,
)
}
@@ -340,7 +340,7 @@
recentsViewContainer.deviceProfile,
taskView.snapshotViews,
task.key.id,
- this
+ this,
)
this.translationX = translationX
this.splitOffsetTranslationY = translationY
@@ -372,7 +372,7 @@
if (taskView.containsMultipleTasks())
context.getString(
R.string.split_app_usage_settings,
- TaskUtils.getTitle(context, task)
+ TaskUtils.getTitle(context, task),
)
else context.getString(R.string.accessibility_app_usage_settings)
return AccessibilityNodeInfo.AccessibilityAction(getAccessibilityActionId(), label)
@@ -394,7 +394,7 @@
/** Used for grid task view, only showing icon and time */
SPLIT_GRID_BANNER_LARGE,
/** Used for grid task view, only showing icon */
- SPLIT_GRID_BANNER_SMALL
+ SPLIT_GRID_BANNER_SMALL,
}
val OPEN_APP_USAGE_SETTINGS_TEMPLATE: Intent = Intent(Settings.ACTION_APP_USAGE_SETTINGS)
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f502e31..ba38758 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -236,8 +236,6 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
-import kotlin.Unit;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -251,6 +249,8 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import kotlin.Unit;
+
/**
* A list of recent tasks.
*
@@ -258,7 +258,7 @@
* @param <STATE_TYPE> : the type of base state that will be used
*/
public abstract class RecentsView<
- CONTAINER_TYPE extends Context & RecentsViewContainer,
+ CONTAINER_TYPE extends Context & RecentsViewContainer & StatefulContainer<STATE_TYPE>,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
TaskVisualsChangeListener {
@@ -1209,6 +1209,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+
updateTaskStackListenerState();
mModel.getThumbnailCache().getHighResLoadingState().removeCallback(this);
mContainer.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index 462c53a..d8036aa 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -44,7 +44,7 @@
* Returns an instance of an implementation of RecentsViewContainer
* @param context will find instance of recentsViewContainer from given context.
*/
- static <T extends RecentsViewContainer> T containerFromContext(Context context) {
+ static <T extends Context & RecentsViewContainer> T containerFromContext(Context context) {
if (context instanceof RecentsViewContainer) {
return (T) context;
} else if (context instanceof ContextWrapper) {
@@ -138,12 +138,6 @@
void runOnBindToTouchInteractionService(Runnable r);
/**
- * @see Activity#getComponentName()
- * @return ComponentName
- */
- ComponentName getComponentName();
-
- /**
* @see
* BaseActivity#addMultiWindowModeChangedListener(BaseActivity.MultiWindowModeChangedListener)
* @param listener {@link BaseActivity.MultiWindowModeChangedListener}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index 2a0aa4c..fc37b59 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -45,7 +45,9 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.util.SystemUiController;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
@@ -62,16 +64,18 @@
import java.util.HashMap;
public abstract class AbsSwipeUpHandlerTestCase<
- RECENTS_CONTAINER extends Context & RecentsViewContainer,
- STATE extends BaseState<STATE>,
- RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>,
+ RECENTS_CONTAINER extends Context & RecentsViewContainer & StatefulContainer<STATE>,
+ STATE extends BaseState<STATE>, RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>,
ACTIVITY_TYPE extends StatefulActivity<STATE> & RecentsViewContainer,
ACTIVITY_INTERFACE extends BaseActivityInterface<STATE, ACTIVITY_TYPE>,
SWIPE_HANDLER extends AbsSwipeUpHandler<RECENTS_CONTAINER, RECENTS_VIEW, STATE>> {
protected final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
- protected final TaskAnimationManager mTaskAnimationManager = new TaskAnimationManager(mContext);
+ protected final RecentsWindowManager mRecentsWindowManager =
+ RecentsWindowManager.Companion.getOrCreateInstance(mContext);
+ protected final TaskAnimationManager mTaskAnimationManager =
+ new TaskAnimationManager(mContext, mRecentsWindowManager);
protected final RecentsAnimationDeviceState mRecentsAnimationDeviceState =
new RecentsAnimationDeviceState(mContext, true);
protected final InputConsumerController mInputConsumerController =
diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 28c8a4a..ec07b93 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -29,6 +29,8 @@
import androidx.test.filters.SmallTest;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -42,6 +44,9 @@
private Context mContext;
@Mock
+ private RecentsWindowManager mRecentsWindowManager;
+
+ @Mock
private SystemUiProxy mSystemUiProxy;
private TaskAnimationManager mTaskAnimationManager;
@@ -49,7 +54,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTaskAnimationManager = new TaskAnimationManager(mContext) {
+ mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsWindowManager) {
@Override
SystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index fec94fe..2e75261 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -188,7 +188,7 @@
public SystemUiController getSystemUiController() {
if (mSystemUiController == null) {
- mSystemUiController = new SystemUiController(getWindow());
+ mSystemUiController = new SystemUiController(getWindow().getDecorView());
}
return mSystemUiController;
}
diff --git a/src/com/android/launcher3/statemanager/StatefulContainer.java b/src/com/android/launcher3/statemanager/StatefulContainer.java
index 702dc6a..b10af0a 100644
--- a/src/com/android/launcher3/statemanager/StatefulContainer.java
+++ b/src/com/android/launcher3/statemanager/StatefulContainer.java
@@ -20,11 +20,9 @@
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
-import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
-import android.view.Window;
import androidx.annotation.CallSuper;
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index df54fd7..368b267 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -17,7 +17,6 @@
package com.android.launcher3.util;
import android.view.View;
-import android.view.Window;
import androidx.annotation.IntDef;
@@ -54,11 +53,11 @@
})
public @interface SystemUiControllerFlags {}
- private final Window mWindow;
+ private final View mView;
private final int[] mStates = new int[5];
- public SystemUiController(Window window) {
- mWindow = window;
+ public SystemUiController(View view) {
+ mView = view;
}
public void updateUiState(int uiState, boolean isLight) {
@@ -72,14 +71,14 @@
}
mStates[uiState] = flags;
- int oldFlags = mWindow.getDecorView().getSystemUiVisibility();
+ int oldFlags = mView.getSystemUiVisibility();
// Apply the state flags in priority order
int newFlags = oldFlags;
for (int stateFlag : mStates) {
newFlags = getSysUiVisibilityFlags(stateFlag, newFlags);
}
if (newFlags != oldFlags) {
- mWindow.getDecorView().setSystemUiVisibility(newFlags);
+ mView.setSystemUiVisibility(newFlags);
}
}
@@ -88,7 +87,7 @@
*/
public int getBaseSysuiVisibility() {
return getSysUiVisibilityFlags(
- mStates[UI_STATE_BASE_WINDOW], mWindow.getDecorView().getSystemUiVisibility());
+ mStates[UI_STATE_BASE_WINDOW], mView.getSystemUiVisibility());
}
private int getSysUiVisibilityFlags(int stateFlag, int currentVisibility) {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index a86caf3..9676b38 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -30,6 +30,7 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
@@ -175,7 +176,7 @@
/**
* The root view to support drag-and-drop and popup support.
*/
- BaseDragLayer<?> getDragLayer();
+ BaseDragLayer getDragLayer();
/**
* @see Activity#getWindow()
@@ -187,6 +188,14 @@
}
/**
+ * @see Activity#getComponentName()
+ * @return ComponentName
+ */
+ default ComponentName getComponentName() {
+ return null;
+ }
+
+ /**
* The all apps container, if it exists in this context.
*/
default ActivityAllAppsContainerView<?> getAppsView() {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt
index 612fcd4..043bdac 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt
@@ -52,7 +52,7 @@
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(window.decorView).thenReturn(decorView)
- underTest = SystemUiController(window)
+ underTest = SystemUiController(window.decorView)
}
@Test