blob: bbe8e89225994c4a161cde3fdecdc67166349d6e [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
Sunny Goyal4c7f2152017-10-17 17:17:16 -070031import static com.android.launcher3.LauncherState.NORMAL;
32import static com.android.launcher3.LauncherState.OVERVIEW;
Tony Wickhamdadb3042016-02-24 11:07:00 -080033
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;
Sunny Goyal966d9012017-06-06 16:43:59 -070059 private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 1;
60 private static final int INDEX_SCRIM = 2;
Sunny Goyaled268c22016-03-24 15:27:42 -070061
Sunny Goyal966d9012017-06-06 16:43:59 -070062 private final Animator[] mAnimators = new Animator[3];
Sunny Goyaled268c22016-03-24 15:27:42 -070063
Tony Wickhamdadb3042016-02-24 11:07:00 -080064 private Launcher mLauncher;
65 private Workspace mWorkspace;
66
67 private float mOverviewScale;
68 private float mOverviewTranslationY;
69 private int mNormalOverviewTransitionDuration;
Tony Wickhamdadb3042016-02-24 11:07:00 -080070 private boolean mIsAnimating;
71
Tony Wickhamdadb3042016-02-24 11:07:00 -080072 public PinchAnimationManager(Launcher launcher) {
73 mLauncher = launcher;
74 mWorkspace = launcher.mWorkspace;
75
76 mOverviewScale = mWorkspace.getOverviewModeShrinkFactor();
77 mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY();
Sunny Goyalaeb16432017-10-16 11:46:41 -070078 mNormalOverviewTransitionDuration = LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
Tony Wickhamdadb3042016-02-24 11:07:00 -080079 }
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 Wickhamf898b972016-04-05 18:36:36 -070097 @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 Wickhamdadb3042016-02-24 11:07:00 -0800105 );
106 animator.addListener(new AnimatorListenerAdapter() {
107 @Override
108 public void onAnimationEnd(Animator animation) {
109 mIsAnimating = false;
110 thresholdManager.reset();
Sunny Goyaldb364372016-10-26 19:12:47 -0700111 mWorkspace.onEndStateTransition();
Tony Wickhamdadb3042016-02-24 11:07:00 -0800112 }
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 Goyalaeb16432017-10-16 11:46:41 -0700133 int alpha = (int) ((1f - interpolatedProgress) * 255);
134 setOverviewPanelsAlpha(alpha, 0);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800135 }
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 Goyal4c7f2152017-10-17 17:17:16 -0700144 * @param startState {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}.
145 * @param goingTowards {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}.
Sunny Goyaled268c22016-03-24 15:27:42 -0700146 * Note that this doesn't have to be the opposite of startState;
Tony Wickhamdadb3042016-02-24 11:07:00 -0800147 */
Sunny Goyal4c7f2152017-10-17 17:17:16 -0700148 public void animateThreshold(float threshold, LauncherState startState,
149 LauncherState goingTowards) {
Tony Wickhamdadb3042016-02-24 11:07:00 -0800150 if (threshold == PinchThresholdManager.THRESHOLD_ONE) {
151 if (startState == OVERVIEW) {
152 animateOverviewPanelButtons(goingTowards == OVERVIEW);
153 } else if (startState == NORMAL) {
Sunny Goyal857bfcf2016-07-14 15:30:24 -0700154 animateHotseatAndQsb(goingTowards == NORMAL);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800155 }
156 } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
157 if (startState == OVERVIEW) {
Sunny Goyal857bfcf2016-07-14 15:30:24 -0700158 animateHotseatAndQsb(goingTowards == NORMAL);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800159 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 Mirandac4de1372016-10-05 15:40:51 -0700167 mLauncher.getUserEventDispatcher().logActionOnContainer(
Sunny Goyal6c46a6d2016-11-23 02:24:32 +0530168 Action.Touch.PINCH, Action.Direction.NONE,
169 ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
Tony Wickhamdadb3042016-02-24 11:07:00 -0800170 mLauncher.showWorkspace(true);
Tony Wickhamf898b972016-04-05 18:36:36 -0700171 mWorkspace.snapToPage(mWorkspace.getCurrentPage());
Tony Wickhamdadb3042016-02-24 11:07:00 -0800172 } else if (startState == NORMAL && goingTowards == OVERVIEW) {
Jon Mirandac4de1372016-10-05 15:40:51 -0700173 mLauncher.getUserEventDispatcher().logActionOnContainer(
Sunny Goyal6c46a6d2016-11-23 02:24:32 +0530174 Action.Touch.PINCH, Action.Direction.NONE,
175 ContainerType.WORKSPACE, mWorkspace.getCurrentPage());
Tony Wickhamdadb3042016-02-24 11:07:00 -0800176 mLauncher.showOverviewMode(true);
177 }
178 } else {
179 Log.e(TAG, "Received unknown threshold to animate: " + threshold);
180 }
181 }
182
Sunny Goyalaeb16432017-10-16 11:46:41 -0700183 private void setOverviewPanelsAlpha(int alpha, int duration) {
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700184 int childCount = mWorkspace.getChildCount();
185 for (int i = 0; i < childCount; i++) {
186 final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800187 if (duration == 0) {
Sunny Goyalaeb16432017-10-16 11:46:41 -0700188 cl.getScrimBackground().setAlpha(alpha);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800189 } else {
Sunny Goyalaeb16432017-10-16 11:46:41 -0700190 ObjectAnimator.ofInt(cl.getScrimBackground(),
191 LauncherAnimUtils.DRAWABLE_ALPHA, 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);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800199 }
200
Tony Wickhamdadb3042016-02-24 11:07:00 -0800201 private void animateOverviewPanelButtons(boolean show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700202 animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800203 }
204
205 private void animateScrim(boolean show) {
Sunny Goyalaeb16432017-10-16 11:46:41 -0700206 int endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0;
207 startAnimator(INDEX_SCRIM, ObjectAnimator.ofInt(
208 mLauncher.getDragLayer().getScrim(), LauncherAnimUtils.DRAWABLE_ALPHA, endValue),
Sunny Goyaled268c22016-03-24 15:27:42 -0700209 mNormalOverviewTransitionDuration);
210 }
211
212 private void animateShowHideView(int index, final View view, boolean show) {
Sunny Goyal9e76f682017-02-13 12:13:43 -0800213 Animator animator = ObjectAnimator.ofFloat(view, View.ALPHA, show ? 1 : 0);
214 animator.addListener(new AnimationLayerSet(view));
Tony Wickhamdadb3042016-02-24 11:07:00 -0800215 if (show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700216 view.setVisibility(View.VISIBLE);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800217 } else {
Sunny Goyaled268c22016-03-24 15:27:42 -0700218 animator.addListener(new AnimatorListenerAdapter() {
Sunny Goyal1c581c62016-11-24 06:46:50 +0530219 private boolean mCancelled = false;
220
221 @Override
222 public void onAnimationCancel(Animator animation) {
223 mCancelled = true;
224 }
225
Sunny Goyaled268c22016-03-24 15:27:42 -0700226 @Override
227 public void onAnimationEnd(Animator animation) {
Sunny Goyal1c581c62016-11-24 06:46:50 +0530228 if (!mCancelled) {
229 view.setVisibility(View.INVISIBLE);
230 }
Sunny Goyaled268c22016-03-24 15:27:42 -0700231 }
232 });
Tony Wickhamdadb3042016-02-24 11:07:00 -0800233 }
Sunny Goyaled268c22016-03-24 15:27:42 -0700234 startAnimator(index, animator, THRESHOLD_ANIM_DURATION);
235 }
236
237 private void startAnimator(int index, Animator animator, long duration) {
238 if (mAnimators[index] != null) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700239 mAnimators[index].cancel();
240 }
241 mAnimators[index] = animator;
242 mAnimators[index].setInterpolator(INTERPOLATOR);
243 mAnimators[index].setDuration(duration).start();
Tony Wickhamdadb3042016-02-24 11:07:00 -0800244 }
245}