From 7f5e4ca06c9766ce3f231d641fe16074f1d2a642 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 14 Dec 2021 19:08:55 +0000 Subject: [PATCH 1/3] Ensure some taskbar states are initialized correctly - Update FLAG_IN_APP to account for setup state to ensure that the stashed state is correct. This needs to be done in the stash controller since SUW is the home activity on startup and the launcher state controller will not be initialized until after SUW finishes - Initialize the launcher state and resumed flags in case Launcher restarts while another app is resumed Bug: 204384193 Test: Run through SUW, ensure the background is not visible Change-Id: I5ce061ad16e79226c8428339ccd0b5ac55c07205 --- .../launcher3/taskbar/TaskbarLauncherStateController.java | 5 +++++ .../android/launcher3/taskbar/TaskbarStashController.java | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index e2ba459068..7a50d0bf63 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -111,6 +111,11 @@ import java.util.function.Supplier; onIconAlignmentRatioChangedForAppAndHomeTransition(); mLauncher.getStateManager().addStateListener(mStateListener); + + // Initialize to the current launcher state + updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed()); + mLauncherState = launcher.getStateManager().getState(); + applyState(0); } public void onDestroy() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 5c31e05996..afb4c9a332 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -171,9 +171,13 @@ public class TaskbarStashController { boolean isManuallyStashedInApp = supportsManualStashing() && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF); + boolean isInSetup = !mActivity.isUserSetupComplete() || sharedState.setupUIVisible; updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp); - updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, - !mActivity.isUserSetupComplete() || sharedState.setupUIVisible); + updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup); + if (isInSetup) { + // Update the in-app state to ensure isStashed() reflects right state during SUW + updateStateForFlag(FLAG_IN_APP, true); + } applyState(); notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp()); From 8581dfc218c0ca00603cef9d47f10fddfdb583eb Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 7 Dec 2021 13:07:32 -0800 Subject: [PATCH 2/3] Wait for the Launcher activity to bind to TIS before animating - Mainly a test issue where we destroy the activity and immediately enter overview without preloading launcher. In this case, the task bar does not animate due to the delayed cycled of activity create -> bind service -> service connected -> taskbar ui controller init. As a result, the bar can occlude content from Launcher preventing the test from finding the overview actions. Instead, in cases where we need to animate with launcher, we wait for Launcher to be created (generally already the case) and the service has bound before proceeding. Bug: 189807374 Bug: 204891006 Test: atest NexusLauncherTests:com.android.quickstep.StartLauncherViaGestureTests Change-Id: I2cfccae67ac0e5a591639c6c99df032451dae16d --- .../launcher3/BaseQuickstepLauncher.java | 5 ++ .../android/quickstep/AbsSwipeUpHandler.java | 83 ++++++++++++------- .../android/quickstep/RecentsActivity.java | 5 ++ .../android/quickstep/util/TISBindHelper.java | 21 +++++ .../statemanager/StatefulActivity.java | 7 ++ 5 files changed, 89 insertions(+), 32 deletions(-) diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 38e8e72cb8..4269f2d906 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -320,6 +320,11 @@ public abstract class BaseQuickstepLauncher extends Launcher mOverviewCommandHelper = binder.getOverviewCommandHelper(); } + @Override + public void runOnBindToTouchInteractionService(Runnable r) { + mTISBindHelper.runOnBindToTouchInteractionService(r); + } + private void initUnfoldTransitionProgressProvider() { final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this); if (config.isEnabled()) { diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 0e5282af91..1f5f2a5625 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -180,45 +180,51 @@ public abstract class AbsSwipeUpHandler, getFlagForIndex(0, "STATE_LAUNCHER_PRESENT"); protected static final int STATE_LAUNCHER_STARTED = getFlagForIndex(1, "STATE_LAUNCHER_STARTED"); - protected static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN"); + protected static final int STATE_LAUNCHER_DRAWN = + getFlagForIndex(2, "STATE_LAUNCHER_DRAWN"); + // Called when the Launcher has connected to the touch interaction service (and the taskbar + // ui controller is initialized) + protected static final int STATE_LAUNCHER_BIND_TO_SERVICE = + getFlagForIndex(3, "STATE_LAUNCHER_BIND_TO_SERVICE"); // Internal initialization states private static final int STATE_APP_CONTROLLER_RECEIVED = - getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED"); + getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED"); // Interaction finish states private static final int STATE_SCALED_CONTROLLER_HOME = - getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME"); + getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME"); private static final int STATE_SCALED_CONTROLLER_RECENTS = - getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS"); + getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS"); protected static final int STATE_HANDLER_INVALIDATED = - getFlagForIndex(6, "STATE_HANDLER_INVALIDATED"); + getFlagForIndex(7, "STATE_HANDLER_INVALIDATED"); private static final int STATE_GESTURE_STARTED = - getFlagForIndex(7, "STATE_GESTURE_STARTED"); + getFlagForIndex(8, "STATE_GESTURE_STARTED"); private static final int STATE_GESTURE_CANCELLED = - getFlagForIndex(8, "STATE_GESTURE_CANCELLED"); + getFlagForIndex(9, "STATE_GESTURE_CANCELLED"); private static final int STATE_GESTURE_COMPLETED = - getFlagForIndex(9, "STATE_GESTURE_COMPLETED"); + getFlagForIndex(10, "STATE_GESTURE_COMPLETED"); private static final int STATE_CAPTURE_SCREENSHOT = - getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT"); + getFlagForIndex(11, "STATE_CAPTURE_SCREENSHOT"); protected static final int STATE_SCREENSHOT_CAPTURED = - getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED"); + getFlagForIndex(12, "STATE_SCREENSHOT_CAPTURED"); private static final int STATE_SCREENSHOT_VIEW_SHOWN = - getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN"); + getFlagForIndex(13, "STATE_SCREENSHOT_VIEW_SHOWN"); private static final int STATE_RESUME_LAST_TASK = - getFlagForIndex(13, "STATE_RESUME_LAST_TASK"); + getFlagForIndex(14, "STATE_RESUME_LAST_TASK"); private static final int STATE_START_NEW_TASK = - getFlagForIndex(14, "STATE_START_NEW_TASK"); + getFlagForIndex(15, "STATE_START_NEW_TASK"); private static final int STATE_CURRENT_TASK_FINISHED = - getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED"); + getFlagForIndex(16, "STATE_CURRENT_TASK_FINISHED"); private static final int STATE_FINISH_WITH_NO_END = - getFlagForIndex(16, "STATE_FINISH_WITH_NO_END"); + getFlagForIndex(17, "STATE_FINISH_WITH_NO_END"); private static final int LAUNCHER_UI_STATES = - STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED; + STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED | + STATE_LAUNCHER_BIND_TO_SERVICE; public static final long MAX_SWIPE_DURATION = 350; public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS; @@ -429,6 +435,7 @@ public abstract class AbsSwipeUpHandler, setupRecentsViewUi(); linkRecentsViewScroll(); + activity.runOnBindToTouchInteractionService(this::onLauncherBindToService); mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks); return true; @@ -510,6 +517,11 @@ public abstract class AbsSwipeUpHandler, mStateCallback.setState(STATE_LAUNCHER_STARTED); } + private void onLauncherBindToService() { + mStateCallback.setState(STATE_LAUNCHER_BIND_TO_SERVICE); + flushOnRecentsAnimationAndLauncherBound(); + } + private void onLauncherPresentAndGestureStarted() { // Re-setup the recents UI when gesture starts, as the state could have been changed during // that time by a previous window transition. @@ -812,12 +824,7 @@ public abstract class AbsSwipeUpHandler, } // Notify when the animation starts - if (!mRecentsAnimationStartCallbacks.isEmpty()) { - for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) { - action.run(); - } - mRecentsAnimationStartCallbacks.clear(); - } + flushOnRecentsAnimationAndLauncherBound(); TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps, false); @@ -1195,7 +1202,7 @@ public abstract class AbsSwipeUpHandler, @UiThread private void animateToProgress(float start, float end, long duration, Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) { - runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration, + runOnRecentsAnimationAndLauncherBound(() -> animateToProgressInternal(start, end, duration, interpolator, target, velocityPxPerMs)); } @@ -1820,12 +1827,12 @@ public abstract class AbsSwipeUpHandler, SurfaceTransactionApplier.create(mRecentsView, applier -> { runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams() .setSyncTransactionApplier(applier)); - runOnRecentsAnimationStart(() -> + runOnRecentsAnimationAndLauncherBound(() -> mRecentsAnimationTargets.addReleaseCheck(applier)); }); mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener); - runOnRecentsAnimationStart(() -> + runOnRecentsAnimationAndLauncherBound(() -> mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController, mRecentsAnimationTargets)); mRecentsViewScrollLinked = true; @@ -1871,14 +1878,26 @@ public abstract class AbsSwipeUpHandler, } /** - * Runs the given {@param action} if the recents animation has already started, or queues it to - * be run when it is next started. + * Runs the given {@param action} if the recents animation has already started and Launcher has + * been created and bound to the TouchInteractionService, or queues it to be run when it this + * next happens. */ - protected void runOnRecentsAnimationStart(Runnable action) { - if (mRecentsAnimationTargets == null) { - mRecentsAnimationStartCallbacks.add(action); - } else { - action.run(); + private void runOnRecentsAnimationAndLauncherBound(Runnable action) { + mRecentsAnimationStartCallbacks.add(action); + flushOnRecentsAnimationAndLauncherBound(); + } + + private void flushOnRecentsAnimationAndLauncherBound() { + if (mRecentsAnimationTargets == null || + !mStateCallback.hasStates(STATE_LAUNCHER_BIND_TO_SERVICE)) { + return; + } + + if (!mRecentsAnimationStartCallbacks.isEmpty()) { + for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) { + action.run(); + } + mRecentsAnimationStartCallbacks.clear(); } } diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index d6efc7193d..103f350c0f 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -141,6 +141,11 @@ public final class RecentsActivity extends StatefulActivity { mTaskbarManager.setActivity(this); } + @Override + public void runOnBindToTouchInteractionService(Runnable r) { + mTISBindHelper.runOnBindToTouchInteractionService(r); + } + public void setTaskbarUIController(FallbackTaskbarUIController taskbarUIController) { mTaskbarUIController = taskbarUIController; } diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java index 92c60c84b5..7b122c6c66 100644 --- a/quickstep/src/com/android/quickstep/util/TISBindHelper.java +++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java @@ -26,6 +26,7 @@ import android.util.Log; import com.android.quickstep.TouchInteractionService; import com.android.quickstep.TouchInteractionService.TISBinder; +import java.util.ArrayList; import java.util.function.Consumer; /** @@ -44,9 +45,11 @@ public class TISBindHelper implements ServiceConnection { private final Runnable mConnectionRunnable = this::internalBindToTIS; private final Context mContext; private final Consumer mConnectionCallback; + private final ArrayList mPendingConnectedCallbacks = new ArrayList<>(); private short mConnectionAttempts; private boolean mTisServiceBound; + private boolean mIsConnected; public TISBindHelper(Context context, Consumer connectionCallback) { mContext = context; @@ -66,7 +69,13 @@ public class TISBindHelper implements ServiceConnection { } Log.d(TAG, "TIS service connected"); + mIsConnected = true; mConnectionCallback.accept((TISBinder) iBinder); + // Flush the pending callbacks + for (Runnable r : mPendingConnectedCallbacks) { + r.run(); + } + mPendingConnectedCallbacks.clear(); resetServiceBindRetryState(); } @@ -79,6 +88,16 @@ public class TISBindHelper implements ServiceConnection { internalBindToTIS(); } + /** + * Runs the given {@param r} runnable when the service is connected. + */ + public void runOnBindToTouchInteractionService(Runnable r) { + if (mIsConnected) { + r.run(); + } else { + mPendingConnectedCallbacks.add(r); + } + } /** * Binds to {@link TouchInteractionService}. If the binding fails, attempts to retry via @@ -120,5 +139,7 @@ public class TISBindHelper implements ServiceConnection { public void onDestroy() { internalUnbindToTIS(); resetServiceBindRetryState(); + mIsConnected = false; + mPendingConnectedCallbacks.clear(); } } diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java index 7a23caa884..e03694321e 100644 --- a/src/com/android/launcher3/statemanager/StatefulActivity.java +++ b/src/com/android/launcher3/statemanager/StatefulActivity.java @@ -185,4 +185,11 @@ public abstract class StatefulActivity> @RequiresApi(api = Build.VERSION_CODES.R) public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder, WindowInsets oldInsets) { } + + /** + * Runs the given {@param r} runnable when this activity binds to the touch interaction service. + */ + public void runOnBindToTouchInteractionService(Runnable r) { + r.run(); + } } From 32ec4d52e121e84d00838296663eb2d031c88579 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 14 Dec 2021 15:20:12 -0800 Subject: [PATCH 3/3] Scale, crop, and translate window when swiping back to home. This makes the animation consistent with what happens when swiping up to home. Bug: 208292857 Test: open app, swipe back to close, test on portrait and landscape Change-Id: I096b20fe3d3398001e442d41981350e7b0321a46 --- .../launcher3/QuickstepTransitionManager.java | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 0123c4f5b1..a8a085b54e 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -660,10 +660,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile, windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1], hasSplashScreen, floatingView.isDifferentFromAppIcon()); - int left = (int) (prop.cropCenterXStart - prop.cropWidthStart / 2); - int top = (int) (prop.cropCenterYStart - prop.cropHeightStart / 2); - int right = (int) (left + prop.cropWidthStart); - int bottom = (int) (top + prop.cropHeightStart); + int left = prop.cropCenterXStart - prop.cropWidthStart / 2; + int top = prop.cropCenterYStart - prop.cropHeightStart / 2; + int right = left + prop.cropWidthStart; + int bottom = top + prop.cropHeightStart; // Set the crop here so we can calculate the corner radius below. crop.set(left, top, right, bottom); @@ -1329,6 +1329,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener mDeviceProfile); // Hook up floating views to the closing window animators. + final int rotationChange = getRotationChange(targets); + Rect windowTargetBounds = getWindowTargetBounds(targets, rotationChange); if (floatingIconView != null) { anim.addAnimatorListener(floatingIconView); floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged); @@ -1339,7 +1341,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // FolderIconView can be seen morphing into the icon shape. final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION; - RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect) { + RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect, + windowTargetBounds) { @Override public void onUpdate(RectF currentRectF, float progress) { finalFloatingIconView.update(1f, 255 /* fgAlpha */, currentRectF, progress, @@ -1356,7 +1359,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener final float floatingWidgetAlpha = isTransluscent ? 0 : 1; FloatingWidgetView finalFloatingWidget = floatingWidget; - RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect) { + RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect, + windowTargetBounds) { @Override public void onUpdate(RectF currentRectF, float progress) { final float fallbackBackgroundAlpha = @@ -1767,12 +1771,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private final float mStartRadius; private final float mEndRadius; private final SurfaceTransactionApplier mSurfaceApplier; + private final Rect mWindowTargetBounds = new Rect(); - SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect) { + private final Rect mTmpRect = new Rect(); + + SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect, + Rect windowTargetBounds) { mAppTargets = appTargets; mStartRadius = QuickStepContract.getWindowCornerRadius(mLauncher); mEndRadius = Math.max(1, targetRect.width()) / 2f; mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer); + mWindowTargetBounds.set(windowTargetBounds); } public float getCornerRadius(float progress) { @@ -1793,13 +1802,36 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } if (target.mode == MODE_CLOSING) { - float alpha = getWindowAlpha(progress); currentRectF.round(mCurrentRect); + // Scale the target window to match the currentRectF. + final float scale; + + // We need to infer the crop (we crop the window to match the currentRectF). + if (mWindowTargetBounds.height() > mWindowTargetBounds.width()) { + scale = Math.min(1f, currentRectF.width() / mWindowTargetBounds.width()); + + int unscaledHeight = (int) (mCurrentRect.height() * (1f / scale)); + int croppedHeight = mWindowTargetBounds.height() - unscaledHeight; + mTmpRect.set(0, 0, mWindowTargetBounds.width(), + mWindowTargetBounds.height() - croppedHeight); + } else { + scale = Math.min(1f, currentRectF.height() / mWindowTargetBounds.height()); + + int unscaledWidth = (int) (mCurrentRect.width() * (1f / scale)); + int croppedWidth = mWindowTargetBounds.width() - unscaledWidth; + mTmpRect.set(0, 0, mWindowTargetBounds.width() - croppedWidth, + mWindowTargetBounds.height()); + } + + // Match size and position of currentRect. + mMatrix.setScale(scale, scale); + mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top); + builder.withMatrix(mMatrix) - .withWindowCrop(mCurrentRect) - .withAlpha(alpha) - .withCornerRadius(getCornerRadius(progress)); + .withWindowCrop(mTmpRect) + .withAlpha(getWindowAlpha(progress)) + .withCornerRadius(getCornerRadius(progress) / scale); } else if (target.mode == MODE_OPENING) { mMatrix.setTranslate(mTmpPos.x, mTmpPos.y); builder.withMatrix(mMatrix)