blob: f8196e5f6d412f22c35298a4b4de2b166a3b51c0 [file] [log] [blame]
Tony Wickham1743ac42016-03-17 11:53:26 -07001/*
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 Wickhamdadb3042016-02-24 11:07:00 -080017package com.android.launcher3;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.animation.ValueAnimator;
23import android.util.Log;
24import android.view.View;
Sunny Goyaled268c22016-03-24 15:27:42 -070025import android.view.animation.LinearInterpolator;
Tony Wickhamdadb3042016-02-24 11:07:00 -080026
Sunny Goyal9e76f682017-02-13 12:13:43 -080027import com.android.launcher3.anim.AnimationLayerSet;
Sunny Goyal6c46a6d2016-11-23 02:24:32 +053028import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
29import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
Jon Mirandac4de1372016-10-05 15:40:51 -070030
Tony Wickhamdadb3042016-02-24 11:07:00 -080031import static com.android.launcher3.Workspace.State.NORMAL;
32import static com.android.launcher3.Workspace.State.OVERVIEW;
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 */
52public class PinchAnimationManager {
53 private static final String TAG = "PinchAnimationManager";
54
55 private static final int THRESHOLD_ANIM_DURATION = 150;
Sunny Goyaled268c22016-03-24 15:27:42 -070056 private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator();
Tony Wickhamdadb3042016-02-24 11:07:00 -080057
Sunny Goyal6178f132016-07-11 17:30:03 -070058 private static final int INDEX_HOTSEAT = 0;
59 private static final int INDEX_QSB = 1;
Tony Wickhamf898b972016-04-05 18:36:36 -070060 private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 2;
61 private static final int INDEX_SCRIM = 3;
Sunny Goyaled268c22016-03-24 15:27:42 -070062
63 private final Animator[] mAnimators = new Animator[4];
64
Tony Wickhamdadb3042016-02-24 11:07:00 -080065 private Launcher mLauncher;
66 private Workspace mWorkspace;
67
68 private float mOverviewScale;
69 private float mOverviewTranslationY;
70 private int mNormalOverviewTransitionDuration;
Tony Wickhamdadb3042016-02-24 11:07:00 -080071 private boolean mIsAnimating;
72
Tony Wickhamdadb3042016-02-24 11:07:00 -080073 public PinchAnimationManager(Launcher launcher) {
74 mLauncher = launcher;
75 mWorkspace = launcher.mWorkspace;
76
77 mOverviewScale = mWorkspace.getOverviewModeShrinkFactor();
78 mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY();
79 mNormalOverviewTransitionDuration = mWorkspace.getStateTransitionAnimation()
80 .mOverviewTransitionTime;
Tony Wickhamdadb3042016-02-24 11:07:00 -080081 }
82
83 public int getNormalOverviewTransitionDuration() {
84 return mNormalOverviewTransitionDuration;
85 }
86
87 /**
88 * Interpolate from {@param currentProgress} to {@param toProgress}, calling
89 * {@link #setAnimationProgress(float)} throughout the duration. If duration is -1,
90 * the default overview transition duration is used.
91 */
92 public void animateToProgress(float currentProgress, float toProgress, int duration,
93 final PinchThresholdManager thresholdManager) {
94 if (duration == -1) {
95 duration = mNormalOverviewTransitionDuration;
96 }
97 ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress);
98 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
Tony Wickhamf898b972016-04-05 18:36:36 -070099 @Override
100 public void onAnimationUpdate(ValueAnimator animation) {
101 float pinchProgress = (Float) animation.getAnimatedValue();
102 setAnimationProgress(pinchProgress);
103 thresholdManager.updateAndAnimatePassedThreshold(pinchProgress,
104 PinchAnimationManager.this);
105 }
106 }
Tony Wickhamdadb3042016-02-24 11:07:00 -0800107 );
108 animator.addListener(new AnimatorListenerAdapter() {
109 @Override
110 public void onAnimationEnd(Animator animation) {
111 mIsAnimating = false;
112 thresholdManager.reset();
Sunny Goyaldb364372016-10-26 19:12:47 -0700113 mWorkspace.onEndStateTransition();
Tony Wickhamdadb3042016-02-24 11:07:00 -0800114 }
115 });
116 animator.setDuration(duration).start();
117 mIsAnimating = true;
118 }
119
120 public boolean isAnimating() {
121 return mIsAnimating;
122 }
123
124 /**
125 * Animates to the specified progress. This should be called repeatedly throughout the pinch
126 * gesture to run animations that interpolate throughout the gesture.
127 * @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace.
128 */
129 public void setAnimationProgress(float interpolatedProgress) {
130 float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale;
131 float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY;
132 mWorkspace.setScaleX(interpolatedScale);
133 mWorkspace.setScaleY(interpolatedScale);
134 mWorkspace.setTranslationY(interpolatedTranslationY);
135 setOverviewPanelsAlpha(1f - interpolatedProgress, 0);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800136 }
137
138 /**
139 * Animates certain properties based on which threshold was passed, and in what direction. The
140 * starting state must also be taken into account because the thresholds mean different things
141 * when going from workspace to overview and vice versa.
142 * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE},
143 * {@link PinchThresholdManager#THRESHOLD_TWO}, or
144 * {@link PinchThresholdManager#THRESHOLD_THREE}
145 * @param startState {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
146 * @param goingTowards {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
Sunny Goyaled268c22016-03-24 15:27:42 -0700147 * Note that this doesn't have to be the opposite of startState;
Tony Wickhamdadb3042016-02-24 11:07:00 -0800148 */
149 public void animateThreshold(float threshold, Workspace.State startState,
150 Workspace.State goingTowards) {
151 if (threshold == PinchThresholdManager.THRESHOLD_ONE) {
152 if (startState == OVERVIEW) {
153 animateOverviewPanelButtons(goingTowards == OVERVIEW);
154 } else if (startState == NORMAL) {
Sunny Goyal857bfcf2016-07-14 15:30:24 -0700155 animateHotseatAndQsb(goingTowards == NORMAL);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800156 }
157 } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
158 if (startState == OVERVIEW) {
Sunny Goyal857bfcf2016-07-14 15:30:24 -0700159 animateHotseatAndQsb(goingTowards == NORMAL);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800160 animateScrim(goingTowards == OVERVIEW);
161 } else if (startState == NORMAL) {
162 animateOverviewPanelButtons(goingTowards == OVERVIEW);
163 animateScrim(goingTowards == OVERVIEW);
164 }
165 } else if (threshold == PinchThresholdManager.THRESHOLD_THREE) {
166 // Passing threshold 3 ends the pinch and snaps to the new state.
167 if (startState == OVERVIEW && goingTowards == NORMAL) {
Jon Mirandac4de1372016-10-05 15:40:51 -0700168 mLauncher.getUserEventDispatcher().logActionOnContainer(
Sunny Goyal6c46a6d2016-11-23 02:24:32 +0530169 Action.Touch.PINCH, Action.Direction.NONE,
170 ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
Tony Wickhamdadb3042016-02-24 11:07:00 -0800171 mLauncher.showWorkspace(true);
Tony Wickhamf898b972016-04-05 18:36:36 -0700172 mWorkspace.snapToPage(mWorkspace.getCurrentPage());
Tony Wickhamdadb3042016-02-24 11:07:00 -0800173 } else if (startState == NORMAL && goingTowards == OVERVIEW) {
Jon Mirandac4de1372016-10-05 15:40:51 -0700174 mLauncher.getUserEventDispatcher().logActionOnContainer(
Sunny Goyal6c46a6d2016-11-23 02:24:32 +0530175 Action.Touch.PINCH, Action.Direction.NONE,
176 ContainerType.WORKSPACE, mWorkspace.getCurrentPage());
Tony Wickhamdadb3042016-02-24 11:07:00 -0800177 mLauncher.showOverviewMode(true);
178 }
179 } else {
180 Log.e(TAG, "Received unknown threshold to animate: " + threshold);
181 }
182 }
183
184 private void setOverviewPanelsAlpha(float alpha, int duration) {
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700185 int childCount = mWorkspace.getChildCount();
186 for (int i = 0; i < childCount; i++) {
187 final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800188 if (duration == 0) {
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700189 cl.setBackgroundAlpha(alpha);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800190 } else {
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700191 ObjectAnimator.ofFloat(cl, "backgroundAlpha", alpha).setDuration(duration).start();
Tony Wickhamdadb3042016-02-24 11:07:00 -0800192 }
193 }
194 }
195
Sunny Goyal857bfcf2016-07-14 15:30:24 -0700196 private void animateHotseatAndQsb(boolean show) {
Sunny Goyal6178f132016-07-11 17:30:03 -0700197 startAnimator(INDEX_HOTSEAT,
198 mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION);
199 startAnimator(INDEX_QSB, mWorkspace.mQsbAlphaController.animateAlphaAtIndex(
200 show ? 1 : 0, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE), THRESHOLD_ANIM_DURATION);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800201 }
202
Tony Wickhamdadb3042016-02-24 11:07:00 -0800203 private void animateOverviewPanelButtons(boolean show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700204 animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800205 }
206
207 private void animateScrim(boolean show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700208 float endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0;
209 startAnimator(INDEX_SCRIM,
210 ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha", endValue),
211 mNormalOverviewTransitionDuration);
212 }
213
214 private void animateShowHideView(int index, final View view, boolean show) {
Sunny Goyal9e76f682017-02-13 12:13:43 -0800215 Animator animator = ObjectAnimator.ofFloat(view, View.ALPHA, show ? 1 : 0);
216 animator.addListener(new AnimationLayerSet(view));
Tony Wickhamdadb3042016-02-24 11:07:00 -0800217 if (show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700218 view.setVisibility(View.VISIBLE);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800219 } else {
Sunny Goyaled268c22016-03-24 15:27:42 -0700220 animator.addListener(new AnimatorListenerAdapter() {
Sunny Goyal1c581c62016-11-24 06:46:50 +0530221 private boolean mCancelled = false;
222
223 @Override
224 public void onAnimationCancel(Animator animation) {
225 mCancelled = true;
226 }
227
Sunny Goyaled268c22016-03-24 15:27:42 -0700228 @Override
229 public void onAnimationEnd(Animator animation) {
Sunny Goyal1c581c62016-11-24 06:46:50 +0530230 if (!mCancelled) {
231 view.setVisibility(View.INVISIBLE);
232 }
Sunny Goyaled268c22016-03-24 15:27:42 -0700233 }
234 });
Tony Wickhamdadb3042016-02-24 11:07:00 -0800235 }
Sunny Goyaled268c22016-03-24 15:27:42 -0700236 startAnimator(index, animator, THRESHOLD_ANIM_DURATION);
237 }
238
239 private void startAnimator(int index, Animator animator, long duration) {
240 if (mAnimators[index] != null) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700241 mAnimators[index].cancel();
242 }
243 mAnimators[index] = animator;
244 mAnimators[index].setInterpolator(INTERPOLATOR);
245 mAnimators[index].setDuration(duration).start();
Tony Wickhamdadb3042016-02-24 11:07:00 -0800246 }
247}