Tony Wickham | 1743ac4 | 2016-03-17 11:53:26 -0700 | [diff] [blame] | 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 | |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 17 | package com.android.launcher3; |
| 18 | |
| 19 | import android.animation.Animator; |
| 20 | import android.animation.AnimatorListenerAdapter; |
| 21 | import android.animation.ObjectAnimator; |
| 22 | import android.animation.ValueAnimator; |
| 23 | import android.util.Log; |
| 24 | import android.view.View; |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 25 | import android.view.animation.LinearInterpolator; |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 26 | |
Sunny Goyal | 9e76f68 | 2017-02-13 12:13:43 -0800 | [diff] [blame] | 27 | import com.android.launcher3.anim.AnimationLayerSet; |
Sunny Goyal | 6c46a6d | 2016-11-23 02:24:32 +0530 | [diff] [blame] | 28 | import com.android.launcher3.userevent.nano.LauncherLogProto.Action; |
| 29 | import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; |
Jon Miranda | c4de137 | 2016-10-05 15:40:51 -0700 | [diff] [blame] | 30 | |
Sunny Goyal | 4c7f215 | 2017-10-17 17:17:16 -0700 | [diff] [blame] | 31 | import static com.android.launcher3.LauncherState.NORMAL; |
| 32 | import static com.android.launcher3.LauncherState.OVERVIEW; |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 33 | |
| 34 | /** |
| 35 | * Manages the animations that play as the user pinches to/from overview mode. |
| 36 | * |
| 37 | * It will look like this pinching in: |
| 38 | * - Workspace scales down |
| 39 | * - At some threshold 1, hotseat and QSB fade out (full animation) |
| 40 | * - At a later threshold 2, panel buttons fade in and scrim fades in |
| 41 | * - At a final threshold 3, snap to overview |
| 42 | * |
| 43 | * Pinching out: |
| 44 | * - Workspace scales up |
| 45 | * - At threshold 1, panel buttons fade out |
| 46 | * - At threshold 2, hotseat and QSB fade in and scrim fades out |
| 47 | * - At threshold 3, snap to workspace |
| 48 | * |
| 49 | * @see PinchToOverviewListener |
| 50 | * @see PinchThresholdManager |
| 51 | */ |
| 52 | public class PinchAnimationManager { |
| 53 | private static final String TAG = "PinchAnimationManager"; |
| 54 | |
| 55 | private static final int THRESHOLD_ANIM_DURATION = 150; |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 56 | private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator(); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 57 | |
Sunny Goyal | 6178f13 | 2016-07-11 17:30:03 -0700 | [diff] [blame] | 58 | private static final int INDEX_HOTSEAT = 0; |
Sunny Goyal | 966d901 | 2017-06-06 16:43:59 -0700 | [diff] [blame] | 59 | private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 1; |
| 60 | private static final int INDEX_SCRIM = 2; |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 61 | |
Sunny Goyal | 966d901 | 2017-06-06 16:43:59 -0700 | [diff] [blame] | 62 | private final Animator[] mAnimators = new Animator[3]; |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 63 | |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 64 | private Launcher mLauncher; |
| 65 | private Workspace mWorkspace; |
| 66 | |
| 67 | private float mOverviewScale; |
| 68 | private float mOverviewTranslationY; |
| 69 | private int mNormalOverviewTransitionDuration; |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 70 | private boolean mIsAnimating; |
| 71 | |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 72 | public PinchAnimationManager(Launcher launcher) { |
| 73 | mLauncher = launcher; |
| 74 | mWorkspace = launcher.mWorkspace; |
| 75 | |
| 76 | mOverviewScale = mWorkspace.getOverviewModeShrinkFactor(); |
| 77 | mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY(); |
Sunny Goyal | aeb1643 | 2017-10-16 11:46:41 -0700 | [diff] [blame] | 78 | mNormalOverviewTransitionDuration = LauncherAnimUtils.OVERVIEW_TRANSITION_MS; |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | public int getNormalOverviewTransitionDuration() { |
| 82 | return mNormalOverviewTransitionDuration; |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Interpolate from {@param currentProgress} to {@param toProgress}, calling |
| 87 | * {@link #setAnimationProgress(float)} throughout the duration. If duration is -1, |
| 88 | * the default overview transition duration is used. |
| 89 | */ |
| 90 | public void animateToProgress(float currentProgress, float toProgress, int duration, |
| 91 | final PinchThresholdManager thresholdManager) { |
| 92 | if (duration == -1) { |
| 93 | duration = mNormalOverviewTransitionDuration; |
| 94 | } |
| 95 | ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress); |
| 96 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
Tony Wickham | f898b97 | 2016-04-05 18:36:36 -0700 | [diff] [blame] | 97 | @Override |
| 98 | public void onAnimationUpdate(ValueAnimator animation) { |
| 99 | float pinchProgress = (Float) animation.getAnimatedValue(); |
| 100 | setAnimationProgress(pinchProgress); |
| 101 | thresholdManager.updateAndAnimatePassedThreshold(pinchProgress, |
| 102 | PinchAnimationManager.this); |
| 103 | } |
| 104 | } |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 105 | ); |
| 106 | animator.addListener(new AnimatorListenerAdapter() { |
| 107 | @Override |
| 108 | public void onAnimationEnd(Animator animation) { |
| 109 | mIsAnimating = false; |
| 110 | thresholdManager.reset(); |
Sunny Goyal | db36437 | 2016-10-26 19:12:47 -0700 | [diff] [blame] | 111 | mWorkspace.onEndStateTransition(); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 112 | } |
| 113 | }); |
| 114 | animator.setDuration(duration).start(); |
| 115 | mIsAnimating = true; |
| 116 | } |
| 117 | |
| 118 | public boolean isAnimating() { |
| 119 | return mIsAnimating; |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Animates to the specified progress. This should be called repeatedly throughout the pinch |
| 124 | * gesture to run animations that interpolate throughout the gesture. |
| 125 | * @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace. |
| 126 | */ |
| 127 | public void setAnimationProgress(float interpolatedProgress) { |
| 128 | float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale; |
| 129 | float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY; |
| 130 | mWorkspace.setScaleX(interpolatedScale); |
| 131 | mWorkspace.setScaleY(interpolatedScale); |
| 132 | mWorkspace.setTranslationY(interpolatedTranslationY); |
Sunny Goyal | aeb1643 | 2017-10-16 11:46:41 -0700 | [diff] [blame] | 133 | int alpha = (int) ((1f - interpolatedProgress) * 255); |
| 134 | setOverviewPanelsAlpha(alpha, 0); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | /** |
| 138 | * Animates certain properties based on which threshold was passed, and in what direction. The |
| 139 | * starting state must also be taken into account because the thresholds mean different things |
| 140 | * when going from workspace to overview and vice versa. |
| 141 | * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE}, |
| 142 | * {@link PinchThresholdManager#THRESHOLD_TWO}, or |
| 143 | * {@link PinchThresholdManager#THRESHOLD_THREE} |
Sunny Goyal | 4c7f215 | 2017-10-17 17:17:16 -0700 | [diff] [blame] | 144 | * @param startState {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}. |
| 145 | * @param goingTowards {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}. |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 146 | * Note that this doesn't have to be the opposite of startState; |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 147 | */ |
Sunny Goyal | 4c7f215 | 2017-10-17 17:17:16 -0700 | [diff] [blame] | 148 | public void animateThreshold(float threshold, LauncherState startState, |
| 149 | LauncherState goingTowards) { |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 150 | if (threshold == PinchThresholdManager.THRESHOLD_ONE) { |
| 151 | if (startState == OVERVIEW) { |
| 152 | animateOverviewPanelButtons(goingTowards == OVERVIEW); |
| 153 | } else if (startState == NORMAL) { |
Sunny Goyal | 857bfcf | 2016-07-14 15:30:24 -0700 | [diff] [blame] | 154 | animateHotseatAndQsb(goingTowards == NORMAL); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 155 | } |
| 156 | } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) { |
| 157 | if (startState == OVERVIEW) { |
Sunny Goyal | 857bfcf | 2016-07-14 15:30:24 -0700 | [diff] [blame] | 158 | animateHotseatAndQsb(goingTowards == NORMAL); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 159 | animateScrim(goingTowards == OVERVIEW); |
| 160 | } else if (startState == NORMAL) { |
| 161 | animateOverviewPanelButtons(goingTowards == OVERVIEW); |
| 162 | animateScrim(goingTowards == OVERVIEW); |
| 163 | } |
| 164 | } else if (threshold == PinchThresholdManager.THRESHOLD_THREE) { |
| 165 | // Passing threshold 3 ends the pinch and snaps to the new state. |
| 166 | if (startState == OVERVIEW && goingTowards == NORMAL) { |
Jon Miranda | c4de137 | 2016-10-05 15:40:51 -0700 | [diff] [blame] | 167 | mLauncher.getUserEventDispatcher().logActionOnContainer( |
Sunny Goyal | 6c46a6d | 2016-11-23 02:24:32 +0530 | [diff] [blame] | 168 | Action.Touch.PINCH, Action.Direction.NONE, |
| 169 | ContainerType.OVERVIEW, mWorkspace.getCurrentPage()); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 170 | mLauncher.showWorkspace(true); |
Tony Wickham | f898b97 | 2016-04-05 18:36:36 -0700 | [diff] [blame] | 171 | mWorkspace.snapToPage(mWorkspace.getCurrentPage()); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 172 | } else if (startState == NORMAL && goingTowards == OVERVIEW) { |
Jon Miranda | c4de137 | 2016-10-05 15:40:51 -0700 | [diff] [blame] | 173 | mLauncher.getUserEventDispatcher().logActionOnContainer( |
Sunny Goyal | 6c46a6d | 2016-11-23 02:24:32 +0530 | [diff] [blame] | 174 | Action.Touch.PINCH, Action.Direction.NONE, |
| 175 | ContainerType.WORKSPACE, mWorkspace.getCurrentPage()); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 176 | mLauncher.showOverviewMode(true); |
| 177 | } |
| 178 | } else { |
| 179 | Log.e(TAG, "Received unknown threshold to animate: " + threshold); |
| 180 | } |
| 181 | } |
| 182 | |
Sunny Goyal | aeb1643 | 2017-10-16 11:46:41 -0700 | [diff] [blame] | 183 | private void setOverviewPanelsAlpha(int alpha, int duration) { |
Sunny Goyal | 9ccafbf | 2016-10-26 13:06:08 -0700 | [diff] [blame] | 184 | int childCount = mWorkspace.getChildCount(); |
| 185 | for (int i = 0; i < childCount; i++) { |
| 186 | final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 187 | if (duration == 0) { |
Sunny Goyal | aeb1643 | 2017-10-16 11:46:41 -0700 | [diff] [blame] | 188 | cl.getScrimBackground().setAlpha(alpha); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 189 | } else { |
Sunny Goyal | aeb1643 | 2017-10-16 11:46:41 -0700 | [diff] [blame] | 190 | ObjectAnimator.ofInt(cl.getScrimBackground(), |
| 191 | LauncherAnimUtils.DRAWABLE_ALPHA, alpha).setDuration(duration).start(); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 192 | } |
| 193 | } |
| 194 | } |
| 195 | |
Sunny Goyal | 857bfcf | 2016-07-14 15:30:24 -0700 | [diff] [blame] | 196 | private void animateHotseatAndQsb(boolean show) { |
Sunny Goyal | 6178f13 | 2016-07-11 17:30:03 -0700 | [diff] [blame] | 197 | startAnimator(INDEX_HOTSEAT, |
| 198 | mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 199 | } |
| 200 | |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 201 | private void animateOverviewPanelButtons(boolean show) { |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 202 | animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | private void animateScrim(boolean show) { |
Sunny Goyal | aeb1643 | 2017-10-16 11:46:41 -0700 | [diff] [blame] | 206 | int endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0; |
| 207 | startAnimator(INDEX_SCRIM, ObjectAnimator.ofInt( |
| 208 | mLauncher.getDragLayer().getScrim(), LauncherAnimUtils.DRAWABLE_ALPHA, endValue), |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 209 | mNormalOverviewTransitionDuration); |
| 210 | } |
| 211 | |
| 212 | private void animateShowHideView(int index, final View view, boolean show) { |
Sunny Goyal | 9e76f68 | 2017-02-13 12:13:43 -0800 | [diff] [blame] | 213 | Animator animator = ObjectAnimator.ofFloat(view, View.ALPHA, show ? 1 : 0); |
| 214 | animator.addListener(new AnimationLayerSet(view)); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 215 | if (show) { |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 216 | view.setVisibility(View.VISIBLE); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 217 | } else { |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 218 | animator.addListener(new AnimatorListenerAdapter() { |
Sunny Goyal | 1c581c6 | 2016-11-24 06:46:50 +0530 | [diff] [blame] | 219 | private boolean mCancelled = false; |
| 220 | |
| 221 | @Override |
| 222 | public void onAnimationCancel(Animator animation) { |
| 223 | mCancelled = true; |
| 224 | } |
| 225 | |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 226 | @Override |
| 227 | public void onAnimationEnd(Animator animation) { |
Sunny Goyal | 1c581c6 | 2016-11-24 06:46:50 +0530 | [diff] [blame] | 228 | if (!mCancelled) { |
| 229 | view.setVisibility(View.INVISIBLE); |
| 230 | } |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 231 | } |
| 232 | }); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 233 | } |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 234 | startAnimator(index, animator, THRESHOLD_ANIM_DURATION); |
| 235 | } |
| 236 | |
| 237 | private void startAnimator(int index, Animator animator, long duration) { |
| 238 | if (mAnimators[index] != null) { |
Sunny Goyal | ed268c2 | 2016-03-24 15:27:42 -0700 | [diff] [blame] | 239 | mAnimators[index].cancel(); |
| 240 | } |
| 241 | mAnimators[index] = animator; |
| 242 | mAnimators[index].setInterpolator(INTERPOLATOR); |
| 243 | mAnimators[index].setDuration(duration).start(); |
Tony Wickham | dadb304 | 2016-02-24 11:07:00 -0800 | [diff] [blame] | 244 | } |
| 245 | } |