diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2017-12-07 16:27:49 -0800 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2017-12-07 16:28:21 -0800 |
commit | a3e0350329aa97a3a7bbad6a163f3143ddfc6c75 (patch) | |
tree | 9158c7a392258f9b659eb9b417048b0ad0aec815 /quickstep | |
parent | daa8467e6752719591cd3d6a76418c3e8ba77975 (diff) | |
download | android_packages_apps_Trebuchet-a3e0350329aa97a3a7bbad6a163f3143ddfc6c75.tar.gz android_packages_apps_Trebuchet-a3e0350329aa97a3a7bbad6a163f3143ddfc6c75.tar.bz2 android_packages_apps_Trebuchet-a3e0350329aa97a3a7bbad6a163f3143ddfc6c75.zip |
Creating a copy of vertical swipe detector for quickstep
Change-Id: Ie38e0c11e8ea9aa476e450f074295358623c6942
Diffstat (limited to 'quickstep')
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java | 285 | ||||
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/UiFactory.java | 3 |
2 files changed, 286 insertions, 2 deletions
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java new file mode 100644 index 000000000..9081865ee --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2017 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.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.support.animation.SpringAnimation; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Utilities; +import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.SpringAnimationHandler; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.util.TouchController; + +import java.util.ArrayList; + +/** + * Handles vertical touch gesture on the DragLayer + */ +public class TwoStepSwipeController extends AnimatorListenerAdapter + implements TouchController, SwipeDetector.Listener { + + private static final String TAG = "TwoStepSwipeController"; + + private static final float RECATCH_REJECTION_FRACTION = .0875f; + private static final int SINGLE_FRAME_MS = 16; + + // Progress after which the transition is assumed to be a success in case user does not fling + private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; + + private final Launcher mLauncher; + private final SwipeDetector mDetector; + + private boolean mNoIntercept; + private int mStartContainerType; + + private AnimatorPlaybackController mCurrentAnimation; + private LauncherState mToState; + + private float mStartProgress; + // Ratio of transition process [0, 1] to drag displacement (px) + private float mProgressMultiplier; + + private SpringAnimationHandler[] mSpringHandlers; + + public TwoStepSwipeController(Launcher l) { + mLauncher = l; + mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL); + } + + private boolean canInterceptTouch(MotionEvent ev) { + if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) { + // Don't listen for the swipe gesture if we are already in some other state. + return false; + } + if (mCurrentAnimation != null) { + // If we are already animating from a previous state, we can intercept. + return true; + } + if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) { + return false; + } + if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + return false; + } + + return true; + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) { + Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception()); + mDetector.finishedScrolling(); + mCurrentAnimation = null; + } + } + + private void initSprings() { + AllAppsContainerView appsView = mLauncher.getAppsView(); + + SpringAnimationHandler handler = appsView.getSpringAnimationHandler(); + if (handler == null) { + mSpringHandlers = new SpringAnimationHandler[0]; + return; + } + + ArrayList<SpringAnimationHandler> handlers = new ArrayList<>(); + handlers.add(handler); + + SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling(); + if (searchSpring != null) { + SpringAnimationHandler searchHandler = + new SpringAnimationHandler(Y_DIRECTION, handler.getFactory()); + searchHandler.add(searchSpring, true /* setDefaultValues */); + handlers.add(searchHandler); + } + + mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]); + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mNoIntercept = !canInterceptTouch(ev); + if (mNoIntercept) { + return false; + } + + // Now figure out which direction scroll events the controller will start + // calling the callbacks. + final int directionsToDetectScroll; + boolean ignoreSlopWhenSettling = false; + + if (mCurrentAnimation != null) { + if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) { + directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; + } else { + directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; + ignoreSlopWhenSettling = true; + } + } else { + if (mLauncher.isInState(ALL_APPS)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; + mStartContainerType = ContainerType.ALLAPPS; + } else { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ? + ContainerType.HOTSEAT : ContainerType.WORKSPACE; + } + } + + mDetector.setDetectableScrollConditions( + directionsToDetectScroll, ignoreSlopWhenSettling); + + if (mSpringHandlers == null) { + initSprings(); + } + } + + if (mNoIntercept) { + return false; + } + + onControllerTouchEvent(ev); + return mDetector.isDraggingOrSettling(); + } + + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + for (SpringAnimationHandler h : mSpringHandlers) { + h.addMovement(ev); + } + return mDetector.onTouchEvent(ev); + } + + @Override + public void onDragStart(boolean start) { + if (mCurrentAnimation == null) { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + + // Build current animation + mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS; + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, maxAccuracy); + mCurrentAnimation.getTarget().addListener(this); + mStartProgress = 0; + mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range; + mCurrentAnimation.dispatchOnStart(); + } else { + mCurrentAnimation.pause(); + mStartProgress = mCurrentAnimation.getProgressFraction(); + } + + for (SpringAnimationHandler h : mSpringHandlers) { + h.skipToEnd(); + } + } + + private float getShiftRange() { + return mLauncher.getAllAppsController().getShiftRange(); + } + + @Override + public boolean onDrag(float displacement, float velocity) { + float deltaProgress = mProgressMultiplier * displacement; + mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress); + return true; + } + + @Override + public void onDragEnd(float velocity, boolean fling) { + final long animationDuration; + final int logAction; + final LauncherState targetState; + final float progress = mCurrentAnimation.getProgressFraction(); + + if (fling) { + logAction = Touch.FLING; + if (velocity < 0) { + targetState = ALL_APPS; + animationDuration = SwipeDetector.calculateDuration(velocity, + mToState == ALL_APPS ? (1 - progress) : progress); + } else { + targetState = NORMAL; + animationDuration = SwipeDetector.calculateDuration(velocity, + mToState == ALL_APPS ? progress : (1 - progress)); + } + // snap to top or bottom using the release velocity + } else { + logAction = Touch.SWIPE; + if (progress > SUCCESS_TRANSITION_PROGRESS) { + targetState = mToState; + animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress); + } else { + targetState = mToState == ALL_APPS ? NORMAL : ALL_APPS; + animationDuration = SwipeDetector.calculateDuration(velocity, progress); + } + } + + if (fling && targetState == ALL_APPS) { + for (SpringAnimationHandler h : mSpringHandlers) { + // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.) + h.animateToFinalPosition(0 /* pos */, 1 /* startValue */); + } + } + + mCurrentAnimation.setEndAction(new Runnable() { + @Override + public void run() { + if (targetState == mToState) { + // Transition complete. log the action + mLauncher.getUserEventDispatcher().logActionOnContainer(logAction, + mToState == ALL_APPS ? Direction.UP : Direction.DOWN, + mStartContainerType, mLauncher.getWorkspace().getCurrentPage()); + } else { + mLauncher.getStateManager().goToState( + mToState == ALL_APPS ? NORMAL : ALL_APPS, false); + } + mDetector.finishedScrolling(); + mCurrentAnimation = null; + } + }); + + float nextFrameProgress = Utilities.boundToRange( + progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f); + + ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); + anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f); + anim.setDuration(animationDuration); + anim.setInterpolator(scrollInterpolatorForVelocity(velocity)); + anim.start(); + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index a48a65ce4..5e280b696 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -24,14 +24,13 @@ import android.widget.Toast; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.R; -import com.android.launcher3.VerticalSwipeController; import com.android.launcher3.util.TouchController; import com.android.launcher3.widget.WidgetsFullSheet; public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { - return new TouchController[] {new VerticalSwipeController(launcher)}; + return new TouchController[] {new TwoStepSwipeController(launcher)}; } public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() { |