appInfo = Stream.of(APP_INFO);
+ if (mHideSplitOptions) {
+ return appInfo;
+ }
+
return Stream.concat(
Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
.stream()
.map(this::createSplitShortcutFactory),
- Stream.of(APP_INFO)
+ appInfo
);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
index 1d3757fca2..cdc6d59bf1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -69,4 +69,13 @@ public class TaskbarScrimView extends View {
mRenderer.getPaint().setAlpha((int) (alpha * 255));
invalidate();
}
+
+ /**
+ * Sets the roundness of the round corner above Taskbar.
+ * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+ */
+ protected void setCornerRoundness(float cornerRoundness) {
+ mRenderer.setCornerRoundness(cornerRoundness);
+ invalidate();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index c3b0f57c2d..88767dd61d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -22,7 +22,7 @@ import android.animation.ObjectAnimator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
-import com.android.quickstep.AnimatedFloat;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
@@ -30,7 +30,8 @@ import java.io.PrintWriter;
/**
* Handles properties/data collection, and passes the results to {@link TaskbarScrimView} to render.
*/
-public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController {
+public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController,
+ TaskbarControllers.BackgroundRendererController {
private static final float SCRIM_ALPHA = 0.6f;
@@ -94,6 +95,11 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa
SystemUiProxy.INSTANCE.get(mActivity).onBackPressed();
}
+ @Override
+ public void setCornerRoundness(float cornerRoundness) {
+ mScrimView.setCornerRoundness(cornerRoundness);
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarScrimViewController:");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 4b0adb192f..c269648f7d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,13 +17,20 @@ package com.android.launcher3.taskbar;
import static android.view.HapticFeedbackConstants.LONG_PRESS;
-import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.config.FeatureFlags.FORCE_PERSISTENT_TASKBAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
import static com.android.launcher3.taskbar.Utilities.appendFlag;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,15 +44,19 @@ import android.view.View;
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.launcher3.Alarm;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.AnimatedFloat;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
@@ -62,23 +73,24 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
public static final int FLAG_IN_APP = 1 << 0;
public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
- public static final int FLAG_STASHED_IN_APP_PINNED = 1 << 2; // app pinning
+ public static final int FLAG_STASHED_IN_SYSUI_STATE = 1 << 2; // app pinning, keyguard, etc.
public static final int FLAG_STASHED_IN_APP_EMPTY = 1 << 3; // no hotseat icons
public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 4; // setup wizard and AllSetActivity
public static final int FLAG_STASHED_IN_APP_IME = 1 << 5; // IME is visible
public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 6;
- public static final int FLAG_STASHED_IN_APP_ALL_APPS = 1 << 7; // All apps is visible.
+ public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 7; // All apps is visible.
public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard
public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 9; // phone screen gesture nav, stashed
+ public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 10; // Autohide (transient taskbar).
// If any of these flags are enabled, isInApp should return true.
private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
// If we're in an app and any of these flags are enabled, taskbar should be stashed.
private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
- | FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP
- | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_APP_ALL_APPS |
- FLAG_STASHED_SMALL_SCREEN;
+ | FLAG_STASHED_IN_SYSUI_STATE | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP
+ | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS
+ | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
private static final int FLAGS_STASHED_IN_APP_IGNORING_IME =
FLAGS_STASHED_IN_APP & ~FLAG_STASHED_IN_APP_IME;
@@ -88,7 +100,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
// Currently any flag that causes us to stash in an app is included, except for IME or All Apps
// since those cover the underlying app anyway and thus the app shouldn't change insets.
private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP
- & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_APP_ALL_APPS;
+ & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_TASKBAR_ALL_APPS;
/**
* How long to stash/unstash when manually invoked via long press.
@@ -132,6 +144,9 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
*/
private static final boolean DEFAULT_STASHED_PREF = false;
+ // Auto stashes when user has not interacted with the Taskbar after X ms.
+ private static final long NO_TOUCH_TIMEOUT_TO_STASH_MS = 5000;
+
private final TaskbarActivityContext mActivity;
private final SharedPreferences mPrefs;
private final int mStashedHeight;
@@ -144,11 +159,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private AnimatedFloat mTaskbarBackgroundOffset;
private AnimatedFloat mTaskbarImeBgAlpha;
// TaskbarView icon properties.
- private AlphaProperty mIconAlphaForStash;
+ private MultiProperty mIconAlphaForStash;
private AnimatedFloat mIconScaleForStash;
private AnimatedFloat mIconTranslationYForStash;
// Stashed handle properties.
- private AlphaProperty mTaskbarStashedHandleAlpha;
+ private MultiProperty mTaskbarStashedHandleAlpha;
private AnimatedFloat mTaskbarStashedHandleHintScale;
/** Whether we are currently visually stashed (might change based on launcher state). */
@@ -162,26 +177,36 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private boolean mEnableManualStashingDuringTests = false;
+ private final Alarm mTimeoutAlarm = new Alarm();
+ private boolean mEnableBlockingTimeoutDuringTests = false;
+
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
flags -> {
boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
+ boolean stashedInTaskbarAllApps =
+ hasAnyFlag(flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS);
boolean stashedForSmallScreen = hasAnyFlag(flags, FLAG_STASHED_SMALL_SCREEN);
return (inApp && stashedInApp) || (!inApp && stashedLauncherState)
- || stashedForSmallScreen;
+ || stashedInTaskbarAllApps || stashedForSmallScreen;
});
public TaskbarStashController(TaskbarActivityContext activity) {
mActivity = activity;
- mPrefs = Utilities.getPrefs(mActivity);
+ mPrefs = LauncherPrefs.getPrefs(mActivity);
mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
if (isPhoneMode()) {
// DeviceProfile's taskbar vars aren't initialized w/ the flag off
Resources resources = mActivity.getResources();
- mUnstashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_size);
- mStashedHeight = resources.getDimensionPixelOffset(R.dimen.taskbar_stashed_size);
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ mUnstashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
+ ? R.dimen.transient_taskbar_size
+ : R.dimen.taskbar_size);
+ mStashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
+ ? R.dimen.transient_taskbar_stashed_size
+ : R.dimen.taskbar_stashed_size);
} else {
mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarSize;
@@ -197,24 +222,28 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
mTaskbarImeBgAlpha = dragLayerController.getImeBgTaskbar();
TaskbarViewController taskbarViewController = controllers.taskbarViewController;
- mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().getProperty(
+ mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().get(
TaskbarViewController.ALPHA_INDEX_STASH);
mIconScaleForStash = taskbarViewController.getTaskbarIconScaleForStash();
mIconTranslationYForStash = taskbarViewController.getTaskbarIconTranslationYForStash();
StashedHandleViewController stashedHandleController =
controllers.stashedHandleViewController;
- mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().getProperty(
+ mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().get(
StashedHandleViewController.ALPHA_INDEX_STASHED);
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
// We use supportsVisualStashing() here instead of supportsManualStashing() because we want
// it to work properly for tests that recreate taskbar. This check is here just to ensure
// that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false).
boolean isManuallyStashedInApp = supportsVisualStashing()
+ && !isTransientTaskbar
+ && !FORCE_PERSISTENT_TASKBAR.get()
&& mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isTransientTaskbar);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
updateStateForFlag(FLAG_IN_SETUP, isInSetup);
updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
@@ -239,18 +268,33 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
* Returns whether the user can manually stash the taskbar based on the current device state.
*/
protected boolean supportsManualStashing() {
+ if (FORCE_PERSISTENT_TASKBAR.get()) {
+ return false;
+ }
return supportsVisualStashing()
- && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests);
+ && isInApp()
+ && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests)
+ && !DisplayController.isTransientTaskbar(mActivity);
}
/**
* Enables support for manual stashing. This should only be used to add this functionality
* to Launcher specific tests.
*/
+ @VisibleForTesting
public void enableManualStashingDuringTests(boolean enableManualStashing) {
mEnableManualStashingDuringTests = enableManualStashing;
}
+ /**
+ * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar
+ * testing.
+ */
+ @VisibleForTesting
+ public void enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout) {
+ mEnableBlockingTimeoutDuringTests = enableBlockingTimeout;
+ }
+
/**
* Sets the flag indicating setup UI is visible
*/
@@ -316,23 +360,31 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
return hasAnyFlag(FLAGS_IN_APP);
}
+ /**
+ * Returns the height that taskbar will be touchable.
+ */
+ public int getTouchableHeight() {
+ return mIsStashed ? mStashedHeight : mUnstashedHeight;
+ }
+
/**
* Returns the height that taskbar will inset when inside apps.
* @see WindowInsets.Type#navigationBars()
* @see WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
- if (isPhoneMode() && !mActivity.isThreeButtonNav()) {
+ if ((isPhoneMode() && !mActivity.isThreeButtonNav())
+ || DisplayController.isTransientTaskbar(mActivity)) {
return getStashedHeight();
}
if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
DeviceProfile dp = mActivity.getDeviceProfile();
- if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent && !dp.isLandscape) {
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent) {
// We always show the back button in SUW but in portrait the SUW layout may not
- // be wide enough to support overlapping the nav bar with its content. For now,
- // just inset by the bar height.
- return mUnstashedHeight;
+ // be wide enough to support overlapping the nav bar with its content.
+ // We're sending different res values in portrait vs landscape
+ return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets);
}
boolean isAnimating = mAnimator != null && mAnimator.isStarted();
if (!mControllers.stashedHandleViewController.isStashedHandleVisible()
@@ -345,6 +397,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
return mStashedHeight;
}
+
return mUnstashedHeight;
}
@@ -361,6 +414,20 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
return mStashedHeight;
}
+ /**
+ * Stash or unstashes the transient taskbar.
+ */
+ public void updateAndAnimateTransientTaskbar(boolean stash) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) {
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash);
+ applyState();
+ }
+ }
+
/**
* Should be called when long pressing the nav region when taskbar is present.
* @return Whether taskbar was stashed and now is unstashed.
@@ -423,7 +490,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
/* isStashed= */ false,
placeholderDuration,
/* startDelay= */ 0,
- /* animateBg= */ false);
+ /* animateBg= */ false,
+ /* changedFlags=*/ 0);
animation.play(mAnimator);
}
@@ -434,8 +502,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
* @param startDelay how many milliseconds to delay the animation after starting it.
* @param animateBg whether the taskbar's background should be animated
*/
- private void createAnimToIsStashed(
- boolean isStashed, long duration, long startDelay, boolean animateBg) {
+ private void createAnimToIsStashed(boolean isStashed, long duration, long startDelay,
+ boolean animateBg, int changedFlags) {
if (mAnimator != null) {
mAnimator.cancel();
}
@@ -473,6 +541,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
final float firstHalfDurationScale;
final float secondHalfDurationScale;
+ // If Hotseat is not the top element during animation to/from Launcher, fade in/out a
+ // already stashed Taskbar.
+ boolean skipStashAnimation = !mControllers.uiController.isHotseatIconOnTopWhenAligned()
+ && hasAnyFlag(changedFlags, FLAG_IN_APP);
if (isStashed) {
firstHalfDurationScale = 0.75f;
secondHalfDurationScale = 0.5f;
@@ -493,6 +565,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
secondHalfAnimatorSet.playTogether(
mTaskbarStashedHandleAlpha.animateToValue(1)
);
+
+ if (skipStashAnimation) {
+ fullLengthAnimatorSet.setInterpolator(INSTANT);
+ firstHalfAnimatorSet.setInterpolator(INSTANT);
+ }
} else {
firstHalfDurationScale = 0.5f;
secondHalfDurationScale = 0.75f;
@@ -513,6 +590,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
secondHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(1)
);
+
+ if (skipStashAnimation) {
+ fullLengthAnimatorSet.setInterpolator(FINAL_FRAME);
+ secondHalfAnimatorSet.setInterpolator(FINAL_FRAME);
+ }
}
fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
@@ -534,11 +616,17 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
public void onAnimationStart(Animator animation) {
mIsStashed = isStashed;
onIsStashedChanged(mIsStashed);
+
+ cancelTimeoutIfExists();
}
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
+
+ if (!mIsStashed) {
+ tryStartTaskbarTimeout();
+ }
}
});
}
@@ -635,34 +723,26 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
// Only update the following flags when system gesture is not in progress.
boolean shouldStashForIme = shouldStashForIme();
- maybeResetStashedInAppAllApps(
- hasAnyFlag(FLAG_STASHED_IN_APP_IME) == shouldStashForIme);
+ updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != shouldStashForIme) {
updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme);
applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme());
+ } else {
+ applyState(mControllers.taskbarOverlayController.getCloseDuration());
}
}
/**
- * Reset stashed in all apps only if no system gesture is in progress.
+ * Resets the flag if no system gesture is in progress.
*
* Otherwise, the reset should be deferred until after the gesture is finished.
*
* @see #setSystemGestureInProgress
*/
- public void maybeResetStashedInAppAllApps() {
- maybeResetStashedInAppAllApps(true);
- }
-
- private void maybeResetStashedInAppAllApps(boolean applyState) {
- if (mIsSystemGestureInProgress) {
- return;
- }
-
- updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, false);
- if (applyState) {
- applyState(ALL_APPS.getTransitionDuration(
- mControllers.taskbarActivityContext, false /* isToState */));
+ public void resetFlagIfNoGestureInProgress(int flag) {
+ if (!mIsSystemGestureInProgress) {
+ updateStateForFlag(flag, false);
+ applyState(mControllers.taskbarOverlayController.getCloseDuration());
}
}
@@ -685,12 +765,18 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
long animDuration = TASKBAR_STASH_DURATION;
long startDelay = 0;
- updateStateForFlag(FLAG_STASHED_IN_APP_PINNED,
- hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
+ updateStateForFlag(FLAG_STASHED_IN_SYSUI_STATE, hasAnyFlag(systemUiStateFlags,
+ SYSUI_STATE_SCREEN_PINNING
+ | SYSUI_STATE_BOUNCER_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+ | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+ | SYSUI_STATE_QUICK_SETTINGS_EXPANDED));
// Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING);
+
if (!mIsSystemGestureInProgress) {
updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme());
animDuration = TASKBAR_STASH_DURATION_FOR_IME;
@@ -700,8 +786,20 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
}
+ /**
+ * We stash when IME or IME switcher is showing AND NOT
+ * * in small screen AND
+ * * 3 button nav AND
+ * * landscape (or seascape)
+ * We do not stash if taskbar is transient
+ */
private boolean shouldStashForIme() {
- return mIsImeShowing || mIsImeSwitcherShowing;
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ return false;
+ }
+ return (mIsImeShowing || mIsImeSwitcherShowing) &&
+ !(isPhoneMode() && mActivity.isThreeButtonNav()
+ && mActivity.getDeviceProfile().isLandscape);
}
/**
@@ -756,6 +854,54 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
mControllers.rotationButtonController.onTaskbarStateChange(visible, stashed);
}
+ /**
+ * Cancels a timeout if any exists.
+ */
+ public void cancelTimeoutIfExists() {
+ if (mTimeoutAlarm.alarmPending()) {
+ mTimeoutAlarm.cancelAlarm();
+ }
+ }
+
+ /**
+ * Updates the status of the taskbar timeout.
+ * @param isAutohideSuspended If true, cancels any existing timeout
+ * If false, attempts to re/start the timeout
+ */
+ public void updateTaskbarTimeout(boolean isAutohideSuspended) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+ if (isAutohideSuspended) {
+ cancelTimeoutIfExists();
+ } else {
+ tryStartTaskbarTimeout();
+ }
+ }
+
+ /**
+ * Attempts to start timer to auto hide the taskbar based on time.
+ */
+ public void tryStartTaskbarTimeout() {
+ if (!DisplayController.isTransientTaskbar(mActivity)
+ || mIsStashed
+ || mEnableBlockingTimeoutDuringTests) {
+ return;
+ }
+
+ cancelTimeoutIfExists();
+
+ mTimeoutAlarm.setOnAlarmListener(this::onTaskbarTimeout);
+ mTimeoutAlarm.setAlarm(NO_TOUCH_TIMEOUT_TO_STASH_MS);
+ }
+
+ private void onTaskbarTimeout(Alarm alarm) {
+ if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
+ return;
+ }
+ updateAndAnimateTransientTaskbar(true);
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarStashController:");
@@ -771,23 +917,25 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
private static String getStateString(int flags) {
- StringJoiner str = new StringJoiner("|");
- appendFlag(str, flags, FLAGS_IN_APP, "FLAG_IN_APP");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_PINNED, "FLAG_STASHED_IN_APP_PINNED");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
- appendFlag(str, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_ALL_APPS, "FLAG_STASHED_IN_APP_ALL_APPS");
- appendFlag(str, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP");
- return str.toString();
+ StringJoiner sj = new StringJoiner("|");
+ appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
+ appendFlag(sj, flags, FLAG_STASHED_IN_SYSUI_STATE, "FLAG_STASHED_IN_SYSUI_STATE");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
+ appendFlag(sj, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE");
+ appendFlag(sj, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_TASKBAR_ALL_APPS");
+ appendFlag(sj, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_AUTO, "FLAG_STASHED_IN_APP_AUTO");
+ return sj.toString();
}
private class StatePropertyHolder {
private final IntPredicate mStashCondition;
private boolean mIsStashed;
+ private boolean mIsHotseatIconOnTopWhenAligned;
private int mPrevFlags;
StatePropertyHolder(IntPredicate stashCondition) {
@@ -817,7 +965,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
mPrevFlags = flags;
}
boolean isStashed = mStashCondition.test(flags);
- if (mIsStashed != isStashed) {
+ boolean isHotseatIconOnTopWhenAligned =
+ mControllers.uiController.isHotseatIconOnTopWhenAligned();
+ // If an animation has started and mIsHotseatIconOnTopWhenAligned is changed, we need
+ // to restart the animation with new parameters.
+ if (mIsStashed != isStashed
+ || (mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned
+ && mAnimator != null && mAnimator.isStarted())) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.TASKBAR_IN_APP_STATE, String.format(
"setState: mIsStashed=%b, isStashed=%b, duration=%d, start=:%b",
@@ -827,9 +981,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
start));
}
mIsStashed = isStashed;
+ mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
// This sets mAnimator.
- createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true);
+ createAnimToIsStashed(
+ mIsStashed, duration, startDelay, /* animateBg= */ true, changedFlags);
if (start) {
mAnimator.start();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
new file mode 100644
index 0000000000..5dba444ada
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.anim.AnimatedFloat.VALUE;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.util.DisplayController;
+
+import java.io.PrintWriter;
+
+/**
+ * Class responsible for translating the transient taskbar UI during a swipe gesture.
+ *
+ * The translation is controlled, in priority order:
+ * - animation to home
+ * - a spring animation
+ * - controlled by user
+ *
+ * The spring animation will play start once the user lets go or when user pauses to go to overview.
+ * When the user goes home, the stash animation will play.
+ */
+public class TaskbarTranslationController implements TaskbarControllers.LoggableTaskbarController {
+
+ private final TaskbarActivityContext mContext;
+ private TaskbarControllers mControllers;
+ private final AnimatedFloat mTranslationYForSwipe = new AnimatedFloat(
+ this::updateTranslationYForSwipe);
+
+ private boolean mHasSprungOnceThisGesture;
+ private @Nullable ValueAnimator mSpringBounce;
+ private boolean mGestureEnded;
+ private boolean mAnimationToHomeRunning;
+
+ private final boolean mIsTransientTaskbar;
+
+ private final TransitionCallback mCallback;
+
+ public TaskbarTranslationController(TaskbarActivityContext context) {
+ mContext = context;
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mCallback = new TransitionCallback();
+ }
+
+ /**
+ * Initialization method.
+ */
+ public void init(TaskbarControllers controllers) {
+ mControllers = controllers;
+ }
+
+ /**
+ * Called to cancel any existing animations.
+ */
+ public void cancelAnimationIfExists() {
+ if (mSpringBounce != null) {
+ mSpringBounce.cancel();
+ mSpringBounce = null;
+ }
+ reset();
+ }
+
+ private void updateTranslationYForSwipe() {
+ if (!mIsTransientTaskbar) {
+ return;
+ }
+
+ float transY = mTranslationYForSwipe.value;
+ mControllers.stashedHandleViewController.setTranslationYForSwipe(transY);
+ mControllers.taskbarViewController.setTranslationYForSwipe(transY);
+ mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY);
+ }
+
+ /**
+ * Starts a spring aniamtion to set the views back to the resting state.
+ */
+ public void startSpring() {
+ if (mHasSprungOnceThisGesture || mAnimationToHomeRunning) {
+ return;
+ }
+ mSpringBounce = new SpringAnimationBuilder(mContext)
+ .setStartValue(mTranslationYForSwipe.value)
+ .setEndValue(0)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .build(mTranslationYForSwipe, VALUE);
+ mSpringBounce.addListener(forEndCallback(() -> {
+ if (mGestureEnded) {
+ reset();
+ }
+ }));
+ mSpringBounce.start();
+ mHasSprungOnceThisGesture = true;
+ }
+
+ private void reset() {
+ mGestureEnded = false;
+ mHasSprungOnceThisGesture = false;
+ }
+
+ /**
+ * Returns a callback to help monitor the swipe gesture.
+ */
+ public TransitionCallback getTransitionCallback() {
+ return mCallback;
+ }
+
+ /**
+ * Returns an animation to reset the taskbar translation for animation back to launcher.
+ */
+ public ObjectAnimator createAnimToLauncher(long duration) {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0);
+ animator.setInterpolator(Interpolators.LINEAR);
+ animator.setDuration(duration);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ cancelAnimationIfExists();
+ mAnimationToHomeRunning = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimationToHomeRunning = false;
+ reset();
+ }
+ });
+ return animator;
+ }
+
+ /**
+ * Helper class to communicate to/from the input consumer.
+ */
+ public class TransitionCallback {
+
+ /**
+ * Called when there is movement to move the taskbar.
+ */
+ public void onActionMove(float dY) {
+ if (mAnimationToHomeRunning
+ || (mHasSprungOnceThisGesture && !mGestureEnded)) {
+ return;
+ }
+
+ mTranslationYForSwipe.updateValue(dY);
+ }
+
+ /**
+ * Called when swipe gesture has ended.
+ */
+ public void onActionEnd() {
+ if (mHasSprungOnceThisGesture) {
+ reset();
+ } else {
+ mGestureEnded = true;
+ startSpring();
+ }
+ }
+ }
+
+ @Override
+ public void dumpLogs(String prefix, PrintWriter pw) {
+ pw.println(prefix + "TaskbarTranslationController:");
+
+ pw.println(prefix + "\tmTranslationYForSwipe=" + mTranslationYForSwipe.value);
+ pw.println(prefix + "\tmHasSprungOnceThisGesture=" + mHasSprungOnceThisGesture);
+ pw.println(prefix + "\tmAnimationToHomeRunning=" + mAnimationToHomeRunning);
+ pw.println(prefix + "\tmGestureEnded=" + mGestureEnded);
+ pw.println(prefix + "\tmSpringBounce is running=" + (mSpringBounce != null
+ && mSpringBounce.isRunning()));
+ }
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 114bfecb6e..7b0374614c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,15 +15,23 @@
*/
package com.android.launcher3.taskbar;
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.CallSuper;
+import androidx.annotation.Nullable;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
import java.io.PrintWriter;
-import java.util.stream.Stream;
+import java.util.function.Consumer;
/**
* Base class for providing different taskbar UI
@@ -60,10 +68,6 @@ public class TaskbarUIController {
protected void onStashedInAppChanged() { }
- public Stream getAppIconsForEdu() {
- return Stream.empty();
- }
-
/** Called when an icon is launched. */
public void onTaskbarIconLaunched(ItemInfo item) { }
@@ -80,10 +84,13 @@ public class TaskbarUIController {
}
/**
- * Manually closes the all apps window.
+ * Manually closes the overlay window.
*/
- public void hideAllApps() {
- mControllers.taskbarAllAppsController.hide();
+ public void hideOverlayWindow() {
+ if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)
+ || mControllers.taskbarAllAppsController.isOpen()) {
+ mControllers.taskbarOverlayController.hideWindow();
+ }
}
/**
@@ -97,6 +104,50 @@ public class TaskbarUIController {
}
}
+ /**
+ * Returns {@code true} iff taskbar is stashed.
+ */
+ public boolean isTaskbarStashed() {
+ return mControllers.taskbarStashController.isStashed();
+ }
+
+ /**
+ * Returns {@code true} iff taskbar All Apps is open.
+ */
+ public boolean isTaskbarAllAppsOpen() {
+ return mControllers.taskbarAllAppsController.isOpen();
+ }
+
+ /**
+ * Called at the end of the swipe gesture on Transient taskbar.
+ */
+ public void startTranslationSpring() {
+ mControllers.taskbarActivityContext.startTranslationSpring();
+ }
+
+ /*
+ * @param ev MotionEvent in screen coordinates.
+ * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
+ */
+ public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
+ return mControllers.taskbarViewController.isEventOverAnyItem(ev)
+ || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
+ }
+
+ /**
+ * Returns true if icons should be aligned to hotseat in the current transition.
+ */
+ public boolean isIconAlignedWithHotseat() {
+ return false;
+ }
+
+ /**
+ * Returns true if hotseat icons are on top of view hierarchy when aligned in the current state.
+ */
+ public boolean isHotseatIconOnTopWhenAligned() {
+ return true;
+ }
+
@CallSuper
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(String.format(
@@ -104,4 +155,45 @@ public class TaskbarUIController {
prefix,
getClass().getSimpleName()));
}
+
+ /**
+ * Returns RecentsView. Overwritten in LauncherTaskbarUIController and
+ * FallbackTaskbarUIController with Launcher-specific implementations. Returns null for other
+ * UI controllers (like DesktopTaskbarUIController) that don't have a RecentsView.
+ */
+ public @Nullable RecentsView getRecentsView() {
+ return null;
+ }
+
+ /**
+ * Uses the clicked Taskbar icon to launch a second app for splitscreen.
+ */
+ public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
+ RecentsView recents = getRecentsView();
+ recents.findLastActiveTaskAndDoSplitOperation(
+ info.getTargetComponent(),
+ (Consumer) foundTask -> {
+ if (foundTask != null) {
+ TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
+ // There is already a running app of this type, use that as second app.
+ recents.confirmSplitSelect(
+ foundTaskView,
+ foundTaskView.getTask(),
+ foundTaskView.getIconView().getDrawable(),
+ foundTaskView.getThumbnail(),
+ foundTaskView.getThumbnail().getThumbnail(),
+ null /* intent */);
+ } else {
+ // No running app of that type, create a new instance as second app.
+ recents.confirmSplitSelect(
+ null /* containerTaskView */,
+ null /* task */,
+ new BitmapDrawable(info.bitmap.icon),
+ startingView,
+ null /* thumbnail */,
+ intent);
+ }
+ }
+ );
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
index 64a4fa79df..4c937a7f9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
@@ -15,13 +15,13 @@
*/
package com.android.launcher3.taskbar;
-import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -41,16 +41,20 @@ public class TaskbarUnfoldAnimationController implements
public TaskbarUnfoldAnimationController(BaseTaskbarContext context,
ScopedUnfoldTransitionProgressProvider source,
- WindowManager windowManager, IWindowManager iWindowManager) {
+ WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mScopedUnfoldTransitionProgressProvider = source;
mNaturalUnfoldTransitionProgressProvider =
- new NaturalRotationUnfoldProgressProvider(context, iWindowManager, source);
+ new NaturalRotationUnfoldProgressProvider(context,
+ rotationChangeProvider,
+ source);
mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
}
/**
* Initializes the controller
+ *
* @param taskbarControllers references to all other taskbar controllers
*/
public void init(TaskbarControllers taskbarControllers) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index bb82d19734..2fcd64b5e6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,17 +16,14 @@
package com.android.launcher3.taskbar;
import android.content.Context;
-import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.os.SystemProperties;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.LayoutRes;
@@ -45,9 +42,9 @@ import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.AllAppsButton;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
import java.util.function.Predicate;
@@ -62,10 +59,11 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
public int mThemeIconsBackground;
private final int[] mTempOutLocation = new int[2];
- private final Rect mIconLayoutBounds = new Rect();
+ private final Rect mIconLayoutBounds;
private final int mIconTouchSize;
private final int mItemMarginLeftRight;
private final int mItemPadding;
+ private final boolean mIsRtl;
private final TaskbarActivityContext mActivityContext;
@@ -81,16 +79,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
private @Nullable FolderIcon mLeaveBehindFolderIcon;
// Only non-null when device supports having an All Apps button.
- private @Nullable AllAppsButton mAllAppsButton;
+ private @Nullable View mAllAppsButton;
private View mQsb;
- // Only non-null when device supports having a floating task.
- private @Nullable BubbleTextView mFloatingTaskButton;
- private @Nullable Intent mFloatingTaskIntent;
- private static final boolean FLOATING_TASKS_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
-
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -108,13 +100,16 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mActivityContext = ActivityContext.lookupContext(context);
-
+ mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds();
Resources resources = getResources();
- mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
+ mIsRtl = Utilities.isRtl(resources);
int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx;
+ mIconTouchSize = Math.max(actualIconSize,
+ resources.getDimensionPixelSize(R.dimen.taskbar_icon_min_touch_size));
+
// We layout the icons to be of mIconTouchSize in width and height
mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
mItemPadding = (mIconTouchSize - actualIconSize) / 2;
@@ -125,27 +120,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
mThemeIconsBackground = calculateThemeIconsBackground();
if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
- mAllAppsButton = new AllAppsButton(context);
- mAllAppsButton.setLayoutParams(
- new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
+ mAllAppsButton = LayoutInflater.from(context)
+ .inflate(R.layout.taskbar_all_apps_button, this, false);
mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ mAllAppsButton.setScaleX(mIsRtl ? -1 : 1);
+ if (mActivityContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
+ mAllAppsButton.setVisibility(GONE);
+ }
}
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
- if (FLOATING_TASKS_ENABLED) {
- mFloatingTaskIntent = FloatingTaskIntentResolver.getIntent(context);
- if (mFloatingTaskIntent != null) {
- mFloatingTaskButton = new LaunchFloatingTaskButton(context);
- mFloatingTaskButton.setLayoutParams(
- new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
- mFloatingTaskButton.setPadding(mItemPadding, mItemPadding, mItemPadding,
- mItemPadding);
- } else {
- Log.d(TAG, "Floating tasks is enabled but no intent was found!");
- }
- }
}
private int getColorWithGivenLuminance(int color, float luminance) {
@@ -173,10 +159,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
if (mAllAppsButton != null) {
mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
}
- if (mFloatingTaskButton != null) {
- mFloatingTaskButton.setOnClickListener(
- mControllerCallbacks.getFloatingTaskButtonListener(mFloatingTaskIntent));
- }
}
private void removeAndRecycle(View view) {
@@ -201,9 +183,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
removeView(mQsb);
- if (mFloatingTaskButton != null) {
- removeView(mFloatingTaskButton);
- }
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
@@ -277,20 +256,15 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
if (mAllAppsButton != null) {
- int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
+ int index = mIsRtl ? getChildCount() : 0;
addView(mAllAppsButton, index);
}
if (mActivityContext.getDeviceProfile().isQsbInline) {
- addView(mQsb, Utilities.isRtl(getResources()) ? getChildCount() : 0);
+ addView(mQsb, mIsRtl ? getChildCount() : 0);
// Always set QSB to invisible after re-adding.
mQsb.setVisibility(View.INVISIBLE);
}
- if (mFloatingTaskButton != null) {
- int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
- addView(mFloatingTaskButton, index);
- }
-
mThemeIconsBackground = calculateThemeIconsBackground();
setThemedIconsBackgroundColor(mThemeIconsBackground);
}
@@ -321,12 +295,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
- int countExcludingQsb = count;
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
- if (deviceProfile.isQsbInline) {
- countExcludingQsb--;
- }
- int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ int spaceNeeded = getIconLayoutWidth();
int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
boolean layoutRtl = isLayoutRtl();
int iconEnd = right - (right - left - spaceNeeded) / 2;
@@ -376,13 +346,23 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
return super.dispatchTouchEvent(ev);
}
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ mControllerCallbacks.onInterceptTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mTouchEnabled) {
return true;
}
- if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) {
- // Don't allow long pressing between icons, or above/below them.
+ if (mIconLayoutBounds.left <= event.getX()
+ && event.getX() <= mIconLayoutBounds.right
+ && !DisplayController.isTransientTaskbar(mActivityContext)) {
+ // Don't allow long pressing between icons, or above/below them
+ // unless its transient taskbar.
+ mControllerCallbacks.clearTouchInProgress();
return true;
}
if (mControllerCallbacks.onTouchEvent(event)) {
@@ -399,6 +379,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
public void setTouchesEnabled(boolean touchEnabled) {
this.mTouchEnabled = touchEnabled;
+ mControllerCallbacks.clearTouchInProgress();
}
/**
@@ -416,6 +397,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
return mIconLayoutBounds;
}
+ /**
+ * Returns the space used by the icons
+ */
+ public int getIconLayoutWidth() {
+ int countExcludingQsb = getChildCount();
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ if (deviceProfile.isQsbInline) {
+ countExcludingQsb--;
+ }
+ return countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ }
+
/**
* Returns the app icons currently shown in the taskbar.
*/
@@ -431,6 +424,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
/**
* Returns the all apps button in the taskbar.
*/
+ @Nullable
public View getAllAppsButtonView() {
return mAllAppsButton;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 00d5083dd3..168c353273 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,20 +17,25 @@ package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.anim.AnimatedFloat.VALUE;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
-import static com.android.quickstep.AnimatedFloat.VALUE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import android.annotation.NonNull;
-import android.content.Intent;
import android.graphics.Rect;
import android.util.FloatProperty;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
+import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.OneShotPreDrawListener;
@@ -40,6 +45,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
@@ -47,12 +53,13 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -86,12 +93,18 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
this::updateTranslationY);
private AnimatedFloat mTaskbarNavButtonTranslationY;
private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
+ private float mTaskbarIconTranslationYForSwipe;
+
+ private final int mTaskbarBottomMargin;
private final AnimatedFloat mThemeIconsBackground = new AnimatedFloat(
this::updateIconsBackground);
private final TaskbarModelCallbacks mModelCallbacks;
+ // Captures swipe down action to close transient taskbar.
+ protected @Nullable SingleAxisSwipeDetector mSwipeDownDetector;
+
// Initialized in init.
private TaskbarControllers mControllers;
@@ -101,6 +114,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
private Runnable mOnControllerPreCreateCallback = NO_OP;
private int mThemeIconsColor;
+ private boolean mIsHotseatIconOnTopWhenAligned;
+
+ private final DeviceProfile.OnDeviceProfileChangeListener mDeviceProfileChangeListener =
+ dp -> commitRunningAppsToUI();
public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
mActivity = activity;
@@ -108,6 +125,34 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
mTaskbarIconAlpha.setUpdateVisibility(true);
mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView);
+ mTaskbarBottomMargin = DisplayController.isTransientTaskbar(activity)
+ ? activity.getResources().getDimensionPixelSize(R.dimen.transient_taskbar_margin)
+ : 0;
+
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ mSwipeDownDetector = new SingleAxisSwipeDetector(activity,
+ new SingleAxisSwipeDetector.Listener() {
+ private float mLastDisplacement;
+
+ @Override
+ public boolean onDrag(float displacement) {
+ mLastDisplacement = displacement;
+ return false;
+ }
+
+ @Override
+ public void onDragEnd(float velocity) {
+ if (mLastDisplacement > 0) {
+ mControllers.taskbarStashController
+ .updateAndAnimateTransientTaskbar(true);
+ }
+ }
+
+ @Override
+ public void onDragStart(boolean start, float startDisplacement) {}
+ }, VERTICAL);
+ mSwipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
+ }
}
public void init(TaskbarControllers controllers) {
@@ -129,10 +174,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
mTaskbarNavButtonTranslationYForInAppDisplay = controllers.navbarButtonsViewController
.getTaskbarNavButtonTranslationYForInAppDisplay();
+
+ mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
}
public void onDestroy() {
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
+ mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
mModelCallbacks.unregisterListeners();
}
@@ -140,7 +188,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
return mTaskbarView.areIconsVisible();
}
- public MultiValueAlpha getTaskbarIconAlpha() {
+ public MultiPropertyFactory getTaskbarIconAlpha() {
return mTaskbarIconAlpha;
}
@@ -148,14 +196,15 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
* Should be called when the IME visibility changes, so we can make Taskbar not steal touches.
*/
public void setImeIsVisible(boolean isImeVisible) {
- mTaskbarView.setTouchesEnabled(!isImeVisible);
+ mTaskbarView.setTouchesEnabled(!isImeVisible
+ || DisplayController.isTransientTaskbar(mActivity));
}
/**
* Should be called when the IME switcher visibility changes.
*/
public void setIsImeSwitcherVisible(boolean isImeSwitcherVisible) {
- mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
+ mTaskbarIconAlpha.get(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
isImeSwitcherVisible ? 0 : 1);
}
@@ -164,7 +213,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
*/
public void setRecentsButtonDisabled(boolean isDisabled) {
// TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha.
- mTaskbarIconAlpha.getProperty(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
+ mTaskbarIconAlpha.get(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
}
/**
@@ -187,10 +236,15 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
return mTaskbarView.getIconLayoutBounds();
}
+ public int getIconLayoutWidth() {
+ return mTaskbarView.getIconLayoutWidth();
+ }
+
public View[] getIconViews() {
return mTaskbarView.getIconViews();
}
+ @Nullable
public View getAllAppsButtonView() {
return mTaskbarView.getAllAppsButtonView();
}
@@ -212,9 +266,18 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
mTaskbarView.setScaleY(scale);
}
+ /**
+ * Sets the translation of the TaskbarView during the swipe up gesture.
+ */
+ public void setTranslationYForSwipe(float transY) {
+ mTaskbarIconTranslationYForSwipe = transY;
+ updateTranslationY();
+ }
+
private void updateTranslationY() {
mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
- + mTaskbarIconTranslationYForStash.value);
+ + mTaskbarIconTranslationYForStash.value
+ + mTaskbarIconTranslationYForSwipe);
}
private void updateIconsBackground() {
@@ -232,10 +295,14 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
* 0 => not aligned
* 1 => fully aligned
*/
- public void setLauncherIconAlignment(float alignmentRatio, Float endAlignment,
- DeviceProfile launcherDp) {
- if (mIconAlignControllerLazy == null) {
- mIconAlignControllerLazy = createIconAlignmentController(launcherDp, endAlignment);
+ public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
+ boolean isHotseatIconOnTopWhenAligned =
+ mControllers.uiController.isHotseatIconOnTopWhenAligned();
+ // When mIsHotseatIconOnTopWhenAligned changes, animation needs to be re-created.
+ if (mIconAlignControllerLazy == null
+ || mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned) {
+ mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
+ mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
}
mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
if (alignmentRatio <= 0 || alignmentRatio >= 1) {
@@ -247,8 +314,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
/**
* Creates an animation for aligning the taskbar icons with the provided Launcher device profile
*/
- private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp,
- Float endAlignment) {
+ private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
mOnControllerPreCreateCallback.run();
PendingAnimation setter = new PendingAnimation(100);
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
@@ -260,10 +326,15 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
borderSpacing,
launcherDp.numShownHotseatIcons);
+ boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
+ // If Hotseat is not the top element, Taskbar should maintain in-app state as it fades out,
+ // or fade in while already in in-app state.
+ Interpolator interpolator = mIsHotseatIconOnTopWhenAligned ? LINEAR : FINAL_FRAME;
+
int offsetY = launcherDp.getTaskbarOffsetY();
- setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
- setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, LINEAR);
- setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, LINEAR);
+ setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, interpolator);
+ setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, interpolator);
+ setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, interpolator);
if (Utilities.isDarkTheme(mTaskbarView.getContext())) {
setter.addFloat(mThemeIconsBackground, VALUE, 0f, 1f, LINEAR);
@@ -274,27 +345,24 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
- boolean isToHome = endAlignment != null && endAlignment == 1;
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
int positionInHotseat;
- if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
- && child == mTaskbarView.getAllAppsButtonView()) {
- // Note that there is no All Apps button in the hotseat, this position is only used
- // as its convenient for animation purposes.
- positionInHotseat = Utilities.isRtl(child.getResources())
- ? -1
- : taskbarDp.numShownHotseatIcons;
+ boolean isAllAppsButton = FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
+ && child == mTaskbarView.getAllAppsButtonView();
+ if (!mIsHotseatIconOnTopWhenAligned) {
+ // When going to home, the EMPHASIZED interpolator in TaskbarLauncherStateController
+ // plays iconAlignment to 1 really fast, therefore moving the fading towards the end
+ // to avoid icons disappearing rather than fading out visually.
+ setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
+ } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())) {
+ setter.setViewAlpha(child, 0,
+ isToHome
+ ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
+ : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
+ }
- if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
- setter.setViewAlpha(child, 0,
- isToHome
- ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
- : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
- }
- } else if (child.getTag() instanceof ItemInfo) {
- positionInHotseat = ((ItemInfo) child.getTag()).screenId;
- } else if (child == mTaskbarView.getQsb()) {
+ if (child == mTaskbarView.getQsb()) {
boolean isRtl = Utilities.isRtl(child.getResources());
float hotseatIconCenter = isRtl
? launcherDp.widthPx - hotseatPadding.right + borderSpacing
@@ -305,24 +373,38 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
(launcherDp.hotseatQsbWidth - taskbarDp.iconSizePx) / 2f;
setter.addFloat(child, ICON_TRANSLATE_X,
isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
- hotseatIconCenter - childCenter, LINEAR);
+ hotseatIconCenter - childCenter, interpolator);
float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
- setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
+ setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator);
- setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
- isToHome
- ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
- : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+
+ if (mIsHotseatIconOnTopWhenAligned) {
+ setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
+ isToHome
+ ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
+ : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+ }
setter.addOnFrameListener(animator -> AlphaUpdateListener.updateVisibility(child));
float qsbInsetFraction = halfQsbIconWidthDiff / launcherDp.hotseatQsbWidth;
- if (child instanceof HorizontalInsettableView) {
+ if (child instanceof HorizontalInsettableView) {
setter.addFloat((HorizontalInsettableView) child,
HorizontalInsettableView.HORIZONTAL_INSETS, qsbInsetFraction, 0,
- LINEAR);
+ interpolator);
}
continue;
+ }
+
+ if (isAllAppsButton) {
+ // Note that there is no All Apps button in the hotseat, this position is only used
+ // as its convenient for animation purposes.
+ positionInHotseat = Utilities.isRtl(child.getResources())
+ ? taskbarDp.numShownHotseatIcons
+ : -1;
+ } else if (child.getTag() instanceof ItemInfo) {
+ positionInHotseat = ((ItemInfo) child.getTag()).screenId;
} else {
Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
continue;
@@ -333,9 +415,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
+ hotseatCellSize / 2f;
float childCenter = (child.getLeft() + child.getRight()) / 2f;
- setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+ setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
- setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+
+ setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
}
AnimatorPlaybackController controller = setter.createPlaybackController();
@@ -344,8 +428,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
public void onRotationChanged(DeviceProfile deviceProfile) {
- if (mControllers.taskbarStashController.isInApp()) {
- // We only translate on rotation when on home
+ if (!mControllers.uiController.isIconAlignedWithHotseat()) {
+ // We only translate on rotation when icon is aligned with hotseat
return;
}
mActivity.setTaskbarWindowHeight(
@@ -418,6 +502,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
private float mDownX, mDownY;
private boolean mCanceledStashHint;
+ private boolean mTouchInProgress;
+
public View.OnClickListener getIconOnClickListener() {
return mActivity.getItemOnClickListener();
}
@@ -429,13 +515,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
};
}
- public View.OnClickListener getFloatingTaskButtonListener(@NonNull Intent intent) {
- return v -> {
- SystemUiProxy proxy = SystemUiProxy.INSTANCE.get(v.getContext());
- proxy.showFloatingTask(intent);
- };
- }
-
public View.OnLongClickListener getIconOnLongClickListener() {
return mControllers.taskbarDragController::startDragOnLongClick;
}
@@ -445,38 +524,76 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
.updateAndAnimateIsManuallyStashedInApp(true);
}
+ /**
+ * Simply listens to all intercept touch events passed to TaskbarView.
+ */
+ public void onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchInProgress = true;
+ }
+
+ if (mTouchInProgress && mSwipeDownDetector != null) {
+ mSwipeDownDetector.onTouchEvent(ev);
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_UP
+ || ev.getAction() == MotionEvent.ACTION_CANCEL) {
+ clearTouchInProgress();
+ }
+ }
+
/**
* Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to
* consume the touch so TaskbarView treats it as an ACTION_CANCEL.
*/
public boolean onTouchEvent(MotionEvent motionEvent) {
+ boolean shouldConsumeTouch = false;
+ boolean clearTouchInProgress = false;
+
final float x = motionEvent.getRawX();
final float y = motionEvent.getRawY();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
+ mTouchInProgress = true;
mDownX = x;
mDownY = y;
mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
mCanceledStashHint = false;
break;
case MotionEvent.ACTION_MOVE:
- if (!mCanceledStashHint
+ if (mTouchInProgress
+ && !mCanceledStashHint
&& squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
mCanceledStashHint = true;
- return true;
+ shouldConsumeTouch = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (!mCanceledStashHint) {
+ if (mTouchInProgress && !mCanceledStashHint) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
}
+ clearTouchInProgress = true;
break;
}
- return false;
+
+ if (mTouchInProgress && mSwipeDownDetector != null) {
+ mSwipeDownDetector.onTouchEvent(motionEvent);
+ }
+ if (clearTouchInProgress) {
+ clearTouchInProgress();
+ }
+ return shouldConsumeTouch;
+ }
+
+ /**
+ * Ensures that we do not pass any more touch events to the SwipeDetector.
+ */
+ public void clearTouchInProgress() {
+ mTouchInProgress = false;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index 076900cd39..a033507b8e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -14,7 +14,8 @@ private const val STASHED_HANDLE_FADE_DURATION = 180L
* Controls Taskbar behavior while Voice Interaction Window (assistant) is showing.
*/
class VoiceInteractionWindowController(val context: TaskbarActivityContext)
- : TaskbarControllers.LoggableTaskbarController {
+ : TaskbarControllers.LoggableTaskbarController,
+ TaskbarControllers.BackgroundRendererController {
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
@@ -63,11 +64,11 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext)
// Fade out taskbar icons and stashed handle.
val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f
val fadeTaskbarIcons = controllers.taskbarViewController.taskbarIconAlpha
- .getProperty(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(TASKBAR_ICONS_FADE_DURATION)
val fadeStashedHandle = controllers.stashedHandleViewController.stashedHandleAlpha
- .getProperty(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(STASHED_HANDLE_FADE_DURATION)
fadeTaskbarIcons.start()
@@ -111,6 +112,11 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext)
}
}
+ override fun setCornerRoundness(cornerRoundness: Float) {
+ taskbarBackgroundRenderer.setCornerRoundness(cornerRoundness)
+ separateWindowForTaskbarBackground.invalidate()
+ }
+
override fun dumpLogs(prefix: String, pw: PrintWriter) {
pw.println(prefix + "VoiceInteractionWindowController:")
pw.println("$prefix\tisVoiceInteractionWindowVisible=$isVoiceInteractionWindowVisible")
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index 51fa4d9f3a..eeca329e80 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -17,17 +17,17 @@ package com.android.launcher3.taskbar.allapps;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.View;
import android.view.WindowInsets;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
-import com.android.launcher3.allapps.BaseAdapterProvider;
-import com.android.launcher3.allapps.BaseAllAppsAdapter;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/** All apps container accessible from taskbar. */
public class TaskbarAllAppsContainerView extends
- ActivityAllAppsContainerView {
+ ActivityAllAppsContainerView {
public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -44,10 +44,33 @@ public class TaskbarAllAppsContainerView extends
}
@Override
- protected BaseAllAppsAdapter createAdapter(
- AlphabeticalAppsList appsList,
- BaseAdapterProvider[] adapterProviders) {
- return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
- adapterProviders);
+ protected View inflateSearchBox() {
+ // Remove top padding of header, since we do not have any search
+ mHeader.setPadding(mHeader.getPaddingLeft(), 0,
+ mHeader.getPaddingRight(), mHeader.getPaddingBottom());
+
+ TaskbarAllAppsFallbackSearchContainer searchView =
+ new TaskbarAllAppsFallbackSearchContainer(getContext(), null);
+ searchView.setId(R.id.search_container_all_apps);
+ searchView.setVisibility(GONE);
+ return searchView;
+ }
+
+ @Override
+ protected boolean isSearchSupported() {
+ return false;
+ }
+
+ @Override
+ protected void updateBackground(DeviceProfile deviceProfile) {
+ super.updateBackground(deviceProfile);
+ // TODO(b/240670050): Remove this and add header protection for the taskbar entrypoint.
+ mBottomSheetBackground.setBackgroundResource(R.drawable.bg_rounded_corner_bottom_sheet);
+ }
+
+ @Override
+ public boolean isInAllApps() {
+ // All apps is always open
+ return true;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
deleted file mode 100644
index 0372f67b75..0000000000
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar.allapps;
-
-import static android.view.KeyEvent.ACTION_UP;
-import static android.view.KeyEvent.KEYCODE_BACK;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.content.Context;
-import android.graphics.Insets;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.WindowInsets;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.taskbar.BaseTaskbarContext;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarDragController;
-import com.android.launcher3.taskbar.TaskbarStashController;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.views.BaseDragLayer;
-
-/**
- * Window context for the taskbar all apps overlay.
- *
- * All apps has its own window and needs a window context. Some properties are delegated to the
- * {@link TaskbarActivityContext} such as {@link DeviceProfile} and {@link PopupDataProvider}.
- */
-class TaskbarAllAppsContext extends BaseTaskbarContext {
- private final TaskbarActivityContext mTaskbarContext;
- private final OnboardingPrefs mOnboardingPrefs;
-
- private final TaskbarAllAppsController mWindowController;
- private final TaskbarAllAppsViewController mAllAppsViewController;
- private final TaskbarDragController mDragController;
- private final TaskbarAllAppsDragLayer mDragLayer;
- private final TaskbarAllAppsContainerView mAppsView;
-
- // We automatically stash taskbar when all apps is opened in gesture navigation mode.
- private final boolean mWillTaskbarBeVisuallyStashed;
- private final int mStashedTaskbarHeight;
-
- TaskbarAllAppsContext(
- TaskbarActivityContext taskbarContext,
- TaskbarAllAppsController windowController,
- TaskbarControllers taskbarControllers) {
- super(taskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null));
- mTaskbarContext = taskbarContext;
- mWindowController = windowController;
- mDragController = new TaskbarDragController(this);
- mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
-
- mDragLayer = new TaskbarAllAppsDragLayer(this);
- TaskbarAllAppsSlideInView slideInView = (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(
- R.layout.taskbar_all_apps, mDragLayer, false);
- mAllAppsViewController = new TaskbarAllAppsViewController(
- this,
- slideInView,
- windowController,
- taskbarControllers);
- mAppsView = slideInView.getAppsView();
-
- TaskbarStashController taskbarStashController = taskbarControllers.taskbarStashController;
- mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
- mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
- }
-
- TaskbarAllAppsViewController getAllAppsViewController() {
- return mAllAppsViewController;
- }
-
- @Override
- public DeviceProfile getDeviceProfile() {
- return mWindowController.getDeviceProfile();
- }
-
- @Override
- public TaskbarDragController getDragController() {
- return mDragController;
- }
-
- @Override
- public TaskbarAllAppsDragLayer getDragLayer() {
- return mDragLayer;
- }
-
- @Override
- public TaskbarAllAppsContainerView getAppsView() {
- return mAppsView;
- }
-
- @Override
- public OnboardingPrefs getOnboardingPrefs() {
- return mOnboardingPrefs;
- }
-
- @Override
- public boolean isBindingItems() {
- return mTaskbarContext.isBindingItems();
- }
-
- @Override
- public View.OnClickListener getItemOnClickListener() {
- return mTaskbarContext.getItemOnClickListener();
- }
-
- @Override
- public PopupDataProvider getPopupDataProvider() {
- return mTaskbarContext.getPopupDataProvider();
- }
-
- @Override
- public DotInfo getDotInfoForItem(ItemInfo info) {
- return mTaskbarContext.getDotInfoForItem(info);
- }
-
- @Override
- public void onDragStart() {}
-
- @Override
- public void onDragEnd() {
- mWindowController.maybeCloseWindow();
- }
-
- @Override
- public void onPopupVisibilityChanged(boolean isVisible) {}
-
- @Override
- public SearchAdapterProvider> createSearchAdapterProvider(
- ActivityAllAppsContainerView> appsView) {
- return new DefaultSearchAdapterProvider(this);
- }
-
- /** Root drag layer for this context. */
- private static class TaskbarAllAppsDragLayer extends
- BaseDragLayer implements OnComputeInternalInsetsListener {
-
- private TaskbarAllAppsDragLayer(Context context) {
- super(context, null, 1);
- setClipChildren(false);
- recreateControllers();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
-
- @Override
- public void recreateControllers() {
- mControllers = new TouchController[]{mActivity.mDragController};
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
- if (topView != null && topView.onBackPressed()) {
- return true;
- }
- }
- return super.dispatchKeyEvent(event);
- }
-
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- if (mActivity.mDragController.isSystemDragInProgress()) {
- inoutInfo.touchableRegion.setEmpty();
- inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- }
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- return updateInsetsDueToStashing(insets);
- }
-
- /**
- * Taskbar automatically stashes when opening all apps, but we don't report the insets as
- * changing to avoid moving the underlying app. But internally, the apps view should still
- * layout according to the stashed insets rather than the unstashed insets. So this method
- * does two things:
- * 1) Sets navigationBars bottom inset to stashedHeight.
- * 2) Sets tappableInsets bottom inset to 0.
- */
- private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
- if (!mActivity.mWillTaskbarBeVisuallyStashed) {
- return oldInsets;
- }
- WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
-
- Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
- Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
- mActivity.mStashedTaskbarHeight);
- updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
-
- Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
- Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
- oldTappableInsets.right, 0);
- updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
-
- return updatedInsetsBuilder.build();
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 1671a0f1d7..7a34869e2a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -15,34 +15,18 @@
*/
package com.android.launcher3.taskbar.allapps;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import java.util.List;
-import java.util.Optional;
/**
* Handles the all apps overlay window initialization, updates, and its data.
@@ -57,35 +41,16 @@ import java.util.Optional;
*/
public final class TaskbarAllAppsController {
- private static final String WINDOW_TITLE = "Taskbar All Apps";
-
- private final TaskbarActivityContext mTaskbarContext;
- private final TaskbarAllAppsProxyView mProxyView;
- private final LayoutParams mLayoutParams;
-
- private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onTaskStackChanged() {
- mProxyView.close(false);
- }
- };
-
- private DeviceProfile mDeviceProfile;
private TaskbarControllers mControllers;
- /** Window context for all apps if it is open. */
- private @Nullable TaskbarAllAppsContext mAllAppsContext;
+ private @Nullable TaskbarAllAppsSlideInView mSlideInView;
+ private @Nullable TaskbarAllAppsContainerView mAppsView;
// Application data models.
private AppInfo[] mApps;
private int mAppsModelFlags;
private List mPredictedApps;
-
- public TaskbarAllAppsController(TaskbarActivityContext context, DeviceProfile dp) {
- mDeviceProfile = dp;
- mTaskbarContext = context;
- mProxyView = new TaskbarAllAppsProxyView(mTaskbarContext);
- mLayoutParams = createLayoutParams();
- }
+ private boolean mDisallowGlobalDrag;
+ private boolean mDisallowLongClick;
/** Initialize the controller. */
public void init(TaskbarControllers controllers, boolean allAppsVisible) {
@@ -111,11 +76,19 @@ public final class TaskbarAllAppsController {
mApps = apps;
mAppsModelFlags = flags;
- if (mAllAppsContext != null) {
- mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
+ if (mAppsView != null) {
+ mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
}
}
+ public void setDisallowGlobalDrag(boolean disableDragForOverviewState) {
+ mDisallowGlobalDrag = disableDragForOverviewState;
+ }
+
+ public void setDisallowLongClick(boolean disallowLongClick) {
+ mDisallowLongClick = disallowLongClick;
+ }
+
/** Updates the current predictions. */
public void setPredictedApps(List predictedApps) {
if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
@@ -123,8 +96,8 @@ public final class TaskbarAllAppsController {
}
mPredictedApps = predictedApps;
- if (mAllAppsContext != null) {
- mAllAppsContext.getAppsView().getFloatingHeaderView()
+ if (mAppsView != null) {
+ mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
}
@@ -135,121 +108,49 @@ public final class TaskbarAllAppsController {
show(true);
}
+ /** Returns {@code true} if All Apps is open. */
+ public boolean isOpen() {
+ return mSlideInView != null && mSlideInView.isOpen();
+ }
+
private void show(boolean animate) {
- if (mProxyView.isOpen()) {
+ if (mAppsView != null) {
return;
}
- mProxyView.show();
// mControllers and getSharedState should never be null here. Do not handle null-pointer
// to catch invalid states.
mControllers.getSharedState().allAppsVisible = true;
- mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext, this, mControllers);
- mAllAppsContext.getDragController().init(mControllers);
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
- .ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
+ TaskbarOverlayContext overlayContext =
+ mControllers.taskbarOverlayController.requestWindow();
+ mSlideInView = (TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate(
+ R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false);
+ mSlideInView.addOnCloseListener(() -> {
+ mControllers.getSharedState().allAppsVisible = false;
+ mSlideInView = null;
+ mAppsView = null;
+ });
+ TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
+ overlayContext, mSlideInView, mControllers);
- mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
- mAllAppsContext.getAppsView().getFloatingHeaderView()
+ viewController.show(animate);
+ mAppsView = overlayContext.getAppsView();
+ mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
+ mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
- mAllAppsContext.getAllAppsViewController().show(animate);
+ // 1 alternative that would be more work:
+ // Create a shared drag layer between taskbar and taskbarAllApps so that when dragging
+ // starts and taskbarAllApps can close, but the drag layer that the view is being dragged in
+ // doesn't also close
+ overlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag);
+ overlayContext.getDragController().setDisallowLongClick(mDisallowLongClick);
}
- /** Closes the {@link TaskbarAllAppsContainerView}. */
- public void hide() {
- mProxyView.close(true);
- }
- /**
- * Removes the all apps window from the hierarchy, if all floating views are closed and there is
- * no system drag operation in progress.
- *
- * This method should be called after an exit animation finishes, if applicable.
- */
- void maybeCloseWindow() {
- if (mAllAppsContext != null && (AbstractFloatingView.hasOpenView(mAllAppsContext, TYPE_ALL)
- || mAllAppsContext.getDragController().isSystemDragInProgress())) {
- return;
- }
- mProxyView.close(false);
- // mControllers and getSharedState should never be null here. Do not handle null-pointer
- // to catch invalid states.
- mControllers.getSharedState().allAppsVisible = false;
- onDestroy();
- }
-
- /** Destroys the controller and any All Apps window if present. */
- public void onDestroy() {
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mAllAppsContext)
- .map(c -> c.getSystemService(WindowManager.class))
- .ifPresent(m -> m.removeViewImmediate(mAllAppsContext.getDragLayer()));
- mAllAppsContext = null;
- }
-
- /** Updates {@link DeviceProfile} instance for Taskbar's All Apps window. */
- public void updateDeviceProfile(DeviceProfile dp) {
- mDeviceProfile = dp;
- Optional.ofNullable(mAllAppsContext).ifPresent(c -> {
- AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
- c.dispatchDeviceProfileChanged();
- });
- }
-
- DeviceProfile getDeviceProfile() {
- return mDeviceProfile;
- }
-
- private LayoutParams createLayoutParams() {
- LayoutParams layoutParams = new LayoutParams(
- TYPE_APPLICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- layoutParams.setTitle(WINDOW_TITLE);
- layoutParams.gravity = Gravity.BOTTOM;
- layoutParams.packageName = mTaskbarContext.getPackageName();
- layoutParams.setFitInsetsTypes(0); // Handled by container view.
- layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- layoutParams.setSystemApplicationOverlay(true);
- return layoutParams;
- }
-
- /**
- * Proxy view connecting taskbar drag layer to the all apps window.
- *
- * The all apps view is in a separate window and has its own drag layer, but this proxy lets it
- * behave as though its in the taskbar drag layer. For instance, when the taskbar closes all
- * {@link AbstractFloatingView} instances, the all apps window will also close.
- */
- private class TaskbarAllAppsProxyView extends AbstractFloatingView {
-
- private TaskbarAllAppsProxyView(Context context) {
- super(context, null);
- }
-
- private void show() {
- mIsOpen = true;
- mTaskbarContext.getDragLayer().addView(this);
- }
-
- @Override
- protected void handleClose(boolean animate) {
- mTaskbarContext.getDragLayer().removeView(this);
- Optional.ofNullable(mAllAppsContext)
- .map(TaskbarAllAppsContext::getAllAppsViewController)
- .ifPresent(v -> v.close(animate));
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_TASKBAR_ALL_APPS) != 0;
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
+ @VisibleForTesting
+ public int getTaskbarAllAppsTopPadding() {
+ // Allow null-pointer since this should only be null if the apps view is not showing.
+ return mAppsView.getActiveRecyclerView().getClipBounds().top;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 9d48c8de89..8502752652 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar.allapps;
-import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import android.animation.PropertyValuesHolder;
@@ -28,14 +27,19 @@ import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.views.AbstractSlideInView;
/** Wrapper for taskbar all apps with slide-in behavior. */
-public class TaskbarAllAppsSlideInView extends AbstractSlideInView
+public class TaskbarAllAppsSlideInView extends AbstractSlideInView
implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
private TaskbarAllAppsContainerView mAppsView;
private float mShiftRange;
+ // Initialized in init.
+ private TaskbarAllAppsCallbacks mAllAppsCallbacks;
+
public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -45,6 +49,10 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView {
mNavbarButtonsViewController.setSlideInViewVisible(false);
AbstractFloatingView.closeOpenContainer(
mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
- // Post in case view is closing due to gesture navigation. If a gesture is in progress,
- // wait to unstash until after the gesture is finished.
- mSlideInView.post(mTaskbarStashController::maybeResetStashedInAppAllApps);
+
+ if (DisplayController.isTransientTaskbar(mContext)) {
+ mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
+ mTaskbarStashController.applyState(mOverlayController.getCloseDuration());
+ } else {
+ // Post in case view is closing due to gesture navigation. If a gesture is in
+ // progress, wait to unstash until after the gesture is finished.
+ MAIN_EXECUTOR.post(() -> mTaskbarStashController.resetFlagIfNoGestureInProgress(
+ FLAG_STASHED_IN_TASKBAR_ALL_APPS));
+ }
});
}
+
+ class TaskbarAllAppsCallbacks {
+ int getOpenDuration() {
+ return mOverlayController.getOpenDuration();
+ }
+
+ int getCloseDuration() {
+ return mOverlayController.getCloseDuration();
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
new file mode 100644
index 0000000000..68ea27a8ec
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Meant to be a simple container for data subclasses will need
+ *
+ * Assumes that the 3 navigation buttons (back/home/recents) have already been added to
+ * [navButtonContainer]
+ *
+ * @property navButtonContainer ViewGroup that holds the 3 navigation buttons.
+ * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss).
+ * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y).
+ */
+abstract class AbstractNavButtonLayoutter(
+ val resources: Resources,
+ val navButtonContainer: LinearLayout,
+ protected val endContextualContainer: ViewGroup,
+ protected val startContextualContainer: ViewGroup
+) : NavButtonLayoutter {
+ protected val homeButton: ImageView = navButtonContainer
+ .findViewById(R.id.home)
+ protected val recentsButton: ImageView = navButtonContainer
+ .findViewById(R.id.recent_apps)
+ protected val backButton: ImageView = navButtonContainer
+ .findViewById(R.id.back)
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
new file mode 100644
index 0000000000..c67ab7964e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.graphics.Color
+import android.graphics.drawable.PaintDrawable
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_ICON_SIZE_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_BACK_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS
+
+class KidsNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ val iconSize: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_ICON_SIZE_KIDS)
+ val buttonWidth: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS)
+ val buttonHeight: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS)
+ val buttonRadius: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS)
+ val paddingLeft = (buttonWidth - iconSize) / 2
+ val paddingTop = (buttonHeight - iconSize) / 2
+
+ // Update icons
+ backButton.setImageDrawable(
+ backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS))
+ backButton.scaleType = ImageView.ScaleType.FIT_CENTER
+ backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+ homeButton.setImageDrawable(
+ homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS))
+ homeButton.scaleType = ImageView.ScaleType.FIT_CENTER
+ homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+
+ // Home button layout
+ val homeLayoutparams = LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ )
+ val homeButtonLeftMargin: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS)
+ homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0)
+ homeButton.layoutParams = homeLayoutparams
+
+ // Back button layout
+ val backLayoutParams = LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ )
+ val backButtonLeftMargin: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS)
+ backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0)
+ backButton.layoutParams = backLayoutParams
+
+ // Button backgrounds
+ val whiteWith10PctAlpha = Color.argb(0.1f, 1f, 1f, 1f)
+ val buttonBackground = PaintDrawable(whiteWith10PctAlpha)
+ buttonBackground.setCornerRadius(buttonRadius.toFloat())
+ homeButton.background = buttonBackground
+ backButton.background = buttonBackground
+
+ // Update alignment within taskbar
+ val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ navButtonsLayoutParams.apply {
+ marginStart = navButtonsLayoutParams.marginEnd / 2
+ marginEnd = navButtonsLayoutParams.marginStart
+ gravity = Gravity.CENTER
+ }
+ navButtonContainer.requestLayout()
+
+ homeButton.onLongClickListener = null
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
new file mode 100644
index 0000000000..0d9b855c91
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton;
+
+import android.annotation.DimenRes;
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+
+import com.android.launcher3.R;
+
+/**
+ * A class for retrieving resources in Kotlin.
+ *
+ * This class should be removed once the build system supports resources loading in Kotlin.
+ */
+public final class LayoutResourceHelper {
+
+ // --------------------------
+ // Kids Nav Layout
+ @DimenRes
+ public static final int DIMEN_TASKBAR_ICON_SIZE_KIDS = R.dimen.taskbar_icon_size_kids;
+ @DrawableRes
+ public static final int DRAWABLE_SYSBAR_BACK_KIDS = R.drawable.ic_sysbar_back_kids;
+ @DrawableRes
+ public static final int DRAWABLE_SYSBAR_HOME_KIDS = R.drawable.ic_sysbar_home_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS =
+ R.dimen.taskbar_home_button_left_margin_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS =
+ R.dimen.taskbar_back_button_left_margin_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS =
+ R.dimen.taskbar_nav_buttons_width_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS =
+ R.dimen.taskbar_nav_buttons_height_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS =
+ R.dimen.taskbar_nav_buttons_corner_radius_kids;
+
+ // --------------------------
+ // Nav Layout Factory
+ @IdRes
+ public static final int ID_START_CONTEXTUAL_BUTTONS = R.id.start_contextual_buttons;
+ @IdRes
+ public static final int ID_END_CONTEXTUAL_BUTTONS = R.id.end_contextual_buttons;
+ @IdRes
+ public static final int ID_END_NAV_BUTTONS = R.id.end_nav_buttons;
+
+ private LayoutResourceHelper() {
+
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
new file mode 100644
index 0000000000..db0a2d8d44
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Select the correct layout for nav buttons
+ *
+ * Since layouts are done dynamically for the nav buttons on Taskbar, this
+ * class returns a corresponding [NavButtonLayoutter] via
+ * [Companion.getUiLayoutter]
+ * that can help position the buttons based on the current [DeviceProfile]
+ */
+class NavButtonLayoutFactory {
+ companion object {
+ /**
+ * Get the correct instance of [NavButtonLayoutter]
+ *
+ * No layouts supported for configurations where:
+ * * taskbar isn't showing AND
+ * * the device is not in [phoneMode]
+ * OR
+ * * phone is showing
+ * * device is using gesture navigation
+ *
+ * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups
+ * @param isKidsMode no-op when taskbar is hidden/not showing
+ * @param isInSetup no-op when taskbar is hidden/not showing
+ * @param phoneMode refers to the device using the taskbar window on phones
+ * @param isThreeButtonNav are no-ops when taskbar is present/showing
+ */
+ fun getUiLayoutter(deviceProfile: DeviceProfile,
+ navButtonsView: FrameLayout,
+ resources: Resources,
+ isKidsMode: Boolean,
+ isInSetup: Boolean,
+ isThreeButtonNav: Boolean,
+ phoneMode: Boolean):
+ NavButtonLayoutter {
+ val navButtonContainer =
+ navButtonsView.findViewById(ID_END_NAV_BUTTONS)
+ val endContextualContainer =
+ navButtonsView.findViewById(ID_END_CONTEXTUAL_BUTTONS)
+ val startContextualContainer =
+ navButtonsView.findViewById(ID_START_CONTEXTUAL_BUTTONS)
+ val isPhoneNavMode = phoneMode && isThreeButtonNav
+ return when {
+ isPhoneNavMode -> {
+ if (!deviceProfile.isLandscape) {
+ PhonePortraitNavLayoutter(resources, navButtonContainer,
+ endContextualContainer, startContextualContainer)
+ } else {
+ PhoneLandscapeNavLayoutter(resources, navButtonContainer,
+ endContextualContainer, startContextualContainer)
+ }
+ }
+ deviceProfile.isTaskbarPresent -> {
+ return when {
+ isInSetup -> {
+ SetupNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ isKidsMode -> {
+ KidsNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ else ->
+ TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ }
+ else -> error("No layoutter found")
+ }
+ }
+ }
+
+ /** Lays out and provides access to the home, recents, and back buttons for various mischief */
+ interface NavButtonLayoutter {
+ fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
new file mode 100644
index 0000000000..a89476e15a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.core.view.children
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhoneLandscapeNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // TODO(b/230395757): Polish pending, this is just to make it usable
+ val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ val endStartMargins =
+ resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+ TaskbarManager.isPhoneMode(dp))
+ navButtonContainer.removeAllViews()
+ navButtonContainer.orientation = LinearLayout.VERTICAL
+
+ navContainerParams.apply {
+ width = taskbarDimensions.x
+ height = ViewGroup.LayoutParams.MATCH_PARENT
+ gravity = Gravity.CENTER
+ topMargin = endStartMargins
+ bottomMargin = endStartMargins
+ marginEnd = 0
+ marginStart = 0
+ }
+
+ // Swap recents and back button
+ navButtonContainer.addView(recentsButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(backButton)
+
+ navButtonContainer.layoutParams = navContainerParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween: Int =
+ resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+ navButtonContainer.children.forEachIndexed { i, navButton ->
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 1f
+ when (i) {
+ 0 -> {
+ buttonLayoutParams.bottomMargin = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ buttonLayoutParams.topMargin = spaceInBetween / 2
+ }
+ else -> {
+ buttonLayoutParams.bottomMargin = spaceInBetween / 2
+ buttonLayoutParams.topMargin = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
new file mode 100644
index 0000000000..275f59faef
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup) :
+ AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer,
+ startContextualContainer) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // TODO(b/230395757): Polish pending, this is just to make it usable
+ val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+ TaskbarManager.isPhoneMode(dp))
+ val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ navContainerParams.width = taskbarDimensions.x
+ navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ navContainerParams.gravity = Gravity.CENTER_VERTICAL
+
+ // Ensure order of buttons is correct
+ navButtonContainer.removeAllViews()
+ navButtonContainer.orientation = LinearLayout.HORIZONTAL
+ navContainerParams.topMargin = 0
+ navContainerParams.bottomMargin = 0
+ navContainerParams.marginEnd = endStartMargins
+ navContainerParams.marginStart = endStartMargins
+ // Swap recents and back button in case we were landscape prior to this
+ navButtonContainer.addView(backButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(recentsButton)
+
+ navButtonContainer.layoutParams = navContainerParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween =
+ resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+ for (i in 0 until navButtonContainer.childCount) {
+ val navButton = navButtonContainer.getChildAt(i)
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 1f
+ when (i) {
+ 0 -> {
+ // First button
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ // Last button
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ }
+ else -> {
+ // other buttons
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
new file mode 100644
index 0000000000..afe70d6c21
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+
+class SetupNavLayoutter(
+ resources: Resources,
+ navButtonContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // Since setup wizard only has back button enabled, it looks strange to be
+ // end-aligned, so start-align instead.
+ val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ navButtonsLayoutParams.apply {
+ marginStart = navButtonsLayoutParams.marginEnd
+ marginEnd = 0
+ gravity = Gravity.START
+ }
+ navButtonContainer.requestLayout()
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
new file mode 100644
index 0000000000..b2ca2afa04
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+
+/**
+ * Layoutter for showing 3 button navigation on large screen
+ */
+class TaskbarNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // Add spacing after the end of the last nav button
+ val navButtonParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt()
+ val contextualWidth = endContextualContainer.width
+ // If contextual buttons are showing, we check if the end margin is enough for the
+ // contextual button to be showing - if not, move the nav buttons over a smidge
+ if (isContextualButtonShowing && navMarginEnd < contextualWidth) {
+ // Additional spacing, eat up half of space between last icon and nav button
+ navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2
+ }
+
+ navButtonParams.apply {
+ gravity = Gravity.END
+ width = FrameLayout.LayoutParams.WRAP_CONTENT
+ height = ViewGroup.LayoutParams.MATCH_PARENT
+ marginEnd = navMarginEnd
+ }
+ navButtonContainer.orientation = LinearLayout.HORIZONTAL
+ navButtonContainer.layoutParams = navButtonParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween = resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween)
+ for (i in 0 until navButtonContainer.childCount) {
+ val navButton = navButtonContainer.getChildAt(i)
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 0f
+ when (i) {
+ 0 -> {
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ }
+ else -> {
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
new file mode 100644
index 0000000000..38b6dfd39b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.overlay;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.BaseTaskbarContext;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarDragController;
+import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
+import com.android.launcher3.util.OnboardingPrefs;
+
+/**
+ * Window context for the taskbar overlays such as All Apps and EDU.
+ *
+ * Overlays have their own window and need a window context. Some properties are delegated to the
+ * {@link TaskbarActivityContext} such as {@link PopupDataProvider}.
+ */
+public class TaskbarOverlayContext extends BaseTaskbarContext {
+ private final TaskbarActivityContext mTaskbarContext;
+ private final OnboardingPrefs mOnboardingPrefs;
+
+ private final TaskbarOverlayController mOverlayController;
+ private final TaskbarDragController mDragController;
+ private final TaskbarOverlayDragLayer mDragLayer;
+
+ // We automatically stash taskbar when All Apps is opened in gesture navigation mode.
+ private final boolean mWillTaskbarBeVisuallyStashed;
+ private final int mStashedTaskbarHeight;
+
+ public TaskbarOverlayContext(
+ Context windowContext,
+ TaskbarActivityContext taskbarContext,
+ TaskbarControllers controllers) {
+ super(windowContext);
+ mTaskbarContext = taskbarContext;
+ mOverlayController = controllers.taskbarOverlayController;
+ mDragController = new TaskbarDragController(this);
+ mDragController.init(controllers);
+ mOnboardingPrefs = new OnboardingPrefs<>(this, LauncherPrefs.getPrefs(this));
+ mDragLayer = new TaskbarOverlayDragLayer(this);
+
+ TaskbarStashController taskbarStashController = controllers.taskbarStashController;
+ mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
+ mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
+ }
+
+ boolean willTaskbarBeVisuallyStashed() {
+ return mWillTaskbarBeVisuallyStashed;
+ }
+
+ int getStashedTaskbarHeight() {
+ return mStashedTaskbarHeight;
+ }
+
+ public TaskbarOverlayController getOverlayController() {
+ return mOverlayController;
+ }
+
+ @Override
+ public DeviceProfile getDeviceProfile() {
+ return mOverlayController.getLauncherDeviceProfile();
+ }
+
+ @Override
+ public TaskbarDragController getDragController() {
+ return mDragController;
+ }
+
+ @Override
+ public TaskbarOverlayDragLayer getDragLayer() {
+ return mDragLayer;
+ }
+
+ @Override
+ public TaskbarAllAppsContainerView getAppsView() {
+ return mDragLayer.findViewById(R.id.apps_view);
+ }
+
+ @Override
+ public OnboardingPrefs getOnboardingPrefs() {
+ return mOnboardingPrefs;
+ }
+
+ @Override
+ public boolean isBindingItems() {
+ return mTaskbarContext.isBindingItems();
+ }
+
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return mTaskbarContext.getItemOnClickListener();
+ }
+
+ @Override
+ public PopupDataProvider getPopupDataProvider() {
+ return mTaskbarContext.getPopupDataProvider();
+ }
+
+ @Override
+ public DotInfo getDotInfoForItem(ItemInfo info) {
+ return mTaskbarContext.getDotInfoForItem(info);
+ }
+
+ @Override
+ public void onDragStart() {}
+
+ @Override
+ public void onDragEnd() {
+ mOverlayController.maybeCloseWindow();
+ }
+
+ @Override
+ public void onPopupVisibilityChanged(boolean isVisible) {}
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
new file mode 100644
index 0000000000..476e0a8bab
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.overlay;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.Optional;
+
+/**
+ * Handles the Taskbar overlay window lifecycle.
+ *
+ * Overlays need to be inflated in a separate window so that have the correct hierarchy. For
+ * instance, they need to be below the notification tray. If there are multiple overlays open, the
+ * same window is used.
+ */
+public final class TaskbarOverlayController {
+
+ private static final String WINDOW_TITLE = "Taskbar Overlay";
+
+ private final TaskbarActivityContext mTaskbarContext;
+ private final Context mWindowContext;
+ private final TaskbarOverlayProxyView mProxyView;
+ private final LayoutParams mLayoutParams;
+
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ mProxyView.close(false);
+ }
+
+ @Override
+ public void onTaskMovedToFront(int taskId) {
+ mProxyView.close(false);
+ }
+ };
+
+ private DeviceProfile mLauncherDeviceProfile;
+ private @Nullable TaskbarOverlayContext mOverlayContext;
+ private TaskbarControllers mControllers; // Initialized in init.
+
+ public TaskbarOverlayController(
+ TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile) {
+ mTaskbarContext = taskbarContext;
+ mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+ mProxyView = new TaskbarOverlayProxyView();
+ mLayoutParams = createLayoutParams();
+ mLauncherDeviceProfile = launcherDeviceProfile;
+ }
+
+ /** Initialize the controller. */
+ public void init(TaskbarControllers controllers) {
+ mControllers = controllers;
+ }
+
+ /**
+ * Creates a window for Taskbar overlays, if it does not already exist. Returns the window
+ * context for the current overlay window.
+ */
+ public TaskbarOverlayContext requestWindow() {
+ if (mOverlayContext == null) {
+ mOverlayContext = new TaskbarOverlayContext(
+ mWindowContext, mTaskbarContext, mControllers);
+ }
+
+ if (!mProxyView.isOpen()) {
+ mProxyView.show();
+ Optional.ofNullable(mOverlayContext.getSystemService(WindowManager.class))
+ .ifPresent(m -> m.addView(mOverlayContext.getDragLayer(), mLayoutParams));
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+ }
+
+ return mOverlayContext;
+ }
+
+ /** Hides the current overlay window with animation. */
+ public void hideWindow() {
+ mProxyView.close(true);
+ }
+
+ /**
+ * Removes the overlay window from the hierarchy, if all floating views are closed and there is
+ * no system drag operation in progress.
+ *
+ * This method should be called after an exit animation finishes, if applicable.
+ */
+ @SuppressLint("WrongConstant")
+ void maybeCloseWindow() {
+ if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)
+ || mOverlayContext.getDragController().isSystemDragInProgress())) {
+ return;
+ }
+ mProxyView.close(false);
+ onDestroy();
+ }
+
+ /** Destroys the controller and any overlay window if present. */
+ public void onDestroy() {
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ Optional.ofNullable(mOverlayContext)
+ .map(c -> c.getSystemService(WindowManager.class))
+ .ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
+ mOverlayContext = null;
+ }
+
+ /** The current device profile for the overlay window. */
+ public DeviceProfile getLauncherDeviceProfile() {
+ return mLauncherDeviceProfile;
+ }
+
+ /** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
+ public void updateLauncherDeviceProfile(DeviceProfile dp) {
+ mLauncherDeviceProfile = dp;
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> {
+ AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
+ c.dispatchDeviceProfileChanged();
+ });
+ }
+
+ /** The default open duration for overlays. */
+ public int getOpenDuration() {
+ return ALL_APPS.getTransitionDuration(mTaskbarContext, true);
+ }
+
+ /** The default close duration for overlays. */
+ public int getCloseDuration() {
+ return ALL_APPS.getTransitionDuration(mTaskbarContext, false);
+ }
+
+ @SuppressLint("WrongConstant")
+ private LayoutParams createLayoutParams() {
+ LayoutParams layoutParams = new LayoutParams(
+ TYPE_APPLICATION_OVERLAY,
+ LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.setTitle(WINDOW_TITLE);
+ layoutParams.gravity = Gravity.BOTTOM;
+ layoutParams.packageName = mTaskbarContext.getPackageName();
+ layoutParams.setFitInsetsTypes(0); // Handled by container view.
+ layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ layoutParams.setSystemApplicationOverlay(true);
+ return layoutParams;
+ }
+
+ /**
+ * Proxy view connecting taskbar drag layer to the overlay window.
+ *
+ * Overlays are in a separate window and has its own drag layer, but this proxy lets its views
+ * behave as though they are in the taskbar drag layer. For instance, when the taskbar closes
+ * all {@link AbstractFloatingView} instances, the overlay window will also close.
+ */
+ private class TaskbarOverlayProxyView extends AbstractFloatingView {
+
+ private TaskbarOverlayProxyView() {
+ super(mTaskbarContext, null);
+ }
+
+ private void show() {
+ mIsOpen = true;
+ mTaskbarContext.getDragLayer().addView(this);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ mTaskbarContext.getDragLayer().removeView(this);
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_TASKBAR_OVERLAY_PROXY) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
new file mode 100644
index 0000000000..044afd6725
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.overlay;
+
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+
+/** Root drag layer for the Taskbar overlay window. */
+public class TaskbarOverlayDragLayer extends
+ BaseDragLayer implements
+ ViewTreeObserver.OnComputeInternalInsetsListener {
+
+ TaskbarOverlayDragLayer(Context context) {
+ super(context, null, 1);
+ setClipChildren(false);
+ recreateControllers();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ public void recreateControllers() {
+ mControllers = new TouchController[]{mActivity.getDragController()};
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null && topView.onBackPressed()) {
+ return true;
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ if (mActivity.getDragController().isSystemDragInProgress()) {
+ inoutInfo.touchableRegion.setEmpty();
+ inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+ }
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ return updateInsetsDueToStashing(insets);
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ mActivity.getOverlayController().maybeCloseWindow();
+ }
+
+ /**
+ * Taskbar automatically stashes when opening all apps, but we don't report the insets as
+ * changing to avoid moving the underlying app. But internally, the apps view should still
+ * layout according to the stashed insets rather than the unstashed insets. So this method
+ * does two things:
+ * 1) Sets navigationBars bottom inset to stashedHeight.
+ * 2) Sets tappableInsets bottom inset to 0.
+ */
+ private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
+ if (!mActivity.willTaskbarBeVisuallyStashed()) {
+ return oldInsets;
+ }
+ WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+ Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+ Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+ mActivity.getStashedTaskbarHeight());
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
+
+ Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+ Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+ oldTappableInsets.right, 0);
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+
+ return updatedInsetsBuilder.build();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index f450496b3b..ca7ce746b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -21,6 +21,9 @@ import android.content.pm.ShortcutInfo;
import com.android.launcher3.Utilities;
+/**
+ * A wrapper for the hidden API calls
+ */
public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 5f3a990750..bf0f8f72e9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -49,6 +49,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -271,7 +272,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
mIsPinned = true;
applyFromWorkspaceItem(info);
setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = true;
invalidate();
}
@@ -280,7 +281,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
*/
public void finishBinding(OnLongClickListener longClickListener) {
setOnLongClickListener(longClickListener);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = false;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = false;
setTextVisibility(false);
verifyHighRes();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
new file mode 100644
index 0000000000..6659fa0ee7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+
+import java.util.function.IntConsumer;
+
+/**
+ * {@link AppWidgetHost} that is used to receive the changes to the widgets without
+ * storing any {@code Activity} info like that of the launcher.
+ */
+final class QuickstepAppWidgetHost extends AppWidgetHost {
+ private final @NonNull Context mContext;
+ private final @NonNull IntConsumer mAppWidgetRemovedCallback;
+ private final @NonNull LauncherWidgetHolder.ProviderChangedListener mProvidersChangedListener;
+
+ QuickstepAppWidgetHost(@NonNull Context context, @NonNull IntConsumer appWidgetRemovedCallback,
+ @NonNull LauncherWidgetHolder.ProviderChangedListener listener,
+ @NonNull Looper looper) {
+ super(context, APPWIDGET_HOST_ID, null, looper);
+ mContext = context;
+ mAppWidgetRemovedCallback = appWidgetRemovedCallback;
+ mProvidersChangedListener = listener;
+ }
+
+ @Override
+ protected void onProvidersChanged() {
+ mProvidersChangedListener.notifyWidgetProvidersChanged();
+ }
+
+ @Override
+ public void onAppWidgetRemoved(int appWidgetId) {
+ mAppWidgetRemovedCallback.accept(appWidgetId);
+ }
+
+ @Override
+ protected void onProviderChanged(int appWidgetId, @NonNull AppWidgetProviderInfo appWidget) {
+ LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
+ mContext, appWidget);
+ super.onProviderChanged(appWidgetId, info);
+ // The super method updates the dimensions of the providerInfo. Update the
+ // launcher spans accordingly.
+ info.initSpans(mContext, LauncherAppState.getIDP(mContext));
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 192ac6251e..9be569e895 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_WIDGET_PICKER_DEPTH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
@@ -46,7 +45,6 @@ import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.util.BaseDepthController.WIDGET_DEPTH;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import android.animation.AnimatorSet;
@@ -60,17 +58,23 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
+import android.media.permission.SafeCloseable;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.SystemProperties;
+import android.util.Log;
import android.view.Display;
import android.view.HapticFeedbackConstants;
+import android.view.RemoteAnimationTarget;
import android.view.View;
+import android.view.WindowManagerGlobal;
import android.window.SplashScreen;
+import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -96,10 +100,12 @@ import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.proxy.StartActivityParams;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.taskbar.TaskbarManager;
+import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
@@ -118,10 +124,9 @@ import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.TouchController;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
@@ -133,19 +138,22 @@ import com.android.quickstep.util.QuickstepOnboardingPrefs;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SplitToWorkspaceController;
+import com.android.quickstep.util.SplitWithKeyboardShortcutController;
import com.android.quickstep.util.TISBindHelper;
-import com.android.quickstep.util.ViewCapture;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.unfold.UnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -165,6 +173,7 @@ public class QuickstepLauncher extends Launcher {
private FixedContainerItems mAllAppsPredictions;
private HotseatPredictionController mHotseatPredictionController;
private DepthController mDepthController;
+ private DesktopVisibilityController mDesktopVisibilityController;
private QuickstepTransitionManager mAppTransitionManager;
private OverviewActionsView mActionsView;
private TISBindHelper mTISBindHelper;
@@ -174,7 +183,13 @@ public class QuickstepLauncher extends Launcher {
// Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null;
private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
+ private @Nullable RotationChangeProvider mRotationChangeProvider;
private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
+
+ private SplitSelectStateController mSplitSelectStateController;
+ private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
+ private SplitToWorkspaceController mSplitToWorkspaceController;
+
/**
* If Launcher restarted while in the middle of an Overview split select, it needs this data to
* recover. In all other cases this will remain null.
@@ -183,16 +198,22 @@ public class QuickstepLauncher extends Launcher {
private SafeCloseable mViewCapture;
+ private boolean mEnableWidgetDepth;
+
@Override
protected void setupViews() {
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
- RecentsView overviewPanel = (RecentsView) getOverviewPanel();
- SplitSelectStateController controller =
+ RecentsView overviewPanel = getOverviewPanel();
+ mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
getDepthController(), getStatsLogManager());
- overviewPanel.init(mActionsView, controller);
+ overviewPanel.init(mActionsView, mSplitSelectStateController);
+ mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
+ mSplitSelectStateController);
+ mSplitToWorkspaceController = new SplitToWorkspaceController(this,
+ mSplitSelectStateController);
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
@@ -202,7 +223,13 @@ public class QuickstepLauncher extends Launcher {
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
+ mDesktopVisibilityController = new DesktopVisibilityController(this);
mHotseatPredictionController = new HotseatPredictionController(this);
+
+ mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
+ && SystemProperties.getBoolean("ro.launcher.depth.widget", true);
+ getWorkspace().addOverlayCallback(progress ->
+ onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX));
}
@Override
@@ -311,6 +338,17 @@ public class QuickstepLauncher extends Launcher {
super.showAllAppsFromIntent(alreadyOnHome);
}
+ protected void onItemClicked(View view) {
+ if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) {
+ QuickstepLauncher.super.getItemOnClickListener().onClick(view);
+ }
+ }
+
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return this::onItemClicked;
+ }
+
@Override
public Stream getSupportedShortcuts() {
Stream base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
@@ -392,7 +430,8 @@ public class QuickstepLauncher extends Launcher {
super.onDestroy();
mHotseatPredictionController.destroy();
- mViewCapture.close();
+ mSplitWithKeyboardShortcutController.onDestroy();
+ if (mViewCapture != null) mViewCapture.close();
}
@Override
@@ -477,10 +516,13 @@ public class QuickstepLauncher extends Launcher {
return new QuickstepAtomicAnimationFactory(this);
}
- protected LauncherAppWidgetHost createAppWidgetHost() {
- LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost();
- appWidgetHost.setInteractionHandler(new QuickstepInteractionHandler(this));
- return appWidgetHost;
+ @Override
+ protected LauncherWidgetHolder createAppWidgetHolder() {
+ final QuickstepHolderFactory factory =
+ (QuickstepHolderFactory) LauncherWidgetHolder.HolderFactory.newFactory(this);
+ return factory.newInstance(this,
+ appWidgetId -> getWorkspace().removeWidget(appWidgetId),
+ new QuickstepInteractionHandler(this));
}
@Override
@@ -492,7 +534,9 @@ public class QuickstepLauncher extends Launcher {
}
addMultiWindowModeChangedListener(mDepthController);
initUnfoldTransitionProgressProvider();
- mViewCapture = ViewCapture.INSTANCE.get(this).startCapture(getWindow());
+ if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+ mViewCapture = ViewCapture.getInstance().startCapture(getWindow());
+ }
}
@Override
@@ -559,21 +603,8 @@ public class QuickstepLauncher extends Launcher {
@Override
protected void onScreenOff() {
super.onScreenOff();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = getOverviewPanel();
- recentsView.finishRecentsAnimation(true /* toRecents */, null);
- }
- }
-
- /**
- * {@code LauncherOverlayCallbacks} scroll amount.
- * Indicates transition progress to -1 screen.
- * @param progress From 0 to 1.
- */
- @Override
- public void onScrollChanged(float progress) {
- super.onScrollChanged(progress);
- onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX);
+ RecentsView recentsView = getOverviewPanel();
+ recentsView.finishRecentsAnimation(true /* toRecents */, null);
}
@Override
@@ -586,12 +617,9 @@ public class QuickstepLauncher extends Launcher {
public void onWidgetsTransition(float progress) {
super.onWidgetsTransition(progress);
onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
- // Change of wallpaper depth in widget picker is disabled for tests as it causes flakiness
- // on very slow cuttlefish devices.
- if (ENABLE_WIDGET_PICKER_DEPTH.get() && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- WIDGET_DEPTH.set(getDepthController(),
- Utilities.mapToRange(progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth,
- EMPHASIZED));
+ if (mEnableWidgetDepth) {
+ getDepthController().widgetDepth.setValue(Utilities.mapToRange(
+ progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth, EMPHASIZED));
}
}
@@ -636,6 +664,20 @@ public class QuickstepLauncher extends Launcher {
}
}
+ @Override
+ public void setResumed() {
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ DesktopVisibilityController controller = mDesktopVisibilityController;
+ if (controller != null && controller.areFreeformTasksVisible()) {
+ // Return early to skip setting activity to appear as resumed
+ // TODO(b/255649902): shouldn't be needed when we have a separate launcher state
+ // for desktop that we can use to control other parts of launcher
+ return;
+ }
+ }
+ super.setResumed();
+ }
+
@Override
protected void onDeferredResumed() {
super.onDeferredResumed();
@@ -667,8 +709,8 @@ public class QuickstepLauncher extends Launcher {
private void initUnfoldTransitionProgressProvider() {
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
if (config.isEnabled()) {
- mUnfoldTransitionProgressProvider =
- UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
+ UnfoldSharedComponent unfoldComponent =
+ UnfoldTransitionFactory.createUnfoldSharedComponent(
/* context= */ this,
config,
ProxyScreenStatusProvider.INSTANCE,
@@ -680,14 +722,23 @@ public class QuickstepLauncher extends Launcher {
getMainThreadHandler(),
getMainExecutor(),
/* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
- /* tracingTagPrefix= */ "launcher"
+ /* tracingTagPrefix= */ "launcher",
+ WindowManagerGlobal.getWindowManagerService()
);
+ mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider()
+ .orElseThrow(() -> new IllegalStateException(
+ "Trying to create UnfoldTransitionProgressProvider when the "
+ + "transition is disabled"));
+
+ mRotationChangeProvider = unfoldComponent.getRotationChangeProvider();
mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
- this,
+ /* launcher= */ this,
getWindowManager(),
- mUnfoldTransitionProgressProvider
+ mUnfoldTransitionProgressProvider,
+ mRotationChangeProvider
);
+ Log.d("b/261320823", "initUnfoldTransitionProgressProvider completed");
}
}
@@ -699,6 +750,10 @@ public class QuickstepLauncher extends Launcher {
return mTaskbarUIController;
}
+ public SplitSelectStateController getSplitSelectStateController() {
+ return mSplitSelectStateController;
+ }
+
public T getActionsView() {
return (T) mActionsView;
}
@@ -720,6 +775,10 @@ public class QuickstepLauncher extends Launcher {
return mDepthController;
}
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ return mDesktopVisibilityController;
+ }
+
@Nullable
public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
return mUnfoldTransitionProgressProvider;
@@ -749,8 +808,8 @@ public class QuickstepLauncher extends Launcher {
QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
@Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
// On the first call clear the reference.
signal.cancel();
@@ -812,6 +871,12 @@ public class QuickstepLauncher extends Launcher {
return activityOptions;
}
+ @Override
+ @BinderThread
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+ mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
+ }
+
/**
* Adds a new launch cookie for the activity launch if supported.
*
@@ -872,7 +937,11 @@ public class QuickstepLauncher extends Launcher {
// When changing screens, force moving to rest state similar to StatefulActivity.onStop, as
// StatefulActivity isn't called consistently.
if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
- getStateManager().moveToRestState();
+ // Do not animate moving to rest state, as it can clash with Launcher#onIdpChanged
+ // where reapplyUi calls StateManager's reapplyState during the state change animation,
+ // and cancel the state change unexpectedly. The screen will be off during screen
+ // transition, hiding the unanimated transition.
+ getStateManager().moveToRestState(/* isAnimated = */false);
}
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
new file mode 100644
index 0000000000..a8edd511f6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -0,0 +1,270 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.util.SparseArray;
+import android.widget.RemoteViews;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+
+/**
+ * {@link LauncherWidgetHolder} that puts the app widget host in the background
+ */
+public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
+ private static final List sHolders = new ArrayList<>();
+ private static final SparseArray sListeners =
+ new SparseArray<>();
+
+ private static AppWidgetHost sWidgetHost = null;
+
+ private final @Nullable RemoteViews.InteractionHandler mInteractionHandler;
+
+ private final @NonNull IntConsumer mAppWidgetRemovedCallback;
+
+ private final ArrayList mProviderChangedListeners = new ArrayList<>();
+
+ @Thunk
+ QuickstepWidgetHolder(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback,
+ @Nullable RemoteViews.InteractionHandler interactionHandler) {
+ super(context, appWidgetRemovedCallback);
+ mAppWidgetRemovedCallback = appWidgetRemovedCallback != null ? appWidgetRemovedCallback
+ : i -> {};
+ mInteractionHandler = interactionHandler;
+ sHolders.add(this);
+ }
+
+ @Override
+ @NonNull
+ protected AppWidgetHost createHost(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ if (sWidgetHost == null) {
+ sWidgetHost = new QuickstepAppWidgetHost(context.getApplicationContext(),
+ i -> MAIN_EXECUTOR.execute(() ->
+ sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))),
+ () -> MAIN_EXECUTOR.execute(() ->
+ sHolders.forEach(h -> h.mProviderChangedListeners.forEach(
+ ProviderChangedListener::notifyWidgetProvidersChanged))),
+ UI_HELPER_EXECUTOR.getLooper());
+ if (!WidgetsModel.GO_DISABLE_WIDGETS) {
+ sWidgetHost.startListening();
+ }
+ }
+ return sWidgetHost;
+ }
+
+ /**
+ * Delete the specified app widget from the host
+ * @param appWidgetId The ID of the app widget to be deleted
+ */
+ @Override
+ public void deleteAppWidgetId(int appWidgetId) {
+ super.deleteAppWidgetId(appWidgetId);
+ sListeners.remove(appWidgetId);
+ }
+
+ /**
+ * Called when the launcher is destroyed
+ */
+ @Override
+ public void destroy() {
+ sHolders.remove(this);
+ }
+
+ /**
+ * Add a listener that is triggered when the providers of the widgets are changed
+ * @param listener The listener that notifies when the providers changed
+ */
+ @Override
+ public void addProviderChangeListener(
+ @NonNull LauncherWidgetHolder.ProviderChangedListener listener) {
+ mProviderChangedListeners.add(listener);
+ }
+
+ /**
+ * Remove the specified listener from the host
+ * @param listener The listener that is to be removed from the host
+ */
+ @Override
+ public void removeProviderChangeListener(
+ LauncherWidgetHolder.ProviderChangedListener listener) {
+ mProviderChangedListeners.remove(listener);
+ }
+
+ /**
+ * Stop the host from updating the widget views
+ */
+ @Override
+ public void stopListening() {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ return;
+ }
+
+ sWidgetHost.setAppWidgetHidden();
+ setListeningFlag(false);
+ }
+
+ /**
+ * Create a view for the specified app widget
+ * @param context The activity context for which the view is created
+ * @param appWidgetId The ID of the widget
+ * @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget
+ * @return A view for the widget
+ */
+ @NonNull
+ @Override
+ public LauncherAppWidgetHostView createView(@NonNull Context context, int appWidgetId,
+ @NonNull LauncherAppWidgetProviderInfo appWidget) {
+ LauncherAppWidgetHostView widgetView = getPendingView(appWidgetId);
+ if (widgetView != null) {
+ removePendingView(appWidgetId);
+ } else {
+ widgetView = new LauncherAppWidgetHostView(context);
+ }
+ widgetView.setInteractionHandler(mInteractionHandler);
+ widgetView.setAppWidget(appWidgetId, appWidget);
+
+ QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId);
+ if (listener == null) {
+ listener = new QuickstepWidgetHolderListener(this, widgetView);
+ sWidgetHost.setListener(appWidgetId, listener);
+ sListeners.put(appWidgetId, listener);
+ } else {
+ listener.resetView(this, widgetView);
+ }
+
+ return widgetView;
+ }
+
+ /**
+ * Clears all the views from the host
+ */
+ @Override
+ public void clearViews() {
+ for (int i = sListeners.size() - 1; i >= 0; i--) {
+ sListeners.valueAt(i).mView.remove(this);
+ }
+ }
+
+ private static class QuickstepWidgetHolderListener
+ implements AppWidgetHost.AppWidgetHostListener {
+ @NonNull
+ private final Map mView = new WeakHashMap<>();
+
+ @Nullable
+ private RemoteViews mRemoteViews = null;
+
+ QuickstepWidgetHolderListener(@NonNull QuickstepWidgetHolder holder,
+ @NonNull LauncherAppWidgetHostView view) {
+ mView.put(holder, view);
+ }
+
+ @UiThread
+ public void resetView(@NonNull QuickstepWidgetHolder holder,
+ @NonNull AppWidgetHostView view) {
+ mView.put(holder, view);
+ view.updateAppWidget(mRemoteViews);
+ }
+
+ @Override
+ @WorkerThread
+ public void onUpdateProviderInfo(@Nullable AppWidgetProviderInfo info) {
+ mRemoteViews = null;
+ executeOnMainExecutor(v -> v.onUpdateProviderInfo(info));
+ }
+
+ @Override
+ @WorkerThread
+ public void updateAppWidget(@Nullable RemoteViews views) {
+ mRemoteViews = views;
+ executeOnMainExecutor(v -> v.updateAppWidget(mRemoteViews));
+ }
+
+ @Override
+ @WorkerThread
+ public void onViewDataChanged(int viewId) {
+ executeOnMainExecutor(v -> v.onViewDataChanged(viewId));
+ }
+
+ private void executeOnMainExecutor(Consumer consumer) {
+ MAIN_EXECUTOR.execute(() -> mView.values().forEach(consumer));
+ }
+ }
+
+ /**
+ * {@code HolderFactory} subclass that takes an interaction handler as one of the parameters
+ * when creating a new instance.
+ */
+ public static class QuickstepHolderFactory extends HolderFactory {
+
+ @SuppressWarnings("unused")
+ public QuickstepHolderFactory(Context context) { }
+
+ @Override
+ public LauncherWidgetHolder newInstance(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ return newInstance(context, appWidgetRemovedCallback, null);
+ }
+
+ /**
+ * @param context The context of the caller
+ * @param appWidgetRemovedCallback The callback that is called when widgets are removed
+ * @param interactionHandler The interaction handler when the widgets are clicked
+ * @return A new {@link LauncherWidgetHolder} instance
+ */
+ public LauncherWidgetHolder newInstance(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback,
+ @Nullable RemoteViews.InteractionHandler interactionHandler) {
+
+ if (!FeatureFlags.ENABLE_WIDGET_HOST_IN_BACKGROUND.get()) {
+ return new LauncherWidgetHolder(context, appWidgetRemovedCallback) {
+ @Override
+ protected AppWidgetHost createHost(Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ AppWidgetHost host = super.createHost(context, appWidgetRemovedCallback);
+ host.setInteractionHandler(interactionHandler);
+ return host;
+ }
+ };
+ }
+ return new QuickstepWidgetHolder(context, appWidgetRemovedCallback, interactionHandler);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 910b99b36a..e8e83288cd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -31,17 +32,18 @@ import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.FloatProperty;
+import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.ClearAllButton;
@@ -88,6 +90,13 @@ public final class RecentsViewStateController extends
// While animating into recents, update the visible task data as needed
builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d("b/246283207", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
+ + mRecentsView.getCurrentPage()
+ + ", getScrollForPage(getCurrentPage())): "
+ + mRecentsView.getScrollForPage(mRecentsView.getCurrentPage()));
+ }
} else {
builder.addListener(
AnimatorListeners.forSuccessCallback(mRecentsView::resetTaskVisuals));
@@ -155,7 +164,7 @@ public final class RecentsViewStateController extends
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
- MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
+ MULTI_PROPERTY_VALUE, overviewButtonAlpha, config.getInterpolator(
ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
index 5afeca7e3b..faa900b714 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
@@ -18,11 +18,11 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
-import com.android.launcher3.Utilities;
-import com.android.systemui.shared.plugins.PluginEnabler;
-
import androidx.preference.PreferenceDataStore;
+import com.android.launcher3.LauncherPrefs;
+import com.android.systemui.shared.plugins.PluginEnabler;
+
public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnabler {
private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
@@ -30,7 +30,7 @@ public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnab
final private SharedPreferences mSharedPrefs;
public PluginEnablerImpl(Context context) {
- mSharedPrefs = Utilities.getDevicePrefs(context);
+ mSharedPrefs = LauncherPrefs.getDevicePrefs(context);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index fe0bca6646..e3d386cae6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -28,9 +28,9 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginActionManager;
import com.android.systemui.shared.plugins.PluginInstance;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index fd184c63a4..2a42175b5b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,12 +20,11 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAP
import android.content.Context;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
/**
* Definition for AllApps state
@@ -40,7 +39,7 @@ public class AllAppsState extends LauncherState {
}
@Override
- public
+ public
int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
return isToState
? context.getDeviceProfile().allAppsOpenDuration
@@ -78,7 +77,7 @@ public class AllAppsState extends LauncherState {
}
@Override
- protected
+ protected
float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
if (context.getDeviceProfile().isTablet) {
return context.getDeviceProfile().bottomSheetDepth;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 4150d40bea..733c6a8b48 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -26,6 +26,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -96,7 +97,7 @@ public class BackgroundAppState extends OverviewState {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
- if (dp.isTaskbarPresentInApps) {
+ if (dp.isTaskbarPresentInApps && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
return launcher.getColor(R.color.taskbar_background);
}
return Color.TRANSPARENT;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 6f07568cf5..d075750330 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -26,6 +26,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.util.LayoutUtils;
@@ -104,7 +105,12 @@ public class OverviewState extends LauncherState {
@Override
public boolean isTaskbarStashed(Launcher launcher) {
- return true;
+ return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
+ }
+
+ @Override
+ public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
+ return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index bcd722f4e3..5eeeb36981 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -113,19 +113,19 @@ public class QuickstepAtomicAnimationFactory extends
config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, EMPHASIZED_DECELERATE);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
+
+ // Scroll RecentsView to page 0 as it goes offscreen, if necessary.
+ int numPagesToScroll = overview.getNextPage() - DEFAULT_PAGE;
+ long scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
+ numPagesToScroll * PER_PAGE_SCROLL_DURATION);
+ config.duration = Math.max(config.duration, scrollDuration);
+ overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
} else {
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
}
- // Scroll RecentsView to page 0 as it goes offscreen, if necessary.
- int numPagesToScroll = overview.getNextPage() - DEFAULT_PAGE;
- long scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
- numPagesToScroll * PER_PAGE_SCROLL_DURATION);
- config.duration = Math.max(config.duration, scrollDuration);
- overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
-
Workspace> workspace = mActivity.getWorkspace();
// Start from a higher workspace scale, but only if we're invisible so we don't jump.
boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
@@ -197,7 +197,8 @@ public class QuickstepAtomicAnimationFactory extends
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR,
timings.getActionsFadeStartOffset(),
timings.getActionsFadeEndOffset()));
- } else if (fromState == NORMAL && toState == OVERVIEW_SPLIT_SELECT) {
+ } else if ((fromState == NORMAL || fromState == ALL_APPS)
+ && toState == OVERVIEW_SPLIT_SELECT) {
// Splitting from Home is currently only available on tablets
SplitAnimationTimings timings = SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(LINEAR,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 30bb892c9a..40dfd82f5d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -25,7 +25,6 @@ import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_TRANSLATION;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -139,9 +138,7 @@ public class NavBarToHomeTouchController implements TouchController,
AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
builder);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- builder.addOnFrameCallback(recentsView::redrawLiveTile);
- }
+ builder.addOnFrameCallback(recentsView::redrawLiveTile);
AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_TASK_MENU);
} else if (mStartState == ALL_APPS) {
@@ -182,11 +179,9 @@ public class NavBarToHomeTouchController implements TouchController,
boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
|| (velocity < 0 && fling);
if (success) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = mLauncher.getOverviewPanel();
- recentsView.switchToScreenshot(null,
- () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
- }
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.switchToScreenshot(null,
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
if (mStartState.overviewUi) {
new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState))
.animateWithVelocity(velocity);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 918b3c1bf8..b5afda388a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -25,7 +25,8 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.ObjectAnimator;
@@ -41,11 +42,11 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.OverviewToHomeAnim;
-import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.views.RecentsView;
/**
@@ -86,6 +87,11 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+ boolean isOneHandedModeActive = (SystemUiProxy.INSTANCE.get(mLauncher)
+ .getLastSystemUiStateFlags() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
+ // Reset touch slop multiplier to default 1.0f if one-handed-mode is not active
+ mDetector.setTouchSlopMultiplier(
+ isOneHandedModeActive ? ONE_HANDED_ACTIVATED_SLOP_MULTIPLIER : 1f /* default */);
return super.canInterceptTouch(ev) && !mLauncher.isInState(HINT_STATE);
}
@@ -277,14 +283,4 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
private float dpiFromPx(float pixels) {
return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics().densityDpi);
}
-
- @Override
- public void onOneHandedModeStateChanged(boolean activated) {
- if (activated) {
- mDetector.setTouchSlopMultiplier(ONE_HANDED_ACTIVATED_SLOP_MULTIPLIER);
- } else {
- // Reset touch slop multiplier to default 1.0f
- mDetector.setTouchSlopMultiplier(1f /* default */);
- }
- }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 922679b5f4..e0cb0b4ddb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -41,8 +41,8 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
-import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
@@ -62,6 +62,7 @@ import android.view.animation.Interpolator;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
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.states.StateAnimationConfig;
@@ -69,12 +70,11 @@ import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.TouchController;
-import com.android.quickstep.AnimatedFloat;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 9efbc34a17..8368f9ca03 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -186,12 +186,17 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
case MotionEvent.ACTION_DOWN:
InteractionJankMonitorWrapper.begin(
mLauncher.getRootView(), InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.begin(
+ mLauncher.getRootView(),
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
break;
}
return super.onControllerInterceptTouchEvent(ev);
@@ -204,6 +209,10 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
if (newToState != ALL_APPS) {
InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
}
+ if (newToState != NORMAL) {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
+ }
}
@Override
@@ -211,6 +220,9 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
super.onReachedFinalState(toState);
if (toState == ALL_APPS) {
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ } else if (toState == NORMAL) {
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
}
}
@@ -218,5 +230,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
protected void clearState() {
super.clearState();
InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index c49848a5b4..eddc50c64f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -41,8 +41,9 @@ import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.util.VibratorWrapper;
+import com.android.quickstep.util.VibrationConstants;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -61,7 +62,7 @@ public abstract class TaskViewTouchController
Utilities.ATLEAST_R ? VibrationEffect.Composition.PRIMITIVE_TICK : -1;
public static final float TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE = 1f;
public static final VibrationEffect TASK_DISMISS_VIBRATION_FALLBACK =
- VibratorWrapper.EFFECT_TEXTURE_TICK;
+ VibrationConstants.EFFECT_TEXTURE_TICK;
protected final T mActivity;
private final SingleAxisSwipeDetector mDetector;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index f3630c187b..8409475e4c 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -26,7 +27,7 @@ import static com.android.launcher3.PagedView.INVALID_PAGE;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_REVISED_THRESHOLDS;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@@ -37,6 +38,7 @@ import static com.android.launcher3.uioverrides.QuickstepLauncher.ENABLE_PIP_KEE
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
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.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
@@ -48,12 +50,10 @@ import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELE
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
-import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -65,6 +65,7 @@ import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -74,8 +75,10 @@ import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowInsets;
@@ -93,14 +96,18 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.launcher3.util.DisplayController;
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;
@@ -116,10 +123,10 @@ import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TaskViewSimulator;
-import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -127,13 +134,15 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -249,6 +258,13 @@ public abstract class AbsSwipeUpHandler,
private static final float MAX_QUICK_SWITCH_RECENTS_SCALE_PROGRESS = 0.07f;
+ // Controls task thumbnail splash's reveal animation after landing on a task from quickswitch.
+ // These values match WindowManager/Shell starting_window_app_reveal_* config values.
+ private static final int SPLASH_FADE_OUT_DURATION = 133;
+ private static final int SPLASH_APP_REVEAL_DELAY = 83;
+ private static final int SPLASH_APP_REVEAL_DURATION = 266;
+ private static final int SPLASH_ANIMATION_DURATION = 349;
+
/**
* Used as the page index for logging when we return to the last task at the end of the gesture.
*/
@@ -277,7 +293,6 @@ public abstract class AbsSwipeUpHandler,
private boolean mWasLauncherAlreadyVisible;
- private boolean mPassedOverviewThreshold;
private boolean mGestureStarted;
private boolean mLogDirectionUpOrLeft = true;
private PointF mDownPos;
@@ -286,6 +301,8 @@ public abstract class AbsSwipeUpHandler,
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
+ private final int mSplashMainWindowShiftLength;
+
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
@@ -298,6 +315,18 @@ public abstract class AbsSwipeUpHandler,
// Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
private final float mQuickSwitchScaleScrollThreshold;
+ private final int mTaskbarAppWindowThreshold;
+ private final int mTaskbarHomeOverviewThreshold;
+ private final int mTaskbarCatchUpThreshold;
+ private final boolean mTaskbarAlreadyOpen;
+ private final boolean mIsTaskbarAllAppsOpen;
+ private final boolean mIsTransientTaskbar;
+ // May be set to false when mIsTransientTaskbar is true.
+ private boolean mCanSlowSwipeGoHome = true;
+
+ @Nullable
+ private RemoteAnimationTargets.ReleaseCheck mSwipePipToHomeReleaseCheck = null;
+
public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
@@ -318,11 +347,32 @@ public abstract class AbsSwipeUpHandler,
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
- mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
- R.dimen.quick_switch_scaling_scroll_threshold);
- initAfterSubclassConstructor();
+ Resources res = context.getResources();
+ mQuickSwitchScaleScrollThreshold = res
+ .getDimension(R.dimen.quick_switch_scaling_scroll_threshold);
+
+ mSplashMainWindowShiftLength = -res
+ .getDimensionPixelSize(R.dimen.starting_surface_exit_animation_window_shift_length);
+
+ initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
+ .getOrientationState().getLauncherDeviceProfile());
initStateCallbacks();
+
+ mIsTransientTaskbar = mDp.isTaskbarPresent
+ && DisplayController.isTransientTaskbar(mActivity);
+ TaskbarUIController controller = mActivityInterface.getTaskbarController();
+ mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed();
+ mIsTaskbarAllAppsOpen = controller != null && controller.isTaskbarAllAppsOpen();
+ mTaskbarAppWindowThreshold = res
+ .getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
+ ? R.dimen.taskbar_app_window_threshold_v2
+ : R.dimen.taskbar_app_window_threshold);
+ mTaskbarHomeOverviewThreshold = res.getDimensionPixelSize(
+ ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
+ ? R.dimen.taskbar_home_overview_threshold_v2
+ : R.dimen.taskbar_home_overview_threshold);
+ mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold);
}
@Nullable
@@ -400,12 +450,6 @@ public abstract class AbsSwipeUpHandler,
this::resetStateForAnimationCancel);
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_FINISH_WITH_NO_END,
this::resetStateForAnimationCancel);
-
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
- | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
- (b) -> mRecentsView.setRunningTaskHidden(!b));
- }
}
protected boolean onActivityInit(Boolean alreadyOnHome) {
@@ -583,14 +627,10 @@ public abstract class AbsSwipeUpHandler,
}
private void onDeferredActivityLaunch() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivityInterface.switchRunningTaskViewToScreenshot(
- null, () -> {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- });
- } else {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- }
+ mActivityInterface.switchRunningTaskViewToScreenshot(
+ null, () -> {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ });
}
private void setupRecentsViewUi() {
@@ -637,6 +677,8 @@ public abstract class AbsSwipeUpHandler,
public void onMotionPauseDetected() {
mHasMotionEverBeenPaused = true;
maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
+ Optional.ofNullable(mActivityInterface.getTaskbarController())
+ .ifPresent(TaskbarUIController::startTranslationSpring);
performHapticFeedback();
}
@@ -668,7 +710,7 @@ public abstract class AbsSwipeUpHandler,
if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
- RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
: null;
final boolean recentsAttachedToAppWindow;
@@ -708,6 +750,15 @@ public abstract class AbsSwipeUpHandler,
}
}
+ /**
+ * Returns threshold that needs to be met in order for motion pause to be allowed.
+ */
+ public float getThresholdToAllowMotionPause() {
+ return mIsTransientTaskbar
+ ? mTaskbarHomeOverviewThreshold
+ : 0;
+ }
+
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
}
@@ -770,14 +821,6 @@ public abstract class AbsSwipeUpHandler,
@UiThread
@Override
public void updateFinalShift() {
- final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
- if (passed != mPassedOverviewThreshold) {
- mPassedOverviewThreshold = passed;
- if (mDeviceState.isTwoButtonNavMode() && !mGestureState.isHandlingAtomicEvent()) {
- performHapticFeedback();
- }
- }
-
updateSysUiFlags(mCurrentShift.value);
applyScrollAndTransform();
@@ -829,11 +872,14 @@ public abstract class AbsSwipeUpHandler,
mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
+ mSwipePipToHomeReleaseCheck = new RemoteAnimationTargets.ReleaseCheck();
+ mSwipePipToHomeReleaseCheck.setCanRelease(true);
+ mRecentsAnimationTargets.addReleaseCheck(mSwipePipToHomeReleaseCheck);
// Only initialize the device profile, if it has not been initialized before, as in some
// configurations targets.homeContentInsets may not be correct.
if (mActivity == null) {
- RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[0];
+ RemoteAnimationTarget primaryTaskTarget = targets.apps[0];
// orientation state is independent of which remote target handle we use since both
// should be pointing to the same one. Just choose index 0 for now since that works for
// both split and non-split
@@ -862,8 +908,6 @@ public abstract class AbsSwipeUpHandler,
mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
this::startInterceptingTouchesForGesture);
mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
-
- mPassedOverviewThreshold = false;
}
@Override
@@ -905,6 +949,8 @@ public abstract class AbsSwipeUpHandler,
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
InteractionJankMonitorWrapper.begin(mRecentsView,
InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ InteractionJankMonitorWrapper.begin(mRecentsView,
+ InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
rv.post(() -> rv.getViewTreeObserver().removeOnDrawListener(this));
}
@@ -912,11 +958,33 @@ public abstract class AbsSwipeUpHandler,
}
notifyGestureStartedAsync();
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
+
+ if (mIsTransientTaskbar && !mTaskbarAlreadyOpen && !isLikelyToStartNewTask) {
+ setClampScrollOffset(true);
+ }
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
- SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
}
+ /**
+ * Sets whether or not we should clamp the scroll offset.
+ * This is used to avoid x-axis movement when swiping up transient taskbar.
+ * @param clampScrollOffset When true, we clamp the scroll to 0 before the clamp threshold is
+ * met.
+ */
+ private void setClampScrollOffset(boolean clampScrollOffset) {
+ if (!mIsTransientTaskbar) {
+ return;
+ }
+ if (mRecentsView == null) {
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT,
+ () -> mRecentsView.setClampScrollOffset(clampScrollOffset));
+ return;
+ }
+ mRecentsView.setClampScrollOffset(clampScrollOffset);
+ }
+
+
/**
* Notifies the launcher that the swipe gesture has started. This can be called multiple times.
*/
@@ -941,26 +1009,27 @@ public abstract class AbsSwipeUpHandler,
}
/**
- * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
- * @param velocity The x and y components of the velocity when the gesture ends.
+ * @param endVelocityPxPerMs The velocity in the direction of the nav bar to the middle of the
+ * screen.
+ * @param velocityPxPerMs The x and y components of the velocity when the gesture ends.
* @param downPos The x and y value of where the gesture started.
*/
@UiThread
- public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
+ public void onGestureEnded(float endVelocityPxPerMs, PointF velocityPxPerMs, PointF downPos) {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_speed);
boolean isFling = mGestureStarted && !mIsMotionPaused
- && Math.abs(endVelocity) > flingThreshold;
+ && Math.abs(endVelocityPxPerMs) > flingThreshold;
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
- boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x);
+ boolean isVelocityVertical = Math.abs(velocityPxPerMs.y) > Math.abs(velocityPxPerMs.x);
if (isVelocityVertical) {
- mLogDirectionUpOrLeft = velocity.y < 0;
+ mLogDirectionUpOrLeft = velocityPxPerMs.y < 0;
} else {
- mLogDirectionUpOrLeft = velocity.x < 0;
+ mLogDirectionUpOrLeft = velocityPxPerMs.x < 0;
}
mDownPos = downPos;
- Runnable handleNormalGestureEndCallback = () ->
- handleNormalGestureEnd(endVelocity, isFling, velocity, /* isCancel= */ false);
+ Runnable handleNormalGestureEndCallback = () -> handleNormalGestureEnd(
+ endVelocityPxPerMs, isFling, velocityPxPerMs, /* isCancel= */ false);
if (mRecentsView != null) {
mRecentsView.runOnPageScrollsInitialized(handleNormalGestureEndCallback);
} else {
@@ -1006,12 +1075,16 @@ public abstract class AbsSwipeUpHandler,
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
}
+ if (endTarget != RECENTS) {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
+ }
switch (endTarget) {
case HOME:
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
- // Notify swipe-to-home (recents animation) is finished
- SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished();
+ // Notify the SysUI to use fade-in animation when entering PiP
+ SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
break;
case RECENTS:
mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
@@ -1038,7 +1111,7 @@ public abstract class AbsSwipeUpHandler,
}
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
@@ -1051,8 +1124,20 @@ public abstract class AbsSwipeUpHandler,
return false;
}
+ private float dpiFromPx(float pixels) {
+ return Utilities.dpiFromPx(pixels, mContext.getResources().getDisplayMetrics().densityDpi);
+ }
+
private GestureEndTarget calculateEndTarget(
- PointF velocity, float endVelocity, boolean isFlingY, boolean isCancel) {
+ PointF velocityPxPerMs, float endVelocityPxPerMs, boolean isFlingY, boolean isCancel) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("calculateEndTarget: velocities=(x=")
+ .append(Float.toString(dpiFromPx(velocityPxPerMs.x)))
+ .append("dp/ms, y=")
+ .append(Float.toString(dpiFromPx(velocityPxPerMs.y)))
+ .append("dp/ms), angle=")
+ .append(Double.toString(Math.toDegrees(Math.atan2(
+ -velocityPxPerMs.y, velocityPxPerMs.x)))));
if (mGestureState.isHandlingAtomicEvent()) {
// Button mode, this is only used to go to recents.
@@ -1063,9 +1148,9 @@ public abstract class AbsSwipeUpHandler,
if (isCancel) {
endTarget = LAST_TASK;
} else if (isFlingY) {
- endTarget = calculateEndTargetForFlingY(velocity, endVelocity);
+ endTarget = calculateEndTargetForFlingY(velocityPxPerMs, endVelocityPxPerMs);
} else {
- endTarget = calculateEndTargetForNonFling(velocity);
+ endTarget = calculateEndTargetForNonFling(velocityPxPerMs);
}
if (mDeviceState.isOverviewDisabled() && endTarget == RECENTS) {
@@ -1086,20 +1171,11 @@ public abstract class AbsSwipeUpHandler,
return willGoToNewTask || isCenteredOnNewTask ? NEW_TASK : LAST_TASK;
}
- if (!mDeviceState.isFullyGesturalNavMode()) {
- return (!hasReachedOverviewThreshold() && willGoToNewTask) ? NEW_TASK : RECENTS;
- }
return willGoToNewTask ? NEW_TASK : HOME;
}
private GestureEndTarget calculateEndTargetForNonFling(PointF velocity) {
final boolean isScrollingToNewTask = isScrollingToNewTask();
- final boolean reachedOverviewThreshold = hasReachedOverviewThreshold();
- if (!mDeviceState.isFullyGesturalNavMode()) {
- return reachedOverviewThreshold && mGestureStarted
- ? RECENTS
- : (isScrollingToNewTask ? NEW_TASK : LAST_TASK);
- }
// Fully gestural mode.
final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
@@ -1112,10 +1188,8 @@ public abstract class AbsSwipeUpHandler,
return RECENTS;
} else if (isScrollingToNewTask) {
return NEW_TASK;
- } else if (reachedOverviewThreshold) {
- return HOME;
}
- return LAST_TASK;
+ return velocity.y < 0 && mCanSlowSwipeGoHome ? HOME : LAST_TASK;
}
private boolean isScrollingToNewTask() {
@@ -1131,17 +1205,25 @@ public abstract class AbsSwipeUpHandler,
return runningTaskIndex >= 0 && mRecentsView.getNextPage() != runningTaskIndex;
}
- private boolean hasReachedOverviewThreshold() {
- return mCurrentShift.value > MIN_PROGRESS_FOR_OVERVIEW;
+ /**
+ * Sets whether a slow swipe can go to the HOME end target when the user lets go. A slow swipe
+ * for this purpose must meet two criteria:
+ * 1) y-velocity is less than quickstep_fling_threshold_speed
+ * AND
+ * 2) motion pause has not been detected (possibly because
+ * {@link MotionPauseDetector#setDisallowPause} has been called with disallowPause == true)
+ */
+ public void setCanSlowSwipeGoHome(boolean canSlowSwipeGoHome) {
+ mCanSlowSwipeGoHome = canSlowSwipeGoHome;
}
@UiThread
- private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
- boolean isCancel) {
+ private void handleNormalGestureEnd(
+ float endVelocityPxPerMs, boolean isFling, PointF velocityPxPerMs, boolean isCancel) {
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
- final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
- isFling, isCancel);
+ final GestureEndTarget endTarget = calculateEndTarget(
+ velocityPxPerMs, endVelocityPxPerMs, isFling, isCancel);
// Set the state, but don't notify until the animation completes
mGestureState.setEndTarget(endTarget, false /* isAtomic */);
mAnimationFactory.setEndTarget(endTarget);
@@ -1154,16 +1236,16 @@ public abstract class AbsSwipeUpHandler,
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
startShift = currentShift;
} else {
- startShift = Utilities.boundToRange(currentShift - velocity.y
+ startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
* getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
if (mTransitionDragLength > 0) {
- float distanceToTravel = (endShift - currentShift) * mTransitionDragLength;
+ float distanceToTravel = (endShift - currentShift) * mTransitionDragLength;
- // we want the page's snap velocity to approximately match the velocity at
- // which the user flings, so we scale the duration by a value near to the
- // derivative of the scroll interpolator at zero, ie. 2.
- long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y));
- duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
+ // we want the page's snap velocity to approximately match the velocity at
+ // which the user flings, so we scale the duration by a value near to the
+ // derivative of the scroll interpolator at zero, ie. 2.
+ long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
+ duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
}
}
Interpolator interpolator;
@@ -1219,16 +1301,19 @@ public abstract class AbsSwipeUpHandler,
// Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
// or resumeLastTask().
+ Runnable onPageTransitionEnd = () -> {
+ mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
+ setClampScrollOffset(false);
+ };
if (mRecentsView != null) {
ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
.SET_ON_PAGE_TRANSITION_END_CALLBACK);
- mRecentsView.setOnPageTransitionEndCallback(
- () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
+ mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
} else {
- mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
+ onPageTransitionEnd.run();
}
- animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity);
+ animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
}
private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
@@ -1277,7 +1362,7 @@ public abstract class AbsSwipeUpHandler,
protected abstract HomeAnimationFactory createHomeAnimationFactory(
ArrayList launchCookies, long duration, boolean isTargetTranslucent,
- boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget);
+ boolean appCanEnterPip, RemoteAnimationTarget runningTaskTarget);
private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
@Override
@@ -1304,6 +1389,10 @@ public abstract class AbsSwipeUpHandler,
// If we are transitioning to launcher, then listen for the activity to be restarted while
// the transition is in progress
if (mGestureState.getEndTarget().isLauncher) {
+ // This is also called when the launcher is resumed, in order to clear the pending
+ // widgets that have yet to be configured.
+ DragView.removeAllViews(mActivity);
+
TaskStackChangeListeners.getInstance().registerTaskStackListener(
mActivityRestartListener);
@@ -1323,7 +1412,7 @@ public abstract class AbsSwipeUpHandler,
if (mGestureState.getEndTarget() == HOME) {
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
- final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ final RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
: null;
final ArrayList cookies = runningTaskTarget != null
@@ -1347,9 +1436,16 @@ public abstract class AbsSwipeUpHandler,
mSwipePipToHomeAnimator = createWindowAnimationToPip(
homeAnimFactory, runningTaskTarget, start);
mSwipePipToHomeAnimators[0] = mSwipePipToHomeAnimator;
+ if (mSwipePipToHomeReleaseCheck != null) {
+ mSwipePipToHomeReleaseCheck.setCanRelease(false);
+ }
windowAnim = mSwipePipToHomeAnimators;
} else {
mSwipePipToHomeAnimator = null;
+ if (mSwipePipToHomeReleaseCheck != null) {
+ mSwipePipToHomeReleaseCheck.setCanRelease(true);
+ mSwipePipToHomeReleaseCheck = null;
+ }
windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
windowAnim[0].addAnimatorListener(new AnimationSuccessListener() {
@@ -1433,7 +1529,7 @@ public abstract class AbsSwipeUpHandler,
}
}
- private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget,
+ private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget,
RecentsOrientedState orientationState) {
if (runningTaskTarget.rotationChange != 0
&& TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -1446,7 +1542,7 @@ public abstract class AbsSwipeUpHandler,
@Nullable
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
- RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
+ RemoteAnimationTarget runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = runningTaskTarget.taskInfo;
final RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
@@ -1652,10 +1748,6 @@ public abstract class AbsSwipeUpHandler,
private void resumeLastTask() {
if (mRecentsAnimationController != null) {
mRecentsAnimationController.finish(false /* toRecents */, null);
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRecentsAnimation",
- /* extras= */ false,
- /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
}
doLogGesture(LAST_TASK, null);
reset();
@@ -1719,8 +1811,7 @@ public abstract class AbsSwipeUpHandler,
}
private void invalidateHandler() {
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get() || !mActivityInterface.isInLiveTileMode()
- || mGestureState.getEndTarget() != RECENTS) {
+ if (!mActivityInterface.isInLiveTileMode() || mGestureState.getEndTarget() != RECENTS) {
mInputConsumerProxy.destroy();
mTaskAnimationManager.setLiveTileCleanUpHandler(null);
}
@@ -1765,10 +1856,6 @@ public abstract class AbsSwipeUpHandler,
* continued quick switch gesture, which cancels the previous handler but doesn't invalidate it.
*/
private void resetLauncherListeners() {
- // Reset the callback for deferred activity launches
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivityInterface.setOnDeferredActivityLaunchCallback(null);
- }
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
@@ -1790,7 +1877,6 @@ public abstract class AbsSwipeUpHandler,
mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else {
final int runningTaskId = mGestureState.getRunningTaskId();
- final boolean refreshView = !ENABLE_QUICKSTEP_LIVE_TILE.get() /* refreshView */;
boolean finishTransitionPosted = false;
if (mRecentsAnimationController != null) {
// Update the screenshot of the task
@@ -1799,16 +1885,27 @@ public abstract class AbsSwipeUpHandler,
if (mRecentsAnimationController == null) return;
final ThumbnailData taskSnapshot =
mRecentsAnimationController.screenshotTask(runningTaskId);
+ // If split case, we should update all split tasks snapshot
+ if (mIsSwipeForSplit) {
+ int[] splitTaskIds = TopTaskTracker.INSTANCE.get(
+ mContext).getRunningSplitTaskIds();
+ for (int i = 0; i < splitTaskIds.length; i++) {
+ // Skip running one because done above.
+ if (splitTaskIds[i] == runningTaskId) continue;
+
+ mRecentsAnimationController.screenshotTask(splitTaskIds[i]);
+ }
+ }
MAIN_EXECUTOR.execute(() -> {
mTaskSnapshot = taskSnapshot;
- if (!updateThumbnail(runningTaskId, refreshView)) {
+ if (!updateThumbnail(runningTaskId, false /* refreshView */)) {
setScreenshotCapturedState();
}
});
});
return;
}
- finishTransitionPosted = updateThumbnail(runningTaskId, refreshView);
+ finishTransitionPosted = updateThumbnail(runningTaskId, false /* refreshView */);
}
if (!finishTransitionPosted) {
setScreenshotCapturedState();
@@ -1846,22 +1943,19 @@ public abstract class AbsSwipeUpHandler,
}
private void finishCurrentTransitionToRecents() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsView != null
+ && mActivityInterface.getDesktopVisibilityController() != null
+ && mActivityInterface.getDesktopVisibilityController().areFreeformTasksVisible()) {
+ mRecentsView.switchToScreenshot(() -> {
+ mRecentsView.finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ });
+ } else {
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
if (mRecentsAnimationController != null) {
mRecentsAnimationController.detachNavigationBarFromApp(true);
}
- } else if (!hasTargets() || mRecentsAnimationController == null) {
- // If there are no targets or the animation not started, then there is nothing to finish
- mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- } else {
- mRecentsAnimationController.finish(true /* toRecents */,
- () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRecentsAnimation",
- /* extras= */ true,
- /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
}
private void finishCurrentTransitionToHome() {
@@ -1873,10 +1967,10 @@ public abstract class AbsSwipeUpHandler,
finishRecentsControllerToHome(
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRecentsAnimation",
- /* extras= */ true,
- /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
+ if (mSwipePipToHomeReleaseCheck != null) {
+ mSwipePipToHomeReleaseCheck.setCanRelease(true);
+ mSwipePipToHomeReleaseCheck = null;
+ }
doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
}
@@ -1920,31 +2014,20 @@ public abstract class AbsSwipeUpHandler,
}
endLauncherTransitionController();
mRecentsView.onSwipeUpAnimationSuccess();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mTaskAnimationManager.setLiveTileCleanUpHandler(() -> {
- mRecentsView.cleanupRemoteTargets();
- mInputConsumerProxy.destroy();
- });
- mTaskAnimationManager.enableLiveTileRestartListener();
- }
+ mTaskAnimationManager.setLiveTileCleanUpHandler(() -> {
+ mRecentsView.cleanupRemoteTargets();
+ mInputConsumerProxy.destroy();
+ });
+ mTaskAnimationManager.enableLiveTileRestartListener();
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
reset();
}
- private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
+ private static boolean isNotInRecents(RemoteAnimationTarget app) {
return app.isNotInRecents
- || app.activityType == ACTIVITY_TYPE_HOME;
- }
-
- /**
- * To be called at the end of constructor of subclasses. This calls various methods which can
- * depend on proper class initialization.
- */
- protected void initAfterSubclassConstructor() {
- initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
- .getOrientationState().getLauncherDeviceProfile());
+ || app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
}
protected void performHapticFeedback() {
@@ -1989,6 +2072,9 @@ public abstract class AbsSwipeUpHandler,
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
+ if (!hasTaskPreviouslyAppeared) {
+ ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
+ }
nextTask.launchTask(success -> {
resultCallback.accept(success);
if (success) {
@@ -2058,19 +2144,59 @@ public abstract class AbsSwipeUpHandler,
}
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
if (mRecentsAnimationController != null) {
if (handleTaskAppeared(appearedTaskTargets)) {
- mRecentsAnimationController.finish(false /* toRecents */,
- null /* onFinishComplete */);
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRecentsAnimation",
- /* extras= */ false,
- /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
+ Optional taskTargetOptional =
+ Arrays.stream(appearedTaskTargets)
+ .filter(targetCompat ->
+ targetCompat.taskId == mGestureState.getLastStartedTaskId())
+ .findFirst();
+ if (!taskTargetOptional.isPresent()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+ RemoteAnimationTarget taskTarget = taskTargetOptional.get();
+ TaskView taskView = mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
+ if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+
+ ViewGroup splashView = mActivity.getDragLayer();
+
+ // When revealing the app with launcher splash screen, make the app visible
+ // and behind the splash view before the splash is animated away.
+ SurfaceTransactionApplier surfaceApplier =
+ new SurfaceTransactionApplier(splashView);
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ for (RemoteAnimationTarget target : appearedTaskTargets) {
+ transaction.forSurface(target.leash).setAlpha(1).setLayer(-1);
+ }
+ surfaceApplier.scheduleApply(transaction);
+
+ SplashScreenExitAnimationUtils.startAnimations(splashView, taskTarget.leash,
+ mSplashMainWindowShiftLength, new TransactionPool(), new Rect(),
+ SPLASH_ANIMATION_DURATION, SPLASH_FADE_OUT_DURATION,
+ /* iconStartAlpha= */ 0, /* brandingStartAlpha= */ 0,
+ SPLASH_APP_REVEAL_DELAY, SPLASH_APP_REVEAL_DURATION,
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishRecentsAnimationOnTasksAppeared();
+ }
+ });
}
}
}
+ private void finishRecentsAnimationOnTasksAppeared() {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false /* toRecents */, null /* onFinishComplete */);
+ }
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ }
+
/**
* @return The index of the TaskView in RecentsView whose taskId matches the task that will
* resume if we finish the controller.
@@ -2157,6 +2283,35 @@ public abstract class AbsSwipeUpHandler,
return scaleProgress;
}
+ /**
+ * Overrides the gesture displacement to keep the app window at the bottom of the screen while
+ * the transient taskbar is being swiped in.
+ *
+ * There is also a catch up period so that the window can start moving 1:1 with the swipe.
+ */
+ @Override
+ protected float overrideDisplacementForTransientTaskbar(float displacement) {
+ if (!mIsTransientTaskbar) {
+ return displacement;
+ }
+
+ if (mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen) {
+ return displacement;
+ }
+
+ if (displacement < mTaskbarAppWindowThreshold) {
+ return 0;
+ }
+
+ // "Catch up" with the displacement at mTaskbarCatchUpThreshold.
+ if (displacement < mTaskbarCatchUpThreshold) {
+ return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold,
+ mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL);
+ }
+
+ return displacement;
+ }
+
private void setDividerShown(boolean shown, boolean immediate) {
if (mDividerAnimator != null) {
mDividerAnimator.cancel();
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 226b173ecd..274b686ea4 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -30,6 +30,7 @@ import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
@@ -40,6 +41,7 @@ import android.graphics.Rect;
import android.os.Build;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import androidx.annotation.Nullable;
@@ -50,6 +52,7 @@ import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarUIController;
@@ -61,7 +64,6 @@ import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.HashMap;
import java.util.Optional;
@@ -118,9 +120,6 @@ public abstract class BaseActivityInterface callback);
@@ -140,6 +139,11 @@ public abstract class BaseActivityInterface thumbnailDatas,
@@ -474,33 +479,38 @@ public abstract class BaseActivityInterface launchCookies,
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
mAppCanEnterPip = appCanEnterPip;
if (appCanEnterPip) {
return new FallbackPipToHomeAnimationFactory();
@@ -154,7 +155,7 @@ public class FallbackSwipeHandler extends
private void startHomeIntent(
@Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory,
- @Nullable RemoteAnimationTargetCompat runningTaskTarget) {
+ @Nullable RemoteAnimationTarget runningTaskTarget) {
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
Intent intent = new Intent(mGestureState.getHomeIntent());
if (gestureContractAnimationFactory != null && runningTaskTarget != null) {
@@ -164,7 +165,7 @@ public class FallbackSwipeHandler extends
}
@Override
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
if (mActiveAnimationFactory != null
&& mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
mActiveAnimationFactory = null;
@@ -279,13 +280,13 @@ public class FallbackSwipeHandler extends
return mTargetRect;
}
- private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withAlpha(mRecentsAlpha.value);
+ private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ builder.setAlpha(mRecentsAlpha.value);
}
- private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
+ private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
}
@@ -304,12 +305,12 @@ public class FallbackSwipeHandler extends
}
}
- public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
- RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
- if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
+ public boolean handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
+ if (appearedTaskTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
RemoteAnimationTargets targets = new RemoteAnimationTargets(
- new RemoteAnimationTargetCompat[] {appearedTaskTarget},
- new RemoteAnimationTargetCompat[0], new RemoteAnimationTargetCompat[0],
+ new RemoteAnimationTarget[] {appearedTaskTarget},
+ new RemoteAnimationTarget[0], new RemoteAnimationTarget[0],
appearedTaskTarget.mode);
mHomeAlphaParams.setTargetSet(targets);
updateHomeAlpha();
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index bcd96878e3..31b78b3ec3 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -21,13 +21,13 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERV
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_LAST_TASK;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
+import android.view.RemoteAnimationTarget;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -37,7 +37,6 @@ import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -143,7 +142,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
private CachedTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
- private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private RemoteAnimationTarget mLastAppearedTaskTarget;
private Set mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
private RecentsAnimationController mRecentsAnimationController;
@@ -272,7 +271,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
/**
* Updates the last task that appeared during this gesture.
*/
- public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
+ public void updateLastAppearedTaskTarget(RemoteAnimationTarget lastAppearedTaskTarget) {
mLastAppearedTaskTarget = lastAppearedTaskTarget;
if (lastAppearedTaskTarget != null) {
mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
@@ -341,8 +340,6 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
break;
case LAST_TASK:
- ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_LAST_TASK);
- break;
case RECENTS:
default:
// No-Op
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
deleted file mode 100644
index 758c6e08ef..0000000000
--- a/quickstep/src/com/android/quickstep/KtR.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 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 com.android.launcher3.R;
-
-/**
- * Bridge class to allow using resources in Kotlin.
- *
- * TODO(b/204069723) Can't use resources directly in Kotlin
- */
-public class KtR {
- public static final class id {
- public static int menu_option_layout = R.id.menu_option_layout;
- }
-
- public static final class dimen {
- public static int task_menu_spacing = R.dimen.task_menu_spacing;
- public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
- public static int taskbar_ime_size = R.dimen.taskbar_ime_size;
- }
-
- public static final class layout {
- public static int task_menu_with_arrow = R.layout.task_menu_with_arrow;
- public static int task_view_menu_option = R.layout.task_view_menu_option;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 1127e2c8e7..9ff941671b 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -21,25 +21,27 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -52,7 +54,6 @@ import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -107,15 +108,6 @@ public final class LauncherActivityInterface extends
launcher.onAssistantVisibilityChanged(visibility);
}
- @Override
- public void onOneHandedModeStateChanged(boolean activated) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- launcher.onOneHandedStateChanged(activated);
- }
-
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer callback) {
@@ -129,8 +121,9 @@ public final class LauncherActivityInterface extends
// Animate the blur and wallpaper zoom
float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
float toDepthRatio = OVERVIEW.getDepth(activity);
- pa.addFloat(getDepthController(),
- new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+ pa.addFloat(getDepthController().stateDepth,
+ new LauncherAnimUtils.ClampedProperty<>(
+ MULTI_PROPERTY_VALUE, fromDepthRatio, toDepthRatio),
fromDepthRatio, toDepthRatio, LINEAR);
}
};
@@ -173,6 +166,16 @@ public final class LauncherActivityInterface extends
return launcher.getDepthController();
}
+ @Nullable
+ @Override
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ QuickstepLauncher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return null;
+ }
+ return launcher.getDesktopVisibilityController();
+ }
+
@Nullable
@Override
public LauncherTaskbarUIController getTaskbarController() {
@@ -203,8 +206,7 @@ public final class LauncherActivityInterface extends
private Launcher getVisibleLauncher() {
Launcher launcher = getCreatedActivity();
return (launcher != null) && launcher.isStarted()
- && ((ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())
- || launcher.hasBeenResumed()) ? launcher : null;
+ && (isInLiveTileMode() || launcher.hasBeenResumed()) ? launcher : null;
}
@Override
@@ -213,7 +215,7 @@ public final class LauncherActivityInterface extends
if (launcher == null) {
return false;
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode()) {
+ if (isInLiveTileMode()) {
RecentsView recentsView = getVisibleRecentsView();
if (recentsView == null) {
return false;
@@ -253,7 +255,7 @@ public final class LauncherActivityInterface extends
}
@Override
- public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+ public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
return homeBounds;
}
@@ -291,10 +293,6 @@ public final class LauncherActivityInterface extends
} else {
om.hideOverlay(150);
}
- LauncherTaskbarUIController taskbarController = getTaskbarController();
- if (taskbarController != null) {
- taskbarController.hideEdu();
- }
}
@Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 7a281dd2a7..27417516a4 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -36,6 +36,7 @@ import android.view.SurfaceControl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.window.BackEvent;
+import android.window.BackProgressAnimator;
import android.window.IOnBackInvokedCallback;
import com.android.launcher3.AbstractFloatingView;
@@ -45,8 +46,6 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
/**
* Controls the animation of swiping back and returning to launcher.
@@ -84,13 +83,14 @@ public class LauncherBackAnimationController {
private final Interpolator mCancelInterpolator;
private final PointF mInitialTouchPos = new PointF();
- private RemoteAnimationTargetCompat mBackTarget;
+ private RemoteAnimationTarget mBackTarget;
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mSpringAnimationInProgress = false;
private boolean mAnimatorSetInProgress = false;
private float mBackProgress = 0;
private boolean mBackInProgress = false;
private IOnBackInvokedCallback mBackCallback;
+ private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
public LauncherBackAnimationController(
QuickstepLauncher launcher,
@@ -119,30 +119,41 @@ public class LauncherBackAnimationController {
mBackCallback = new IOnBackInvokedCallback.Stub() {
@Override
public void onBackCancelled() {
- handler.post(() -> resetPositionAnimated());
+ handler.post(() -> {
+ resetPositionAnimated();
+ mProgressAnimator.reset();
+ });
}
@Override
public void onBackInvoked() {
- handler.post(() -> startTransition());
+ handler.post(() -> {
+ startTransition();
+ mProgressAnimator.reset();
+ });
}
@Override
public void onBackProgressed(BackEvent backEvent) {
- mBackProgress = backEvent.getProgress();
- // TODO: Update once the interpolation curve spec is finalized.
- mBackProgress =
- 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
- - mBackProgress);
- if (!mBackInProgress) {
- startBack(backEvent);
- } else {
- updateBackProgress(mBackProgress, backEvent);
- }
+ handler.post(() -> {
+ mProgressAnimator.onBackProgressed(backEvent);
+ });
}
@Override
- public void onBackStarted() { }
+ public void onBackStarted(BackEvent backEvent) {
+ handler.post(() -> {
+ startBack(backEvent);
+ mProgressAnimator.onBackStarted(backEvent, event -> {
+ mBackProgress = event.getProgress();
+ // TODO: Update once the interpolation curve spec is finalized.
+ mBackProgress =
+ 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
+ - mBackProgress);
+ updateBackProgress(mBackProgress, event);
+ });
+ });
+ }
};
SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback);
}
@@ -170,6 +181,7 @@ public class LauncherBackAnimationController {
if (mBackCallback != null) {
SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback(mBackCallback);
}
+ mProgressAnimator.reset();
mBackCallback = null;
}
@@ -183,33 +195,25 @@ public class LauncherBackAnimationController {
mTransaction.show(appTarget.leash).apply();
mTransaction.setAnimationTransaction();
- mBackTarget = new RemoteAnimationTargetCompat(appTarget);
+ mBackTarget = appTarget;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
// TODO(b/218916755): Offset start rectangle in multiwindow mode.
mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
+ mCurrentRect.set(mStartRect);
}
private void updateBackProgress(float progress, BackEvent event) {
- if (mBackTarget == null) {
+ if (!mBackInProgress || mBackTarget == null) {
return;
}
float screenWidth = mStartRect.width();
float screenHeight = mStartRect.height();
- float dX = Math.abs(event.getTouchX() - mInitialTouchPos.x);
- // The 'follow width' is the width of the window if it completely matches
- // the gesture displacement.
- float followWidth = screenWidth - dX;
- // The 'progress width' is the width of the window if it strictly linearly interpolates
- // to minimum scale base on progress.
- float progressWidth = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
- // The final width is derived from interpolating between the follow with and progress width
- // using gesture progress.
- float width = Utilities.mapRange(progress, followWidth, progressWidth);
+ float width = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
float height = screenHeight / screenWidth * width;
float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight;
// Base the window movement in the Y axis on the touch movement in the Y axis.
- float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY;
+ float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY * progress;
// Move the window along the Y axis.
float top = (screenHeight - height) * 0.5f + deltaY;
// Move the window along the X axis.
@@ -242,20 +246,17 @@ public class LauncherBackAnimationController {
/** Transform the target window to match the target rect. */
private void applyTransform(RectF targetRect, float cornerRadius) {
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
- new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
final float scale = targetRect.width() / mStartRect.width();
mTransformMatrix.reset();
mTransformMatrix.setScale(scale, scale);
mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
- builder.withMatrix(mTransformMatrix)
- .withWindowCrop(mStartRect)
- .withCornerRadius(cornerRadius);
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(mTransaction);
+ if (mBackTarget.leash.isValid()) {
+ mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]);
+ mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
+ mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
}
+
mTransaction.apply();
}
@@ -284,8 +285,8 @@ public class LauncherBackAnimationController {
mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
Pair pair =
mQuickstepTransitionManager.createWallpaperOpenAnimations(
- new RemoteAnimationTargetCompat[]{mBackTarget},
- new RemoteAnimationTargetCompat[]{},
+ new RemoteAnimationTarget[]{mBackTarget},
+ new RemoteAnimationTarget[0],
false /* fromUnlock */,
mCurrentRect,
cornerRadius);
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 36ca993f85..bb781c82b7 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -30,6 +30,7 @@ import android.graphics.RectF;
import android.os.IBinder;
import android.os.UserHandle;
import android.util.Size;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import androidx.annotation.NonNull;
@@ -49,7 +50,6 @@ import com.android.quickstep.views.FloatingWidgetView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -70,7 +70,7 @@ public class LauncherSwipeHandlerV2 extends
@Override
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList launchCookies,
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
if (mActivity == null) {
mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
isPresent -> mRecentsView.startHome());
@@ -84,7 +84,8 @@ public class LauncherSwipeHandlerV2 extends
final View workspaceView = findWorkspaceView(launchCookies,
mRecentsView.getRunningTaskView());
- boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
+ boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow()
+ && workspaceView.getHeight() > 0;
mActivity.getRootView().setForceHideBackArrow(true);
if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -143,7 +144,7 @@ public class LauncherSwipeHandlerV2 extends
private HomeAnimationFactory createWidgetHomeAnimationFactory(
LauncherAppWidgetHostView hostView, boolean isTargetTranslucent,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
RectF backgroundLocation = new RectF();
Rect crop = new Rect();
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 875b72cb34..5a09e021b2 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -116,7 +116,7 @@ public class OverviewCommandHelper {
*/
@BinderThread
public void addCommand(int type) {
- if (mPendingCommands.size() > MAX_QUEUE_SIZE) {
+ if (mPendingCommands.size() >= MAX_QUEUE_SIZE) {
return;
}
CommandInfo cmd = new CommandInfo(type);
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 9e3173c1f0..83f2a0a114 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -34,6 +34,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.util.Log;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
@@ -54,6 +55,8 @@ import java.util.function.Consumer;
* and provide callers the relevant classes.
*/
public final class OverviewComponentObserver {
+ private static final String TAG = "OverviewComponentObserver";
+
private final BroadcastReceiver mUserPreferenceChangeReceiver =
new SimpleBroadcastReceiver(this::updateOverviewTargets);
private final BroadcastReceiver mOtherHomeAppUpdateReceiver =
@@ -115,11 +118,6 @@ public final class OverviewComponentObserver {
if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
updateOverviewTargets();
}
-
- // Notify ALL_APPS touch controller when one handed mode state activated or deactivated
- if (mDeviceState.isOneHandedModeEnabled()) {
- mActivityInterface.onOneHandedModeStateChanged(mDeviceState.isOneHandedModeActive());
- }
}
private void updateOverviewTargets(Intent unused) {
@@ -151,7 +149,12 @@ public final class OverviewComponentObserver {
}
}
- if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
+ // TODO(b/258022658): Remove temporary logging.
+ Log.i(TAG, "updateOverviewTargets: mIsHomeDisabled=" + mIsHomeDisabled
+ + ", isDefaultHomeNull=" + (defaultHome == null)
+ + ", mIsDefaultHome=" + mIsDefaultHome);
+
+ if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
mActivityInterface = LauncherActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = true;
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index b7cdecd115..54e4a0d3f1 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,5 +1,6 @@
package com.android.quickstep;
+import static com.android.launcher3.testing.shared.TestProtocol.NPE_TRANSIENT_TASKBAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.Activity;
@@ -7,19 +8,23 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
+import android.util.Log;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.TISBindHelper;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import java.util.function.Function;
public class QuickstepTestInformationHandler extends TestInformationHandler {
@@ -112,6 +117,33 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
return response;
}
+
+ case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: {
+ return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
+ tisBinder.getTaskbarManager()
+ .getCurrentActivityContext()
+ .getTaskbarAllAppsTopPadding());
+ }
+
+ case TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT:
+ runOnTISBinder(tisBinder -> {
+ enableBlockingTimeout(tisBinder, true);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT:
+ runOnTISBinder(tisBinder -> {
+ enableBlockingTimeout(tisBinder, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_ENABLE_TRANSIENT_TASKBAR:
+ enableTransientTaskbar(true);
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_TRANSIENT_TASKBAR:
+ enableTransientTaskbar(false);
+ return response;
}
return super.call(method, arg, extras);
@@ -141,6 +173,23 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
enable);
}
+ private void enableBlockingTimeout(
+ TouchInteractionService.TISBinder tisBinder, boolean enable) {
+ TaskbarActivityContext context = tisBinder.getTaskbarManager().getCurrentActivityContext();
+ if (context == null) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(NPE_TRANSIENT_TASKBAR, "enableBlockingTimeout: enable=" + enable,
+ new Exception());
+ }
+ } else {
+ context.enableBlockingTimeoutDuringTests(enable);
+ }
+ }
+
+ private void enableTransientTaskbar(boolean enable) {
+ DisplayController.INSTANCE.get(mContext).enableTransientTaskbarForTests(enable);
+ }
+
/**
* Runs the given command on the UI thread, after ensuring we are connected to
* TouchInteractionService.
@@ -159,4 +208,16 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
throw new RuntimeException(e);
}
}
+
+ private Bundle getTISBinderUIProperty(
+ BundleSetter bundleSetter, Function provider) {
+ Bundle response = new Bundle();
+
+ runOnTISBinder(tisBinder -> bundleSetter.set(
+ response,
+ TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ provider.apply(tisBinder)));
+
+ return response;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 6b616b1b07..b33ceca838 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -17,10 +17,14 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
+import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.KeyguardManager;
+import android.app.TaskInfo;
+import android.content.ComponentName;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
@@ -30,6 +34,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.recents.IRecentTasksListener;
@@ -40,6 +45,8 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Manages the recent task list from the system, caching it as necessary.
@@ -124,14 +131,18 @@ public class RecentTasksList {
* @return The change id of the current task list
*/
public synchronized int getTasks(boolean loadKeysOnly,
- Consumer> callback) {
+ Consumer> callback, Predicate filter) {
final int requestLoadId = mChangeId;
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
// The list is up to date, send the callback on the next frame,
// so that requestID can be returned first.
if (callback != null) {
// Copy synchronously as the changeId might change by next frame
- ArrayList result = copyOf(mResultsUi);
+ // and filter GroupTasks
+ ArrayList result = mResultsUi.stream().filter(filter)
+ .map(GroupTask::copy)
+ .collect(Collectors.toCollection(ArrayList::new));
+
mMainThreadExecutor.post(() -> {
callback.accept(result);
});
@@ -151,7 +162,11 @@ public class RecentTasksList {
mLoadingTasksInBackground = false;
mResultsUi = loadResult;
if (callback != null) {
- ArrayList result = copyOf(mResultsUi);
+ // filter the tasks if needed before passing them into the callback
+ ArrayList result = mResultsUi.stream().filter(filter)
+ .map(GroupTask::copy)
+ .collect(Collectors.toCollection(ArrayList::new));
+
callback.accept(result);
}
});
@@ -253,8 +268,9 @@ public class RecentTasksList {
};
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
+
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- if (rawTask.getType() == GroupedRecentTaskInfo.TYPE_FREEFORM) {
+ if (DESKTOP_MODE_SUPPORTED && rawTask.getType() == TYPE_FREEFORM) {
GroupTask desktopTask = createDesktopTask(rawTask);
allTasks.add(desktopTask);
continue;
@@ -284,14 +300,18 @@ public class RecentTasksList {
return allTasks;
}
- private GroupTask createDesktopTask(GroupedRecentTaskInfo taskInfo) {
- // TODO(b/244348395): create a subclass of GroupTask for desktop tile
- // We need a single task information as the primary task. Use the first task
- Task.TaskKey key = new Task.TaskKey(taskInfo.getTaskInfo1());
- Task task = new Task(key);
- task.desktopTile = true;
- task.topActivity = key.sourceComponent;
- return new GroupTask(task, null, null);
+ private DesktopTask createDesktopTask(GroupedRecentTaskInfo recentTaskInfo) {
+ ArrayList tasks = new ArrayList<>(recentTaskInfo.getTaskInfoList().size());
+ for (ActivityManager.RecentTaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
+ Task.TaskKey key = new Task.TaskKey(taskInfo);
+ Task task = Task.from(key, taskInfo, false);
+ task.setLastSnapshotData(taskInfo);
+ task.positionInParent = taskInfo.positionInParent;
+ task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
+ // TODO(b/244348395): tasks should be sorted from oldest to most recently used
+ tasks.add(task);
+ }
+ return new DesktopTask(tasks);
}
private SplitConfigurationOptions.SplitBounds convertSplitBounds(
@@ -306,7 +326,7 @@ public class RecentTasksList {
private ArrayList copyOf(ArrayList tasks) {
ArrayList newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
- newTasks.add(new GroupTask(tasks.get(i)));
+ newTasks.add(tasks.get(i).copy());
}
return newTasks;
}
@@ -316,8 +336,14 @@ public class RecentTasksList {
writer.println(prefix + " mChangeId=" + mChangeId);
writer.println(prefix + " mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks=");
for (GroupTask task : mResultsUi) {
- writer.println(prefix + " t1=" + task.task1.key.id
- + " t2=" + (task.hasMultipleTasks() ? task.task2.key.id : "-1"));
+ Task task1 = task.task1;
+ Task task2 = task.task2;
+ ComponentName cn1 = task1.getTopComponent();
+ ComponentName cn2 = task2 != null ? task2.getTopComponent() : null;
+ writer.println(prefix + " t1: (id=" + task1.key.id
+ + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
+ + " t2: (id=" + (task2 != null ? task2.key.id : "-1")
+ + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
}
writer.println(prefix + " ]");
int currentUserId = Process.myUserHandle().getIdentifier();
@@ -325,8 +351,14 @@ public class RecentTasksList {
mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
writer.println(prefix + " rawTasks=[");
for (GroupedRecentTaskInfo task : rawTasks) {
- writer.println(prefix + " t1=" + task.getTaskInfo1().taskId
- + " t2=" + (task.getTaskInfo2() != null ? task.getTaskInfo2().taskId : "-1"));
+ TaskInfo taskInfo1 = task.getTaskInfo1();
+ TaskInfo taskInfo2 = task.getTaskInfo2();
+ ComponentName cn1 = taskInfo1.topActivity;
+ ComponentName cn2 = taskInfo2 != null ? taskInfo2.topActivity : null;
+ writer.println(prefix + " t1: (id=" + taskInfo1.taskId
+ + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
+ + " t2: (id=" + (taskInfo2 != null ? taskInfo2.taskId : "-1")
+ + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
}
writer.println(prefix + " ]");
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index c879494c7d..dc405ff8ba 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,17 +15,17 @@
*/
package com.android.quickstep;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,8 +37,11 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl.Transaction;
import android.view.View;
+import android.window.RemoteTransition;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -78,8 +81,6 @@ import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -239,9 +240,9 @@ public final class RecentsActivity extends StatefulActivity {
mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
@Override
- public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
wallpaperTargets, nonAppTargets);
@@ -259,13 +260,11 @@ public final class RecentsActivity extends StatefulActivity {
final LauncherAnimationRunner wrapper = new LauncherAnimationRunner(
mUiHandler, mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */);
- RemoteAnimationAdapterCompat adapterCompat = new RemoteAnimationAdapterCompat(
- wrapper, RECENTS_LAUNCH_DURATION,
- RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
- - STATUS_BAR_TRANSITION_PRE_DELAY, getIApplicationThread());
final ActivityOptions options = ActivityOptions.makeRemoteAnimation(
- adapterCompat.getWrapped(),
- adapterCompat.getRemoteTransition().getTransition());
+ new RemoteAnimationAdapter(wrapper, RECENTS_LAUNCH_DURATION,
+ RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
+ - STATUS_BAR_TRANSITION_PRE_DELAY),
+ new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread()));
final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(options,
onEndCallback);
activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
@@ -280,9 +279,9 @@ public final class RecentsActivity extends StatefulActivity {
* Composes the animations for a launch from the recents list if possible.
*/
private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets) {
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets) {
AnimatorSet target = new AnimatorSet();
boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
@@ -393,40 +392,33 @@ public final class RecentsActivity extends StatefulActivity {
}
public void startHome() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = getOverviewPanel();
- recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
- this::startHomeInternal));
- } else {
- startHomeInternal();
- }
+ RecentsView recentsView = getOverviewPanel();
+ recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
+ this::startHomeInternal));
}
private void startHomeInternal() {
LauncherAnimationRunner runner = new LauncherAnimationRunner(
getMainThreadHandler(), mAnimationToHomeFactory, true);
- RemoteAnimationAdapterCompat adapterCompat =
- new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
- getIApplicationThread());
ActivityOptions options = ActivityOptions.makeRemoteAnimation(
- adapterCompat.getWrapped(),
- adapterCompat.getRemoteTransition().getTransition());
+ new RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0),
+ new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread()));
startHomeIntentSafely(this, options.toBundle());
}
private final RemoteAnimationFactory mAnimationToHomeFactory =
new RemoteAnimationFactory() {
@Override
- public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
AnimatorPlaybackController controller = getStateManager()
.createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
controller.dispatchOnStart();
RemoteAnimationTargets targets = new RemoteAnimationTargets(
appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
- for (RemoteAnimationTargetCompat app : targets.apps) {
+ for (RemoteAnimationTarget app : targets.apps) {
new Transaction().setAlpha(app.leash, 1).apply();
}
AnimatorSet anim = new AnimatorSet();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index b233521ff2..b82ff03b34 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -33,9 +33,7 @@ import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
@@ -88,17 +86,17 @@ public class RecentsAnimationCallbacks implements
@BinderThread
@Deprecated
public final void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] appTargets, Rect homeContentInsets,
+ RemoteAnimationTarget[] appTargets, Rect homeContentInsets,
Rect minimizedHomeBounds) {
- onAnimationStart(controller, appTargets, new RemoteAnimationTargetCompat[0],
+ onAnimationStart(controller, appTargets, new RemoteAnimationTarget[0],
homeContentInsets, minimizedHomeBounds);
}
// Called only in R+ platform
@BinderThread
public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
mController = new RecentsAnimationController(animationController,
mAllowMinimizeSplitScreen, this::onAnimationFinished);
@@ -107,12 +105,13 @@ public class RecentsAnimationCallbacks implements
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
mController::finishAnimationToApp);
} else {
- final RemoteAnimationTarget[] nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(
- Arrays.stream(appTargets).map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new));
+ RemoteAnimationTarget[] nonAppTargets =
+ mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
+ }
final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
- wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
- homeContentInsets, minimizedHomeBounds);
+ wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureLog.INSTANCE.addLog(
@@ -131,7 +130,7 @@ public class RecentsAnimationCallbacks implements
public final void onAnimationCanceled(HashMap thumbnailDatas) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "onRecentsAnimationCancelled",
+ /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled",
/* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationCanceled(thumbnailDatas);
@@ -141,9 +140,9 @@ public class RecentsAnimationCallbacks implements
@BinderThread
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
+ public void onTasksAppeared(RemoteAnimationTarget[] apps) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
- ActiveGestureLog.INSTANCE.addLog("onTasksAppeared",
+ ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared",
ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
for (RecentsAnimationListener listener : getListeners()) {
listener.onTasksAppeared(apps);
@@ -165,6 +164,8 @@ public class RecentsAnimationCallbacks implements
private final void onAnimationFinished(RecentsAnimationController controller) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationFinished");
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationFinished(controller);
}
@@ -197,7 +198,7 @@ public class RecentsAnimationCallbacks implements
/**
* Callback made when a task started from the recents is ready for an app transition.
*/
- default void onTasksAppeared(@NonNull RemoteAnimationTargetCompat[] appearedTaskTarget) {}
+ default void onTasksAppeared(@NonNull RemoteAnimationTarget[] appearedTaskTarget) {}
/**
* @return whether this will call onFinished or not (onFinished should only be called once).
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 542c0d4b21..4adfae5ee8 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,11 +18,13 @@ package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.window.PictureInPictureSurfaceTransaction;
@@ -37,7 +39,6 @@ import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
@@ -114,7 +115,7 @@ public class RecentsAnimationController {
* {@link RecentsAnimationCallbacks#onTasksAppeared}}.
*/
@UiThread
- public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
+ public void removeTaskTarget(@NonNull RemoteAnimationTarget target) {
UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
}
@@ -155,6 +156,10 @@ public class RecentsAnimationController {
mPendingFinishCallbacks.add(callback);
return;
}
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "finishRecentsAnimation",
+ /* extras= */ toRecents,
+ /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
// Finish not yet requested
mFinishRequested = true;
@@ -165,6 +170,8 @@ public class RecentsAnimationController {
mController.finish(toRecents, sendUserLeaveHint);
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
MAIN_EXECUTOR.execute(mPendingFinishCallbacks::executeAllAndDestroy);
});
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index e87fdad933..9e25555eec 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -46,10 +46,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_S
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.app.ActivityTaskManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.graphics.Region;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
@@ -63,12 +60,12 @@ import android.view.MotionEvent;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
-import com.android.launcher3.Utilities;
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.NavigationMode;
import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -114,15 +111,12 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
private boolean mIsUserUnlocked;
private final ArrayList mUserUnlockedActions = new ArrayList<>();
- private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- mIsUserUnlocked = true;
- notifyUserUnlocked();
- }
+ private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> {
+ if (ACTION_USER_UNLOCKED.equals(i.getAction())) {
+ mIsUserUnlocked = true;
+ notifyUserUnlocked();
}
- };
+ });
private int mGestureBlockingTaskId = -1;
private @NonNull Region mExclusionRegion = new Region();
@@ -153,10 +147,9 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
mIsUserUnlocked = context.getSystemService(UserManager.class)
.isUserUnlocked(Process.myUserHandle());
if (!mIsUserUnlocked) {
- mContext.registerReceiver(mUserUnlockedReceiver,
- new IntentFilter(ACTION_USER_UNLOCKED));
+ mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
}
- runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver));
+ runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@@ -347,7 +340,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
action.run();
}
mUserUnlockedActions.clear();
- Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index b6d9016727..388e1256d8 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -15,11 +15,10 @@
*/
package com.android.quickstep;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import android.graphics.Rect;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
/**
* Extension of {@link RemoteAnimationTargets} with additional information about swipe
@@ -30,8 +29,8 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets {
public final Rect homeContentInsets;
public final Rect minimizedHomeBounds;
- public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public RecentsAnimationTargets(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
Rect homeContentInsets, Rect minimizedHomeBounds) {
super(apps, wallpapers, nonApps, MODE_CLOSING);
this.homeContentInsets = homeContentInsets;
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
new file mode 100644
index 0000000000..ff6951d33f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import androidx.annotation.Nullable;
+
+import com.android.quickstep.util.GroupTask;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+/**
+ * Keeps track of the state of {@code RecentsView}.
+ *
+ * More specifically, used for keeping track of the state of filters applied on tasks
+ * in {@code RecentsView} for multi-instance management.
+ */
+public class RecentsFilterState {
+ // the minimum number of tasks per package present to allow filtering
+ public static final int MIN_FILTERING_TASK_COUNT = 2;
+
+ // default filter that returns true for any input
+ public static final Predicate DEFAULT_FILTER = (groupTask -> true);
+
+ // the package name to filter recent tasks by
+ @Nullable
+ private String mPackageNameToFilter = null;
+
+ // the callback that gets executed upon filter change
+ @Nullable
+ private Runnable mOnFilterUpdatedListener = null;
+
+ // map maintaining the count for each unique base activity package name currently in the recents
+ @Nullable
+ private Map mInstanceCountMap;
+
+ /**
+ * Returns {@code true} if {@code RecentsView} filters tasks by some package name.
+ */
+ public boolean isFiltered() {
+ return mPackageNameToFilter != null;
+ }
+
+ /**
+ * Returns the package name that tasks are filtered by.
+ */
+ @Nullable
+ public String getPackageNameToFilter() {
+ return mPackageNameToFilter;
+ }
+
+
+ /**
+ * Sets a listener on any changes to the filter.
+ *
+ * @param callback listener to be executed upon filter updates
+ */
+ public void setOnFilterUpdatedListener(@Nullable Runnable callback) {
+ mOnFilterUpdatedListener = callback;
+ }
+
+ /**
+ * Updates the filter such that tasks are filtered by a certain package name.
+ *
+ * @param packageName package name of the base activity to filter tasks by;
+ * if null, filter is turned off
+ */
+ public void setFilterBy(@Nullable String packageName) {
+ if (Objects.equals(packageName, mPackageNameToFilter)) {
+ return;
+ }
+
+ mPackageNameToFilter = packageName;
+
+ if (mOnFilterUpdatedListener != null) {
+ mOnFilterUpdatedListener.run();
+ }
+ }
+
+ /**
+ * Updates the map of package names to their count in the most recent list of tasks.
+ *
+ * @param groupTaskList the list of tasks that map update is be based on
+ */
+ public void updateInstanceCountMap(List groupTaskList) {
+ mInstanceCountMap = getInstanceCountMap(groupTaskList);
+ }
+
+ /**
+ * Returns the map of package names to their count in the most recent list of tasks.
+ */
+ @Nullable
+ public Map getInstanceCountMap() {
+ return mInstanceCountMap;
+ }
+
+ /**
+ * Returns a predicate for filtering out GroupTasks by package name.
+ *
+ * @param packageName package name to filter GroupTasks by
+ * if null, Predicate always returns true.
+ */
+ public static Predicate getFilter(@Nullable String packageName) {
+ if (packageName == null) {
+ return DEFAULT_FILTER;
+ }
+
+ return (groupTask) -> (groupTask.task2 != null
+ && groupTask.task2.key.getPackageName().equals(packageName))
+ || groupTask.task1.key.getPackageName().equals(packageName);
+ }
+
+ /**
+ * Returns a map of package names to their frequencies in a list of GroupTasks.
+ *
+ * @param groupTasks the list to go through to create the map
+ */
+ public static Map getInstanceCountMap(List groupTasks) {
+ Map instanceCountMap = new HashMap<>();
+
+ for (GroupTask groupTask : groupTasks) {
+ final String firstTaskPkgName = groupTask.task1.key.getPackageName();
+ final String secondTaskPkgName =
+ groupTask.task2 == null ? null : groupTask.task2.key.getPackageName();
+
+ // increment the instance count for the first task's base activity package name
+ incrementOrAddIfNotExists(instanceCountMap, firstTaskPkgName);
+
+ // check if second task is non existent
+ if (secondTaskPkgName != null) {
+ // increment the instance count for the second task's base activity package name
+ incrementOrAddIfNotExists(instanceCountMap, secondTaskPkgName);
+ }
+ }
+
+ return instanceCountMap;
+ }
+
+ /**
+ * Returns true if tasks of provided package name should show filter UI.
+ *
+ * @param taskPackageName package name of the task in question
+ */
+ public boolean shouldShowFilterUI(String taskPackageName) {
+ // number of occurrences in recents overview with the package name of this task
+ int instanceCount = getInstanceCountMap().get(taskPackageName);
+
+ // if the number of occurrences isn't enough make sure tasks can't be filtered by
+ // the package name of this task
+ return !(isFiltered() || instanceCount < MIN_FILTERING_TASK_COUNT);
+ }
+
+ private static void incrementOrAddIfNotExists(Map map, String pkgName) {
+ if (!map.containsKey(pkgName)) {
+ map.put(pkgName, 0);
+ }
+ map.put(pkgName, map.get(pkgName) + 1);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 305347414c..913f08f41d 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -50,6 +50,7 @@ import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Singleton class to load and manage recents model.
@@ -96,14 +97,30 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
}
/**
- * Fetches the list of recent tasks.
+ * Fetches the list of recent tasks. Tasks are ordered by recency, with the latest active tasks
+ * at the end of the list.
*
* @param callback The callback to receive the task plan once its complete or null. This is
* always called on the UI thread.
* @return the request id associated with this call.
*/
public int getTasks(Consumer> callback) {
- return mTaskList.getTasks(false /* loadKeysOnly */, callback);
+ return mTaskList.getTasks(false /* loadKeysOnly */, callback,
+ RecentsFilterState.DEFAULT_FILTER);
+ }
+
+
+ /**
+ * Fetches the list of recent tasks, based on a filter
+ *
+ * @param callback The callback to receive the task plan once its complete or null. This is
+ * always called on the UI thread.
+ * @param filter Returns true if a GroupTask should be included into the list passed into
+ * callback.
+ * @return the request id associated with this call.
+ */
+ public int getTasks(Consumer> callback, Predicate filter) {
+ return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
}
/**
@@ -125,8 +142,9 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
* Checks if a task has been removed or not.
*
* @param callback Receives true if task is removed, false otherwise
+ * @param filter Returns true if GroupTask should be in the list of considerations
*/
- public void isTaskRemoved(int taskId, Consumer callback) {
+ public void isTaskRemoved(int taskId, Consumer callback, Predicate filter) {
mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> {
for (GroupTask group : taskGroups) {
if (group.containsTask(taskId)) {
@@ -135,7 +153,7 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
}
}
callback.accept(true);
- });
+ }, filter);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 1bd808d13d..80aaad0dd9 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -15,9 +15,11 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -29,41 +31,40 @@ public class RemoteAnimationTargets {
private final CopyOnWriteArrayList mReleaseChecks = new CopyOnWriteArrayList<>();
- public final RemoteAnimationTargetCompat[] unfilteredApps;
- public final RemoteAnimationTargetCompat[] apps;
- public final RemoteAnimationTargetCompat[] wallpapers;
- public final RemoteAnimationTargetCompat[] nonApps;
+ public final RemoteAnimationTarget[] unfilteredApps;
+ public final RemoteAnimationTarget[] apps;
+ public final RemoteAnimationTarget[] wallpapers;
+ public final RemoteAnimationTarget[] nonApps;
public final int targetMode;
public final boolean hasRecents;
private boolean mReleased = false;
- public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public RemoteAnimationTargets(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
int targetMode) {
- ArrayList filteredApps = new ArrayList<>();
+ ArrayList filteredApps = new ArrayList<>();
boolean hasRecents = false;
if (apps != null) {
- for (RemoteAnimationTargetCompat target : apps) {
+ for (RemoteAnimationTarget target : apps) {
if (target.mode == targetMode) {
filteredApps.add(target);
}
- hasRecents |= target.activityType ==
- RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
+ hasRecents |= target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS;
}
}
this.unfilteredApps = apps;
- this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
+ this.apps = filteredApps.toArray(new RemoteAnimationTarget[filteredApps.size()]);
this.wallpapers = wallpapers;
this.targetMode = targetMode;
this.hasRecents = hasRecents;
this.nonApps = nonApps;
}
- public RemoteAnimationTargetCompat findTask(int taskId) {
- for (RemoteAnimationTargetCompat target : apps) {
+ public RemoteAnimationTarget findTask(int taskId) {
+ for (RemoteAnimationTarget target : apps) {
if (target.taskId == taskId) {
return target;
}
@@ -74,12 +75,12 @@ public class RemoteAnimationTargets {
/**
* Gets the navigation bar remote animation target if exists.
*/
- public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+ public RemoteAnimationTarget getNavBarRemoteAnimationTarget() {
return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
}
- public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
- for (RemoteAnimationTargetCompat target : nonApps) {
+ public RemoteAnimationTarget getNonAppTargetOfType(int type) {
+ for (RemoteAnimationTarget target : nonApps) {
if (target.windowType == type) {
return target;
}
@@ -88,19 +89,19 @@ public class RemoteAnimationTargets {
}
/** Returns the first opening app target. */
- public RemoteAnimationTargetCompat getFirstAppTarget() {
+ public RemoteAnimationTarget getFirstAppTarget() {
return apps.length > 0 ? apps[0] : null;
}
/** Returns the task id of the first opening app target, or -1 if none is found. */
public int getFirstAppTargetTaskId() {
- RemoteAnimationTargetCompat target = getFirstAppTarget();
+ RemoteAnimationTarget target = getFirstAppTarget();
return target == null ? -1 : target.taskId;
}
public boolean isAnimatingHome() {
- for (RemoteAnimationTargetCompat target : unfilteredApps) {
- if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ for (RemoteAnimationTarget target : unfilteredApps) {
+ if (target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
return true;
}
}
@@ -123,15 +124,19 @@ public class RemoteAnimationTargets {
}
mReleaseChecks.clear();
mReleased = true;
+ release(unfilteredApps);
+ release(wallpapers);
+ release(nonApps);
+ }
- for (RemoteAnimationTargetCompat target : unfilteredApps) {
- target.release();
- }
- for (RemoteAnimationTargetCompat target : wallpapers) {
- target.release();
- }
- for (RemoteAnimationTargetCompat target : nonApps) {
- target.release();
+ private static void release(RemoteAnimationTarget[] targets) {
+ for (RemoteAnimationTarget target : targets) {
+ if (target.leash != null) {
+ target.leash.release();
+ }
+ if (target.startLeash != null) {
+ target.startLeash.release();
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 7183c49ca2..4c41bef26a 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,6 +17,8 @@
package com.android.quickstep;
import android.content.Context;
+import android.graphics.Rect;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -24,7 +26,6 @@ import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -75,7 +76,7 @@ public class RemoteTargetGluer {
*/
public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
- RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i];
+ RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
mRemoteTargetHandles[i].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, null));
mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
@@ -100,8 +101,8 @@ public class RemoteTargetGluer {
*/
public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
int[] splitIds) {
- RemoteAnimationTargetCompat topLeftTarget; // only one set if single/fullscreen task
- RemoteAnimationTargetCompat bottomRightTarget;
+ RemoteAnimationTarget topLeftTarget; // only one set if single/fullscreen task
+ RemoteAnimationTarget bottomRightTarget;
if (mRemoteTargetHandles.length == 1) {
// If we're not in split screen, the splitIds count doesn't really matter since we
// should always hit this case.
@@ -119,8 +120,8 @@ public class RemoteTargetGluer {
// remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
// vice versa
mSplitBounds = new SplitBounds(
- topLeftTarget.startScreenSpaceBounds,
- bottomRightTarget.startScreenSpaceBounds, splitIds[0], splitIds[1]);
+ getStartBounds(topLeftTarget),
+ getStartBounds(bottomRightTarget), splitIds[0], splitIds[1]);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
@@ -134,6 +135,10 @@ public class RemoteTargetGluer {
return mRemoteTargetHandles;
}
+ private Rect getStartBounds(RemoteAnimationTarget target) {
+ return target.startBounds == null ? target.screenSpaceBounds : target.startBounds;
+ }
+
/**
* Ensures that we aren't excluding ancillary targets such as home/recents
*
@@ -144,11 +149,10 @@ public class RemoteTargetGluer {
*/
private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
RemoteAnimationTargets targets,
- RemoteAnimationTargetCompat targetToExclude) {
- ArrayList targetsWithoutExcluded =
- new ArrayList();
+ RemoteAnimationTarget targetToExclude) {
+ ArrayList targetsWithoutExcluded = new ArrayList<>();
- for (RemoteAnimationTargetCompat targetCompat : targets.unfilteredApps) {
+ for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
if (targetCompat == targetToExclude) {
continue;
}
@@ -162,9 +166,8 @@ public class RemoteTargetGluer {
targetsWithoutExcluded.add(targetCompat);
}
- final RemoteAnimationTargetCompat[] filteredApps =
- targetsWithoutExcluded.toArray(
- new RemoteAnimationTargetCompat[targetsWithoutExcluded.size()]);
+ final RemoteAnimationTarget[] filteredApps = targetsWithoutExcluded.toArray(
+ new RemoteAnimationTarget[targetsWithoutExcluded.size()]);
return new RemoteAnimationTargets(
filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode);
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index baeb514e18..291f8354d2 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -24,12 +24,14 @@ import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -37,11 +39,10 @@ import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -65,6 +66,7 @@ public abstract class SwipeUpAnimationLogic implements
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
// visible.
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+ protected float mCurrentDisplacement;
// The distance needed to drag to reach the task size in recents.
protected int mTransitionDragLength;
@@ -115,7 +117,9 @@ public abstract class SwipeUpAnimationLogic implements
@UiThread
public void updateDisplacement(float displacement) {
// We are moving in the negative x/y direction
- displacement = -displacement;
+ displacement = overrideDisplacementForTransientTaskbar(-displacement);
+ mCurrentDisplacement = displacement;
+
float shift;
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
shift = mDragLengthFactor;
@@ -127,6 +131,17 @@ public abstract class SwipeUpAnimationLogic implements
mCurrentShift.updateValue(shift);
}
+ /**
+ * When Transient Taskbar is enabled, subclasses can override the displacement to keep the app
+ * window at the bottom of the screen while taskbar is being swiped in.
+ * @param displacement The distance the user has swiped up from the bottom of the screen. This
+ * value will be positive unless the user swipe downwards.
+ * @return the overridden displacement.
+ */
+ protected float overrideDisplacementForTransientTaskbar(float displacement) {
+ return displacement;
+ }
+
/**
* Called when the value of {@link #mCurrentShift} changes
*/
@@ -335,11 +350,11 @@ public abstract class SwipeUpAnimationLogic implements
}
@Override
- public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mCropRect)
- .withCornerRadius(params.getCornerRadius());
+ public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app,
+ TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mCropRect)
+ .setCornerRadius(params.getCornerRadius());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 3b2df315b1..bb973342d5 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -44,6 +44,8 @@ import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.IOnBackInvokedCallback;
+import android.window.RemoteTransition;
+import android.window.TransitionFilter;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -53,13 +55,11 @@ import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -74,6 +74,7 @@ import com.android.wm.shell.util.GroupedRecentTaskInfo;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashMap;
/**
* Holds the reference to SystemUI.
@@ -90,7 +91,6 @@ public class SystemUiProxy implements ISystemUiProxy {
private IPip mPip;
private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
private ISplitScreen mSplitScreen;
- private IFloatingTasks mFloatingTasks;
private IOneHanded mOneHanded;
private IShellTransitions mShellTransitions;
private IStartingWindow mStartingWindow;
@@ -110,7 +110,8 @@ public class SystemUiProxy implements ISystemUiProxy {
private IStartingWindowListener mStartingWindowListener;
private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
- private final ArrayList mRemoteTransitions = new ArrayList<>();
+ private final LinkedHashMap mRemoteTransitions =
+ new LinkedHashMap<>();
private IOnBackInvokedCallback mBackToLauncherCallback;
// Used to dedupe calls to SystemUI
@@ -168,7 +169,7 @@ public class SystemUiProxy implements ISystemUiProxy {
}
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
- IFloatingTasks floatingTasks, IOneHanded oneHanded, IShellTransitions shellTransitions,
+ IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
IBackAnimation backAnimation, IDesktopMode desktopMode) {
@@ -176,7 +177,6 @@ public class SystemUiProxy implements ISystemUiProxy {
mSystemUiProxy = proxy;
mPip = pip;
mSplitScreen = splitScreen;
- mFloatingTasks = floatingTasks;
mOneHanded = oneHanded;
mShellTransitions = shellTransitions;
mStartingWindow = startingWindow;
@@ -187,7 +187,7 @@ public class SystemUiProxy implements ISystemUiProxy {
linkToDeath();
// re-attach the listeners once missing due to setProxy has not been initialized yet.
if (mPipAnimationListener != null && mPip != null) {
- setPinnedStackAnimationListener(mPipAnimationListener);
+ setPipAnimationListener(mPipAnimationListener);
}
if (mSplitScreenListener != null && mSplitScreen != null) {
registerSplitScreenListener(mSplitScreenListener);
@@ -198,9 +198,7 @@ public class SystemUiProxy implements ISystemUiProxy {
if (mSysuiUnlockAnimationController != null && mLauncherUnlockAnimationController != null) {
setLauncherUnlockAnimationController(mLauncherUnlockAnimationController);
}
- for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
- registerRemoteTransition(mRemoteTransitions.get(i));
- }
+ new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
if (mRecentTasksListener != null && mRecentTasks != null) {
registerRecentTasksListener(mRecentTasksListener);
}
@@ -210,7 +208,7 @@ public class SystemUiProxy implements ISystemUiProxy {
}
public void clearProxy() {
- setProxy(null, null, null, null, null, null, null, null, null, null, null);
+ setProxy(null, null, null, null, null, null, null, null, null, null);
}
// TODO(141886704): Find a way to remove this
@@ -346,31 +344,6 @@ public class SystemUiProxy implements ISystemUiProxy {
}
}
- @Override
- public void notifySwipeUpGestureStarted() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifySwipeUpGestureStarted();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifySwipeUpGestureStarted", e);
- }
- }
- }
-
- /**
- * Notifies that swipe-to-home action is finished.
- */
- @Override
- public void notifySwipeToHomeFinished() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifySwipeToHomeFinished();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifySwipeToHomeFinished", e);
- }
- }
- }
-
@Override
public void notifyPrioritizedRotation(int rotation) {
if (mSystemUiProxy != null) {
@@ -475,12 +448,12 @@ public class SystemUiProxy implements ISystemUiProxy {
}
/**
- * Sets listener to get pinned stack animation callbacks.
+ * Sets listener to get pip animation callbacks.
*/
- public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
+ public void setPipAnimationListener(IPipAnimationListener listener) {
if (mPip != null) {
try {
- mPip.setPinnedStackAnimationListener(listener);
+ mPip.setPipAnimationListener(listener);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
}
@@ -522,6 +495,19 @@ public class SystemUiProxy implements ISystemUiProxy {
}
}
+ /**
+ * Sets the next pip animation type to be the alpha animation.
+ */
+ public void setPipAnimationTypeToAlpha() {
+ if (mPip != null) {
+ try {
+ mPip.setPipAnimationTypeToAlpha();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e);
+ }
+ }
+ }
+
//
// Splitscreen
//
@@ -549,15 +535,55 @@ public class SystemUiProxy implements ISystemUiProxy {
}
/** Start multiple tasks in split-screen simultaneously. */
- public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
- @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
- RemoteTransitionCompat remoteTransition, InstanceId instanceId) {
+ public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
+ RemoteTransition remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
- sidePosition, splitRatio, remoteTransition.getTransition(), instanceId);
+ mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
+ splitRatio, remoteTransition, instanceId);
} catch (RemoteException e) {
- Log.w(TAG, "Failed call startTask");
+ Log.w(TAG, "Failed call startTasks");
+ }
+ }
+ }
+
+ public void startIntentAndTask(PendingIntent pendingIntent, Bundle options1, int taskId,
+ Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntentAndTask(pendingIntent, options1, taskId, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startIntentAndTask");
+ }
+ }
+ }
+
+ public void startIntents(PendingIntent pendingIntent1, Bundle options1,
+ PendingIntent pendingIntent2, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntents(pendingIntent1, options1, pendingIntent2, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startIntents");
+ }
+ }
+ }
+
+ public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
+ Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startShortcutAndTask");
}
}
}
@@ -565,13 +591,13 @@ public class SystemUiProxy implements ISystemUiProxy {
/**
* Start multiple tasks in split-screen simultaneously.
*/
- public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
- Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
+ public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2,
+ Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
- sideOptions, sidePosition, splitRatio, adapter, instanceId);
+ mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2,
+ splitPosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTasksWithLegacyTransition");
}
@@ -579,30 +605,42 @@ public class SystemUiProxy implements ISystemUiProxy {
}
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
- Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
- @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
+ Bundle options1, int taskId, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
- taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter,
- instanceId);
+ mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, options1, taskId,
+ options2, splitPosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
}
}
}
- public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, int taskId,
- Bundle mainOptions, Bundle sideOptions,
+ public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1,
+ int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1,
+ taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition");
+ }
+ }
+ }
+
+ public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, Bundle options1,
+ PendingIntent pendingIntent2, Bundle options2,
@SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId,
- mainOptions, sideOptions, sidePosition, splitRatio, adapter, instanceId);
+ mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, options1,
+ pendingIntent2, options2, sidePosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
- Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition");
+ Log.w(TAG, "Failed call startIntentsWithLegacyTransition");
}
}
}
@@ -646,6 +684,7 @@ public class SystemUiProxy implements ISystemUiProxy {
*
* @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
*/
+ @Nullable
public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -657,6 +696,7 @@ public class SystemUiProxy implements ISystemUiProxy {
return null;
}
+ @Nullable
public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -668,20 +708,6 @@ public class SystemUiProxy implements ISystemUiProxy {
return null;
}
- //
- // Floating tasks
- //
-
- public void showFloatingTask(Intent intent) {
- if (mFloatingTasks != null) {
- try {
- mFloatingTasks.showTask(intent);
- } catch (RemoteException e) {
- Log.w(TAG, "Launcher: Failed call showFloatingTask", e);
- }
- }
- }
-
//
// One handed
//
@@ -710,24 +736,24 @@ public class SystemUiProxy implements ISystemUiProxy {
// Remote transitions
//
- public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ public void registerRemoteTransition(
+ RemoteTransition remoteTransition, TransitionFilter filter) {
if (mShellTransitions != null) {
try {
- mShellTransitions.registerRemote(remoteTransition.getFilter(),
- remoteTransition.getTransition());
+ mShellTransitions.registerRemote(filter, remoteTransition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
}
- if (!mRemoteTransitions.contains(remoteTransition)) {
- mRemoteTransitions.add(remoteTransition);
+ if (!mRemoteTransitions.containsKey(remoteTransition)) {
+ mRemoteTransitions.put(remoteTransition, filter);
}
}
- public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
if (mShellTransitions != null) {
try {
- mShellTransitions.unregisterRemote(remoteTransition.getTransition());
+ mShellTransitions.unregisterRemote(remoteTransition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index b9a1b0629f..c45b2f05f8 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -15,12 +15,14 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
+import static com.android.systemui.shared.system.RemoteTransitionCompat.newRemoteTransition;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -29,6 +31,7 @@ import android.content.Intent;
import android.os.SystemProperties;
import android.util.Log;
import android.view.RemoteAnimationTarget;
+import android.window.RemoteTransition;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -36,15 +39,13 @@ import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import java.util.Arrays;
import java.util.HashMap;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
@@ -58,7 +59,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
private RecentsAnimationTargets mTargets;
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
- private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private RemoteAnimationTarget mLastAppearedTaskTarget;
private Runnable mLiveTileCleanUpHandler;
private Context mCtx;
@@ -72,7 +73,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
return;
}
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ if (activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
@@ -102,6 +103,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "startRecentsAnimation",
+ /* gestureEvent= */ START_RECENTS_ANIMATION);
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -152,12 +156,12 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
}
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
- RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
+ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- for (RemoteAnimationTargetCompat compat : appearedTaskTargets) {
- if (compat.activityType == ACTIVITY_TYPE_HOME
+ for (RemoteAnimationTarget compat : appearedTaskTargets) {
+ if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME
&& activityInterface.getCreatedActivity() instanceof RecentsActivity) {
// When receive opening home activity while recents is running, enter home
// and dismiss recents.
@@ -166,25 +170,25 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
}
}
- RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.getNoCreate()
- .onStartingSplitLegacy(Arrays.stream(appearedTaskTargets)
- .map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new));
-
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx)
+ .onStartingSplitLegacy(appearedTaskTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
+ }
+ if (activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView =
activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
appearedTaskTargets,
- new RemoteAnimationTargetCompat[0] /* wallpaper */,
- RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */);
+ new RemoteAnimationTarget[0] /* wallpaper */,
+ nonAppTargets /* nonApps */);
return;
}
- } else if (nonAppTargets != null && nonAppTargets.length > 0) {
+ } else if (nonAppTargets.length > 0) {
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
- RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */,
+ nonAppTargets /* nonApps */,
true /*shown*/, dividerAnimator -> {
dividerAnimator.start();
dividerAnimator.end();
@@ -204,7 +208,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
@Override
public boolean onSwitchToScreenshot(Runnable onFinished) {
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get() || !activityInterface.isInLiveTileMode()
+ if (!activityInterface.isInLiveTileMode()
|| activityInterface.getCreatedActivity() == null) {
// No need to switch since tile is already a screenshot.
onFinished.run();
@@ -225,11 +229,10 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
mCallbacks.addListener(listener);
if (ENABLE_SHELL_TRANSITIONS) {
- RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
+ RemoteTransition transition = newRemoteTransition(mCallbacks,
mController != null ? mController.getController() : null,
mCtx.getIApplicationThread());
- final ActivityOptions options = ActivityOptions.makeRemoteTransition(
- transition.getTransition());
+ final ActivityOptions options = ActivityOptions.makeRemoteTransition(transition);
// Allowing to pause Home if Home is top activity and Recents is not Home. So when user
// start home when recents animation is playing, the home activity can be resumed again
// to let the transition controller collect Home activity.
@@ -252,6 +255,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
* Continues the existing running recents animation for a new gesture.
*/
public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
+ ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation");
mCallbacks.removeListener(mLastGestureState);
mLastGestureState = gestureState;
mCallbacks.addListener(gestureState);
@@ -266,7 +270,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
return;
}
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ if (activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
@@ -290,6 +294,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
*/
public void finishRunningRecentsAnimation(boolean toHome) {
if (mController != null) {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "finishRunningRecentsAnimation", toHome);
mCallbacks.notifyAnimationCanceled();
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome
@@ -322,6 +328,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
* Cleans up the recents animation entirely.
*/
private void cleanUpRecentsAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation");
if (mLiveTileCleanUpHandler != null) {
mLiveTileCleanUpHandler.run();
mLiveTileCleanUpHandler = null;
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index dc60875db8..7c05a1092c 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -249,12 +249,13 @@ public class TaskIconCache implements DisplayInfoChangeListener {
private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
int primaryColor, boolean isInstantApp) {
try (BaseIconFactory bif = getIconFactory()) {
- bif.disableColorExtraction();
bif.setWrapperBackgroundColor(primaryColor);
// User version code O, so that the icon is always wrapped in an adaptive icon container
return bif.createBadgedIconBitmap(drawable,
- new IconOptions().setUser(UserHandle.of(userId)).setInstantApp(isInstantApp));
+ new IconOptions().setUser(UserHandle.of(userId))
+ .setInstantApp(isInstantApp)
+ .setExtractedColor(0));
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index e10cab60e0..d40f2ae017 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -18,7 +18,6 @@ package com.android.quickstep;
import static android.view.Surface.ROTATION_0;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
@@ -174,14 +173,10 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
* @param callback callback to run, after switching to screenshot
*/
public void endLiveTileMode(@NonNull Runnable callback) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
- recentsView.switchToScreenshot(
- () -> recentsView.finishRecentsAnimation(true /* toRecents */,
- false /* shouldPip */, callback));
- } else {
- callback.run();
- }
+ RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
+ recentsView.switchToScreenshot(
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+ false /* shouldPip */, callback));
}
/**
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index eae79dfa19..663525d0c5 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -303,7 +303,8 @@ public interface TaskShortcutFactory {
private boolean isAvailable(BaseDraggingActivity activity, int displayId) {
return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity)
- && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false);
+ && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false)
+ && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
}
};
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index d7227780c6..67360c4cc3 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -26,6 +26,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -34,7 +35,6 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.List;
@@ -87,9 +87,9 @@ public final class TaskUtils {
}
- public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
+ public static boolean taskIsATargetWithMode(RemoteAnimationTarget[] targets,
int taskId, int mode) {
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == mode && target.taskId == taskId) {
return true;
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index a96524e186..67de4b1679 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,12 +15,15 @@
*/
package com.android.quickstep;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_DELAY_NAV_FADE_IN;
@@ -35,17 +38,13 @@ import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncest
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Matrix;
@@ -53,6 +52,7 @@ import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.View;
import android.window.TransitionInfo;
@@ -62,6 +62,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
@@ -72,6 +73,8 @@ import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -82,7 +85,6 @@ import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.ArrayList;
import java.util.List;
@@ -104,7 +106,7 @@ public final class TaskViewUtils {
* opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
*/
public static TaskView findTaskViewToLaunch(
- RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
+ RecentsView recentsView, View v, RemoteAnimationTarget[] targets) {
if (v instanceof TaskView) {
TaskView taskView = (TaskView) v;
return recentsView.isTaskViewVisible(taskView) ? taskView : null;
@@ -134,7 +136,7 @@ public final class TaskViewUtils {
}
// Resolve the opening task id
int openingTaskId = -1;
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == MODE_OPENING) {
openingTaskId = target.taskId;
break;
@@ -157,9 +159,9 @@ public final class TaskViewUtils {
public static void createRecentsWindowAnimator(
@NonNull TaskView v, boolean skipViewChanges,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
@Nullable DepthController depthController,
PendingAnimation out) {
RecentsView recentsView = v.getRecentsView();
@@ -169,7 +171,7 @@ public final class TaskViewUtils {
final RemoteAnimationTargets targets =
new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
MODE_OPENING);
- final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
+ final RemoteAnimationTarget navBarTarget = targets.getNavBarRemoteAnimationTarget();
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
targets.addReleaseCheck(applier);
@@ -252,21 +254,24 @@ public final class TaskViewUtils {
@Override
public void onUpdate(float percent, boolean initOnly) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+
// TODO Do we need to operate over multiple TVSs for the navbar leash?
for (RemoteTargetHandle handle : remoteTargetHandles) {
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceProperties navBuilder =
+ transaction.forSurface(navBarTarget.leash);
+
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
taskViewSimulator.getCurrentCropRect().round(cropRect);
- navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
- .withWindowCrop(cropRect)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(taskViewSimulator.getCurrentMatrix())
+ .setWindowCrop(cropRect)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- handle.getTransformParams().applySurfaceParams(navBuilder.build());
+ handle.getTransformParams().applySurfaceParams(transaction);
}
}
});
@@ -304,9 +309,15 @@ public final class TaskViewUtils {
// to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
// Mt K(0)` K(t) Mt`
TaskThumbnailView[] thumbnails = v.getThumbnails();
- Matrix[] mt = new Matrix[simulatorCopies.length];
- Matrix[] mti = new Matrix[simulatorCopies.length];
- for (int i = 0; i < thumbnails.length; i++) {
+
+ // In case simulator copies and thumbnail size do no match, ensure we get the lesser.
+ // This ensures we do not create arrays with empty elements or attempt to references
+ // indexes out of array bounds.
+ final int matrixSize = Math.min(simulatorCopies.length, thumbnails.length);
+
+ Matrix[] mt = new Matrix[matrixSize];
+ Matrix[] mti = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
TaskThumbnailView ttv = thumbnails[i];
RectF localBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
@@ -321,16 +332,27 @@ public final class TaskViewUtils {
Matrix localMti = new Matrix();
localMt.invert(localMti);
mti[i] = localMti;
+
+ // Translations for child thumbnails also get scaled as the parent taskView scales
+ // Add inverse scaling to keep translations the same
+ float translationY = ttv.getTranslationY();
+ float translationX = ttv.getTranslationX();
+ float fullScreenScale =
+ topMostSimulators[i].getTaskViewSimulator().getFullScreenScale();
+ out.addFloat(ttv, VIEW_TRANSLATE_Y, translationY,
+ translationY / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR);
+ out.addFloat(ttv, VIEW_TRANSLATE_X, translationX,
+ translationX / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR);
}
- Matrix[] k0i = new Matrix[simulatorCopies.length];
- for (int i = 0; i < simulatorCopies.length; i++) {
+ Matrix[] k0i = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
k0i[i] = new Matrix();
simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
}
Matrix animationMatrix = new Matrix();
out.addOnFrameCallback(() -> {
- for (int i = 0; i < simulatorCopies.length; i++) {
+ for (int i = 0; i < matrixSize; i++) {
animationMatrix.set(mt[i]);
animationMatrix.postConcat(k0i[i]);
animationMatrix.postConcat(simulatorCopies[i]
@@ -367,8 +389,8 @@ public final class TaskViewUtils {
});
if (depthController != null) {
- out.setFloat(depthController, STATE_DEPTH, BACKGROUND_APP.getDepth(baseActivity),
- TOUCH_RESPONSE_INTERPOLATOR);
+ out.setFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE,
+ BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE_INTERPOLATOR);
}
}
@@ -390,9 +412,8 @@ public final class TaskViewUtils {
*/
public static void composeRecentsSplitLaunchAnimator(GroupedTaskView launchingTaskView,
@NonNull StateManager stateManager, @Nullable DepthController depthController,
- int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
- @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t,
- @NonNull Runnable finishCallback) {
+ int initialTaskId, int secondTaskId, @NonNull TransitionInfo transitionInfo,
+ SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
if (launchingTaskView != null) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
@@ -402,12 +423,12 @@ public final class TaskViewUtils {
}
});
- final RemoteAnimationTargetCompat[] appTargets =
+ final RemoteAnimationTarget[] appTargets =
RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */);
- final RemoteAnimationTargetCompat[] wallpaperTargets =
+ final RemoteAnimationTarget[] wallpaperTargets =
RemoteAnimationTargetCompat.wrapNonApps(
transitionInfo, true /* wallpapers */, t, null /* leashMap */);
- final RemoteAnimationTargetCompat[] nonAppTargets =
+ final RemoteAnimationTarget[] nonAppTargets =
RemoteAnimationTargetCompat.wrapNonApps(
transitionInfo, false /* wallpapers */, t, null /* leashMap */);
final RecentsView recentsView = launchingTaskView.getRecentsView();
@@ -426,8 +447,10 @@ public final class TaskViewUtils {
TransitionInfo.Change splitRoot2 = null;
for (int i = 0; i < transitionInfo.getChanges().size(); ++i) {
final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
- final int taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
+ if (change.getTaskInfo() == null) continue;
+ final int taskId = change.getTaskInfo().taskId;
final int mode = change.getMode();
+
// Find the target tasks' root tasks since those are the split stages that need to
// be animated (the tasks themselves are children and thus inherit animation).
if (taskId == initialTaskId || taskId == secondTaskId) {
@@ -440,7 +463,7 @@ public final class TaskViewUtils {
+ "root of " + taskId + " is already visible or has broken hierarchy.");
}
}
- if (taskId == initialTaskId && initialTaskId != INVALID_TASK_ID) {
+ if (taskId == initialTaskId) {
splitRoot1 = transitionInfo.getChange(change.getParent());
}
if (taskId == secondTaskId) {
@@ -474,17 +497,16 @@ public final class TaskViewUtils {
* If {@param launchingTaskView} is not null, then this will play the tasks launch animation
* from the position of the GroupedTaskView (when user taps on the TaskView to start it).
* Technically this case should be taken care of by
- * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
+ * {@link #composeRecentsSplitLaunchAnimatorLegacy} below, but the way we launch tasks whether
* it's a single task or multiple tasks results in different entry-points.
*
* If it is null, then it will simply fade in the starting apps and fade out launcher (for the
* case where launcher handles animating starting split tasks from app icon) */
public static void composeRecentsSplitLaunchAnimatorLegacy(
- @Nullable GroupedTaskView launchingTaskView, int initialTaskId,
- @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @Nullable GroupedTaskView launchingTaskView, int initialTaskId, int secondTaskId,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
@NonNull StateManager stateManager,
@Nullable DepthController depthController,
@NonNull Runnable finishCallback) {
@@ -507,7 +529,7 @@ public final class TaskViewUtils {
final ArrayList openingTargets = new ArrayList<>();
final ArrayList closingTargets = new ArrayList<>();
- for (RemoteAnimationTargetCompat appTarget : appTargets) {
+ for (RemoteAnimationTarget appTarget : appTargets) {
final int taskId = appTarget.taskInfo != null ? appTarget.taskInfo.taskId : -1;
final int mode = appTarget.mode;
final SurfaceControl leash = appTarget.leash;
@@ -562,9 +584,9 @@ public final class TaskViewUtils {
}
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing,
@NonNull StateManager stateManager, @NonNull RecentsView recentsView,
@Nullable DepthController depthController) {
boolean skipLauncherChanges = !launcherClosing;
@@ -637,7 +659,7 @@ public final class TaskViewUtils {
};
}
pa.add(launcherAnim);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1) {
+ if (recentsView.getRunningTaskIndex() != -1) {
pa.addOnFrameCallback(recentsView::redrawLiveTile);
}
anim.play(pa.buildAnim());
@@ -656,7 +678,7 @@ public final class TaskViewUtils {
* @return the animator animating the surfaces
*/
public static ValueAnimator createSplitAuxiliarySurfacesAnimator(
- RemoteAnimationTargetCompat[] nonApps, boolean shown,
+ RemoteAnimationTarget[] nonApps, boolean shown,
Consumer animatorHandler) {
if (nonApps == null || nonApps.length == 0) {
return null;
@@ -666,7 +688,7 @@ public final class TaskViewUtils {
List auxiliarySurfaces = new ArrayList<>(nonApps.length);
boolean hasSurfaceToAnimate = false;
for (int i = 0; i < nonApps.length; ++i) {
- final RemoteAnimationTargetCompat targ = nonApps[i];
+ final RemoteAnimationTarget targ = nonApps[i];
final SurfaceControl leash = targ.leash;
if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null && leash.isValid()) {
auxiliarySurfaces.add(leash);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1999701f61..5d17cc77f4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,30 +15,32 @@
*/
package com.android.quickstep;
+import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_DESKTOP_MODE;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
import android.annotation.TargetApi;
import android.app.PendingIntent;
@@ -47,7 +49,6 @@ import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Icon;
import android.os.Build;
@@ -55,11 +56,11 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.Log;
import android.view.Choreographer;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.BinderThread;
@@ -67,11 +68,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarManager;
@@ -84,7 +89,6 @@ import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
@@ -101,8 +105,6 @@ import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
-import com.android.quickstep.util.SplitScreenBounds;
-import com.android.quickstep.util.ViewCapture;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -113,7 +115,6 @@ import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationContro
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
@@ -139,21 +140,7 @@ public class TouchInteractionService extends Service
private static final String TAG = "TouchInteractionService";
- private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
-
- private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
- private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
- private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
-
- /**
- * System Action ID to show all apps.
- * TODO: Use AccessibilityService's corresponding global action constant in S
- */
- private static final int SYSTEM_ACTION_ID_ALL_APPS = 14;
-
- private int mBackGestureNotificationCounter = -1;
private final TISBinder mTISBinder = new TISBinder();
@@ -171,8 +158,6 @@ public class TouchInteractionService extends Service
IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
KEY_EXTRA_SHELL_SPLIT_SCREEN));
- IFloatingTasks floatingTasks = IFloatingTasks.Stub.asInterface(bundle.getBinder(
- KEY_EXTRA_SHELL_FLOATING_TASKS));
IOneHanded onehanded = IOneHanded.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
@@ -183,14 +168,14 @@ public class TouchInteractionService extends Service
ISysuiUnlockAnimationController.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
+ bundle.getBinder(KEY_EXTRA_SHELL_RECENT_TASKS));
IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
IDesktopMode desktopMode = IDesktopMode.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
- splitscreen, floatingTasks, onehanded, shellTransitions, startingWindow,
+ splitscreen, onehanded, shellTransitions, startingWindow,
recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode);
TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()");
preloadOverview(true /* fromInit */);
@@ -231,16 +216,12 @@ public class TouchInteractionService extends Service
@BinderThread
@Override
- public void onTip(int actionType, int viewType) {
- // Please delete this method from the interface
- }
-
- @BinderThread
- @Override
- public void onAssistantAvailable(boolean available) {
+ public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) {
MAIN_EXECUTOR.execute(() -> {
mDeviceState.setAssistantAvailable(available);
TouchInteractionService.this.onAssistantVisibilityChanged();
+ executeForTaskbarManager(() -> mTaskbarManager
+ .onLongPressHomeEnabled(longPressHomeEnabled));
});
}
@@ -253,10 +234,9 @@ public class TouchInteractionService extends Service
});
}
- @BinderThread
- public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
- boolean gestureSwipeLeft) {
- // Remove this method from the interface
+ @Override
+ public void onNavigationBarSurface(SurfaceControl surface) {
+ // TODO: implement
}
@BinderThread
@@ -273,12 +253,6 @@ public class TouchInteractionService extends Service
MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
- @Override
- public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
- WindowBounds wb = new WindowBounds(bounds, insets);
- MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
- }
-
@BinderThread
@Override
public void onScreenTurnedOn() {
@@ -297,6 +271,16 @@ public class TouchInteractionService extends Service
MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff);
}
+ @BinderThread
+ @Override
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+ StatefulActivity activity =
+ mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
+ if (activity != null) {
+ activity.enterStageSplitFromRunningApp(leftOrTop);
+ }
+ }
+
/**
* Preloads the Overview activity.
*
@@ -482,8 +466,6 @@ public class TouchInteractionService extends Service
// Temporarily disable model preload
// new ModelPreload().start(this);
- mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
- .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
@@ -502,7 +484,7 @@ public class TouchInteractionService extends Service
}
// Reset home bounce seen on quick step enabled for first time
- SharedPreferences sharedPrefs = Utilities.getPrefs(this);
+ SharedPreferences sharedPrefs = LauncherPrefs.getPrefs(this);
if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) {
sharedPrefs.edit()
.putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
@@ -521,11 +503,11 @@ public class TouchInteractionService extends Service
Icon.createWithResource(this, R.drawable.ic_apps),
getString(R.string.all_apps_label),
getString(R.string.all_apps_label),
- PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
+ PendingIntent.getActivity(this, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
- am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
+ am.registerSystemAction(allAppsAction, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
} else {
- am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+ am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
}
StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
@@ -544,9 +526,22 @@ public class TouchInteractionService extends Service
mOverviewComponentObserver.onSystemUiStateChanged();
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
- boolean wasExpanded = (lastSysUIFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
- boolean isExpanded =
- (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
+ boolean wasFreeformActive =
+ (lastSysUIFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
+ boolean isFreeformActive =
+ (systemUiStateFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
+ if (wasFreeformActive != isFreeformActive) {
+ DesktopVisibilityController controller = mOverviewComponentObserver
+ .getActivityInterface().getDesktopVisibilityController();
+ if (controller != null) {
+ controller.setFreeformTasksVisible(isFreeformActive);
+ }
+ }
+
+ int isShadeExpandedFlag =
+ SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+ boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0;
+ boolean isExpanded = (systemUiStateFlags & isShadeExpandedFlag) != 0;
if (wasExpanded != isExpanded && isExpanded) {
// End live tile when expanding the notification panel for the first time from
// overview.
@@ -591,7 +586,7 @@ public class TouchInteractionService extends Service
ProtoTracer.INSTANCE.get(this).remove(this);
getSystemService(AccessibilityManager.class)
- .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+ .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
mTaskbarManager.destroy();
sConnected = false;
@@ -816,27 +811,9 @@ public class TouchInteractionService extends Service
if (mDeviceState.isBubblesExpanded()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
- .append("bubbles expanded");
- if (BUBBLES_HOME_GESTURE_ENABLED) {
- reasonString.append(SUBSTRING_PREFIX)
- .append("bubbles can handle the home gesture")
- .append(", trying to use default input consumer");
- // Bubbles can handle home gesture itself.
- base = getDefaultInputConsumer(reasonString);
- } else {
- // If Bubbles is expanded, use the overlay input consumer, which will close
- // Bubbles instead of going all the way home when a swipe up is detected.
- // Notification panel can be expanded on top of expanded bubbles. Bubbles remain
- // expanded in the back. Make sure swipe up is not passed to bubbles in this
- // case.
- if (!mDeviceState.isNotificationPanelExpanded()) {
- reasonString = newCompoundString(reasonPrefix)
- .append(SUBSTRING_PREFIX)
- .append("using SysUiOverlayInputConsumer");
- base = new SysUiOverlayInputConsumer(
- getBaseContext(), mDeviceState, mInputMonitorCompat);
- }
- }
+ .append("bubbles expanded, trying to use default input consumer");
+ // Bubbles can handle home gesture itself.
+ base = getDefaultInputConsumer(reasonString);
}
if (mDeviceState.isSystemUiDialogShowing()) {
@@ -914,6 +891,9 @@ public class TouchInteractionService extends Service
.append(consumer.getName())
.append(". reason(s):")
.append(reasonString));
+ if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
+ ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER);
+ }
}
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
@@ -955,8 +935,7 @@ public class TouchInteractionService extends Service
boolean launcherResumedThroughShellTransition =
gestureState.getActivityInterface().isResumed()
&& !previousGestureState.isRecentsAnimationRunning();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && gestureState.getActivityInterface().isInLiveTileMode()) {
+ if (gestureState.getActivityInterface().isInLiveTileMode()) {
return createOverviewInputConsumer(
previousGestureState,
gestureState,
@@ -1044,8 +1023,7 @@ public class TouchInteractionService extends Service
previousGestureState.isRunningAnimationToLauncher();
boolean forcingOverviewInputConsumer =
ASSISTANT_GIVES_LAUNCHER_FOCUS.get() && forceOverviewInputConsumer;
- boolean isInLiveTileMode = ENABLE_QUICKSTEP_LIVE_TILE.get()
- && gestureState.getActivityInterface().isInLiveTileMode();
+ boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode();
reasonString.append(SUBSTRING_PREFIX)
.append(hasWindowFocus
? "activity has window focus"
@@ -1227,7 +1205,9 @@ public class TouchInteractionService extends Service
}
mTaskbarManager.dumpLogs("", pw);
- ViewCapture.INSTANCE.get(this).dump(pw, fd);
+ if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+ ViewCapture.getInstance().dump(pw, fd, this);
+ }
}
}
@@ -1238,12 +1218,22 @@ public class TouchInteractionService extends Service
}
private void onCommand(PrintWriter pw, LinkedList args) {
- switch (args.pollFirst()) {
+ String cmd = args.pollFirst();
+ if (cmd == null) {
+ pw.println("Command missing");
+ printAvailableCommands(pw);
+ return;
+ }
+ switch (cmd) {
case "clear-touch-log":
ActiveGestureLog.INSTANCE.clear();
break;
case "print-gesture-log":
ActiveGestureLog.INSTANCE.dump("", pw);
+ break;
+ default:
+ pw.println("Command does not exist: " + cmd);
+ printAvailableCommands(pw);
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 19a6c38901..062e50e30b 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -45,7 +45,7 @@ import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.ClearAllButton;
@@ -95,7 +95,7 @@ public class FallbackRecentsStateController implements StateHandler
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 223eba5b95..8b5f091e11 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -55,7 +55,8 @@ public class RecentsState implements BaseState {
public static final RecentsState HOME = new RecentsState(3, 0);
public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
- FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS);
+ FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS
+ | FLAG_DISABLE_RESTORE);
public final int ordinal;
private final int mFlags;
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
index eca61bb07d..db4927a0d1 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.fallback;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.quickstep.RecentsActivity;
@@ -28,8 +26,7 @@ public class RecentsTaskController extends TaskViewTouchController {
- ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
- true /* restoreHomeStackPosition */);
- };
-
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
boolean isDeferredDownTarget, Consumer onCompleteCallback,
@@ -152,7 +141,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mNavBarPosition = mDeviceState.getNavBarPosition();
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
- mMainThreadHandler = new Handler(Looper.getMainLooper());
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
@@ -292,6 +280,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
float upDist = -displacement;
boolean passedSlop = squaredHypot(displacementX, displacementY)
>= mSquaredTouchSlop;
+
if (!mPassedSlopOnThisGesture && passedSlop) {
mPassedSlopOnThisGesture = true;
}
@@ -336,7 +325,10 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
if (mDeviceState.isFullyGesturalNavMode()) {
- mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
+ boolean minSwipeMet = upDist >= Math.max(mMotionPauseMinDisplacement,
+ mInteractionHandler.getThresholdToAllowMotionPause());
+ mInteractionHandler.setCanSlowSwipeGoHome(minSwipeMet);
+ mMotionPauseDetector.setDisallowPause(!minSwipeMet
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(ev);
mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
@@ -373,10 +365,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "startRecentsAnimation",
- /* gestureEvent= */ START_RECENTS_ANIMATION);
-
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
@@ -409,16 +397,16 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mInteractionHandler.onGestureCancelled();
} else {
mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
- float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
- float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = mNavBarPosition.isRightEdge()
- ? velocityX
+ float velocityXPxPerMs = mVelocityTracker.getXVelocity(mActivePointerId);
+ float velocityYPxPerMs = mVelocityTracker.getYVelocity(mActivePointerId);
+ float velocityPxPerMs = mNavBarPosition.isRightEdge()
+ ? velocityXPxPerMs
: mNavBarPosition.isLeftEdge()
- ? -velocityX
- : velocityY;
+ ? -velocityXPxPerMs
+ : velocityYPxPerMs;
mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
- mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
- mDownPos);
+ mInteractionHandler.onGestureEnded(
+ velocityPxPerMs, new PointF(velocityXPxPerMs, velocityYPxPerMs), mDownPos);
}
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
@@ -437,13 +425,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
onConsumerAboutToBeSwitched();
onInteractionGestureFinished();
-
- // Cancel the recents animation if SysUI happens to handle UP before we have a chance
- // to start the recents animation. In addition, workaround for b/126336729 by delaying
- // the cancel of the animation for a period, in case SysUI is slow to handle UP and we
- // handle DOWN & UP and move the home stack before SysUI can start the activity
- mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
- mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100);
}
cleanupAfterGesture();
TraceHelper.INSTANCE.endSection(traceToken);
@@ -465,7 +446,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
@Override
public void onConsumerAboutToBeSwitched() {
Preconditions.assertUIThread();
- mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
if (mInteractionHandler != null) {
// The consumer is being switched while we are active. Set up the shared state to be
// used by the next animation
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 6f35928a9f..64165b66e4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep.inputconsumers;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.media.AudioManager;
@@ -100,27 +99,23 @@ public class OverviewInputConsumer, T extends StatefulAct
@Override
public void onHoverEvent(MotionEvent ev) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivity.dispatchGenericMotionEvent(ev);
- }
+ mActivity.dispatchGenericMotionEvent(ev);
}
@Override
public void onKeyEvent(KeyEvent ev) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- switch (ev.getKeyCode()) {
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_MUTE:
- MediaSessionManager mgr = mActivity.getSystemService(MediaSessionManager.class);
- mgr.dispatchVolumeKeyEventAsSystemService(ev,
- AudioManager.USE_DEFAULT_STREAM_TYPE);
- break;
- default:
- break;
- }
- mActivity.dispatchKeyEvent(ev);
+ switch (ev.getKeyCode()) {
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ MediaSessionManager mgr = mActivity.getSystemService(MediaSessionManager.class);
+ mgr.dispatchVolumeKeyEventAsSystemService(ev,
+ AudioManager.USE_DEFAULT_STREAM_TYPE);
+ break;
+ default:
+ break;
}
+ mActivity.dispatchKeyEvent(ev);
}
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index b9b5e7c2b9..45ffa1c510 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -28,14 +28,12 @@ import android.content.Intent;
import android.graphics.Point;
import android.view.MotionEvent;
-import androidx.annotation.Nullable;
-
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.MultiStateCallback;
@@ -43,7 +41,6 @@ import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -102,8 +99,7 @@ public class ProgressDelegateInputConsumer implements InputConsumer,
mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
// Init states
- mStateCallback = new MultiStateCallback(
- STATE_NAMES, ProgressDelegateInputConsumer::getTrackedEventForState);
+ mStateCallback = new MultiStateCallback(STATE_NAMES);
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
this::endRemoteAnimation);
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
@@ -113,14 +109,6 @@ public class ProgressDelegateInputConsumer implements InputConsumer,
mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
}
- @Nullable
- private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
- if (stateFlag == STATE_HANDLER_INVALIDATED) {
- return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED;
- }
- return null;
- }
-
@Override
public int getType() {
return TYPE_PROGRESS_DELEGATE;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index 3785de4ecd..3afd0a3431 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -15,16 +15,27 @@
*/
package com.android.quickstep.inputconsumers;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_REVISED_THRESHOLDS;
+import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -37,20 +48,41 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
private final GestureDetector mLongPressDetector;
private final float mSquaredTouchSlop;
-
- private float mDownX, mDownY;
+ private float mLongPressDownX, mLongPressDownY;
private boolean mCanceledUnstashHint;
private final float mUnstashArea;
private final float mScreenWidth;
+ private final int mTaskbarNavThresholdY;
+ private final boolean mIsTaskbarAllAppsOpen;
+ private boolean mHasPassedTaskbarNavThreshold;
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ private final boolean mIsTransientTaskbar;
+
+ private final @Nullable TransitionCallback mTransitionCallback;
+
public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
super(delegate, inputMonitor);
mTaskbarActivityContext = taskbarActivityContext;
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx;
- mUnstashArea = context.getResources()
- .getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
+
+ Resources res = context.getResources();
+ mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
+ int taskbarNavThreshold = res.getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
+ ? R.dimen.taskbar_nav_threshold_v2
+ : R.dimen.taskbar_nav_threshold);
+ int screenHeight = taskbarActivityContext.getDeviceProfile().heightPx;
+ mTaskbarNavThresholdY = screenHeight - taskbarNavThreshold;
+ mIsTaskbarAllAppsOpen =
+ mTaskbarActivityContext != null && mTaskbarActivityContext.isTaskbarAllAppsOpen();
+
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(context);
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
@@ -58,6 +90,10 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
onLongPressDetected(motionEvent);
}
});
+
+ mTransitionCallback = mIsTransientTaskbar
+ ? taskbarActivityContext.getTranslationCallbacks()
+ : null;
}
@Override
@@ -76,28 +112,81 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
final float y = ev.getRawY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+
+ mHasPassedTaskbarNavThreshold = false;
+ mTaskbarActivityContext.setAutohideSuspendFlag(
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING, true);
if (isInArea(x)) {
- mDownX = x;
- mDownY = y;
- mTaskbarActivityContext.startTaskbarUnstashHint(
- /* animateForward = */ true);
- mCanceledUnstashHint = false;
+ if (!mIsTransientTaskbar) {
+ mLongPressDownX = x;
+ mLongPressDownY = y;
+ mTaskbarActivityContext.startTaskbarUnstashHint(
+ /* animateForward = */ true);
+ mCanceledUnstashHint = false;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
}
break;
case MotionEvent.ACTION_MOVE:
- if (!mCanceledUnstashHint
- && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
+ if (!mIsTransientTaskbar
+ && !mCanceledUnstashHint
+ && squaredHypot(mLongPressDownX - x, mLongPressDownY - y)
+ > mSquaredTouchSlop) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
mCanceledUnstashHint = true;
}
+
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+ if (mIsTransientTaskbar) {
+ float dY = mLastPos.y - mDownPos.y;
+ boolean passedTaskbarNavThreshold = dY < 0
+ && mLastPos.y < mTaskbarNavThresholdY;
+
+ if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
+ mHasPassedTaskbarNavThreshold = true;
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ }
+
+ if (dY < 0) {
+ dY = -OverScroll.dampedScroll(-dY, mTaskbarNavThresholdY);
+ if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) {
+ mTransitionCallback.onActionMove(dY);
+ }
+ }
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (!mCanceledUnstashHint) {
+ if (!mIsTransientTaskbar && !mCanceledUnstashHint) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
}
+ mTaskbarActivityContext.setAutohideSuspendFlag(
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING, false);
+ if (mTransitionCallback != null) {
+ mTransitionCallback.onActionEnd();
+ }
+ mHasPassedTaskbarNavThreshold = false;
break;
}
}
@@ -111,7 +200,9 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
}
private void onLongPressDetected(MotionEvent motionEvent) {
- if (mTaskbarActivityContext != null && isInArea(motionEvent.getRawX())) {
+ if (mTaskbarActivityContext != null
+ && isInArea(motionEvent.getRawX())
+ && !mIsTransientTaskbar) {
boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar();
if (taskBarPressed) {
setActive(motionEvent);
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 8ad17cb711..e0262d02eb 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -42,6 +42,7 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -53,12 +54,13 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.Executors;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.TISBindHelper;
@@ -78,6 +80,7 @@ public class AllSetActivity extends Activity {
"#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S.:settings:fragment_args_key=gesture_system_navigation_input_summary;S.:settings:show_fragment=com.android.settings.gestures.SystemNavigationGestureSettings;end";
private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
+ private static final String EXTRA_DEVICE_NAME = "suwDeviceName";
private static final float HINT_BOTTOM_FACTOR = 1 - .94f;
@@ -109,7 +112,8 @@ public class AllSetActivity extends Activity {
int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
- int accentColor = getIntent().getIntExtra(
+ Intent intent = getIntent();
+ int accentColor = intent.getIntExtra(
isDarkTheme ? EXTRA_ACCENT_COLOR_DARK_MODE : EXTRA_ACCENT_COLOR_LIGHT_MODE,
isDarkTheme ? Color.WHITE : Color.BLACK);
@@ -120,11 +124,12 @@ public class AllSetActivity extends Activity {
mContentView = findViewById(R.id.content_view);
mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
- boolean isTablet = InvariantDeviceProfile.INSTANCE.get(getApplicationContext())
- .getDeviceProfile(this).isTablet;
TextView subtitle = findViewById(R.id.subtitle);
- subtitle.setText(isTablet
- ? R.string.allset_description_tablet : R.string.allset_description);
+ String suwDeviceName = intent.getStringExtra(EXTRA_DEVICE_NAME);
+ subtitle.setText(getString(
+ R.string.allset_description_generic,
+ !TextUtils.isEmpty(suwDeviceName)
+ ? suwDeviceName : getString(R.string.default_device_name)));
TextView tv = findViewById(R.id.navigation_settings);
tv.setTextColor(accentColor);
@@ -135,14 +140,23 @@ public class AllSetActivity extends Activity {
} catch (URISyntaxException e) {
Log.e(LOG_TAG, "Failed to parse system nav settings intent", e);
}
- finish();
});
- findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+ TextView hintTextView = findViewById(R.id.hint);
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+ if (!dp.isGestureMode) {
+ hintTextView.setText(R.string.allset_button_hint);
+ }
+ hintTextView.setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mVibrator = getSystemService(Vibrator.class);
mAnimatedBackground = findViewById(R.id.animated_background);
+ // There's a bug in the currently used external Lottie library (v5.2.0), and it doesn't load
+ // the correct animation from the raw resources when configuration changes, so we need to
+ // manually load the resource and pass it to Lottie.
+ mAnimatedBackground.setAnimation(getResources().openRawResource(R.raw.all_set_page_bg),
+ null);
startBackgroundAnimation();
}
@@ -248,9 +262,6 @@ public class AllSetActivity extends Activity {
}
private AnimatedFloat createSwipeUpProxy(GestureState state) {
- if (!state.getHomeIntent().getComponent().getPackageName().equals(getPackageName())) {
- return null;
- }
if (state.getRunningTaskId() != getTaskId()) {
return null;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
index 437572b380..8eb40593c9 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
@@ -41,9 +41,9 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.anim.Interpolators;
-import com.android.quickstep.util.VibratorWrapper;
+import com.android.launcher3.testing.shared.ResourceUtils;
+import com.android.launcher3.util.VibratorWrapper;
/** Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java. */
public class EdgeBackGesturePanel extends View {
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index bf7023c217..4a701202db 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -28,8 +28,8 @@ import android.view.Window;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.interaction.TutorialController.TutorialType;
@@ -63,7 +63,7 @@ public class GestureSandboxActivity extends FragmentActivity {
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gesture_tutorial_activity);
- mSharedPrefs = Utilities.getPrefs(this);
+ mSharedPrefs = LauncherPrefs.getPrefs(this);
mStatsLogManager = StatsLogManager.newInstance(getApplicationContext());
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index e7bf7e2d08..57874d9deb 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -16,6 +16,7 @@
package com.android.quickstep.interaction;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT;
@@ -25,7 +26,6 @@ import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestu
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE;
-import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -48,10 +48,10 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
-import com.android.quickstep.util.VibratorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
/** Utility class to handle Home and Assistant gestures. */
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 6b016cee13..05b246b906 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -24,8 +24,8 @@ import android.graphics.PointF;
import android.os.Build;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index fa7bc04b30..670ee9b333 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -33,7 +33,6 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -43,19 +42,21 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
+import com.android.quickstep.util.RecordingSurfaceTransaction;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@TargetApi(Build.VERSION_CODES.R)
abstract class SwipeUpGestureTutorialController extends TutorialController {
@@ -415,21 +416,23 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
private class FakeTransformParams extends TransformParams {
@Override
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, null, this);
- return new SurfaceParams[] {builder.build()};
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+ proxy.onBuildTargetParams(transaction.mockProperties, null, this);
+ return transaction;
}
@Override
- public void applySurfaceParams(SurfaceParams[] params) {
- SurfaceParams p = params[0];
- mFakeTaskView.setAnimationMatrix(p.matrix);
- mFakePreviousTaskView.setAnimationMatrix(p.matrix);
- mFakeTaskViewRect.set(p.windowCrop);
- mFakeTaskViewRadius = p.cornerRadius;
- mFakeTaskView.invalidateOutline();
- mFakePreviousTaskView.invalidateOutline();
+ public void applySurfaceParams(SurfaceTransaction params) {
+ if (params instanceof RecordingSurfaceTransaction) {
+ MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+ mFakeTaskView.setAnimationMatrix(p.matrix);
+ mFakePreviousTaskView.setAnimationMatrix(p.matrix);
+ mFakeTaskViewRect.set(p.windowCrop);
+ mFakeTaskViewRadius = p.cornerRadius;
+ mFakeTaskView.invalidateOutline();
+ mFakePreviousTaskView.invalidateOutline();
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 2ccdfa3b36..5efc45e385 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -16,8 +16,8 @@
package com.android.quickstep.logging;
-import static com.android.launcher3.Utilities.getDevicePrefs;
-import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
+import static com.android.launcher3.LauncherPrefs.getPrefs;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 37a28e554b..0a155cbd47 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -62,11 +62,14 @@ import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
import com.android.launcher3.views.ActivityContext;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.SysUiStatsLog;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -85,6 +88,7 @@ public class StatsLogCompatManager extends StatsLogManager {
private static final String TAG = "StatsLog";
private static final String LATENCY_TAG = "StatsLatencyLog";
+ private static final String IMPRESSION_TAG = "StatsImpressionLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
@@ -119,7 +123,12 @@ public class StatsLogCompatManager extends StatsLogManager {
@Override
protected StatsLatencyLogger createLatencyLogger() {
- return new StatsCompatLatencyLogger(mContext, mActivityContext);
+ return new StatsCompatLatencyLogger();
+ }
+
+ @Override
+ protected StatsImpressionLogger createImpressionLogger() {
+ return new StatsCompatImpressionLogger();
}
/**
@@ -190,7 +199,8 @@ public class StatsLogCompatManager extends StatsLogManager {
getCardinality(info), // cardinality = 16;
info.getWidget().getSpanX(), // span_x = 17 [default = 1];
info.getWidget().getSpanY(), // span_y = 18 [default = 1];
- getAttributes(info) /* attributes */
+ getAttributes(info) /* attributes = 19 [(log_mode) = MODE_BYTES] */,
+ info.getIsKidsMode() /* is_kids_mode = 20 */
);
}
@@ -216,6 +226,7 @@ public class StatsLogCompatManager extends StatsLogManager {
private Optional mEditText = Optional.empty();
private SliceItem mSliceItem;
private LauncherAtom.Slice mSlice;
+ private Optional mCardinality = Optional.empty();
StatsCompatLogger(Context context, ActivityContext activityContext) {
mContext = context;
@@ -302,6 +313,12 @@ public class StatsLogCompatManager extends StatsLogManager {
return this;
}
+ @Override
+ public StatsLogger withCardinality(int cardinality) {
+ this.mCardinality = Optional.of(cardinality);
+ return this;
+ }
+
@Override
public void log(EventEnum event) {
if (!Utilities.ATLEAST_R) {
@@ -325,6 +342,10 @@ public class StatsLogCompatManager extends StatsLogManager {
return;
}
+ if (mItemInfo == null) {
+ return;
+ }
+
if (mItemInfo.container < 0 || appState == null) {
// Write log on the model thread so that logs do not go out of order
// (for eg: drop comes after drag)
@@ -420,6 +441,7 @@ public class StatsLogCompatManager extends StatsLogManager {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return;
}
+ int cardinality = mCardinality.orElseGet(() -> getCardinality(atomInfo));
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
@@ -445,7 +467,7 @@ public class StatsLogCompatManager extends StatsLogManager {
atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
- getCardinality(atomInfo) /* cardinality */,
+ cardinality /* cardinality */,
getFeatures(atomInfo) /* features */,
getSearchAttributes(atomInfo) /* searchAttributes */,
getAttributes(atomInfo) /* attributes */
@@ -457,18 +479,12 @@ public class StatsLogCompatManager extends StatsLogManager {
* Helps to construct and log statsd compatible latency events.
*/
private static class StatsCompatLatencyLogger implements StatsLatencyLogger {
- private final Context mContext;
- private final Optional mActivityContext;
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private LatencyType mType = LatencyType.UNKNOWN;
private int mPackageId = 0;
private long mLatencyInMillis;
private int mQueryLength = -1;
-
- StatsCompatLatencyLogger(Context context, ActivityContext activityContext) {
- mContext = context;
- mActivityContext = Optional.ofNullable(activityContext);
- }
+ private int mSubEventType = 0;
@Override
public StatsLatencyLogger withInstanceId(InstanceId instanceId) {
@@ -500,6 +516,12 @@ public class StatsLogCompatManager extends StatsLogManager {
return this;
}
+ @Override
+ public StatsLatencyLogger withSubEventType(int type) {
+ this.mSubEventType = type;
+ return this;
+ }
+
@Override
public void log(EventEnum event) {
if (IS_VERBOSE) {
@@ -517,7 +539,98 @@ public class StatsLogCompatManager extends StatsLogManager {
mPackageId, // package_id
mLatencyInMillis, // latency_in_millis
mType.getId(), //type
- mQueryLength // query_length
+ mQueryLength, // query_length
+ mSubEventType // sub_event_type
+ );
+ }
+ }
+
+ /**
+ * Helps to construct and log statsd compatible impression events.
+ */
+ private static class StatsCompatImpressionLogger implements StatsImpressionLogger {
+ private final IntArray mResultTypeList = new IntArray();
+ private final IntArray mResultCountList = new IntArray();
+ private final List mAboveKeyboardList = new ArrayList<>();
+ private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
+ private State mLauncherState = State.UNKNOWN;
+ private int mQueryLength = -1;
+
+ @Override
+ public StatsImpressionLogger withInstanceId(InstanceId instanceId) {
+ this.mInstanceId = instanceId;
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withState(State state) {
+ this.mLauncherState = state;
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withQueryLength(int queryLength) {
+ this.mQueryLength = queryLength;
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withResultType(IntArray resultType) {
+ this.mResultTypeList.clear();
+ this.mResultTypeList.addAll(resultType);
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withResultCount(IntArray resultCount) {
+ this.mResultCountList.clear();
+ this.mResultCountList.addAll(resultCount);
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withAboveKeyboard(List aboveKeyboard) {
+ this.mAboveKeyboardList.clear();
+ this.mAboveKeyboardList.addAll(aboveKeyboard);
+ return this;
+ }
+
+ @Override
+ public void log(EventEnum event) {
+ boolean [] mAboveKeyboard = new boolean[mAboveKeyboardList.size()];
+ for (int i = 0; i < mAboveKeyboardList.size(); i++) {
+ mAboveKeyboard[i] = mAboveKeyboardList.get(i);
+ }
+ if (IS_VERBOSE) {
+ String name = (event instanceof Enum) ? ((Enum) event).name() :
+ event.getId() + "";
+ StringBuilder logStringBuilder = new StringBuilder("\n");
+ logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
+ logStringBuilder.append(String.format("ImpressionEvent:%s ", name));
+ logStringBuilder.append(String.format("LauncherState = %s ", mLauncherState));
+ logStringBuilder.append(String.format("QueryLength = %s ", mQueryLength));
+ for (int i = 0; i < mResultTypeList.size(); i++) {
+ logStringBuilder.append(String.format(
+ "\n ResultType = %s with ResultCount = %s with is_above_keyboard = %s",
+ mResultTypeList.get(i), mResultCountList.get(i),
+ mAboveKeyboard[i]));
+ }
+ Log.d(IMPRESSION_TAG, logStringBuilder.toString());
+ }
+
+
+
+ SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_IMPRESSION_EVENT,
+ event.getId(), // event_id
+ mInstanceId.getId(), // instance_id
+ mLauncherState.getLauncherState(), // state
+ mQueryLength, // query_length
+ //result type list
+ mResultTypeList.toArray(),
+ // result count list
+ mResultCountList.toArray(),
+ // above keyboard list
+ mAboveKeyboard
);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 53e0c2b631..60065fb16c 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -33,10 +33,11 @@ public class ActiveGestureErrorDetector {
* Enums associated to gesture navigation events.
*/
public enum GestureEvent {
- MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_LAST_TASK,
- SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
- FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK,
- CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED,
+ MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_NEW_TASK,
+ ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
+ CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
+ CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
+ FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER,
/**
* These GestureEvents are specifically associated to state flags that get set in
@@ -91,35 +92,40 @@ public class ActiveGestureErrorDetector {
case MOTION_UP:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_DOWN),
- /* errorMessage= */ prefix + "\t\tMotion up detected before/without"
+ prefix,
+ /* errorMessage= */ "Motion up detected before/without"
+ " motion down.",
writer);
break;
case ON_SETTLED_ON_END_TARGET:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.SET_END_TARGET),
- /* errorMessage= */ prefix + "\t\tonSettledOnEndTarget called "
+ prefix,
+ /* errorMessage= */ "onSettledOnEndTarget called "
+ "before/without setEndTarget.",
writer);
break;
case FINISH_RECENTS_ANIMATION:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
- /* errorMessage= */ prefix + "\t\tfinishRecentsAnimation called "
+ prefix,
+ /* errorMessage= */ "finishRecentsAnimation called "
+ "before/without startRecentsAnimation.",
writer);
break;
case CANCEL_RECENTS_ANIMATION:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
- /* errorMessage= */ prefix + "\t\tcancelRecentsAnimation called "
+ prefix,
+ /* errorMessage= */ "cancelRecentsAnimation called "
+ "before/without startRecentsAnimation.",
writer);
break;
case CLEANUP_SCREENSHOT:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
- /* errorMessage= */ prefix + "\t\trecents activity screenshot was "
+ prefix,
+ /* errorMessage= */ "recents activity screenshot was "
+ "cleaned up before/without STATE_SCREENSHOT_CAPTURED "
+ "being set.",
writer);
@@ -129,48 +135,66 @@ public class ActiveGestureErrorDetector {
encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME)
&& !encounteredEvents.contains(
GestureEvent.ON_SETTLED_ON_END_TARGET),
- /* errorMessage= */ prefix + "\t\trecents view scroller animation "
+ prefix,
+ /* errorMessage= */ "recents view scroller animation "
+ "aborted after setting end target HOME, but before"
+ " settling on end target.",
writer);
break;
case TASK_APPEARED:
errorDetected |= printErrorIfTrue(
- !encounteredEvents.contains(GestureEvent.SET_END_TARGET_LAST_TASK)
- && !encounteredEvents.contains(
- GestureEvent.SET_END_TARGET_NEW_TASK),
- /* errorMessage= */ prefix + "\t\tonTasksAppeared called "
- + "before/without setting end target to last or new task",
+ !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+ prefix,
+ /* errorMessage= */ "onTasksAppeared called "
+ + "before/without setting end target to new task",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED),
+ prefix,
+ /* errorMessage= */ "onTasksAppeared was not expected to be called",
+ writer);
+ break;
+ case EXPECTING_TASK_APPEARED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+ prefix,
+ /* errorMessage= */ "expecting onTasksAppeared to be called "
+ + "before/without setting end target to new task",
writer);
break;
case STATE_GESTURE_COMPLETED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),
- /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+ "before/without motion up.",
writer);
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
- /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+ "before/without STATE_GESTURE_STARTED.",
writer);
break;
case STATE_GESTURE_CANCELLED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),
- /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+ "before/without motion up.",
writer);
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
- /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+ "before/without STATE_GESTURE_STARTED.",
writer);
break;
case STATE_SCREENSHOT_CAPTURED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT),
- /* errorMessage= */ prefix + "\t\tSTATE_SCREENSHOT_CAPTURED set "
+ prefix,
+ /* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set "
+ "before/without STATE_CAPTURE_SCREENSHOT.",
writer);
break;
@@ -178,7 +202,8 @@ public class ActiveGestureErrorDetector {
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(
GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK),
- /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_SCROLLING_FINISHED "
+ prefix,
+ /* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED "
+ "set before/without calling "
+ "setOnPageTransitionEndCallback.",
writer);
@@ -187,16 +212,19 @@ public class ActiveGestureErrorDetector {
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(
GestureEvent.START_RECENTS_ANIMATION),
- /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED "
+ prefix,
+ /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED "
+ "set before/without startRecentsAnimation.",
writer);
break;
case MOTION_DOWN:
case SET_END_TARGET:
case SET_END_TARGET_HOME:
+ case SET_END_TARGET_NEW_TASK:
case START_RECENTS_ANIMATION:
case SET_ON_PAGE_TRANSITION_END_CALLBACK:
case CANCEL_CURRENT_ANIMATION:
+ case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER:
case STATE_GESTURE_STARTED:
case STATE_END_TARGET_ANIMATION_FINISHED:
case STATE_CAPTURE_SCREENSHOT:
@@ -210,31 +238,36 @@ public class ActiveGestureErrorDetector {
// Check that all required events were found.
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_DOWN),
- /* errorMessage= */ prefix + "\t\tMotion down never detected.",
+ prefix,
+ /* errorMessage= */ "Motion down never detected.",
writer);
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),
- /* errorMessage= */ prefix + "\t\tMotion up never detected.",
+ prefix,
+ /* errorMessage= */ "Motion up never detected.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
&& !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
- /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
+ prefix,
+ /* errorMessage= */ "setEndTarget was called, but "
+ "onSettledOnEndTarget wasn't.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
&& !encounteredEvents.contains(
GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED),
- /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
+ prefix,
+ /* errorMessage= */ "setEndTarget was called, but "
+ "STATE_END_TARGET_ANIMATION_FINISHED was never set.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
&& !encounteredEvents.contains(
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
- /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
+ prefix,
+ /* errorMessage= */ "setEndTarget was called, but "
+ "STATE_RECENTS_SCROLLING_FINISHED was never set.",
writer);
errorDetected |= printErrorIfTrue(
@@ -243,7 +276,8 @@ public class ActiveGestureErrorDetector {
&& encounteredEvents.contains(
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED)
&& !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
- /* errorMessage= */ prefix + "\t\tSTATE_END_TARGET_ANIMATION_FINISHED and "
+ prefix,
+ /* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and "
+ "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget "
+ "wasn't called.",
writer);
@@ -253,7 +287,8 @@ public class ActiveGestureErrorDetector {
GestureEvent.START_RECENTS_ANIMATION)
&& !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
&& !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION),
- /* errorMessage= */ prefix + "\t\tstartRecentsAnimation was called, but "
+ prefix,
+ /* errorMessage= */ "startRecentsAnimation was called, but "
+ "finishRecentsAnimation and cancelRecentsAnimation weren't.",
writer);
@@ -261,7 +296,8 @@ public class ActiveGestureErrorDetector {
/* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED)
&& !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED)
&& !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED),
- /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_STARTED was set, but "
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_STARTED was set, but "
+ "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
writer);
@@ -269,7 +305,8 @@ public class ActiveGestureErrorDetector {
/* condition= */ encounteredEvents.contains(
GestureEvent.STATE_CAPTURE_SCREENSHOT)
&& !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
- /* errorMessage= */ prefix + "\t\tSTATE_CAPTURE_SCREENSHOT was set, but "
+ prefix,
+ /* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but "
+ "STATE_SCREENSHOT_CAPTURED wasn't.",
writer);
@@ -278,15 +315,18 @@ public class ActiveGestureErrorDetector {
GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK)
&& !encounteredEvents.contains(
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
- /* errorMessage= */ prefix + "\t\tsetOnPageTransitionEndCallback called, but "
+ prefix,
+ /* errorMessage= */ "setOnPageTransitionEndCallback called, but "
+ "STATE_RECENTS_SCROLLING_FINISHED wasn't set.",
writer);
errorDetected |= printErrorIfTrue(
- /* condition= */ !encounteredEvents.contains(
- GestureEvent.CANCEL_CURRENT_ANIMATION)
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER)
+ && !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION)
&& !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED),
- /* errorMessage= */ prefix + "\t\tAbsSwipeUpHandler.cancelCurrentAnimation "
+ prefix,
+ /* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation "
+ "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.",
writer);
@@ -294,23 +334,17 @@ public class ActiveGestureErrorDetector {
/* condition= */ encounteredEvents.contains(
GestureEvent.STATE_RECENTS_ANIMATION_CANCELED)
&& !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT),
- /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED was set but "
+ prefix,
+ /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but "
+ "the task screenshot wasn't cleaned up.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(
- GestureEvent.SET_END_TARGET_LAST_TASK)
+ GestureEvent.EXPECTING_TASK_APPEARED)
&& !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
- /* errorMessage= */ prefix + "\t\tend target set to last task, but "
- + "onTaskAppeared wasn't called.",
- writer);
- errorDetected |= printErrorIfTrue(
- /* condition= */ encounteredEvents.contains(
- GestureEvent.SET_END_TARGET_NEW_TASK)
- && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
- /* errorMessage= */ prefix + "\t\tend target set to new task, but "
- + "onTaskAppeared wasn't called.",
+ prefix,
+ /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.",
writer);
if (!errorDetected) {
@@ -320,11 +354,11 @@ public class ActiveGestureErrorDetector {
}
private static boolean printErrorIfTrue(
- boolean condition, String errorMessage, PrintWriter writer) {
+ boolean condition, String prefix, String errorMessage, PrintWriter writer) {
if (!condition) {
return false;
}
- writer.println(errorMessage);
+ writer.println(prefix + "\t\t- " + errorMessage);
return true;
}
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 29ae9a13ca..877e28ab4e 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -25,6 +25,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.systemui.shared.system.BlurUtils;
/**
@@ -45,20 +46,15 @@ public class BaseDepthController {
}
};
- private static final MultiPropertyFactory DEPTH_PROPERTY_FACTORY =
- new MultiPropertyFactory<>("depthProperty", DEPTH, Float::max);
-
- private static final int DEPTH_INDEX_STATE_TRANSITION = 1;
- private static final int DEPTH_INDEX_WIDGET = 2;
-
- /** Property to set the depth for state transition. */
- public static final FloatProperty STATE_DEPTH =
- DEPTH_PROPERTY_FACTORY.get(DEPTH_INDEX_STATE_TRANSITION);
- /** Property to set the depth for widget picker. */
- public static final FloatProperty WIDGET_DEPTH =
- DEPTH_PROPERTY_FACTORY.get(DEPTH_INDEX_WIDGET);
+ private static final int DEPTH_INDEX_STATE_TRANSITION = 0;
+ private static final int DEPTH_INDEX_WIDGET = 1;
+ private static final int DEPTH_INDEX_COUNT = 2;
protected final Launcher mLauncher;
+ /** Property to set the depth for state transition. */
+ public final MultiProperty stateDepth;
+ /** Property to set the depth for widget picker. */
+ public final MultiProperty widgetDepth;
/**
* Blur radius when completely zoomed out, in pixels.
@@ -71,7 +67,7 @@ public class BaseDepthController {
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
*/
- protected float mDepth;
+ private float mDepth;
protected SurfaceControl mSurface;
@@ -92,6 +88,11 @@ public class BaseDepthController {
mLauncher = activity;
mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
mWallpaperManager = activity.getSystemService(WallpaperManager.class);
+
+ MultiPropertyFactory depthProperty =
+ new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
+ stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
+ widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
}
protected void setCrossWindowBlursEnabled(boolean isEnabled) {
@@ -143,7 +144,7 @@ public class BaseDepthController {
}
}
- protected void setDepth(float depth) {
+ private void setDepth(float depth) {
depth = Utilities.boundToRange(depth, 0, 1);
// Round out the depth to dedupe frequent, non-perceptable updates
int depthI = (int) (depth * 256);
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 143042fb31..2a513ee7ee 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -16,12 +16,14 @@
package com.android.quickstep.util;
import android.annotation.CallSuper;
+import android.view.Surface.Rotation;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.util.HashMap;
import java.util.Map;
@@ -32,15 +34,20 @@ import java.util.Map;
public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
+ private final RotationChangeProvider mRotationChangeProvider;
private final Map mOriginalClipToPadding = new HashMap<>();
private final Map mOriginalClipChildren = new HashMap<>();
+ private final UnfoldMoveFromCenterRotationListener mRotationListener =
+ new UnfoldMoveFromCenterRotationListener();
private boolean mAnimationInProgress = false;
- public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager) {
+ public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
+ mRotationChangeProvider = rotationChangeProvider;
}
@CallSuper
@@ -50,6 +57,7 @@ public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProg
mMoveFromCenterAnimation.updateDisplayProperties();
onPrepareViewsForAnimation();
onTransitionProgress(0f);
+ mRotationChangeProvider.addCallback(mRotationListener);
}
@CallSuper
@@ -62,6 +70,7 @@ public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProg
@Override
public void onTransitionFinished() {
mAnimationInProgress = false;
+ mRotationChangeProvider.removeCallback(mRotationListener);
mMoveFromCenterAnimation.onTransitionFinished();
clearRegisteredViews();
}
@@ -109,4 +118,14 @@ public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProg
view.setClipChildren(originalClipChildren);
}
}
+
+ private class UnfoldMoveFromCenterRotationListener implements
+ RotationChangeProvider.RotationListener {
+
+ @Override
+ public void onRotationChanged(@Rotation int newRotation) {
+ mMoveFromCenterAnimation.updateDisplayProperties(newRotation);
+ updateRegisteredViewsIfNeeded();
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
new file mode 100644
index 0000000000..433d23fa97
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+/**
+ * A {@link Task} container that can contain N number of tasks that are part of the desktop in
+ * recent tasks list.
+ */
+public class DesktopTask extends GroupTask {
+
+ public ArrayList tasks;
+
+ public DesktopTask(ArrayList tasks) {
+ super(tasks.get(0), null, null, TaskView.Type.DESKTOP);
+ this.tasks = tasks;
+ }
+
+ @Override
+ public boolean containsTask(int taskId) {
+ for (Task task : tasks) {
+ if (task.key.id == taskId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasMultipleTasks() {
+ return true;
+ }
+
+ @Override
+ public DesktopTask copy() {
+ return new DesktopTask(tasks);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index f30d00c722..2be4f0a519 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -20,6 +20,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
/**
@@ -27,24 +28,25 @@ import com.android.systemui.shared.recents.model.Task;
* are represented as an app-pair in the recents task list.
*/
public class GroupTask {
- public @NonNull Task task1;
- public @Nullable Task task2;
- public @Nullable
- SplitBounds mSplitBounds;
+ @NonNull
+ public final Task task1;
+ @Nullable
+ public final Task task2;
+ @Nullable
+ public final SplitBounds mSplitBounds;
+ @TaskView.Type
+ public final int taskViewType;
- public GroupTask(@NonNull Task t1, @Nullable Task t2,
- @Nullable SplitBounds splitBounds) {
+ public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) {
+ this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE);
+ }
+
+ protected GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds,
+ @TaskView.Type int taskViewType) {
task1 = t1;
task2 = t2;
mSplitBounds = splitBounds;
- }
-
- public GroupTask(@NonNull GroupTask group) {
- task1 = new Task(group.task1);
- task2 = group.task2 != null
- ? new Task(group.task2)
- : null;
- mSplitBounds = group.mSplitBounds;
+ this.taskViewType = taskViewType;
}
public boolean containsTask(int taskId) {
@@ -54,4 +56,14 @@ public class GroupTask {
public boolean hasMultipleTasks() {
return task2 != null;
}
+
+ /**
+ * Create a copy of this instance
+ */
+ public GroupTask copy() {
+ return new GroupTask(
+ new Task(task1),
+ task2 != null ? new Task(task2) : null,
+ mSplitBounds);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 97be4370b9..170c622035 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -18,13 +18,11 @@ package com.android.quickstep.util;
import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_UNFOLD_ANIMATION;
import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
-import static com.android.launcher3.Utilities.comp;
import android.annotation.Nullable;
import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import androidx.core.view.OneShotPreDrawListener;
@@ -34,6 +32,7 @@ import com.android.launcher3.Workspace;
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -62,16 +61,17 @@ public class LauncherUnfoldAnimationController {
public LauncherUnfoldAnimationController(
Launcher launcher,
WindowManager windowManager,
- UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) {
+ UnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
+ RotationChangeProvider rotationChangeProvider) {
mLauncher = launcher;
mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
unfoldTransitionProgressProvider);
mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mNaturalOrientationProgressProvider = new NaturalRotationUnfoldProgressProvider(launcher,
- WindowManagerGlobal.getWindowManagerService(), mProgressProvider);
+ rotationChangeProvider, mProgressProvider);
mNaturalOrientationProgressProvider.init();
// Animated in all orientations
@@ -134,7 +134,7 @@ public class LauncherUnfoldAnimationController {
@Override
public void onTransitionProgress(float progress) {
if (mQsbInsettable != null) {
- float insetPercentage = comp(progress) * MAX_WIDTH_INSET_FRACTION;
+ float insetPercentage = (1 - progress) * MAX_WIDTH_INSET_FRACTION;
mQsbInsettable.setHorizontalInsets(insetPercentage);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java
index 9e7351a2de..3d9e09ee81 100644
--- a/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java
+++ b/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java
@@ -16,10 +16,6 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
-
-import android.view.animation.Interpolator;
-
/**
* Timings for the OverviewSplitSelect > confirmed animation on phones.
*/
@@ -33,8 +29,4 @@ public class PhoneSplitToConfirmTimings
public int getStagedRectSlideEnd() { return 333; }
public int getDuration() { return PHONE_CONFIRM_DURATION; }
- public Interpolator getStagedRectXInterpolator() { return EMPHASIZED; }
- public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; }
- public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; }
- public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; }
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index c459f303d1..db8c7f23b9 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -45,7 +45,7 @@ import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
@@ -139,7 +139,7 @@ public class RecentsOrientedState implements
public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
IntConsumer rotationChangeListener) {
mContext = context;
- mSharedPrefs = Utilities.getPrefs(context);
+ mSharedPrefs = LauncherPrefs.getPrefs(context);
mOrientationListener = new OrientationEventListener(context) {
@Override
public void onOrientationChanged(int degrees) {
diff --git a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
new file mode 100644
index 0000000000..a2f48ddc9b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+/**
+ * Extension for {@link SurfaceTransaction} which records the commands for mocking
+ */
+public class RecordingSurfaceTransaction extends SurfaceTransaction {
+
+ /**
+ * A mock builder which can be used for recording values
+ */
+ public final MockProperties mockProperties = new MockProperties();
+
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index ee82ae67df..10f2eaa2f0 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -16,23 +16,22 @@
package com.android.quickstep.util;
import android.animation.AnimatorSet;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
public abstract class RemoteAnimationProvider {
- public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets);
+ public abstract AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets);
/**
* @return the target with the lowest opaque layer for a certain app animation, or null.
*/
- public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
- RemoteAnimationTargetCompat[] appTargets, int mode) {
+ public static RemoteAnimationTarget findLowestOpaqueLayerTarget(
+ RemoteAnimationTarget[] appTargets, int mode) {
int lowestLayer = Integer.MAX_VALUE;
int lowestLayerIndex = -1;
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
+ RemoteAnimationTarget target = appTargets[i];
if (target.mode == mode && !target.isTranslucent) {
int layer = target.prefixOrderIndex;
if (layer < lowestLayer) {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 81c124f7e2..382cf79e1e 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -15,14 +15,14 @@
*/
package com.android.quickstep.util;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl.Transaction;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
/**
* Animation listener which fades out the closing targets
@@ -32,24 +32,24 @@ public class RemoteFadeOutAnimationListener implements AnimatorUpdateListener {
private final RemoteAnimationTargets mTarget;
private boolean mFirstFrame = true;
- public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ public RemoteFadeOutAnimationListener(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets,
- new RemoteAnimationTargetCompat[0], MODE_CLOSING);
+ new RemoteAnimationTarget[0], MODE_CLOSING);
}
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- TransactionCompat t = new TransactionCompat();
+ Transaction t = new Transaction();
if (mFirstFrame) {
- for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
+ for (RemoteAnimationTarget target : mTarget.unfilteredApps) {
t.show(target.leash);
}
mFirstFrame = false;
}
float alpha = 1 - valueAnimator.getAnimatedFraction();
- for (RemoteAnimationTargetCompat app : mTarget.apps) {
+ for (RemoteAnimationTarget app : mTarget.apps) {
t.setAlpha(app.leash, alpha);
}
t.apply();
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
index 2966fbb5db..7dc1b32858 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
@@ -25,7 +25,7 @@ import android.view.animation.Interpolator;
*/
public interface SplitAnimationTimings {
int TABLET_ENTER_DURATION = 866;
- int TABLET_CONFIRM_DURATION = 383;
+ int TABLET_CONFIRM_DURATION = 500;
int PHONE_ENTER_DURATION = 517;
int PHONE_CONFIRM_DURATION = 333;
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
deleted file mode 100644
index 483a1c652a..0000000000
--- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2020 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.util;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.WindowBounds;
-
-import java.util.ArrayList;
-
-/**
- * Utility class to hold the information abound a window bounds for split screen
- */
-@TargetApi(Build.VERSION_CODES.R)
-public class SplitScreenBounds {
-
- public static final SplitScreenBounds INSTANCE = new SplitScreenBounds();
- private final ArrayList mListeners = new ArrayList<>();
-
- @Nullable
- private WindowBounds mBounds;
-
- private SplitScreenBounds() { }
-
- @UiThread
- public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) {
- if (!bounds.equals(mBounds)) {
- mBounds = bounds;
- for (OnChangeListener listener : mListeners) {
- listener.onSecondaryWindowBoundsChanged();
- }
- }
- }
-
- public @NonNull WindowBounds getSecondaryWindowBounds(Context context) {
- if (mBounds == null) {
- mBounds = createDefaultWindowBounds(context);
- }
- return mBounds;
- }
-
- /**
- * Creates window bounds as 50% of device size
- */
- private static WindowBounds createDefaultWindowBounds(Context context) {
- WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
- WindowBounds bounds = WindowBounds.fromWindowMetrics(wm);
-
- int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
- int halfDividerSize = context.getResources()
- .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
-
- if (rotation == ROTATION_0 || rotation == ROTATION_180) {
- bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize;
- bounds.insets.top = 0;
- } else {
- bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize;
- bounds.insets.left = 0;
- }
- return new WindowBounds(bounds.bounds, bounds.insets);
- }
-
- public void addOnChangeListener(OnChangeListener listener) {
- mListeners.add(listener);
- }
-
- public void removeOnChangeListener(OnChangeListener listener) {
- mListeners.remove(listener);
- }
-
- /**
- * Interface to receive window bounds changes
- */
- public interface OnChangeListener {
-
- /**
- * Called when window bounds for secondary window changes
- */
- void onSecondaryWindowBoundsChanged();
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index efbe783125..681f068e52 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -22,10 +22,10 @@ import static android.app.PendingIntent.FLAG_MUTABLE;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.getOppositeStagePosition;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.PendingIntent;
@@ -35,12 +35,16 @@ import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.RemoteTransition;
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
@@ -58,14 +62,11 @@ import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.RemoteTransitionRunner;
import java.util.function.Consumer;
@@ -86,8 +87,8 @@ public class SplitSelectStateController {
private ItemInfo mItemInfo;
private Intent mInitialTaskIntent;
private int mInitialTaskId = INVALID_TASK_ID;
+ private Intent mSecondTaskIntent;
private int mSecondTaskId = INVALID_TASK_ID;
- private String mSecondTaskPackageName;
private boolean mRecentsAnimationRunning;
@Nullable
private UserHandle mUser;
@@ -97,6 +98,8 @@ public class SplitSelectStateController {
/** Represents where split is intended to be invoked from. */
private StatsLogManager.EventEnum mSplitEvent;
+ private FloatingTaskView mFirstFloatingTaskView;
+
public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
DepthController depthController, StatsLogManager statsLogManager) {
mContext = context;
@@ -108,19 +111,38 @@ public class SplitSelectStateController {
}
/**
- * To be called after first task selected
+ * To be called after first task selected in Overview.
*/
- public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition,
+ public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
- mInitialTaskId = taskId;
+ mInitialTaskId = task.key.id;
setInitialData(stagePosition, splitEvent, itemInfo);
}
+ /**
+ * To be called after first task selected from home or all apps.
+ */
public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
- @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
- mInitialTaskIntent = intent;
- mUser = itemInfo.user;
- mItemInfo = itemInfo;
+ @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent,
+ @Nullable Task alreadyRunningTask) {
+ if (alreadyRunningTask != null) {
+ mInitialTaskId = alreadyRunningTask.key.id;
+ } else {
+ mInitialTaskIntent = intent;
+ mUser = itemInfo.user;
+ }
+
+ setInitialData(stagePosition, splitEvent, itemInfo);
+ }
+
+ /**
+ * To be called after first task selected from using a split shortcut from the fullscreen
+ * running app.
+ */
+ public void setInitialTaskSelect(ActivityManager.RunningTaskInfo info,
+ @StagePosition int stagePosition, @NonNull ItemInfo itemInfo,
+ StatsLogManager.EventEnum splitEvent) {
+ mInitialTaskId = info.taskId;
setInitialData(stagePosition, splitEvent, itemInfo);
}
@@ -136,26 +158,10 @@ public class SplitSelectStateController {
* to be launched. Call after launcher side animations are complete.
*/
public void launchSplitTasks(Consumer callback) {
- final Intent fillInIntent;
- if (mInitialTaskIntent != null) {
- fillInIntent = new Intent();
- if (TextUtils.equals(mInitialTaskIntent.getComponent().getPackageName(),
- mSecondTaskPackageName)) {
- fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- }
- } else {
- fillInIntent = null;
- }
-
- final PendingIntent pendingIntent = mInitialTaskIntent == null ? null : (mUser != null
- ? PendingIntent.getActivityAsUser(mContext, 0, mInitialTaskIntent,
- FLAG_MUTABLE, null /* options */, mUser)
- : PendingIntent.getActivity(mContext, 0, mInitialTaskIntent, FLAG_MUTABLE));
-
Pair instanceIds =
LogUtils.getShellShareableInstanceId();
- launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition,
- callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
+ launchTasks(mInitialTaskId, mInitialTaskIntent, mSecondTaskId, mSecondTaskIntent,
+ mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
instanceIds.first);
mStatsLogManager.logger()
@@ -164,23 +170,23 @@ public class SplitSelectStateController {
.log(mSplitEvent);
}
-
/**
* To be called as soon as user selects the second task (even if animations aren't complete)
* @param task The second task that will be launched.
*/
public void setSecondTask(Task task) {
mSecondTaskId = task.key.id;
- if (mInitialTaskIntent != null) {
- mSecondTaskPackageName = task.getTopComponent().getPackageName();
- }
+ }
+
+ public void setSecondTask(Intent intent) {
+ mSecondTaskIntent = intent;
}
/**
* To be called when we want to launch split pairs from an existing GroupedTaskView.
*/
- public void launchTasks(GroupedTaskView groupedTaskView,
- Consumer callback, boolean freezeTaskList) {
+ public void launchTasks(GroupedTaskView groupedTaskView, Consumer callback,
+ boolean freezeTaskList) {
mLaunchingTaskView = groupedTaskView;
TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
groupedTaskView.getTaskIdAttributeContainers();
@@ -196,73 +202,118 @@ public class SplitSelectStateController {
*/
public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
Consumer callback, boolean freezeTaskList, float splitRatio) {
- launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2,
- stagePosition, callback, freezeTaskList, splitRatio, null);
+ launchTasks(taskId1, null /* intent1 */, taskId2, null /* intent2 */, stagePosition,
+ callback, freezeTaskList, splitRatio, null);
}
/**
* To be called when we want to launch split pairs from Overview. Split can be initiated from
* either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
* fill in intent with a taskId2 are set.
- * @param taskPendingIntent is null when split is initiated from Overview
+ * @param intent1 is null when split is initiated from Overview
* @param stagePosition representing location of task1
- * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that create
- * a split instance, null for cases that bring existing instaces to the
+ * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that
+ * create a split instance, null for cases that bring existing instaces to the
* foreground (quickswitch, launching previous pairs from overview)
*/
- public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
- @Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition,
+ public void launchTasks(int taskId1, @Nullable Intent intent1, int taskId2,
+ @Nullable Intent intent2, @StagePosition int stagePosition,
Consumer callback, boolean freezeTaskList, float splitRatio,
@Nullable InstanceId shellInstanceId) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "launchSplitTasks");
- // Assume initial task is for top/left part of screen
- final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? new int[]{taskId1, taskId2}
- : new int[]{taskId2, taskId1};
+ final ActivityOptions options1 = ActivityOptions.makeBasic();
+ if (freezeTaskList) {
+ options1.setFreezeRecentTasksReordering();
+ }
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- RemoteSplitLaunchTransitionRunner animationRunner =
- new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2,
- callback);
- mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
- null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
- new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
- ActivityThread.currentActivityThread().getApplicationThread()),
- shellInstanceId);
- // TODO(b/237635859): handle intent/shortcut + task with shell transition
+ final RemoteSplitLaunchTransitionRunner animationRunner =
+ new RemoteSplitLaunchTransitionRunner(taskId1, taskId2, callback);
+ final RemoteTransition remoteTransition = new RemoteTransition(animationRunner,
+ ActivityThread.currentActivityThread().getApplicationThread());
+ if (intent1 == null && intent2 == null) {
+ mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2,
+ null /* options2 */, stagePosition, splitRatio, remoteTransition,
+ shellInstanceId);
+ } else if (intent2 == null) {
+ launchIntentOrShortcut(intent1, options1, taskId2, stagePosition, splitRatio,
+ remoteTransition, shellInstanceId);
+ } else if (intent1 == null) {
+ launchIntentOrShortcut(intent2, options1, taskId1,
+ getOppositeStagePosition(stagePosition), splitRatio, remoteTransition,
+ shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntents(getPendingIntent(intent1), options1.toBundle(),
+ getPendingIntent(intent2), null /* options2 */, stagePosition,
+ splitRatio, remoteTransition, shellInstanceId);
+ }
} else {
- RemoteSplitLaunchAnimationRunner animationRunner =
- new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2,
- callback);
+ final RemoteSplitLaunchAnimationRunner animationRunner =
+ new RemoteSplitLaunchAnimationRunner(taskId1, taskId2, callback);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
- 300, 150,
+ animationRunner, 300, 150,
ActivityThread.currentActivityThread().getApplicationThread());
- ActivityOptions mainOpts = ActivityOptions.makeBasic();
- if (freezeTaskList) {
- mainOpts.setFreezeRecentTasksReordering();
- }
- if (taskPendingIntent == null) {
- mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
- taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
- splitRatio, adapter, shellInstanceId);
+ if (intent1 == null && intent2 == null) {
+ mSystemUiProxy.startTasksWithLegacyTransition(taskId1, options1.toBundle(),
+ taskId2, null /* options2 */, stagePosition, splitRatio, adapter,
+ shellInstanceId);
+ } else if (intent2 == null) {
+ launchIntentOrShortcutLegacy(intent1, options1, taskId2, stagePosition, splitRatio,
+ adapter, shellInstanceId);
+ } else if (intent1 == null) {
+ launchIntentOrShortcutLegacy(intent2, options1, taskId1,
+ getOppositeStagePosition(stagePosition), splitRatio, adapter,
+ shellInstanceId);
} else {
- final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
- taskPendingIntent.getCreatorUserHandle());
- if (shortcutInfo != null) {
- mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId2,
- mainOpts.toBundle(), null /* sideOptions */, stagePosition, splitRatio,
- adapter, shellInstanceId);
- } else {
- mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
- fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
- stagePosition, splitRatio, adapter, shellInstanceId);
- }
+ mSystemUiProxy.startIntentsWithLegacyTransition(getPendingIntent(intent1),
+ options1.toBundle(), getPendingIntent(intent2), null /* options2 */,
+ stagePosition, splitRatio, adapter, shellInstanceId);
}
}
}
+ private void launchIntentOrShortcut(Intent intent, ActivityOptions options1, int taskId,
+ @StagePosition int stagePosition, float splitRatio, RemoteTransition remoteTransition,
+ @Nullable InstanceId shellInstanceId) {
+ PendingIntent pendingIntent = getPendingIntent(intent);
+ final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+ pendingIntent.getCreatorUserHandle());
+ if (shortcutInfo != null) {
+ mSystemUiProxy.startShortcutAndTask(shortcutInfo,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition,
+ splitRatio, remoteTransition, shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntentAndTask(pendingIntent, options1.toBundle(), taskId,
+ null /* options2 */, stagePosition, splitRatio, remoteTransition,
+ shellInstanceId);
+ }
+ }
+
+ private void launchIntentOrShortcutLegacy(Intent intent, ActivityOptions options1, int taskId,
+ @StagePosition int stagePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ @Nullable InstanceId shellInstanceId) {
+ PendingIntent pendingIntent = getPendingIntent(intent);
+ final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+ pendingIntent.getCreatorUserHandle());
+ if (shortcutInfo != null) {
+ mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition,
+ splitRatio, adapter, shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntentAndTaskWithLegacyTransition(pendingIntent,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition, splitRatio,
+ adapter, shellInstanceId);
+ }
+ }
+
+ private PendingIntent getPendingIntent(Intent intent) {
+ return intent == null ? null : (mUser != null
+ ? PendingIntent.getActivityAsUser(mContext, 0, intent,
+ FLAG_MUTABLE, null /* options */, mUser)
+ : PendingIntent.getActivity(mContext, 0, intent, FLAG_MUTABLE));
+ }
+
public @StagePosition int getActiveSplitStagePosition() {
return mStagePosition;
}
@@ -272,7 +323,7 @@ public class SplitSelectStateController {
}
public void setRecentsAnimationRunning(boolean running) {
- this.mRecentsAnimationRunning = running;
+ mRecentsAnimationRunning = running;
}
@Nullable
@@ -300,65 +351,75 @@ public class SplitSelectStateController {
/**
* Requires Shell Transitions
*/
- private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner {
+ private class RemoteSplitLaunchTransitionRunner extends IRemoteTransition.Stub {
private final int mInitialTaskId;
- private final PendingIntent mInitialTaskPendingIntent;
private final int mSecondTaskId;
private final Consumer mSuccessCallback;
- RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
- int secondTaskId, Consumer callback) {
+ RemoteSplitLaunchTransitionRunner(int initialTaskId, int secondTaskId,
+ Consumer callback) {
mInitialTaskId = initialTaskId;
- mInitialTaskPendingIntent = initialTaskPendingIntent;
mSecondTaskId = secondTaskId;
mSuccessCallback = callback;
}
@Override
- public void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
- TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
- mDepthController, mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId,
- info, t, () -> {
- finishCallback.run();
- if (mSuccessCallback != null) {
- mSuccessCallback.accept(true);
- }
- });
- // After successful launch, call resetState
- resetState();
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ final Runnable finishAdapter = () -> {
+ try {
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call transition finished callback", e);
+ }
+ };
+
+ MAIN_EXECUTOR.execute(() -> {
+ TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
+ mDepthController, mInitialTaskId, mSecondTaskId, info, t, () -> {
+ finishAdapter.run();
+ if (mSuccessCallback != null) {
+ mSuccessCallback.accept(true);
+ }
+ });
+ // After successful launch, call resetState
+ resetState();
+ });
}
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishedCallback) { }
}
/**
* LEGACY
* Remote animation runner for animation to launch an app.
*/
- private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat {
+ private class RemoteSplitLaunchAnimationRunner extends RemoteAnimationRunnerCompat {
private final int mInitialTaskId;
- private final PendingIntent mInitialTaskPendingIntent;
private final int mSecondTaskId;
private final Consumer mSuccessCallback;
- RemoteSplitLaunchAnimationRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
- int secondTaskId, Consumer successCallback) {
+ RemoteSplitLaunchAnimationRunner(int initialTaskId, int secondTaskId,
+ Consumer successCallback) {
mInitialTaskId = initialTaskId;
- mInitialTaskPendingIntent = initialTaskPendingIntent;
mSecondTaskId = secondTaskId;
mSuccessCallback = successCallback;
}
@Override
- public void onAnimationStart(int transit, RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
Runnable finishedCallback) {
postAsyncCallback(mHandler,
() -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
- mLaunchingTaskView, mInitialTaskId, mInitialTaskPendingIntent,
- mSecondTaskId, apps, wallpapers, nonApps, mStateManager,
- mDepthController, () -> {
+ mLaunchingTaskView, mInitialTaskId, mSecondTaskId, apps, wallpapers,
+ nonApps, mStateManager, mDepthController, () -> {
finishedCallback.run();
if (mSuccessCallback != null) {
mSuccessCallback.accept(true);
@@ -368,7 +429,7 @@ public class SplitSelectStateController {
}
@Override
- public void onAnimationCancelled() {
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
postAsyncCallback(mHandler, () -> {
if (mSuccessCallback != null) {
// Launching legacy tasks while recents animation is running will always cause
@@ -387,6 +448,7 @@ public class SplitSelectStateController {
mInitialTaskId = INVALID_TASK_ID;
mInitialTaskIntent = null;
mSecondTaskId = INVALID_TASK_ID;
+ mSecondTaskIntent = null;
mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
mRecentsAnimationRunning = false;
mLaunchingTaskView = null;
@@ -399,7 +461,7 @@ public class SplitSelectStateController {
* chosen
*/
public boolean isSplitSelectActive() {
- return isInitialTaskIntentSet() && mSecondTaskId == INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && !isSecondTaskIntentSet();
}
/**
@@ -407,7 +469,7 @@ public class SplitSelectStateController {
* be launched
*/
public boolean isBothSplitAppsConfirmed() {
- return isInitialTaskIntentSet() && mSecondTaskId != INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && isSecondTaskIntentSet();
}
private boolean isInitialTaskIntentSet() {
@@ -417,4 +479,16 @@ public class SplitSelectStateController {
public int getInitialTaskId() {
return mInitialTaskId;
}
+
+ private boolean isSecondTaskIntentSet() {
+ return (mSecondTaskId != INVALID_TASK_ID || mSecondTaskIntent != null);
+ }
+
+ public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) {
+ mFirstFloatingTaskView = floatingTaskView;
+ }
+
+ public FloatingTaskView getFirstFloatingTaskView() {
+ return mFirstFloatingTaskView;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
index 3026e98aa9..f5b00cf42b 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
@@ -16,6 +16,8 @@
package com.android.quickstep.util;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+
import android.view.animation.Interpolator;
/**
@@ -33,12 +35,12 @@ abstract class SplitToConfirmTimings implements SplitAnimationTimings {
// Common timings
public int getInstructionsFadeStart() { return 0; }
public int getInstructionsFadeEnd() { return 67; }
+ public Interpolator getStagedRectXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; }
abstract public int getDuration();
- abstract public Interpolator getStagedRectXInterpolator();
- abstract public Interpolator getStagedRectYInterpolator();
- abstract public Interpolator getStagedRectScaleXInterpolator();
- abstract public Interpolator getStagedRectScaleYInterpolator();
public float getInstructionsFadeStartOffset() {
return (float) getInstructionsFadeStart() / getDuration();
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
new file mode 100644
index 0000000000..e5c74dc2e3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.quickstep.views.FloatingTaskView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+
+/** Handles when the stage split lands on the home screen. */
+public class SplitToWorkspaceController {
+
+ private final Launcher mLauncher;
+ private final DeviceProfile mDP;
+ private final SplitSelectStateController mController;
+
+ private final int mHalfDividerSize;
+
+ public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) {
+ mLauncher = launcher;
+ mDP = mLauncher.getDeviceProfile();
+ mController = controller;
+
+ mHalfDividerSize = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.multi_window_task_divider_size) / 2;
+ }
+
+ /**
+ * Handles second app selection from stage split. If the item can't be opened in split or
+ * it's not in stage split state, we pass it onto Launcher's default item click handler.
+ */
+ public boolean handleSecondAppSelectionForSplit(View view) {
+ if ((!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
+ && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get())
+ || !mController.isSplitSelectActive()) {
+ return false;
+ }
+ Object tag = view.getTag();
+ Intent intent;
+ BitmapInfo bitmapInfo;
+ if (tag instanceof WorkspaceItemInfo) {
+ final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
+ intent = workspaceItemInfo.intent;
+ bitmapInfo = workspaceItemInfo.bitmap;
+ } else if (tag instanceof com.android.launcher3.model.data.AppInfo) {
+ final com.android.launcher3.model.data.AppInfo appInfo =
+ (com.android.launcher3.model.data.AppInfo) tag;
+ intent = appInfo.intent;
+ bitmapInfo = appInfo.bitmap;
+ } else {
+ return false;
+ }
+
+ mController.setSecondTask(intent);
+
+ boolean isTablet = mLauncher.getDeviceProfile().isTablet;
+ SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
+ PendingAnimation pendingAnimation = new PendingAnimation(timings.getDuration());
+
+ Rect firstTaskStartingBounds = new Rect();
+ Rect firstTaskEndingBounds = new Rect();
+ RectF secondTaskStartingBounds = new RectF();
+ Rect secondTaskEndingBounds = new Rect();
+
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.getPagedOrientationHandler().getFinalSplitPlaceholderBounds(mHalfDividerSize,
+ mDP, mController.getActiveSplitStagePosition(), firstTaskEndingBounds,
+ secondTaskEndingBounds);
+
+ FloatingTaskView firstFloatingTaskView = mController.getFirstFloatingTaskView();
+ firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+ firstFloatingTaskView.addConfirmAnimation(pendingAnimation,
+ new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
+ false /* fadeWithThumbnail */, true /* isStagedTask */);
+
+ FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher,
+ view, null /* thumbnail */, bitmapInfo.newIcon(mLauncher),
+ secondTaskStartingBounds);
+ secondFloatingTaskView.setAlpha(1);
+ secondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
+ secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
+
+ pendingAnimation.addListener(new AnimatorListenerAdapter() {
+ private boolean mIsCancelled = false;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCancelled = true;
+ cleanUp();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mIsCancelled) {
+ mController.launchSplitTasks(aBoolean -> cleanUp());
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ }
+ }
+
+ private void cleanUp() {
+ mLauncher.getDragLayer().removeView(firstFloatingTaskView);
+ mLauncher.getDragLayer().removeView(secondFloatingTaskView);
+ mController.resetState();
+ }
+ });
+ pendingAnimation.buildAnim().start();
+ return true;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
new file mode 100644
index 0000000000..24d832640f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.view.View;
+
+import androidx.annotation.BinderThread;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.FloatingTaskView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/** Transitions app from fullscreen to stage split when triggered from keyboard shortcuts. */
+public class SplitWithKeyboardShortcutController {
+
+ private final QuickstepLauncher mLauncher;
+ private final SplitSelectStateController mController;
+ private final OverviewComponentObserver mOverviewComponentObserver;
+
+ private final int mSplitPlaceholderSize;
+ private final int mSplitPlaceholderInset;
+
+ public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
+ SplitSelectStateController controller) {
+ mLauncher = launcher;
+ mController = controller;
+ RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
+ launcher.getApplicationContext());
+ mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
+ deviceState);
+
+ mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_size);
+ mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_inset);
+ }
+
+ @BinderThread
+ public void enterStageSplit(boolean leftOrTop) {
+ if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()) {
+ return;
+ }
+ RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
+ SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
+ false /* allowMinimizeSplitScreen */);
+ SplitWithKeyboardShortcutRecentsAnimationListener listener =
+ new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
+
+ MAIN_EXECUTOR.execute(() -> {
+ callbacks.addListener(listener);
+ UI_HELPER_EXECUTOR.execute(
+ // Transition from fullscreen app to enter stage split in launcher with
+ // recents animation.
+ () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+ mOverviewComponentObserver.getOverviewIntent(),
+ SystemClock.uptimeMillis(), callbacks, null, null));
+ });
+ }
+
+ public void onDestroy() {
+ mOverviewComponentObserver.onDestroy();
+ }
+
+ private class SplitWithKeyboardShortcutRecentsAnimationListener implements
+ RecentsAnimationCallbacks.RecentsAnimationListener {
+
+ private final boolean mLeftOrTop;
+ private final Rect mTempRect = new Rect();
+
+ private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
+ mLeftOrTop = leftOrTop;
+ }
+
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ ActivityManager.RunningTaskInfo runningTaskInfo =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ mController.setInitialTaskSelect(runningTaskInfo,
+ mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
+ null /* itemInfo */,
+ mLeftOrTop ? LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP
+ : LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM);
+
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
+ mSplitPlaceholderSize, mSplitPlaceholderInset, mLauncher.getDeviceProfile(),
+ mController.getActiveSplitStagePosition(), mTempRect);
+
+ PendingAnimation anim = new PendingAnimation(
+ SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
+ RectF startingTaskRect = new RectF();
+ final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
+ mLauncher, mLauncher.getDragLayer(),
+ controller.screenshotTask(runningTaskInfo.taskId).thumbnail,
+ null /* icon */, startingTaskRect);
+ RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
+ .getIconCache()
+ .updateIconInBackground(
+ Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo,
+ false /* isLocked */),
+ (task) -> {
+ if (task.thumbnail != null) {
+ floatingTaskView.setIcon(task.thumbnail.thumbnail);
+ }
+ });
+ floatingTaskView.setAlpha(1);
+ floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
+ false /* fadeWithThumbnail */, true /* isStagedTask */);
+ mController.setFirstFloatingTaskView(floatingTaskView);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ controller.finish(true /* toRecents */, null /* onFinishComplete */,
+ false /* sendUserLeaveHint */);
+ }
+ });
+ anim.buildAnim().start();
+ }
+ };
+}
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index eec8582756..ad54a709d7 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -46,6 +46,7 @@ import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -122,8 +123,7 @@ public class StaggeredWorkspaceAnim {
if (grid.isVerticalBarLayout()) {
for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
View child = hotseatIcons.getChildAt(i);
- CellLayout.LayoutParams lp =
- ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration);
}
} else {
@@ -193,7 +193,7 @@ public class StaggeredWorkspaceAnim {
// Set up springs on workspace items.
for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
View child = itemsContainer.getChildAt(i);
- CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration);
}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
new file mode 100644
index 0000000000..7ab285dfa7
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Helper class for building a {@link Transaction}.
+ */
+public class SurfaceTransaction {
+
+ private final Transaction mTransaction = new Transaction();
+ private final float[] mTmpValues = new float[9];
+
+ /**
+ * Creates a new builder for the provided surface
+ */
+ public SurfaceProperties forSurface(SurfaceControl surface) {
+ return surface.isValid() ? new SurfaceProperties(surface) : new MockProperties();
+ }
+
+ /**
+ * Returns the final transaction
+ */
+ public Transaction getTransaction() {
+ return mTransaction;
+ }
+
+ /**
+ * Utility class to update surface params in a transaction
+ */
+ public class SurfaceProperties {
+
+ private final SurfaceControl mSurface;
+
+ SurfaceProperties(SurfaceControl surface) {
+ mSurface = surface;
+ }
+
+ /**
+ * @param alpha The alpha value to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setAlpha(float alpha) {
+ mTransaction.setAlpha(mSurface, alpha);
+ return this;
+ }
+
+ /**
+ * @param matrix The matrix to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ mTransaction.setMatrix(mSurface, matrix, mTmpValues);
+ return this;
+ }
+
+ /**
+ * @param windowCrop The window crop to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ mTransaction.setWindowCrop(mSurface, windowCrop);
+ return this;
+ }
+
+ /**
+ * @param relativeLayer The relative layer.
+ * @return this Builder
+ */
+ public SurfaceProperties setLayer(int relativeLayer) {
+ mTransaction.setLayer(mSurface, relativeLayer);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for rounded corners to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setCornerRadius(float radius) {
+ mTransaction.setCornerRadius(mSurface, radius);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for the shadows to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setShadowRadius(float radius) {
+ mTransaction.setShadowRadius(mSurface, radius);
+ return this;
+ }
+ }
+
+ /**
+ * Extension of {@link SurfaceProperties} which just stores all the values locally
+ */
+ public class MockProperties extends SurfaceProperties {
+
+ public float alpha = -1;
+ public Matrix matrix = null;
+ public Rect windowCrop = null;
+ public float cornerRadius = 0;
+ public float shadowRadius = 0;
+
+ protected MockProperties() {
+ super(null);
+ }
+
+ @Override
+ public SurfaceProperties setAlpha(float alpha) {
+ this.alpha = alpha;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ this.matrix = matrix;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ this.windowCrop = windowCrop;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setLayer(int relativeLayer) {
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setCornerRadius(float radius) {
+ this.cornerRadius = radius;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setShadowRadius(float radius) {
+ this.shadowRadius = radius;
+ return this;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 1200208e30..95473dc39a 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -25,7 +25,6 @@ import android.view.View;
import android.view.ViewRootImpl;
import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.function.Consumer;
@@ -70,18 +69,12 @@ public class SurfaceTransactionApplier extends ReleaseCheck {
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- public void scheduleApply(final SurfaceParams... params) {
+ public void scheduleApply(SurfaceTransaction params) {
View view = mTargetViewRootImpl.getView();
if (view == null) {
return;
}
- Transaction t = new Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SurfaceParams surfaceParams = params[i];
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(t);
- }
- }
+ Transaction t = params.getTransaction();
mLastSequenceNumber++;
final int toApplySeqNo = mLastSequenceNumber;
@@ -102,7 +95,7 @@ public class SurfaceTransactionApplier extends ReleaseCheck {
}
/**
- * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+ * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
* attached if necessary.
*/
public static void create(
diff --git a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
index 3ea8466148..3756b4af59 100644
--- a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
+++ b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
@@ -16,10 +16,6 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
-import android.view.animation.Interpolator;
-
/**
* Timings for the OverviewSplitSelect > confirmed animation on tablets.
*/
@@ -30,11 +26,7 @@ public class TabletSplitToConfirmTimings
public int getPlaceholderIconFadeInStart() { return 167; }
public int getPlaceholderIconFadeInEnd() { return 250; }
public int getStagedRectSlideStart() { return 0; }
- public int getStagedRectSlideEnd() { return 383; }
+ public int getStagedRectSlideEnd() { return 500; }
public int getDuration() { return TABLET_CONFIRM_DURATION; }
- public Interpolator getStagedRectXInterpolator() { return LINEAR; }
- public Interpolator getStagedRectYInterpolator() { return LINEAR; }
- public Interpolator getStagedRectScaleXInterpolator() { return LINEAR; }
- public Interpolator getStagedRectScaleYInterpolator() { return LINEAR; }
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 1a026fc31d..04af19f729 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -17,7 +17,6 @@ package com.android.quickstep.util;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -36,22 +35,22 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
/**
* A utility class which emulates the layout behavior of TaskView and RecentsView
@@ -172,8 +171,11 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
/**
* Sets the targets which the simulator will control
*/
- public void setPreview(RemoteAnimationTargetCompat runningTarget) {
- setPreviewBounds(runningTarget.startScreenSpaceBounds, runningTarget.contentInsets);
+ public void setPreview(RemoteAnimationTarget runningTarget) {
+ setPreviewBounds(
+ runningTarget.startBounds == null
+ ? runningTarget.screenSpaceBounds : runningTarget.startBounds,
+ runningTarget.contentInsets);
}
/**
@@ -182,7 +184,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
*
* @param splitInfo set to {@code null} when not in staged split mode
*/
- public void setPreview(RemoteAnimationTargetCompat runningTarget, SplitBounds splitInfo) {
+ public void setPreview(RemoteAnimationTarget runningTarget, SplitBounds splitInfo) {
setPreview(runningTarget);
mSplitBounds = splitInfo;
if (mSplitBounds == null) {
@@ -192,6 +194,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
STAGE_POSITION_TOP_OR_LEFT :
STAGE_POSITION_BOTTOM_OR_RIGHT;
+ mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition);
}
/**
@@ -318,9 +321,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
// mIsRecentsRtl is the inverse of TaskView RTL.
boolean isRtlEnabled = !mIsRecentsRtl;
mPositionHelper.updateThumbnailMatrix(
- mThumbnailPosition, mThumbnailData,
- mTaskRect.width(), mTaskRect.height(),
- mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
+ mThumbnailPosition, mThumbnailData, mTaskRect.width(), mTaskRect.height(),
+ mDp.widthPx, mDp.heightPx, mDp.taskbarSize, mDp.isTablet,
+ mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
if (DEBUG) {
Log.d(TAG, " taskRect: " + mTaskRect);
@@ -387,19 +390,19 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
@Override
public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpCropRect)
- .withCornerRadius(getCurrentCornerRadius());
+ SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mTmpCropRect)
+ .setCornerRadius(getCurrentCornerRadius());
// If mDrawsBelowRecents is unset, no reordering will be enforced.
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mDrawsBelowRecents != null) {
+ if (mDrawsBelowRecents != null) {
// In legacy transitions, the animation leashes remain in same hierarchy in the
// TaskDisplayArea, so we don't want to bump the layer too high otherwise it will
// conflict with layers that WM core positions (ie. the input consumers). For shell
// transitions, the animation leashes are reparented to an animation container so we
// can bump layers as needed.
- builder.withLayer(mDrawsBelowRecents
+ builder.setLayer(mDrawsBelowRecents
? Integer.MIN_VALUE + 1
: ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
}
@@ -419,4 +422,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
}
+ /**
+ * TODO(b/254378592): Remove this after consolidation of classes
+ */
+ public static com.android.wm.shell.util.SplitBounds convertSplitBounds(SplitBounds bounds) {
+ return new com.android.wm.shell.util.SplitBounds(
+ bounds.leftTopBounds,
+ bounds.rightBottomBounds,
+ bounds.leftTopTaskId,
+ bounds.rightBottomTaskId
+ );
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index a7f25d40ef..aa9a45bd8b 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -15,16 +15,18 @@
*/
package com.android.quickstep.util;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
import android.util.FloatProperty;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.TransactionCompat;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
public class TransformParams {
@@ -113,8 +115,7 @@ public class TransformParams {
* Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
* are computed based on these TransformParams.
*/
- public TransformParams setSyncTransactionApplier(
- SurfaceTransactionApplier applier) {
+ public TransformParams setSyncTransactionApplier(SurfaceTransactionApplier applier) {
mSyncTransactionApplier = applier;
return this;
}
@@ -137,28 +138,26 @@ public class TransformParams {
return this;
}
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
- final int appLength = targets.unfilteredApps.length;
- final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
- SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
+ SurfaceTransaction transaction = new SurfaceTransaction();
mRecentsSurface = getRecentsSurface(targets);
- for (int i = 0; i < appLength; i++) {
- RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
+ for (int i = 0; i < targets.unfilteredApps.length; i++) {
+ RemoteAnimationTarget app = targets.unfilteredApps[i];
+ SurfaceProperties builder = transaction.forSurface(app.leash);
if (app.mode == targets.targetMode) {
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ int activityType = app.windowConfiguration.getActivityType();
+ if (activityType == ACTIVITY_TYPE_HOME) {
mHomeBuilderProxy.onBuildTargetParams(builder, app, this);
} else {
// Fade out Assistant overlay.
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
- && app.isNotInRecents) {
+ if (activityType == ACTIVITY_TYPE_ASSISTANT && app.isNotInRecents) {
float progress = Utilities.boundToRange(getProgress(), 0, 1);
- builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+ builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
} else {
- builder.withAlpha(getTargetAlpha());
+ builder.setAlpha(getTargetAlpha());
}
proxy.onBuildTargetParams(builder, app, this);
@@ -166,22 +165,22 @@ public class TransformParams {
} else {
mBaseBuilderProxy.onBuildTargetParams(builder, app, this);
}
- surfaceParams[i] = builder.build();
}
+
// always put wallpaper layer to bottom.
+ final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
for (int i = 0; i < wallpaperLength; i++) {
- RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
- surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
- .withLayer(Integer.MIN_VALUE).build();
+ RemoteAnimationTarget wallpaper = targets.wallpapers[i];
+ transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
}
- return surfaceParams;
+ return transaction;
}
private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
for (int i = 0; i < targets.unfilteredApps.length; i++) {
- RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+ RemoteAnimationTarget app = targets.unfilteredApps[i];
if (app.mode == targets.targetMode) {
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
+ if (app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS) {
return app.leash;
}
} else {
@@ -213,15 +212,11 @@ public class TransformParams {
return mTargetSet;
}
- public void applySurfaceParams(SurfaceParams... params) {
+ public void applySurfaceParams(SurfaceTransaction builder) {
if (mSyncTransactionApplier != null) {
- mSyncTransactionApplier.scheduleApply(params);
+ mSyncTransactionApplier.scheduleApply(builder);
} else {
- TransactionCompat t = new TransactionCompat();
- for (SurfaceParams param : params) {
- SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
- }
- t.apply();
+ builder.getTransaction().apply();
}
}
@@ -229,9 +224,9 @@ public class TransformParams {
public interface BuilderProxy {
BuilderProxy NO_OP = (builder, app, params) -> { };
- BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
+ BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
- void onBuildTargetParams(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params);
+ void onBuildTargetParams(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
index dc97dd6f86..01a997a451 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -21,6 +21,7 @@ import android.view.WindowManager;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves hotseat icons from center to the sides (final position)
@@ -29,8 +30,9 @@ public class UnfoldMoveFromCenterHotseatAnimator extends BaseUnfoldMoveFromCente
private final Launcher mLauncher;
- public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
+ super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 354d1579b0..95a4b8f2e2 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -22,6 +22,7 @@ import com.android.launcher3.CellLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves launcher icons and widgets from center to the sides (final position)
@@ -30,8 +31,9 @@ public class UnfoldMoveFromCenterWorkspaceAnimator extends BaseUnfoldMoveFromCen
private final Launcher mLauncher;
- public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
+ super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/util/VibrationConstants.java b/quickstep/src/com/android/quickstep/util/VibrationConstants.java
new file mode 100644
index 0000000000..0f0306e344
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/VibrationConstants.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.os.VibrationEffect;
+
+public class VibrationConstants {
+ public static final VibrationEffect EFFECT_TEXTURE_TICK =
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TEXTURE_TICK);
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java
deleted file mode 100644
index cfcfce0b6a..0000000000
--- a/quickstep/src/com/android/quickstep/util/ViewCapture.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-
-import static java.util.stream.Collectors.toList;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.Trace;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Base64OutputStream;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnDrawListener;
-import android.view.Window;
-
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.view.ViewCaptureData.ExportedData;
-import com.android.launcher3.view.ViewCaptureData.FrameData;
-import com.android.launcher3.view.ViewCaptureData.ViewNode;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Future;
-import java.util.zip.GZIPOutputStream;
-
-/**
- * Utility class for capturing view data every frame
- */
-public class ViewCapture {
-
- private static final String TAG = "ViewCapture";
-
- // These flags are copies of two private flags in the View class.
- private static final int PFLAG_INVALIDATED = 0x80000000;
- private static final int PFLAG_DIRTY_MASK = 0x00200000;
-
- // Number of frames to keep in memory
- private static final int MEMORY_SIZE = 2000;
- // Initial size of the reference pool. This is at least be 5 * total number of views in
- // Launcher. This allows the first free frames avoid object allocation during view capture.
- private static final int INIT_POOL_SIZE = 300;
-
- public static final MainThreadInitializedObject INSTANCE =
- new MainThreadInitializedObject<>(ViewCapture::new);
-
- private final List mListeners = new ArrayList<>();
-
- private final Context mContext;
- private final LooperExecutor mExecutor;
-
- // Pool used for capturing view tree on the UI thread.
- private ViewRef mPool = new ViewRef();
-
- private ViewCapture(Context context) {
- mContext = context;
- if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- Looper looper = createAndStartNewLooper("ViewCapture",
- Process.THREAD_PRIORITY_FOREGROUND);
- mExecutor = new LooperExecutor(looper);
- mExecutor.execute(this::initPool);
- } else {
- mExecutor = UI_HELPER_EXECUTOR;
- }
- }
-
- @UiThread
- private void addToPool(ViewRef start, ViewRef end) {
- end.next = mPool;
- mPool = start;
- }
-
- @WorkerThread
- private void initPool() {
- ViewRef start = new ViewRef();
- ViewRef current = start;
-
- for (int i = 0; i < INIT_POOL_SIZE; i++) {
- current.next = new ViewRef();
- current = current.next;
- }
-
- ViewRef finalCurrent = current;
- MAIN_EXECUTOR.execute(() -> addToPool(start, finalCurrent));
- }
-
- /**
- * Attaches the ViewCapture to the provided window and returns a handle to detach the listener
- */
- public SafeCloseable startCapture(Window window) {
- String title = window.getAttributes().getTitle().toString();
- String name = TextUtils.isEmpty(title) ? window.toString() : title;
- return startCapture(window.getDecorView(), name);
- }
-
- /**
- * Attaches the ViewCapture to the provided window and returns a handle to detach the listener
- */
- public SafeCloseable startCapture(View view, String name) {
- if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- return () -> { };
- }
-
- WindowListener listener = new WindowListener(view, name);
- mExecutor.execute(() -> MAIN_EXECUTOR.execute(listener::attachToRoot));
- mListeners.add(listener);
- return () -> {
- mListeners.remove(listener);
- listener.destroy();
- };
- }
-
- /**
- * Dumps all the active view captures
- */
- public void dump(PrintWriter writer, FileDescriptor out) {
- if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- return;
- }
- ViewIdProvider idProvider = new ViewIdProvider(mContext.getResources());
-
- // Collect all the tasks first so that all the tasks are posted on the executor
- List>> tasks = mListeners.stream()
- .map(l -> Pair.create(l.name, mExecutor.submit(() -> l.dumpToProto(idProvider))))
- .collect(toList());
-
- tasks.forEach(pair -> {
- writer.println();
- writer.println(" ContinuousViewCapture:");
- writer.println(" window " + pair.first + ":");
- writer.println(" pkg:" + mContext.getPackageName());
- writer.print(" data:");
- writer.flush();
- try (OutputStream os = new FileOutputStream(out)) {
- ExportedData data = pair.second.get();
- OutputStream encodedOS = new GZIPOutputStream(new Base64OutputStream(os,
- Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP));
- data.writeTo(encodedOS);
- encodedOS.close();
- os.flush();
- } catch (Exception e) {
- Log.e(TAG, "Error capturing proto", e);
- }
- writer.println();
- writer.println("--end--");
- });
- }
-
- private class WindowListener implements OnDrawListener {
-
- private final View mRoot;
- public final String name;
-
- private final Handler mHandler;
- private final ViewRef mViewRef = new ViewRef();
-
- private int mFrameIndexBg = -1;
- private boolean mIsFirstFrame = true;
- private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
- private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
-
- private boolean mDestroyed = false;
-
- WindowListener(View view, String name) {
- mRoot = view;
- this.name = name;
- mHandler = new Handler(mExecutor.getLooper(), this::captureViewPropertiesBg);
- }
-
- @Override
- public void onDraw() {
- Trace.beginSection("view_capture");
- captureViewTree(mRoot, mViewRef);
- Message m = Message.obtain(mHandler);
- m.obj = mViewRef.next;
- mHandler.sendMessage(m);
- mIsFirstFrame = false;
- Trace.endSection();
- }
-
- /**
- * Captures the View property on the background thread, and transfer all the ViewRef objects
- * back to the pool
- */
- @WorkerThread
- private boolean captureViewPropertiesBg(Message msg) {
- ViewRef viewRefStart = (ViewRef) msg.obj;
- long time = msg.getWhen();
- if (viewRefStart == null) {
- return false;
- }
- mFrameIndexBg++;
- if (mFrameIndexBg >= MEMORY_SIZE) {
- mFrameIndexBg = 0;
- }
- mFrameTimesBg[mFrameIndexBg] = time;
-
- ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
-
- ViewPropertyRef resultStart = null;
- ViewPropertyRef resultEnd = null;
-
- ViewRef viewRefEnd = viewRefStart;
- while (viewRefEnd != null) {
- ViewPropertyRef propertyRef = recycle;
- if (propertyRef == null) {
- propertyRef = new ViewPropertyRef();
- } else {
- recycle = recycle.next;
- propertyRef.next = null;
- }
-
- ViewPropertyRef copy = null;
- if (viewRefEnd.childCount < 0) {
- copy = findInLastFrame(viewRefEnd.view.hashCode());
- viewRefEnd.childCount = (copy != null) ? copy.childCount : 0;
- }
- viewRefEnd.transferTo(propertyRef);
-
- if (resultStart == null) {
- resultStart = propertyRef;
- resultEnd = resultStart;
- } else {
- resultEnd.next = propertyRef;
- resultEnd = resultEnd.next;
- }
-
- if (copy != null) {
- int pending = copy.childCount;
- while (pending > 0) {
- copy = copy.next;
- pending = pending - 1 + copy.childCount;
-
- propertyRef = recycle;
- if (propertyRef == null) {
- propertyRef = new ViewPropertyRef();
- } else {
- recycle = recycle.next;
- propertyRef.next = null;
- }
-
- copy.transferTo(propertyRef);
-
- resultEnd.next = propertyRef;
- resultEnd = resultEnd.next;
- }
- }
-
- if (viewRefEnd.next == null) {
- // The compiler will complain about using a non-final variable from
- // an outer class in a lambda if we pass in viewRefEnd directly.
- final ViewRef finalViewRefEnd = viewRefEnd;
- MAIN_EXECUTOR.execute(() -> addToPool(viewRefStart, finalViewRefEnd));
- break;
- }
- viewRefEnd = viewRefEnd.next;
- }
- mNodesBg[mFrameIndexBg] = resultStart;
- return true;
- }
-
- private ViewPropertyRef findInLastFrame(int hashCode) {
- int lastFrameIndex = (mFrameIndexBg == 0) ? MEMORY_SIZE - 1 : mFrameIndexBg - 1;
- ViewPropertyRef viewPropertyRef = mNodesBg[lastFrameIndex];
- while (viewPropertyRef != null && viewPropertyRef.hashCode != hashCode) {
- viewPropertyRef = viewPropertyRef.next;
- }
- return viewPropertyRef;
- }
-
- void attachToRoot() {
- if (mRoot.isAttachedToWindow()) {
- mRoot.getViewTreeObserver().addOnDrawListener(this);
- } else {
- mRoot.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- if (!mDestroyed) {
- mRoot.getViewTreeObserver().addOnDrawListener(WindowListener.this);
- }
- mRoot.removeOnAttachStateChangeListener(this);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) { }
- });
- }
- }
-
- void destroy() {
- mRoot.getViewTreeObserver().removeOnDrawListener(this);
- mDestroyed = true;
- }
-
- @WorkerThread
- private ExportedData dumpToProto(ViewIdProvider idProvider) {
- ExportedData.Builder dataBuilder = ExportedData.newBuilder();
- ArrayList classList = new ArrayList<>();
-
- int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
- for (int i = size - 1; i >= 0; i--) {
- int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
- ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
- mNodesBg[index].toProto(idProvider, classList, nodeBuilder);
- dataBuilder.addFrameData(FrameData.newBuilder()
- .setNode(nodeBuilder)
- .setTimestamp(mFrameTimesBg[index]));
- }
- return dataBuilder
- .addAllClassname(classList.stream().map(Class::getName).collect(toList()))
- .build();
- }
-
- private ViewRef captureViewTree(View view, ViewRef start) {
- ViewRef ref;
- if (mPool != null) {
- ref = mPool;
- mPool = mPool.next;
- ref.next = null;
- } else {
- ref = new ViewRef();
- }
- ref.view = view;
- start.next = ref;
- if (view instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) view;
- // If a view has not changed since the last frame, we will copy
- // its children from the last processed frame's data.
- if ((view.mPrivateFlags & (PFLAG_INVALIDATED | PFLAG_DIRTY_MASK)) == 0
- && !mIsFirstFrame) {
- // A negative child count is the signal to copy this view from the last frame.
- ref.childCount = -parent.getChildCount();
- return ref;
- }
- ViewRef result = ref;
- int childCount = ref.childCount = parent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- result = captureViewTree(parent.getChildAt(i), result);
- }
- return result;
- } else {
- ref.childCount = 0;
- return ref;
- }
- }
- }
-
- private static class ViewPropertyRef {
- // We store reference in memory to avoid generating and storing too many strings
- public Class clazz;
- public int hashCode;
- public int childCount = 0;
-
- public int id;
- public int left, top, right, bottom;
- public int scrollX, scrollY;
-
- public float translateX, translateY;
- public float scaleX, scaleY;
- public float alpha;
- public float elevation;
-
- public int visibility;
- public boolean willNotDraw;
- public boolean clipChildren;
-
- public ViewPropertyRef next;
-
- public void transferTo(ViewPropertyRef out) {
- out.clazz = this.clazz;
- out.hashCode = this.hashCode;
- out.childCount = this.childCount;
- out.id = this.id;
- out.left = this.left;
- out.top = this.top;
- out.right = this.right;
- out.bottom = this.bottom;
- out.scrollX = this.scrollX;
- out.scrollY = this.scrollY;
- out.scaleX = this.scaleX;
- out.scaleY = this.scaleY;
- out.translateX = this.translateX;
- out.translateY = this.translateY;
- out.alpha = this.alpha;
- out.visibility = this.visibility;
- out.willNotDraw = this.willNotDraw;
- out.clipChildren = this.clipChildren;
- out.next = this.next;
- out.elevation = this.elevation;
- }
-
- /**
- * Converts the data to the proto representation and returns the next property ref
- * at the end of the iteration.
- * @return
- */
- public ViewPropertyRef toProto(ViewIdProvider idProvider, ArrayList classList,
- ViewNode.Builder outBuilder) {
- int classnameIndex = classList.indexOf(clazz);
- if (classnameIndex < 0) {
- classnameIndex = classList.size();
- classList.add(clazz);
- }
- outBuilder
- .setClassnameIndex(classnameIndex)
- .setHashcode(hashCode)
- .setId(idProvider.getName(id))
- .setLeft(left)
- .setTop(top)
- .setWidth(right - left)
- .setHeight(bottom - top)
- .setTranslationX(translateX)
- .setTranslationY(translateY)
- .setScaleX(scaleX)
- .setScaleY(scaleY)
- .setAlpha(alpha)
- .setVisibility(visibility)
- .setWillNotDraw(willNotDraw)
- .setElevation(elevation)
- .setClipChildren(clipChildren);
-
- ViewPropertyRef result = next;
- for (int i = 0; (i < childCount) && (result != null); i++) {
- ViewNode.Builder childBuilder = ViewNode.newBuilder();
- result = result.toProto(idProvider, classList, childBuilder);
- outBuilder.addChildren(childBuilder);
- }
- return result;
- }
- }
-
- private static class ViewRef {
- public View view;
- public int childCount = 0;
- public ViewRef next;
-
- public void transferTo(ViewPropertyRef out) {
- out.childCount = this.childCount;
-
- View view = this.view;
- this.view = null;
-
- out.clazz = view.getClass();
- out.hashCode = view.hashCode();
- out.id = view.getId();
- out.left = view.getLeft();
- out.top = view.getTop();
- out.right = view.getRight();
- out.bottom = view.getBottom();
- out.scrollX = view.getScrollX();
- out.scrollY = view.getScrollY();
-
- out.translateX = view.getTranslationX();
- out.translateY = view.getTranslationY();
- out.scaleX = view.getScaleX();
- out.scaleY = view.getScaleY();
- out.alpha = view.getAlpha();
- out.elevation = view.getElevation();
-
- out.visibility = view.getVisibility();
- out.willNotDraw = view.willNotDraw();
- }
- }
-
- private static final class ViewIdProvider {
-
- private final SparseArray mNames = new SparseArray<>();
- private final Resources mRes;
-
- ViewIdProvider(Resources res) {
- mRes = res;
- }
-
- String getName(int id) {
- String name = mNames.get(id);
- if (name == null) {
- if (id >= 0) {
- try {
- name = mRes.getResourceTypeName(id) + '/' + mRes.getResourceEntryName(id);
- } catch (Resources.NotFoundException e) {
- name = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
- }
- } else {
- name = "NO_ID";
- }
- mNames.put(id, name);
- }
- return name;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 50be5ea565..d098ffc2fb 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -99,6 +99,10 @@ public class ClearAllButton extends Button {
return false;
}
+ public float getScrollAlpha() {
+ return mScrollAlpha;
+ }
+
public void setContentAlpha(float alpha) {
if (mContentAlpha != alpha) {
mContentAlpha = alpha;
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
new file mode 100644
index 0000000000..c878278da5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * TaskView that contains all tasks that are part of the desktop.
+ */
+// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
+public class DesktopTaskView extends TaskView {
+
+ /** Flag to indicate whether desktop windowing proto 2 is enabled */
+ public static final boolean DESKTOP_IS_PROTO2_ENABLED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode_2", false);
+
+ /** Flags to indicate whether desktop mode is available on the device */
+ public static final boolean DESKTOP_MODE_SUPPORTED =
+ SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false)
+ || DESKTOP_IS_PROTO2_ENABLED;
+
+ private static final String TAG = DesktopTaskView.class.getSimpleName();
+
+ private static final boolean DEBUG = true;
+
+ private List mTasks;
+
+ private final ArrayList