diff options
author | Winson Chung <winsonc@google.com> | 2019-06-26 18:33:32 -0700 |
---|---|---|
committer | Winson Chung <winsonc@google.com> | 2019-06-26 18:36:10 -0700 |
commit | 5cd4333b5c62b249d3f0b52020864bb9598f92c9 (patch) | |
tree | fc4c828efc9854fa46dc60117dcfa7364b578542 /quickstep | |
parent | 5ce8c9f73bec627dc74121c868ee73bda51073b5 (diff) | |
parent | 1703eb8b88d1557696b13f6dbbd1db2c15a5993c (diff) | |
download | android_packages_apps_Trebuchet-5cd4333b5c62b249d3f0b52020864bb9598f92c9.tar.gz android_packages_apps_Trebuchet-5cd4333b5c62b249d3f0b52020864bb9598f92c9.tar.bz2 android_packages_apps_Trebuchet-5cd4333b5c62b249d3f0b52020864bb9598f92c9.zip |
Merging ub-launcher3-qt-dev, build 5691374
Test: Manual
Bug:115582915 P2 [Testing] Make all launcher tests gesture-stable
Bug:121280703 P2 Finish implementation of PortraitLandscape annotation for tests
Bug:124524897 P2 Enable some tests in OOP mode
Bug:129158983 P1 Badge bubbles with app icon; use launcher lib for icon, dot, badge rendering
Bug:131116002 P2 Convert tests to TAPL and enable them
Bug:131356741 P1 use transferFocus to implement SWIPE DOWN on homescreen to open noti shade
Bug:131360075 P1 [Gesture Nav] Polish/finish landscape
Bug:132309376 P1 Launcher held ION buffers after clearing all apps in Recent Apps
Bug:132455160 P1 [Gesture Nav] Home to Overview Transition Improvement
Bug:132461400 P1 Fatal exceptions in Launcher3
Bug:132811175 P1 Jump cut if you quickly open an app after going home
Bug:133450867 P1 App window draws outside of icon shape during app to home transition
Bug:133765434 P1 [Flaky test] Launching task didn't open a new window
Bug:133867119 P2 Lab-only flake: want to switch from workspace to all apps; Swipe failed to receive an event for the swipe end
Bug:134609899 P1 Overscrolling on all apps leads to a wrong current task
Bug:135011207 P0 Corner Invocation / Inconsistent - F/C
Bug:135150619 P1 [Launcher] Trigger heapdump on RSS HWM measurements
Bug:135150767 P4 [Launcher] Test reduced resolution snapshots
Bug:135161289 P2 KB3 on qt-dev can't pick Launcher3GoIconRecents from vendor Makefile
Bug:135222111 P1 Major issues with hotseat when predictions are disabled
Bug:135287203 P1 Overview crashes on rotation
Bug:135299165 P1 Configuration changes might result in Launcher ending up in a corrupt state
Bug:135472635 P2 Bad overriden class: java.lang.ClassNotFoundException: com.android.quickstep.QuickstepProcessInitializer
Bug:135473571 P0 Pixel launcher keeps stopping in SuW
Bug:135571566 P4 Search bar is above icons. Even opening and closing launcher does not help
Bug:135686388 P1 Quickswitch sometimes jump
Bug:135687556 P1 Increase assistant gesture touch region on new devices
Bug:135766310 P2 Concerns over config changes during state transitions #2
Bug:135769778 P1 "System navigation changed" notification is shown during P4 setup
Diffstat (limited to 'quickstep')
34 files changed, 858 insertions, 219 deletions
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index eac4dce18..371161ebd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -16,25 +16,32 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator; +import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS; +import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY; +import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.app.ActivityOptions; import android.content.Context; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.anim.SpringObjectAnimator; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -46,11 +53,12 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat; */ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl { - private RecentsView mRecentsView; + public static final int INDEX_SHELF_ANIM = 0; + public static final int INDEX_RECENTS_FADE_ANIM = 1; + public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2; public LauncherAppTransitionManagerImpl(Context context) { super(context); - mRecentsView = mLauncher.getOverviewPanel(); } @Override @@ -133,4 +141,25 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti mLauncher.getStateManager().reapplyState(); }; } + + @Override + public int getStateElementAnimationsCount() { + return 3; + } + + @Override + public Animator createStateElementAnimation(int index, float... values) { + switch (index) { + case INDEX_SHELF_ANIM: + return mLauncher.getAllAppsController().createSpringAnimation(values); + case INDEX_RECENTS_FADE_ANIM: + return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(), + RecentsView.CONTENT_ALPHA, values); + case INDEX_RECENTS_TRANSLATE_X_ANIM: + return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(), + VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values); + default: + return super.createStateElementAnimation(index, values); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index d84362c9f..6ecf1c11b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -16,8 +16,6 @@ package com.android.launcher3.uioverrides; -import static android.view.View.VISIBLE; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; @@ -48,6 +46,7 @@ import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.util.UiThreadHelper.AsyncCommand; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.quickstep.TouchInteractionService; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -62,9 +61,6 @@ public abstract class RecentsUiFactory { private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) -> WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height); - // Scale recents takes before animating in - private static final float RECENTS_PREPARE_SCALE = 1.33f; - public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) { @Override public void mapRect(int left, int top, int right, int bottom, Rect out) { @@ -189,19 +185,10 @@ public abstract class RecentsUiFactory { } /** - * Prepare the recents view to animate in. - * - * @param launcher the launcher activity + * Clears the swipe shared state for the current swipe gesture. */ - public static void prepareToShowOverview(Launcher launcher) { - if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) { - // Overview lives on the side, so doesn't scale in from above. - return; - } - RecentsView overview = launcher.getOverviewPanel(); - if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { - SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); - } + public static void clearSwipeSharedState(boolean finishAnimation) { + TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation); } /** diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 1c6696858..d14de70c5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -83,4 +83,16 @@ public class BackgroundAppState extends OverviewState { public int getVisibleElements(Launcher launcher) { return super.getVisibleElements(launcher) & ~RECENTS_CLEAR_ALL_BUTTON; } + + @Override + public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) { + if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) { + // Translate hotseat offscreen if we show it in overview. + ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher); + scaleAndTranslation.translationY = LayoutUtils.getShelfTrackingDistance(launcher, + launcher.getDeviceProfile()); + return scaleAndTranslation; + } + return super.getHotseatScaleAndTranslation(launcher); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java index bc1d4ba0c..c95476283 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java @@ -14,8 +14,15 @@ * limitations under the License. */ package com.android.launcher3.uioverrides.states; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.Interpolators.INSTANT; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; + import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatorSetBuilder; public class OverviewPeekState extends OverviewState { public OverviewPeekState(int id) { @@ -29,4 +36,13 @@ public class OverviewPeekState extends OverviewState { - launcher.getResources().getDimension(R.dimen.overview_peek_distance); return result; } + + @Override + public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, + AnimatorSetBuilder builder) { + if (this == OVERVIEW_PEEK && fromState == NORMAL) { + builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java index 9a99c1552..5543860ee 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -15,8 +15,20 @@ */ package com.android.launcher3.uioverrides.states; +import static android.view.View.VISIBLE; + import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; +import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; @@ -30,8 +42,11 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.DiscoveryBounce; +import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -40,6 +55,9 @@ import com.android.quickstep.views.TaskView; */ public class OverviewState extends LauncherState { + // Scale recents takes before animating in + private static final float RECENTS_PREPARE_SCALE = 1.33f; + protected static final Rect sTempRect = new Rect(); private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED @@ -127,6 +145,10 @@ public class OverviewState extends LauncherState { // We have no all apps content, so we're still at the fully down progress. return super.getVerticalProgress(launcher); } + return getDefaultVerticalProgress(launcher); + } + + public static float getDefaultVerticalProgress(Launcher launcher) { return 1 - (getDefaultSwipeHeight(launcher) / launcher.getAllAppsController().getShiftRange()); } @@ -156,6 +178,29 @@ public class OverviewState extends LauncherState { } } + @Override + public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, + AnimatorSetBuilder builder) { + if (fromState == NORMAL && this == OVERVIEW) { + if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) { + builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL); + builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); + } else { + builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); + + // Scale up the recents, if it is not coming from the side + RecentsView overview = launcher.getOverviewPanel(); + if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { + SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); + } + } + builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); + builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); + } + } + public static OverviewState newBackgroundState(int id) { return new BackgroundAppState(id); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index e3e339add..3fe4bcfd9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -16,11 +16,20 @@ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; import static com.android.launcher3.LauncherStateManager.ANIM_ALL; import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; +import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; @@ -38,14 +47,14 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.views.RecentsView; -import com.android.systemui.shared.system.QuickStepContract; /** * Touch controller which handles swipe and hold to go to Overview */ public class FlingAndHoldTouchController extends PortraitStatesTouchController { - private static final long PEEK_ANIM_DURATION = 100; + private static final long PEEK_IN_ANIM_DURATION = 240; + private static final long PEEK_OUT_ANIM_DURATION = 100; private static final float MAX_DISPLACEMENT_PERCENT = 0.75f; private final MotionPauseDetector mMotionPauseDetector; @@ -81,9 +90,9 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK; LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL; + long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION; mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState, - new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, - PEEK_ANIM_DURATION); + new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration); mPeekAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -107,6 +116,21 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } @Override + protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState, + LauncherState toState) { + if (fromState == NORMAL && toState == ALL_APPS) { + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + + // Get workspace out of the way quickly, to prepare for potential pause. + builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3); + builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3); + builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3); + return builder; + } + return super.getAnimatorSetBuilderForStates(fromState, toState); + } + + @Override public boolean onDrag(float displacement, MotionEvent event) { float upDisplacement = -displacement; mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement @@ -123,8 +147,11 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } AnimatorSetBuilder builder = new AnimatorSetBuilder(); - builder.setInterpolator(AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); - builder.setInterpolator(AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); + if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2); + } AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation( NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION); overviewAnim.addListener(new AnimatorListenerAdapter() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 0d06c19ab..07c049642 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -18,12 +18,13 @@ package com.android.quickstep; import static android.view.View.TRANSLATION_Y; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_FADE_ANIM; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_TRANSLATE_X_ANIM; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherStateManager.ANIM_ALL; -import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO; -import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.INSTANT; @@ -31,7 +32,6 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; @@ -48,8 +48,6 @@ import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import androidx.dynamicanimation.animation.SpringAnimation; -import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; @@ -60,13 +58,14 @@ import com.android.launcher3.LauncherStateManager; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.StaggeredWorkspaceAnim; +import com.android.quickstep.views.LauncherRecentsView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -216,10 +215,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe activity.getAppsView().getContentView().setVisibility(View.GONE); return new AnimationFactory() { - private Animator mShelfAnim; private ShelfAnimState mShelfState; - private Animator mAttachToWindowFadeAnim; - private SpringAnimation mAttachToWindowTranslationXAnim; private boolean mIsAttachedToWindow; @Override @@ -252,31 +248,26 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe return; } mShelfState = shelfState; - if (mShelfAnim != null) { - mShelfAnim.cancel(); - } + activity.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM); if (mShelfState == ShelfAnimState.CANCEL) { return; } float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity); float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity); + // Peek based on default overview progress so we can see hotseat if we're showing + // that instead of predictions in overview. + float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(activity); float shelfPeekingProgress = shelfHiddenProgress - - (shelfHiddenProgress - shelfOverviewProgress) * 0.25f; + - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f; float toProgress = mShelfState == ShelfAnimState.HIDE ? shelfHiddenProgress : mShelfState == ShelfAnimState.PEEK ? shelfPeekingProgress : shelfOverviewProgress; - mShelfAnim = createShelfAnim(activity, toProgress); - mShelfAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mShelfAnim = null; - } - }); - mShelfAnim.setInterpolator(interpolator); - mShelfAnim.setDuration(duration); - mShelfAnim.start(); + Animator shelfAnim = activity.getStateManager() + .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress); + shelfAnim.setInterpolator(interpolator); + shelfAnim.setDuration(duration).start(); } @Override @@ -285,12 +276,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe return; } mIsAttachedToWindow = attached; - if (mAttachToWindowFadeAnim != null) { - mAttachToWindowFadeAnim.cancel(); - } - RecentsView recentsView = activity.getOverviewPanel(); - mAttachToWindowFadeAnim = ObjectAnimator.ofFloat(recentsView, - RecentsView.CONTENT_ALPHA, attached ? 1 : 0); + LauncherRecentsView recentsView = activity.getOverviewPanel(); + Animator fadeAnim = activity.getStateManager() + .createStateElementAnimation( + INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0); int runningTaskIndex = recentsView.getRunningTaskIndex(); if (runningTaskIndex == 0) { @@ -312,33 +301,28 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0; float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX; - if (mAttachToWindowTranslationXAnim == null) { - mAttachToWindowTranslationXAnim = new SpringAnimation(recentsView, - SpringAnimation.TRANSLATION_X).setSpring(new SpringForce() - .setDampingRatio(0.8f) - .setStiffness(250)); - } + activity.getStateManager() + .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM); + if (!recentsView.isShown() && animate) { recentsView.setTranslationX(fromTranslationX); - mAttachToWindowTranslationXAnim.setStartValue(fromTranslationX); + } else { + fromTranslationX = recentsView.getTranslationX(); } - mAttachToWindowTranslationXAnim.animateToFinalPosition(toTranslationX); - if (!animate && mAttachToWindowTranslationXAnim.canSkipToEnd()) { - mAttachToWindowTranslationXAnim.skipToEnd(); + + if (!animate) { + recentsView.setTranslationX(toTranslationX); + } else { + activity.getStateManager().createStateElementAnimation( + INDEX_RECENTS_TRANSLATE_X_ANIM, + fromTranslationX, toTranslationX).start(); } - mAttachToWindowFadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2); + fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2); } else { - mAttachToWindowFadeAnim.setInterpolator(ACCEL_DEACCEL); + fadeAnim.setInterpolator(ACCEL_DEACCEL); } - mAttachToWindowFadeAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAttachToWindowFadeAnim = null; - } - }); - mAttachToWindowFadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0); - mAttachToWindowFadeAnim.start(); + fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start(); } }; } @@ -354,10 +338,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe if (!activity.getDeviceProfile().isVerticalBarLayout() && SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) { // Don't animate the shelf when the mode is NO_BUTTON, because we update it atomically. - Animator shiftAnim = createShelfAnim(activity, + anim.play(activity.getStateManager().createStateElementAnimation( + INDEX_SHELF_ANIM, fromState.getVerticalProgress(activity), - endState.getVerticalProgress(activity)); - anim.play(shiftAnim); + endState.getVerticalProgress(activity))); } playScaleDownAnim(anim, activity, fromState, endState); @@ -375,13 +359,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe callback.accept(controller); } - private Animator createShelfAnim(Launcher activity, float ... progressValues) { - Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(), - "allAppsSpringFromACH", activity.getAllAppsController().getShiftRange(), - SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues); - return shiftAnim; - } - /** * Scale down recents from the center task being full screen to being in overview. */ diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java index 5eecf1713..ddd28a350 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -19,6 +19,8 @@ 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.Utilities.FLAG_NO_GESTURES; + import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; @@ -182,7 +184,10 @@ public class RecentsAnimationWrapper { } } if (mInputConsumer != null) { + int flags = ev.getEdgeFlags(); + ev.setEdgeFlags(flags | FLAG_NO_GESTURES); mInputConsumer.onMotionEvent(ev); + ev.setEdgeFlags(flags); } return true; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java index 6689ce3d7..c55f656df 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java @@ -72,13 +72,15 @@ public class SwipeSharedState implements SwipeAnimationListener { mLastAnimationRunning = false; } - private void clearListenerState() { + private void clearListenerState(boolean finishAnimation) { if (mRecentsAnimationListener != null) { mRecentsAnimationListener.removeListener(this); mRecentsAnimationListener.cancelListener(); if (mLastAnimationRunning && mLastAnimationTarget != null) { Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), - mLastAnimationTarget::cancelAnimation); + finishAnimation + ? mLastAnimationTarget::finishAnimation + : mLastAnimationTarget::cancelAnimation); mLastAnimationTarget = null; } } @@ -106,7 +108,7 @@ public class SwipeSharedState implements SwipeAnimationListener { } } - clearListenerState(); + clearListenerState(false /* finishAnimation */); boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen(); mRecentsAnimationListener = new RecentsAnimationListenerSet( @@ -138,8 +140,8 @@ public class SwipeSharedState implements SwipeAnimationListener { mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets(); } - public void clearAllState() { - clearListenerState(); + public void clearAllState(boolean finishAnimation) { + clearListenerState(finishAnimation); canGestureBeContinued = false; recentsAnimationFinishInterrupted = false; nextRunningTaskId = -1; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index d0ea73a6f..6897c1e7d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -29,8 +29,6 @@ import android.view.View; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.MultiValueUpdateListener; @@ -123,6 +121,7 @@ public final class TaskViewUtils { new RemoteAnimationTargetSet(targets, MODE_OPENING); targetSet.addDependentTransactionApplier(applier); + final RecentsView recentsView = v.getRecentsView(); final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); appAnimator.addUpdateListener(new MultiValueUpdateListener() { @@ -153,7 +152,10 @@ public final class TaskViewUtils { // TODO: Take into account the current fullscreen progress for animating the insets params.setProgress(1 - percent); RectF taskBounds = inOutHelper.applyTransform(targetSet, params); - if (!skipViewChanges) { + int taskIndex = recentsView.indexOfChild(v); + int centerTaskIndex = recentsView.getCurrentPage(); + boolean parallaxCenterAndAdjacentTask = taskIndex != centerTaskIndex; + if (!skipViewChanges && parallaxCenterAndAdjacentTask) { float scale = taskBounds.width() / mThumbnailRect.width(); v.setScaleX(scale); v.setScaleY(scale); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 769d207ab..53da0f92d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -81,6 +81,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantTouchConsumer; import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; +import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer; import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; @@ -224,14 +225,18 @@ public class TouchInteractionService extends Service implements }; private static boolean sConnected = false; + private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState(); public static boolean isConnected() { return sConnected; } - private final SwipeSharedState mSwipeSharedState = new SwipeSharedState(); + public static SwipeSharedState getSwipeSharedState() { + return sSwipeSharedState; + } + private final InputConsumer mResetGestureInputConsumer = - new ResetGestureInputConsumer(mSwipeSharedState); + new ResetGestureInputConsumer(sSwipeSharedState); private ActivityManagerWrapper mAM; private RecentsModel mRecentsModel; @@ -267,6 +272,9 @@ public class TouchInteractionService extends Service implements private Mode mMode = Mode.THREE_BUTTONS; private int mDefaultDisplayId; private final RectF mSwipeTouchRegion = new RectF(); + private final RectF mAssistantLeftRegion = new RectF(); + private final RectF mAssistantRightRegion = new RectF(); + private ComponentName mGestureBlockingActivity; private Region mExclusionRegion; @@ -349,9 +357,25 @@ public class TouchInteractionService extends Service implements defaultDisplay.getRealSize(realSize); mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y); if (mMode == Mode.NO_BUTTON) { - mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); + int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); + mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - touchHeight; + + final int assistantWidth = getResources() + .getDimensionPixelSize(R.dimen.gestures_assistant_width); + final float assistantHeight = Math.max(touchHeight, + QuickStepContract.getWindowCornerRadius(getResources())); + mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeTouchRegion.bottom; + mAssistantLeftRegion.top = mAssistantRightRegion.top = + mSwipeTouchRegion.bottom - assistantHeight; + + mAssistantLeftRegion.left = 0; + mAssistantLeftRegion.right = assistantWidth; + + mAssistantRightRegion.right = mSwipeTouchRegion.right; + mAssistantRightRegion.left = mSwipeTouchRegion.right - assistantWidth; } else { + mAssistantLeftRegion.setEmpty(); + mAssistantRightRegion.setEmpty(); switch (defaultDisplay.getRotation()) { case Surface.ROTATION_90: mSwipeTouchRegion.left = mSwipeTouchRegion.right @@ -416,7 +440,7 @@ public class TouchInteractionService extends Service implements mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); mIsUserUnlocked = true; - mSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver); + sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver); mInputConsumer.registerInputConsumer(); onSystemUiProxySet(); onSystemUiFlagsChanged(); @@ -491,6 +515,15 @@ public class TouchInteractionService extends Service implements mConsumer = newConsumer(useSharedState, event); TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType()); mUncheckedConsumer = mConsumer; + } else if (mIsUserUnlocked && mMode == Mode.NO_BUTTON + && canTriggerAssistantAction(event)) { + // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should + // not interrupt it. QuickSwitch assumes that interruption can only happen if the + // next gesture is also quick switch. + mUncheckedConsumer = + new AssistantTouchConsumer(this, mISystemUiProxy, + mOverviewComponentObserver.getActivityControlHelper(), + InputConsumer.NO_OP, mInputMonitorCompat); } else { mUncheckedConsumer = InputConsumer.NO_OP; } @@ -505,6 +538,14 @@ public class TouchInteractionService extends Service implements || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0); } + private boolean canTriggerAssistantAction(MotionEvent ev) { + return mAssistantAvailable + && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) + && (mAssistantLeftRegion.contains(ev.getX(), ev.getY()) || + mAssistantRightRegion.contains(ev.getX(), ev.getY())) + && !ActivityManagerWrapper.getInstance().isLockToAppActive(); + } + private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) { boolean isInValidSystemUiState = validSystemUiFlags(); @@ -525,10 +566,7 @@ public class TouchInteractionService extends Service implements if (mMode == Mode.NO_BUTTON) { final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); - if (mAssistantAvailable - && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) - && AssistantTouchConsumer.withinTouchRegion(this, event) - && !ActivityManagerWrapper.getInstance().isLockToAppActive()) { + if (canTriggerAssistantAction(event)) { base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base, mInputMonitorCompat); } @@ -555,7 +593,7 @@ public class TouchInteractionService extends Service implements private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) { final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); if (!useSharedState) { - mSwipeSharedState.clearAllState(); + sSwipeSharedState.clearAllState(false /* finishAnimation */); } if ((mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0) { // This handles apps showing over the lockscreen (e.g. camera) @@ -565,22 +603,26 @@ public class TouchInteractionService extends Service implements final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); - if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher - && !mSwipeSharedState.recentsAnimationFinishInterrupted) { + if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher + && !sSwipeSharedState.recentsAnimationFinishInterrupted) { return mResetGestureInputConsumer; - } else if (mSwipeSharedState.recentsAnimationFinishInterrupted) { + } else if (sSwipeSharedState.recentsAnimationFinishInterrupted) { // If the finish animation was interrupted, then continue using the other activity input // consumer but with the next task as the running task RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); - info.id = mSwipeSharedState.nextRunningTaskId; + info.id = sSwipeSharedState.nextRunningTaskId; return createOtherActivityInputConsumer(event, info); - } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) { + } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()) { return createOverviewInputConsumer(event); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) { return createOverviewInputConsumer(event); } else if (mGestureBlockingActivity != null && runningTaskInfo != null && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) { return mResetGestureInputConsumer; + } else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) { + return new FallbackNoButtonInputConsumer(this, activityControl, + mInputMonitorCompat, sSwipeSharedState, mSwipeTouchRegion, + mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo); } else { return createOtherActivityInputConsumer(event, runningTaskInfo); } @@ -602,13 +644,13 @@ public class TouchInteractionService extends Service implements return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel, mOverviewComponentObserver.getOverviewIntent(), activityControl, shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive, - mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, + sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, disableHorizontalSwipe(event)); } private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) { if (mMode == Mode.NO_BUTTON && taskInfo != null) { - return new DeviceLockedInputConsumer(this, mSwipeSharedState, mInputMonitorCompat, + return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, taskInfo.taskId); } else { return mResetGestureInputConsumer; @@ -623,7 +665,7 @@ public class TouchInteractionService extends Service implements return mResetGestureInputConsumer; } - if (activity.getRootView().hasWindowFocus() || mSwipeSharedState.goingToLauncher) { + if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) { return new OverviewInputConsumer(activity, mInputMonitorCompat, false /* startingInActivityBounds */); } else { @@ -670,7 +712,7 @@ public class TouchInteractionService extends Service implements + mOverviewComponentObserver.getActivityControlHelper().isResumed()); pw.println(" useSharedState=" + mConsumer.useSharedSwipeState()); if (mConsumer.useSharedSwipeState()) { - mSwipeSharedState.dump(" ", pw); + sSwipeSharedState.dump(" ", pw); } pw.println(" mConsumer=" + mConsumer.getName()); pw.println("FeatureFlags:"); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java index 837423ace..38b5a137c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java @@ -39,6 +39,8 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -47,17 +49,14 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.touch.SwipeDetector; import com.android.quickstep.ActivityControlHelper; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.InputMonitorCompat; -import com.android.systemui.shared.system.QuickStepContract; /** * Touch consumer for handling events to launch assistant from launcher */ -public class AssistantTouchConsumer extends DelegateInputConsumer - implements SwipeDetector.Listener { +public class AssistantTouchConsumer extends DelegateInputConsumer { private static final String TAG = "AssistantTouchConsumer"; private static final long RETRACT_ANIMATION_DURATION_MS = 300; @@ -68,7 +67,6 @@ public class AssistantTouchConsumer extends DelegateInputConsumer private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83; private static final String INVOCATION_TYPE_KEY = "invocation_type"; private static final int INVOCATION_TYPE_GESTURE = 1; - private static final int INVOCATION_TYPE_FLING = 6; private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); @@ -90,7 +88,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer private final float mSquaredSlop; private final ISystemUiProxy mSysUiProxy; private final Context mContext; - private final SwipeDetector mSwipeDetector; + private final GestureDetector mGestureDetector; public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy, ActivityControlHelper activityControlHelper, InputConsumer delegate, @@ -107,8 +105,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer mSquaredSlop = slop * slop; mActivityControlHelper = activityControlHelper; - mSwipeDetector = new SwipeDetector(mContext, this, SwipeDetector.VERTICAL); - mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false); + + mGestureDetector = new GestureDetector(context, new AssistantGestureListener()); } @Override @@ -119,7 +117,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer @Override public void onMotionEvent(MotionEvent ev) { // TODO add logging - mSwipeDetector.onTouchEvent(ev); + mGestureDetector.onTouchEvent(ev); switch (ev.getActionMasked()) { case ACTION_DOWN: { @@ -171,13 +169,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer mStartDragPos.set(mLastPos.x, mLastPos.y); mDragTime = SystemClock.uptimeMillis(); - // Determine if angle is larger than threshold for assistant detection - float angle = (float) Math.toDegrees( - Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x)); - mDirection = angle > 90 ? UPLEFT : UPRIGHT; - angle = angle > 90 ? 180 - angle : angle; - - if (angle > mAngleThreshold && angle < 90) { + if (isValidAssistantGestureAngle( + mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) { setActive(ev); } else { mState = STATE_DELEGATE_ACTIVE; @@ -261,40 +254,41 @@ public class AssistantTouchConsumer extends DelegateInputConsumer } } - public static boolean withinTouchRegion(Context context, MotionEvent ev) { - final Resources res = context.getResources(); - final int width = res.getDisplayMetrics().widthPixels; - final int height = res.getDisplayMetrics().heightPixels; - final int size = res.getDimensionPixelSize(R.dimen.gestures_assistant_size); - return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size; - } - - @Override - public void onDragStart(boolean start) { - // do nothing + /** + * Determine if angle is larger than threshold for assistant detection + */ + private boolean isValidAssistantGestureAngle(float deltaX, float deltaY) { + float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX)); + mDirection = angle > 90 ? UPLEFT : UPRIGHT; + + // normalize so that angle is measured clockwise from horizontal in the bottom right corner + // and counterclockwise from horizontal in the bottom left corner + angle = angle > 90 ? 180 - angle : angle; + return (angle > mAngleThreshold && angle < 90); } - @Override - public boolean onDrag(float displacement) { - return false; - } + private class AssistantGestureListener extends SimpleOnGestureListener { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (isValidAssistantGestureAngle(velocityX, -velocityY) + && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) { + mLastProgress = 1; + try { + mSysUiProxy.onAssistantGestureCompletion( + (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY)); + startAssistantInternal(FLING); - @Override - public void onDragEnd(float velocity, boolean fling) { - if (fling && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) { - mLastProgress = 1; - try { - mSysUiProxy.onAssistantGestureCompletion(velocity); - startAssistantInternal(FLING); - - Bundle args = new Bundle(); - args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE); - mSysUiProxy.startAssistant(args); - mLaunchedAssistant = true; - } catch (RemoteException e) { - Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + mLastProgress, - e); + Bundle args = new Bundle(); + args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE); + mSysUiProxy.startAssistant(args); + mLaunchedAssistant = true; + } catch (RemoteException e) { + Log.w(TAG, + "Failed to send SysUI start/send assistant progress: " + mLastProgress, + e); + } } + return true; } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java new file mode 100644 index 000000000..d05ca2a16 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2019 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.inputconsumers; + +import static android.view.MotionEvent.ACTION_CANCEL; +import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_POINTER_DOWN; +import static android.view.MotionEvent.ACTION_POINTER_UP; +import static android.view.MotionEvent.ACTION_UP; +import static android.view.MotionEvent.INVALID_POINTER_ID; + +import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION; +import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; +import android.content.Intent; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.WindowManager; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.R; +import com.android.quickstep.ActivityControlHelper; +import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.SwipeSharedState; +import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.ClipAnimationHelper.TransformParams; +import com.android.quickstep.util.NavBarPosition; +import com.android.quickstep.util.RecentsAnimationListenerSet; +import com.android.quickstep.util.SwipeAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.BackgroundExecutor; +import com.android.systemui.shared.system.InputMonitorCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + +public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener { + + private static final int STATE_NOT_FINISHED = 0; + private static final int STATE_FINISHED_TO_HOME = 1; + private static final int STATE_FINISHED_TO_APP = 2; + + private static final float PROGRESS_TO_END_GESTURE = -2; + + private final ActivityControlHelper mActivityControlHelper; + private final InputMonitorCompat mInputMonitor; + private final Context mContext; + private final NavBarPosition mNavBarPosition; + private final SwipeSharedState mSwipeSharedState; + private final OverviewComponentObserver mOverviewComponentObserver; + private final int mRunningTaskId; + + private final ClipAnimationHelper mClipAnimationHelper; + private final TransformParams mTransformParams = new TransformParams(); + private final float mTransitionDragLength; + private final DeviceProfile mDP; + + private final RectF mSwipeTouchRegion; + private final boolean mDisableHorizontalSwipe; + + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + + private int mActivePointerId = -1; + // Slop used to determine when we say that the gesture has started. + private boolean mPassedPilferInputSlop; + + private VelocityTracker mVelocityTracker; + + // Distance after which we start dragging the window. + private final float mTouchSlop; + + // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar. + private float mStartDisplacement; + private SwipeAnimationTargetSet mSwipeAnimationTargetSet; + private float mProgress; + + private int mState = STATE_NOT_FINISHED; + + public FallbackNoButtonInputConsumer(Context context, + ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor, + SwipeSharedState swipeSharedState, RectF swipeTouchRegion, + OverviewComponentObserver overviewComponentObserver, + boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) { + mContext = context; + mActivityControlHelper = activityControlHelper; + mInputMonitor = inputMonitor; + mOverviewComponentObserver = overviewComponentObserver; + mRunningTaskId = runningTaskInfo.id; + + mSwipeSharedState = swipeSharedState; + mSwipeTouchRegion = swipeTouchRegion; + mDisableHorizontalSwipe = disableHorizontalSwipe; + + mNavBarPosition = new NavBarPosition(context); + mVelocityTracker = VelocityTracker.obtain(); + + mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO + * ViewConfiguration.get(context).getScaledTouchSlop(); + + mClipAnimationHelper = new ClipAnimationHelper(context); + + mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context); + Rect tempRect = new Rect(); + mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength( + mDP, context, tempRect); + mClipAnimationHelper.updateTargetRect(tempRect); + } + + @Override + public int getType() { + return TYPE_FALLBACK_NO_BUTTON; + } + + @Override + public void onMotionEvent(MotionEvent ev) { + if (mVelocityTracker == null) { + return; + } + + mVelocityTracker.addMovement(ev); + if (ev.getActionMasked() == ACTION_POINTER_UP) { + mVelocityTracker.clear(); + } + + switch (ev.getActionMasked()) { + case ACTION_DOWN: { + mActivePointerId = ev.getPointerId(0); + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); + break; + } + case ACTION_POINTER_DOWN: { + if (!mPassedPilferInputSlop) { + // Cancel interaction in case of multi-touch interaction + int ptrIdx = ev.getActionIndex(); + if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) { + forceCancelGesture(ev); + } + } + break; + } + case 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 ACTION_MOVE: { + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + float displacement = getDisplacement(ev); + + if (!mPassedPilferInputSlop) { + if (mDisableHorizontalSwipe && Math.abs(mLastPos.x - mDownPos.x) + > Math.abs(mLastPos.y - mDownPos.y)) { + // Horizontal gesture is not allowed in this region + forceCancelGesture(ev); + break; + } + + if (Math.abs(displacement) >= mTouchSlop) { + mPassedPilferInputSlop = true; + + // Deferred gesture, start the animation and gesture tracking once + // we pass the actual touch slop + startTouchTrackingForWindowAnimation(displacement); + } + } else { + updateDisplacement(displacement - mStartDisplacement); + } + break; + } + case ACTION_CANCEL: + case ACTION_UP: { + finishTouchTracking(ev); + break; + } + } + } + + private void startTouchTrackingForWindowAnimation(float displacement) { + mStartDisplacement = Math.min(displacement, -mTouchSlop); + + RecentsAnimationListenerSet listenerSet = + mSwipeSharedState.newRecentsAnimationListenerSet(); + listenerSet.addListener(this); + Intent homeIntent = mOverviewComponentObserver.getHomeIntent(); + BackgroundExecutor.get().submit( + () -> ActivityManagerWrapper.getInstance().startRecentsActivity( + homeIntent, null, listenerSet, null, null)); + + ActivityManagerWrapper.getInstance().closeSystemWindows( + CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); + mInputMonitor.pilferPointers(); + } + + private void updateDisplacement(float displacement) { + mProgress = displacement / mTransitionDragLength; + mTransformParams.setProgress(mProgress); + + if (mSwipeAnimationTargetSet != null) { + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + } + } + + private void forceCancelGesture(MotionEvent ev) { + int action = ev.getAction(); + ev.setAction(ACTION_CANCEL); + finishTouchTracking(ev); + ev.setAction(action); + } + + /** + * Called when the gesture has ended. Does not correlate to the completion of the interaction as + * the animation can still be running. + */ + private void finishTouchTracking(MotionEvent ev) { + if (ev.getAction() == ACTION_CANCEL) { + mState = STATE_FINISHED_TO_APP; + } else { + mVelocityTracker.computeCurrentVelocity(1000, + ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity()); + float velocityX = mVelocityTracker.getXVelocity(mActivePointerId); + float velocityY = mVelocityTracker.getYVelocity(mActivePointerId); + float velocity = mNavBarPosition.isRightEdge() ? velocityX + : mNavBarPosition.isLeftEdge() ? -velocityX + : velocityY; + float flingThreshold = mContext.getResources() + .getDimension(R.dimen.quickstep_fling_threshold_velocity); + boolean isFling = Math.abs(velocity) > flingThreshold; + + boolean goingHome; + if (!isFling) { + goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW; + } else { + goingHome = velocity < 0; + } + + if (goingHome) { + mState = STATE_FINISHED_TO_HOME; + } else { + mState = STATE_FINISHED_TO_APP; + } + } + + if (mSwipeAnimationTargetSet != null) { + finishAnimationTargetSet(); + } + } + + private void finishAnimationTargetSet() { + if (mState == STATE_FINISHED_TO_APP) { + mSwipeAnimationTargetSet.finishController(false, null, false); + } else { + if (mProgress < PROGRESS_TO_END_GESTURE) { + mSwipeAnimationTargetSet.finishController(true, null, true); + } else { + long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1) + * MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE)); + if (duration < 0) { + duration = MIN_SWIPE_DURATION; + } + + ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE); + anim.addUpdateListener(a -> { + float p = (Float) anim.getAnimatedValue(); + mTransformParams.setProgress(p); + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + }); + anim.setDuration(duration); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSwipeAnimationTargetSet.finishController(true, null, true); + } + }); + anim.start(); + } + } + } + + @Override + public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { + mSwipeAnimationTargetSet = targetSet; + Rect overviewStackBounds = new Rect(0, 0, mDP.widthPx, mDP.heightPx); + RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId); + + mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class)); + if (runningTaskTarget != null) { + mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); + } + mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */); + + overviewStackBounds + .inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5); + mClipAnimationHelper.updateTargetRect(overviewStackBounds); + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + + if (mState != STATE_NOT_FINISHED) { + finishAnimationTargetSet(); + } + } + + @Override + public void onRecentsAnimationCanceled() { } + + private float getDisplacement(MotionEvent ev) { + if (mNavBarPosition.isRightEdge()) { + return ev.getX() - mDownPos.x; + } else if (mNavBarPosition.isLeftEdge()) { + return mDownPos.x - ev.getX(); + } else { + return ev.getY() - mDownPos.y; + } + } + + @Override + public boolean allowInterceptByParent() { + return !mPassedPilferInputSlop; + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java index a1e5d47a5..f5cf654b1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java @@ -33,6 +33,7 @@ public interface InputConsumer { int TYPE_SCREEN_PINNED = 1 << 6; int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7; int TYPE_RESET_GESTURE = 1 << 8; + int TYPE_FALLBACK_NO_BUTTON = 1 << 9; String[] NAMES = new String[] { "TYPE_NO_OP", // 0 @@ -44,6 +45,7 @@ public interface InputConsumer { "TYPE_SCREEN_PINNED", // 6 "TYPE_OVERVIEW_WITHOUT_FOCUS", // 7 "TYPE_RESET_GESTURE", // 8 + "TYPE_FALLBACK_NO_BUTTON", // 9 }; InputConsumer NO_OP = () -> TYPE_NO_OP; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 6bc543f07..4c137d3bf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -79,7 +79,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private static final String UP_EVT = "OtherActivityInputConsumer.UP"; // TODO: Move to quickstep contract - private static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3; + public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3; private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final RunningTaskInfo mRunningTask; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java index 56cba2192..8eede81b8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java @@ -39,7 +39,7 @@ public class ResetGestureInputConsumer implements InputConsumer { public void onMotionEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN && mSwipeSharedState.getActiveListener() != null) { - mSwipeSharedState.clearAllState(); + mSwipeSharedState.clearAllState(false /* finishAnimation */); } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index 9eda2f9d4..07e96869e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -17,19 +17,15 @@ package com.android.quickstep.util; import android.animation.Animator; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.view.View; import android.view.ViewGroup; import androidx.annotation.Nullable; -import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty; import com.android.launcher3.LauncherState; -import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; @@ -40,6 +36,7 @@ import com.android.launcher3.anim.SpringObjectAnimator; import java.util.ArrayList; import java.util.List; +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.anim.Interpolators.LINEAR; @@ -147,9 +144,8 @@ public class StaggeredWorkspaceAnim { long startDelay = (long) ((invertedRow + 1) * APP_CLOSE_ROW_START_DELAY_MS); v.setTranslationY(mSpringTransY); - SpringObjectAnimator springTransY = new SpringObjectAnimator<>( - new ViewProgressProperty(v, View.TRANSLATION_Y), "staggeredSpringTransY", 1f, - DAMPING_RATIO, STIFFNESS, mSpringTransY, 0); + SpringObjectAnimator springTransY = new SpringObjectAnimator<>(v, VIEW_TRANSLATE_Y, + 1f, DAMPING_RATIO, STIFFNESS, mSpringTransY, 0); springTransY.setStartDelay(startDelay); mAnimators.add(springTransY); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java index df9efa247..381c27a28 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java @@ -106,6 +106,10 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet { finishController(false /* toRecents */, null, false /* sendUserLeaveHint */); } + public void finishAnimation() { + finishController(true /* toRecents */, null, false /* sendUserLeaveHint */); + } + public interface SwipeAnimationListener { void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 5b2e27e53..03441c87e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -33,9 +33,11 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateListener; @@ -250,4 +252,16 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL setDisallowScrollToClearAll(!hasClearAllButton); } } + + @Override + protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + // Allow touches to go through to the hotseat. + Hotseat hotseat = mActivity.getHotseat(); + boolean touchingHotseat = hotseat.isShown() + && mActivity.getDragLayer().isEventOverView(hotseat, ev, this); + return !touchingHotseat; + } + return super.shouldStealTouchFromSiblingsBelow(ev); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 9058e7eda..a98df0fa1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -16,9 +16,13 @@ package com.android.quickstep.views; +import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS; + import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; @@ -79,7 +83,6 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty; import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; @@ -124,10 +127,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private static final String TAG = RecentsView.class.getSimpleName(); - public static final float SPRING_MIN_VISIBLE_CHANGE = 0.001f; - public static final float SPRING_DAMPING_RATIO = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY; - public static final float SPRING_STIFFNESS = SpringForce.STIFFNESS_MEDIUM; - public static final FloatProperty<RecentsView> CONTENT_ALPHA = new FloatProperty<RecentsView>("contentAlpha") { @Override @@ -521,6 +520,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl // Do not let touch escape to siblings below this view. + return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev); + } + + protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) { return true; } @@ -1027,9 +1030,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) { addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim); if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView) - addAnim(new SpringObjectAnimator<>(new ViewProgressProperty(taskView, - View.TRANSLATION_Y), "taskViewTransY", SPRING_MIN_VISIBLE_CHANGE, - SPRING_DAMPING_RATIO, SPRING_STIFFNESS, 0, -taskView.getHeight()), + addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y, + MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, + SpringForce.STIFFNESS_MEDIUM, + 0, -taskView.getHeight()), duration, LINEAR, anim); else { addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()), @@ -1105,10 +1109,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl int scrollDiff = newScroll[i] - oldScroll[i] + offset; if (scrollDiff != 0) { if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) { - addAnim(new SpringObjectAnimator<>( - new ViewProgressProperty(child, View.TRANSLATION_X), - "taskViewTransX", SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO, - SPRING_STIFFNESS, 0, scrollDiff), duration, ACCEL, anim); + addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X, + MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, + SpringForce.STIFFNESS_MEDIUM, + 0, scrollDiff), duration, ACCEL, anim); } else { addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration, ACCEL, anim); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 6f10b42fb..d55a52044 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -367,8 +367,11 @@ public class TaskThumbnailView extends View { } mRotated = isRotated; - updateOverlay(); invalidate(); + + // Update can be called from {@link #onSizeChanged} during layout, post handling of overlay + // as overlay could modify the views in the overlay as a side effect of its update. + post(this::updateOverlay); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index e7e41893c..b26fdce92 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -368,6 +368,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } else { mSnapshotView.setThumbnail(null, null); setIcon(null); + // Reset the task thumbnail reference as well (it will be fetched from the cache or + // reloaded next time we need it) + mTask.thumbnail = null; } } @@ -488,9 +491,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mSnapshotView.setThumbnail(mTask, null); setOverlayEnabled(false); onTaskListVisibilityChanged(false); - if (mTask != null) { - mTask.thumbnail = null; - } } @Override diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 82d1aa672..71259fd7a 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -24,7 +24,7 @@ <dimen name="recents_page_spacing">10dp</dimen> <dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen> - <dimen name="overview_peek_distance">32dp</dimen> + <dimen name="overview_peek_distance">96dp</dimen> <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start loading full resolution screenshots. --> @@ -66,7 +66,8 @@ <dimen name="shelf_surface_offset">24dp</dimen> <!-- Assistant Gestures --> - <dimen name="gestures_assistant_size">48dp</dimen> + <!-- Distance from the vertical edges of the screen in which assist gestures are recognized --> + <dimen name="gestures_assistant_width">48dp</dimen> <dimen name="gestures_assistant_drag_threshold">55dp</dimen> <!-- Distance to move elements when swiping up to go home from launcher --> diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 864316095..44324cb2e 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -765,7 +765,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans LauncherAnimationRunner.AnimationResult result) { if (!mLauncher.hasBeenResumed()) { // If launcher is not resumed, wait until new async-frame after resume - mLauncher.setOnResumeCallback(() -> + mLauncher.addOnResumeCallback(() -> postAsyncCallback(mHandler, () -> onCreateAnimation(targetCompats, result))); return; diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java index ab24f5f5a..85a954507 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java @@ -23,6 +23,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.quickstep.SysUINavigationMode; /** * Definition for AllApps state @@ -63,7 +64,13 @@ public class AllAppsState extends LauncherState { public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) { ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW .getWorkspaceScaleAndTranslation(launcher); - scaleAndTranslation.scale = 1; + if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) { + float normalScale = 1; + // Scale down halfway to where we'd be in overview, to prepare for a potential pause. + scaleAndTranslation.scale = (scaleAndTranslation.scale + normalScale) / 2; + } else { + scaleAndTranslation.scale = 1; + } return scaleAndTranslation; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java index fee18204e..f5ba3725d 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java @@ -17,17 +17,22 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_UP; +import static android.view.MotionEvent.ACTION_CANCEL; +import android.graphics.PointF; import android.os.RemoteException; import android.util.Log; +import android.util.SparseArray; import android.view.MotionEvent; import android.view.ViewConfiguration; +import android.view.Window; +import android.view.WindowManager; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.touch.TouchEventTranslator; import com.android.launcher3.util.TouchController; import com.android.quickstep.RecentsModel; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -36,18 +41,29 @@ import java.io.PrintWriter; /** * TouchController for handling touch events that get sent to the StatusBar. Once the - * Once the event delta y passes the touch slop, the events start getting forwarded. + * Once the event delta mDownY passes the touch slop, the events start getting forwarded. * All events are offset by initial Y value of the pointer. */ public class StatusBarTouchController implements TouchController { private static final String TAG = "StatusBarController"; + /** + * Window flag: Enable touches to slide out of a window into neighboring + * windows in mid-gesture instead of being captured for the duration of + * the gesture. + * + * This flag changes the behavior of touch focus for this window only. + * Touches can slide out of the window but they cannot necessarily slide + * back in (unless the other window with touch focus permits it). + */ + private static final int FLAG_SLIPPERY = 0x20000000; + protected final Launcher mLauncher; - protected final TouchEventTranslator mTranslator; private final float mTouchSlop; private ISystemUiProxy mSysUiProxy; private int mLastAction; + private final SparseArray<PointF> mDownEvents; /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/ private boolean mCanIntercept; @@ -56,7 +72,7 @@ public class StatusBarTouchController implements TouchController { mLauncher = l; // Guard against TAPs by increasing the touch slop. mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop(); - mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev)); + mDownEvents = new SparseArray<>(); } @Override @@ -64,7 +80,6 @@ public class StatusBarTouchController implements TouchController { writer.println(prefix + "mCanIntercept:" + mCanIntercept); writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction)); writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null)); - } private void dispatchTouchEvent(MotionEvent ev) { @@ -81,26 +96,31 @@ public class StatusBarTouchController implements TouchController { @Override public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { int action = ev.getActionMasked(); + int idx = ev.getActionIndex(); + int pid = ev.getPointerId(idx); if (action == ACTION_DOWN) { mCanIntercept = canInterceptTouch(ev); if (!mCanIntercept) { return false; } - mTranslator.reset(); - mTranslator.setDownParameters(0, ev); + mDownEvents.put(pid, new PointF(ev.getX(), ev.getY())); } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - // Check!! should only set it only when threshold is not entered. - mTranslator.setDownParameters(ev.getActionIndex(), ev); + // Check!! should only set it only when threshold is not entered. + mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx))); } if (!mCanIntercept) { return false; } if (action == ACTION_MOVE) { - float dy = ev.getY() - mTranslator.getDownY(); - float dx = ev.getX() - mTranslator.getDownX(); - if (dy > mTouchSlop && dy > Math.abs(dx)) { - mTranslator.dispatchDownEvents(ev); - mTranslator.processMotionEvent(ev); + float dy = ev.getY(idx) - mDownEvents.get(pid).y; + float dx = ev.getX(idx) - mDownEvents.get(pid).x; + // Currently input dispatcher will not do touch transfer if there are more than + // one touch pointer. Hence, even if slope passed, only set the slippery flag + // when there is single touch event. (context: InputDispatcher.cpp line 1445) + if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) { + ev.setAction(ACTION_DOWN); + dispatchTouchEvent(ev); + setWindowSlippery(true); return true; } if (Math.abs(dx) > mTouchSlop) { @@ -110,13 +130,27 @@ public class StatusBarTouchController implements TouchController { return false; } - @Override public final boolean onControllerTouchEvent(MotionEvent ev) { - mTranslator.processMotionEvent(ev); + if (ev.getAction() == ACTION_UP || ev.getAction() == ACTION_CANCEL) { + dispatchTouchEvent(ev); + setWindowSlippery(false); + return true; + } return true; } + private void setWindowSlippery(boolean enable) { + Window w = mLauncher.getWindow(); + WindowManager.LayoutParams wlp = w.getAttributes(); + if (enable) { + wlp.flags |= FLAG_SLIPPERY; + } else { + wlp.flags &= ~FLAG_SLIPPERY; + } + w.setAttributes(wlp); + } + private boolean canInterceptTouch(MotionEvent ev) { if (!mLauncher.isInState(LauncherState.NORMAL) || AbstractFloatingView.getTopOpenViewWithType(mLauncher, @@ -132,4 +166,4 @@ public class StatusBarTouchController implements TouchController { mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy(); return mSysUiProxy != null; } -} +}
\ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index 0a73b8b19..0738affa9 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -33,7 +33,6 @@ import android.content.pm.ResolveInfo; import com.android.systemui.shared.system.PackageManagerWrapper; -import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; /** @@ -58,7 +57,9 @@ public final class OverviewComponentObserver { private String mUpdateRegisteredPackage; private ActivityControlHelper mActivityControlHelper; private Intent mOverviewIntent; + private Intent mHomeIntent; private int mSystemUiStateFlags; + private boolean mIsHomeAndOverviewSame; public OverviewComponentObserver(Context context) { mContext = context; @@ -93,11 +94,14 @@ public final class OverviewComponentObserver { final String overviewIntentCategory; ComponentName overviewComponent; + mHomeIntent = null; + if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 && (defaultHome == null || mMyHomeComponent.equals(defaultHome))) { // User default home is same as out home app. Use Overview integrated in Launcher. overviewComponent = mMyHomeComponent; mActivityControlHelper = new LauncherActivityControllerHelper(); + mIsHomeAndOverviewSame = true; overviewIntentCategory = Intent.CATEGORY_HOME; if (mUpdateRegisteredPackage != null) { @@ -109,8 +113,12 @@ public final class OverviewComponentObserver { // The default home app is a different launcher. Use the fallback Overview instead. overviewComponent = new ComponentName(mContext, RecentsActivity.class); mActivityControlHelper = new FallbackActivityControllerHelper(); + mIsHomeAndOverviewSame = false; overviewIntentCategory = Intent.CATEGORY_DEFAULT; + mHomeIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setComponent(defaultHome); // User's default home app can change as a result of package updates of this app (such // as uninstalling the app or removing the "Launcher" feature in an update). // Listen for package updates of this app (and remove any previously attached @@ -135,6 +143,9 @@ public final class OverviewComponentObserver { .addCategory(overviewIntentCategory) .setComponent(overviewComponent) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (mHomeIntent == null) { + mHomeIntent = mOverviewIntent; + } } /** @@ -159,6 +170,20 @@ public final class OverviewComponentObserver { } /** + * Get the current intent for going to the home activity. + */ + public Intent getHomeIntent() { + return mHomeIntent; + } + + /** + * Returns true if home and overview are same activity. + */ + public boolean isHomeAndOverviewSame() { + return mIsHomeAndOverviewSame; + } + + /** * Get the current activity control helper for managing interactions to the overview activity. * * @return the current activity control helper diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java index befeee0db..7bfa9a0f9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepProcessInitializer.java +++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java @@ -15,6 +15,7 @@ */ package com.android.quickstep; +import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; import android.os.UserManager; @@ -22,17 +23,29 @@ import android.util.Log; import com.android.launcher3.BuildConfig; import com.android.launcher3.MainProcessInitializer; +import com.android.launcher3.Utilities; import com.android.systemui.shared.system.ThreadedRendererCompat; @SuppressWarnings("unused") public class QuickstepProcessInitializer extends MainProcessInitializer { private static final String TAG = "QuickstepProcessInitializer"; + private static final int HEAP_LIMIT_MB = 250; public QuickstepProcessInitializer(Context context) { } @Override protected void init(Context context) { + if (Utilities.IS_DEBUG_DEVICE) { + try { + // Trigger a heap dump if the PSS reaches beyond the target heap limit + final ActivityManager am = context.getSystemService(ActivityManager.class); + am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024); + } catch (SecurityException e) { + // Do nothing + } + } + // Workaround for b/120550382, an external app can cause the launcher process to start for // a work profile user which we do not support. Disable the application immediately when we // detect this to be the case. diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 353837312..f27ba8538 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -84,7 +84,7 @@ public class RecentTasksList extends TaskStackChangeListener { final int requestLoadId = mChangeId; Runnable resultCallback = callback == null ? () -> { } - : () -> callback.accept(mTasks); + : () -> callback.accept(copyOf(mTasks)); if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) { // The list is up to date, callback with the same list @@ -183,4 +183,14 @@ public class RecentTasksList extends TaskStackChangeListener { return allTasks; } + + private ArrayList<Task> copyOf(ArrayList<Task> tasks) { + ArrayList<Task> newTasks = new ArrayList<>(); + for (int i = 0; i < tasks.size(); i++) { + Task t = tasks.get(i); + newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable, + t.isLocked, t.taskDescription, t.topActivity)); + } + return newTasks; + } }
\ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index c8aed8191..050bdff09 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -110,7 +110,7 @@ public class LayoutUtils { float y = insets.top + Math.max(topIconMargin, (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2); outRect.set(Math.round(x), Math.round(y), - Math.round(x + outWidth), Math.round(y + outHeight)); + Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight)); } public static int getShelfTrackingDistance(Context context, DeviceProfile dp) { diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 36521e5cc..b6ddb5fd1 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -18,6 +18,7 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; @@ -29,6 +30,7 @@ import android.graphics.Path; import android.graphics.Path.Direction; import android.graphics.Path.Op; import android.util.AttributeSet; +import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; @@ -69,6 +71,9 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis private int mMidAlpha; private float mMidProgress; + private Interpolator mBeforeMidProgressColorInterpolator = ACCEL; + private Interpolator mAfterMidProgressColorInterpolator = ACCEL; + private float mShiftRange; private final float mShelfOffset; @@ -120,6 +125,15 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis @Override public void onNavigationModeChanged(Mode newMode) { mSysUINavigationMode = newMode; + // Note that these interpolators are inverted because progress goes 1 to 0. + if (mSysUINavigationMode == Mode.NO_BUTTON) { + // Show the shelf more quickly before reaching overview progress. + mBeforeMidProgressColorInterpolator = ACCEL_2; + mAfterMidProgressColorInterpolator = ACCEL; + } else { + mBeforeMidProgressColorInterpolator = ACCEL; + mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f); + } } @Override @@ -171,7 +185,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis mRemainingScreenColor = 0; int alpha = Math.round(Utilities.mapToRange( - mProgress, mMidProgress, 1, mMidAlpha, 0, ACCEL)); + mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator)); mShelfColor = setColorAlphaBound(mEndScrim, alpha); } else { mDragHandleOffset += mShiftRange * (mMidProgress - mProgress); @@ -179,7 +193,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis // Note that these ranges and interpolators are inverted because progress goes 1 to 0. int alpha = Math.round( Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha, - (float) mMidAlpha, Interpolators.clampToProgress(ACCEL, 0.5f, 1f))); + (float) mMidAlpha, mAfterMidProgressColorInterpolator)); mShelfColor = setColorAlphaBound(mEndScrim, alpha); int remainingScrimAlpha = Math.round( diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index 013591171..e5f949b88 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -43,10 +43,12 @@ import androidx.test.uiautomator.Until; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.testcomponent.TestCommandReceiver; +import com.android.launcher3.util.rule.FailureWatcher; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.model.Statement; @@ -62,10 +64,14 @@ public class FallbackRecentsTest { private final LauncherInstrumentation mLauncher; private final ActivityInfo mOtherLauncherActivity; - @Rule public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification(); - @Rule public final TestRule mQuickstepOnOffExecutor; + @Rule + public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification(); - @Rule public final TestRule mSetLauncherCommand; + @Rule + public final TestRule mSetLauncherCommand; + + @Rule + public final TestRule mOrderSensitiveRules; public FallbackRecentsTest() throws RemoteException { Instrumentation instrumentation = getInstrumentation(); @@ -74,7 +80,10 @@ public class FallbackRecentsTest { mDevice.setOrientationNatural(); mLauncher = new LauncherInstrumentation(instrumentation); - mQuickstepOnOffExecutor = new NavigationModeSwitchRule(mLauncher); + mOrderSensitiveRules = RuleChain. + outerRule(new NavigationModeSwitchRule(mLauncher)). + around(new FailureWatcher(mDevice)); + mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( getHomeIntentInPackage(context), MATCH_DISABLED_COMPONENTS).get(0).activityInfo; diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index 90763b871..3b35c86af 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -159,13 +159,20 @@ public class NavigationModeSwitchRule implements TestRule { } for (int i = 0; i != 100; ++i) { - if (mLauncher.getNavigationModel() == expectedMode) { - Thread.sleep(5000); - return; - } + if (mLauncher.getNavigationModel() == expectedMode) break; + Thread.sleep(100); + } + Assert.assertTrue("Couldn't switch to " + overlayPackage, + mLauncher.getNavigationModel() == expectedMode); + + for (int i = 0; i != 100; ++i) { + if (mLauncher.getNavigationModeMismatchError() == null) break; Thread.sleep(100); } - Assert.fail("Couldn't switch to " + overlayPackage); + final String error = mLauncher.getNavigationModeMismatchError(); + Assert.assertTrue("Switching nav mode: " + error, error == null); + + Thread.sleep(5000); } private void setOverlayPackageEnabled(String overlayPackage, boolean enable) diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index f02859f46..9e3bf2f56 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -208,7 +208,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch - @PortraitLandscape +// @PortraitLandscape public void testBackground() throws Exception { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); final Background background = mLauncher.getBackground(); |