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