From 8b2b4e24ee784207ad9857fd9cc846f6ba92f703 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 28 Jan 2019 15:11:36 -0800 Subject: Adding support for swipe and hold to overview from home screen to all-apps - After the atomic animation ends, overview jumps slightly because the normal -> all apps transition puts it at a different position than normal -> overview Bug: 111926330 Change-Id: I6ca359b3ef2fc4d0b6b96229d8bf118bd0db9649 --- .../launcher3/uioverrides/RecentsUiFactory.java | 38 ++++++--- .../uioverrides/FlingAndHoldTouchController.java | 95 ++++++++++++++++++++++ .../launcher3/uioverrides/RecentsUiFactory.java | 68 ++++++++++++---- .../OverviewToAllAppsTouchController.java | 2 +- .../uioverrides/PortraitStatesTouchController.java | 10 ++- .../android/launcher3/uioverrides/UiFactory.java | 67 ++------------- .../quickstep/util/MotionPauseDetector.java | 4 + .../touch/AbstractStateChangeTouchController.java | 9 +- 8 files changed, 197 insertions(+), 96 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index d0c255c85..738157451 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -24,27 +24,39 @@ import android.view.View; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; +import com.android.launcher3.Utilities; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.TouchController; +import com.android.quickstep.OverviewInteractionState; + +import java.util.ArrayList; /** * Provides recents-related {@link UiFactory} logic and classes. */ -public final class RecentsUiFactory { +public abstract class RecentsUiFactory { // Scale recents takes before animating in private static final float RECENTS_PREPARE_SCALE = 1.33f; - private RecentsUiFactory() {} + public static TouchController[] createTouchControllers(Launcher launcher) { + ArrayList list = new ArrayList<>(); + list.add(launcher.getDragController()); - /** - * Creates and returns a touch controller for swiping recents tasks. - * - * @param launcher the launcher activity - * @return the touch controller for recents tasks - */ - public static TouchController createTaskSwipeController(Launcher launcher) { - // We leave all input handling to the view itself. - return null; + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + list.add(new OverviewToAllAppsTouchController(launcher)); + list.add(new LandscapeEdgeSwipeController(launcher)); + } else { + boolean allowDragToOverview = OverviewInteractionState.INSTANCE.get(launcher) + .isSwipeUpGestureEnabled(); + list.add(new PortraitStatesTouchController(launcher, allowDragToOverview)); + } + if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE + && !launcher.getDeviceProfile().isMultiWindowMode + && !launcher.getDeviceProfile().isVerticalBarLayout()) { + list.add(new StatusBarTouchController(launcher)); + } + return list.toArray(new TouchController[list.size()]); } /** @@ -62,7 +74,7 @@ public final class RecentsUiFactory { * * @param launcher the launcher activity */ - public static void prepareToShowRecents(Launcher launcher) { + public static void prepareToShowOverview(Launcher launcher) { View overview = launcher.getOverviewPanel(); if (overview.getVisibility() != VISIBLE) { SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); @@ -74,7 +86,7 @@ public final class RecentsUiFactory { * * @param launcher the launcher activity */ - public static void resetRecents(Launcher launcher) {} + public static void resetOverview(Launcher launcher) {} /** * Recents logic that triggers when launcher state changes or launcher activity stops/resumes. diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java new file mode 100644 index 000000000..fb83cd32e --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java @@ -0,0 +1,95 @@ +/* + * 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.launcher3.uioverrides; + +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT; + +import android.animation.ValueAnimator; + +import com.android.launcher3.Launcher; +import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.quickstep.util.MotionPauseDetector; +import com.android.quickstep.views.RecentsView; + +/** + * Touch controller which handles swipe and hold to go to Overview + */ +public class FlingAndHoldTouchController extends PortraitStatesTouchController { + + private final MotionPauseDetector mMotionPauseDetector; + + public FlingAndHoldTouchController(Launcher l) { + super(l, false /* allowDragToOverview */); + mMotionPauseDetector = new MotionPauseDetector(l); + } + + @Override + public void onDragStart(boolean start) { + mMotionPauseDetector.clear(); + + super.onDragStart(start); + + if (mStartState == NORMAL) { + mMotionPauseDetector.setOnMotionPauseListener(isPaused -> { + RecentsView recentsView = mLauncher.getOverviewPanel(); + recentsView.setOverviewStateEnabled(isPaused); + maybeUpdateAtomicAnim(NORMAL, OVERVIEW, isPaused ? 1 : 0); + }); + } + } + + @Override + public boolean onDrag(float displacement) { + mMotionPauseDetector.addPosition(displacement); + return super.onDrag(displacement); + } + + @Override + public void onDragEnd(float velocity, boolean fling) { + if (mMotionPauseDetector.isPaused() && mStartState == NORMAL) { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + + // Let the state manager know that the animation didn't go to the target state, + // but don't cancel ourselves (we already clean up when the animation completes). + Runnable onCancel = mCurrentAnimation.getOnCancelRunnable(); + mCurrentAnimation.setOnCancelRunnable(null); + mCurrentAnimation.dispatchOnCancel(); + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(OVERVIEW, new AnimatorSetBuilder(), maxAccuracy, + onCancel, NON_ATOMIC_COMPONENT); + + final int logAction = fling ? Touch.FLING : Touch.SWIPE; + mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(OVERVIEW, logAction)); + + + ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); + maybeUpdateAtomicAnim(NORMAL, OVERVIEW, 1f); + mCurrentAnimation.dispatchOnStartWithVelocity(1, velocity); + + // TODO: Find a better duration + anim.setDuration(100); + anim.start(); + settleAtomicAnimation(1f, anim.getDuration()); + } else { + super.onDragEnd(velocity, fling); + } + } +} 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 f18f43c4c..51e949599 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 @@ -21,32 +21,65 @@ 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.launcher3.config.FeatureFlags.SWIPE_HOME; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateHandler; +import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.TouchController; +import com.android.launcher3.util.UiThreadHelper; +import com.android.launcher3.util.UiThreadHelper.AsyncCommand; +import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.system.WindowManagerWrapper; + +import java.util.ArrayList; /** * Provides recents-related {@link UiFactory} logic and classes. */ -public final class RecentsUiFactory { +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; - private RecentsUiFactory() {} + public static TouchController[] createTouchControllers(Launcher launcher) { + boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher) + .isSwipeUpGestureEnabled(); + boolean swipeUpToHome = swipeUpEnabled && SWIPE_HOME.get(); + + + ArrayList list = new ArrayList<>(); + list.add(launcher.getDragController()); + + if (swipeUpToHome) { + list.add(new FlingAndHoldTouchController(launcher)); + list.add(new OverviewToAllAppsTouchController(launcher)); + } else { + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + list.add(new OverviewToAllAppsTouchController(launcher)); + list.add(new LandscapeEdgeSwipeController(launcher)); + } else { + list.add(new PortraitStatesTouchController(launcher, + swipeUpEnabled /* allowDragToOverview */)); + } + } - /** - * Creates and returns a touch controller for swiping recents tasks. - * - * @param launcher the launcher activity - * @return the touch controller for recents tasks - */ - public static TouchController createTaskSwipeController(Launcher launcher) { - return new LauncherTaskViewController(launcher); + if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE + && !launcher.getDeviceProfile().isMultiWindowMode + && !launcher.getDeviceProfile().isVerticalBarLayout()) { + list.add(new StatusBarTouchController(launcher)); + } + + list.add(new LauncherTaskViewController(launcher)); + return list.toArray(new TouchController[list.size()]); } /** @@ -64,7 +97,7 @@ public final class RecentsUiFactory { * * @param launcher the launcher activity */ - public static void prepareToShowRecents(Launcher launcher) { + public static void prepareToShowOverview(Launcher launcher) { RecentsView overview = launcher.getOverviewPanel(); if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); @@ -76,9 +109,8 @@ public final class RecentsUiFactory { * * @param launcher the launcher activity */ - public static void resetRecents(Launcher launcher) { - RecentsView recents = launcher.getOverviewPanel(); - recents.reset(); + public static void resetOverview(Launcher launcher) { + launcher.getOverviewPanel().reset(); } /** @@ -88,6 +120,14 @@ public final class RecentsUiFactory { */ public static void onLauncherStateOrResumeChanged(Launcher launcher) { LauncherState state = launcher.getStateManager().getState(); + if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) { + DeviceProfile profile = launcher.getDeviceProfile(); + boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive() + && !profile.isVerticalBarLayout(); + UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD, + visible ? 1 : 0, profile.hotseatBarSizePx); + } + if (state == NORMAL) { launcher.getOverviewPanel().setSwipeDownShouldLaunchApp(false); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java index 0f9b57f03..a069ed593 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java @@ -36,7 +36,7 @@ import com.android.quickstep.views.RecentsView; public class OverviewToAllAppsTouchController extends PortraitStatesTouchController { public OverviewToAllAppsTouchController(Launcher l) { - super(l); + super(l, true /* allowDragToOverview */); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java index ea0e55239..1fc2b6cb9 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java @@ -67,14 +67,17 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper; - private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper(); + private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper(); + + private final boolean mAllowDragToOverview; // If true, we will finish the current animation instantly on second touch. private boolean mFinishFastOnSecondTouch; - public PortraitStatesTouchController(Launcher l) { + public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) { super(l, SwipeDetector.VERTICAL); mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l); + mAllowDragToOverview = allowDragToOverview; } @Override @@ -128,7 +131,8 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr } else if (fromState == OVERVIEW) { return isDragTowardPositive ? ALL_APPS : NORMAL; } else if (fromState == NORMAL && isDragTowardPositive) { - return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS; + return mAllowDragToOverview && TouchInteractionService.isConnected() + ? OVERVIEW : ALL_APPS; } return fromState; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index d0a9e3cce..70aae136c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -35,68 +35,32 @@ import android.os.CancellationSignal; import android.util.Base64; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.QuickstepAppTransitionManagerImpl; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.util.TouchController; -import com.android.launcher3.util.UiThreadHelper; -import com.android.launcher3.util.UiThreadHelper.AsyncCommand; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.RecentsModel; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.systemui.shared.system.ActivityCompat; -import com.android.systemui.shared.system.WindowManagerWrapper; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.zip.Deflater; -public class UiFactory { - - private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) -> - WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height); - - public static TouchController[] createTouchControllers(Launcher launcher) { - boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher) - .isSwipeUpGestureEnabled(); - ArrayList list = new ArrayList<>(); - list.add(launcher.getDragController()); - - if (!swipeUpEnabled || launcher.getDeviceProfile().isVerticalBarLayout()) { - list.add(new OverviewToAllAppsTouchController(launcher)); - } - - if (launcher.getDeviceProfile().isVerticalBarLayout()) { - list.add(new LandscapeEdgeSwipeController(launcher)); - } else { - list.add(new PortraitStatesTouchController(launcher)); - } - if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE - && !launcher.getDeviceProfile().isMultiWindowMode - && !launcher.getDeviceProfile().isVerticalBarLayout()) { - list.add(new StatusBarTouchController(launcher)); - } - TouchController taskSwipeController = - RecentsUiFactory.createTaskSwipeController(launcher); - if (taskSwipeController != null) { - list.add(taskSwipeController); - } - return list.toArray(new TouchController[list.size()]); - } +public class UiFactory extends RecentsUiFactory { public static void setOnTouchControllersChangedListener(Context context, Runnable listener) { OverviewInteractionState.INSTANCE.get(context).setOnSwipeUpSettingChangedListener(listener); } public static StateHandler[] getStateHandler(Launcher launcher) { - return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(), - RecentsUiFactory.createRecentsViewStateController(launcher), + return new StateHandler[] { + launcher.getAllAppsController(), + launcher.getWorkspace(), + createRecentsViewStateController(launcher), new BackButtonAlphaHandler(launcher)}; } @@ -116,10 +80,6 @@ public class UiFactory { .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */); } - public static void resetOverview(Launcher launcher) { - RecentsUiFactory.resetRecents(launcher); - } - public static void onCreate(Launcher launcher) { if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) { launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() { @@ -171,19 +131,6 @@ public class UiFactory { .getHighResLoadingState().setVisible(true); } - public static void onLauncherStateOrResumeChanged(Launcher launcher) { - LauncherState state = launcher.getStateManager().getState(); - if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) { - DeviceProfile profile = launcher.getDeviceProfile(); - boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive() - && !profile.isVerticalBarLayout(); - UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD, - visible ? 1 : 0, profile.hotseatBarSizePx); - } - - RecentsUiFactory.onLauncherStateOrResumeChanged(launcher); - } - public static void onTrimMemory(Context context, int level) { RecentsModel model = RecentsModel.INSTANCE.get(context); if (model != null) { @@ -233,8 +180,4 @@ public class UiFactory { out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING)); return true; } - - public static void prepareToShowOverview(Launcher launcher) { - RecentsUiFactory.prepareToShowRecents(launcher); - } } diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java index 258e9227e..7969eec7c 100644 --- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java +++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java @@ -135,6 +135,10 @@ public class MotionPauseDetector { mIsPaused = mHasEverBeenPaused = false; } + public boolean isPaused() { + return mIsPaused; + } + public interface OnMotionPauseListener { void onMotionPauseChanged(boolean isPaused); } diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index bb143288c..0e2ed6cc2 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -46,7 +46,6 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.compat.AccessibilityManagerCompat; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; @@ -298,7 +297,7 @@ public abstract class AbstractStateChangeTouchController * When going between normal and overview states, see if we passed the overview threshold and * play the appropriate atomic animation if so. */ - private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState, + protected void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState, float progress) { if (!goingBetweenNormalAndOverview(fromState, toState)) { return; @@ -435,7 +434,11 @@ public abstract class AbstractStateChangeTouchController mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity); } anim.start(); - mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration()); + settleAtomicAnimation(endProgress, anim.getDuration()); + } + + protected void settleAtomicAnimation(float endProgress, long duration) { + mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, duration); maybeAutoPlayAtomicComponentsAnim(); } -- cgit v1.2.3