1e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller/* 2e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * Copyright (C) 2016 The Android Open Source Project 3e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * 4e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * Licensed under the Apache License, Version 2.0 (the "License"); 5e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * you may not use this file except in compliance with the License. 6e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * You may obtain a copy of the License at 7e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * 8e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * http://www.apache.org/licenses/LICENSE-2.0 9e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * 10e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * Unless required by applicable law or agreed to in writing, software 11e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * distributed under the License is distributed on an "AS IS" BASIS, 12e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * See the License for the specific language governing permissions and 14e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * limitations under the License. 15e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller */ 16e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerpackage com.android.launcher3.allapps; 17e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 18e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerimport android.animation.ObjectAnimator; 19e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerimport android.view.animation.AnimationUtils; 20e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerimport android.view.animation.Interpolator; 21e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 22e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerimport com.android.launcher3.Launcher; 23e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerimport com.android.launcher3.R; 24e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerimport com.android.launcher3.pageindicators.CaretDrawable; 25f40e94955cba0ca351f587358b9e07496d132a1bHyunyoung Songimport com.android.launcher3.touch.SwipeDetector; 26e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 27e6fe1b59470b653f0275596cb8a211367dd61654Peter Schillerpublic class AllAppsCaretController { 28e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // Determines when the caret should flip. Should be accessed via getThreshold() 29e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private static final float CARET_THRESHOLD = 0.015f; 30e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private static final float CARET_THRESHOLD_LAND = 0.5f; 31e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // The velocity at which the caret will peak (i.e. exhibit a 90 degree bend) 32f40e94955cba0ca351f587358b9e07496d132a1bHyunyoung Song private static final float PEAK_VELOCITY = SwipeDetector.RELEASE_VELOCITY_PX_MS * .7f; 33e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 34e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private Launcher mLauncher; 35e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 36e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private ObjectAnimator mCaretAnimator; 37e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private CaretDrawable mCaretDrawable; 38e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private float mLastCaretProgress; 39e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private boolean mThresholdCrossed; 40e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 41e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller public AllAppsCaretController(CaretDrawable caret, Launcher launcher) { 42e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mLauncher = launcher; 43e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretDrawable = caret; 44e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 45e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller final long caretAnimationDuration = launcher.getResources().getInteger( 46e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller R.integer.config_caretAnimationDuration); 47e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller final Interpolator caretInterpolator = AnimationUtils.loadInterpolator(launcher, 48e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller android.R.interpolator.fast_out_slow_in); 49e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 50e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // We will set values later 51e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretAnimator = ObjectAnimator.ofFloat(mCaretDrawable, "caretProgress", 0); 52e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretAnimator.setDuration(caretAnimationDuration); 53e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretAnimator.setInterpolator(caretInterpolator); 54e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 55e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 56e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller /** 57e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * Updates the state of the caret based on the progress of the {@link AllAppsContainerView}, as 58e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * defined by the {@link AllAppsTransitionController}. Uses the container's velocity to adjust 59e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * angle of caret. 60e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * 61e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * @param containerProgress The progress of the container in the range [0..1] 62e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * @param velocity The velocity of the container 63e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller * @param dragging {@code true} if the container is being dragged 64e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller */ 65e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller public void updateCaret(float containerProgress, float velocity, boolean dragging) { 66e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // If we're in portrait and the shift is not 0 or 1, adjust the caret based on velocity 67e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller if (getThreshold() < containerProgress && containerProgress < 1 - getThreshold() && 68e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller !mLauncher.useVerticalBarLayout()) { 69e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mThresholdCrossed = true; 70e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 71e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // How fast are we moving as a percentage of the peak velocity? 72e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller final float pctOfFlingVelocity = Math.max(-1, Math.min(velocity / PEAK_VELOCITY, 1)); 73e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 74e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretDrawable.setCaretProgress(pctOfFlingVelocity); 75e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 76e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // Set the last caret progress to this progress to prevent animator cancellation 77e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mLastCaretProgress = pctOfFlingVelocity; 78e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // Animate to neutral. This is necessary so the caret doesn't "freeze" when the 79e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // container stops moving (e.g., during a drag or when the threshold is reached). 80e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller animateCaretToProgress(CaretDrawable.PROGRESS_CARET_NEUTRAL); 81e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } else if (!dragging) { 82e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // Otherwise, if we're not dragging, match the caret to the appropriate state 83e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller if (containerProgress <= getThreshold()) { // All Apps is up 84e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_DOWN); 85e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } else if (containerProgress >= 1 - getThreshold()) { // All Apps is down 86e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_UP); 87e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 88e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 89e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 90e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 91e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private void animateCaretToProgress(float progress) { 92e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // If the new progress is the same as the last progress we animated to, terminate early 93e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller if (Float.compare(mLastCaretProgress, progress) == 0) { 94e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller return; 95e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 96e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 97e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller if (mCaretAnimator.isRunning()) { 98e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretAnimator.cancel(); // Stop the animator in its tracks 99e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 100e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 101e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // Update the progress and start the animation 102e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mLastCaretProgress = progress; 103e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretAnimator.setFloatValues(progress); 104e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mCaretAnimator.start(); 105e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 106e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 107e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller private float getThreshold() { 108e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // In landscape, just return the landscape threshold. 109e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller if (mLauncher.useVerticalBarLayout()) { 110e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller return CARET_THRESHOLD_LAND; 111e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 112e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 113e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // Before the threshold is crossed, it is reported as zero. This makes the caret immediately 114e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // responsive when a drag begins. After the threshold is crossed, we return the constant 115e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // value. This means the caret will start its state-based adjustment sooner. That is, we 116e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller // won't have to wait until the panel is completely settled to begin animation. 117e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller return mThresholdCrossed ? CARET_THRESHOLD : 0f; 118e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 119e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller 120e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller public void onDragStart() { 121e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller mThresholdCrossed = false; 122e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller } 123e6fe1b59470b653f0275596cb8a211367dd61654Peter Schiller} 124