• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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