blob: c8c8fa4c819c32f2e6f93fac45a617965f497d20 [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
27import static com.android.launcher3.Workspace.State.NORMAL;
28import static com.android.launcher3.Workspace.State.OVERVIEW;
29
30/**
31 * Manages the animations that play as the user pinches to/from overview mode.
32 *
33 * It will look like this pinching in:
34 * - Workspace scales down
35 * - At some threshold 1, hotseat and QSB fade out (full animation)
36 * - At a later threshold 2, panel buttons fade in and scrim fades in
37 * - At a final threshold 3, snap to overview
38 *
39 * Pinching out:
40 * - Workspace scales up
41 * - At threshold 1, panel buttons fade out
42 * - At threshold 2, hotseat and QSB fade in and scrim fades out
43 * - At threshold 3, snap to workspace
44 *
45 * @see PinchToOverviewListener
46 * @see PinchThresholdManager
47 */
48public class PinchAnimationManager {
49 private static final String TAG = "PinchAnimationManager";
50
51 private static final int THRESHOLD_ANIM_DURATION = 150;
Sunny Goyaled268c22016-03-24 15:27:42 -070052 private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator();
Tony Wickhamdadb3042016-02-24 11:07:00 -080053
Tony Wickhamf898b972016-04-05 18:36:36 -070054 private static final int INDEX_PAGE_INDICATOR = 0;
55 private static final int INDEX_HOTSEAT = 1;
56 private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 2;
57 private static final int INDEX_SCRIM = 3;
Sunny Goyaled268c22016-03-24 15:27:42 -070058
59 private final Animator[] mAnimators = new Animator[4];
60
61 private final int[] mVisiblePageRange = new int[2];
Tony Wickhamdadb3042016-02-24 11:07:00 -080062 private Launcher mLauncher;
63 private Workspace mWorkspace;
64
65 private float mOverviewScale;
66 private float mOverviewTranslationY;
67 private int mNormalOverviewTransitionDuration;
Tony Wickhamdadb3042016-02-24 11:07:00 -080068 private boolean mIsAnimating;
69
Tony Wickhamdadb3042016-02-24 11:07:00 -080070 public PinchAnimationManager(Launcher launcher) {
71 mLauncher = launcher;
72 mWorkspace = launcher.mWorkspace;
73
74 mOverviewScale = mWorkspace.getOverviewModeShrinkFactor();
75 mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY();
76 mNormalOverviewTransitionDuration = mWorkspace.getStateTransitionAnimation()
77 .mOverviewTransitionTime;
Tony Wickhamdadb3042016-02-24 11:07:00 -080078 }
79
80 public int getNormalOverviewTransitionDuration() {
81 return mNormalOverviewTransitionDuration;
82 }
83
84 /**
85 * Interpolate from {@param currentProgress} to {@param toProgress}, calling
86 * {@link #setAnimationProgress(float)} throughout the duration. If duration is -1,
87 * the default overview transition duration is used.
88 */
89 public void animateToProgress(float currentProgress, float toProgress, int duration,
90 final PinchThresholdManager thresholdManager) {
91 if (duration == -1) {
92 duration = mNormalOverviewTransitionDuration;
93 }
94 ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress);
95 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
Tony Wickhamf898b972016-04-05 18:36:36 -070096 @Override
97 public void onAnimationUpdate(ValueAnimator animation) {
98 float pinchProgress = (Float) animation.getAnimatedValue();
99 setAnimationProgress(pinchProgress);
100 thresholdManager.updateAndAnimatePassedThreshold(pinchProgress,
101 PinchAnimationManager.this);
102 }
103 }
Tony Wickhamdadb3042016-02-24 11:07:00 -0800104 );
105 animator.addListener(new AnimatorListenerAdapter() {
106 @Override
107 public void onAnimationEnd(Animator animation) {
108 mIsAnimating = false;
109 thresholdManager.reset();
Tony Wickhamf898b972016-04-05 18:36:36 -0700110 mWorkspace.onLauncherTransitionEnd(mLauncher, false, true);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800111 }
112 });
113 animator.setDuration(duration).start();
114 mIsAnimating = true;
115 }
116
117 public boolean isAnimating() {
118 return mIsAnimating;
119 }
120
121 /**
122 * Animates to the specified progress. This should be called repeatedly throughout the pinch
123 * gesture to run animations that interpolate throughout the gesture.
124 * @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace.
125 */
126 public void setAnimationProgress(float interpolatedProgress) {
127 float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale;
128 float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY;
129 mWorkspace.setScaleX(interpolatedScale);
130 mWorkspace.setScaleY(interpolatedScale);
131 mWorkspace.setTranslationY(interpolatedTranslationY);
132 setOverviewPanelsAlpha(1f - interpolatedProgress, 0);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800133 }
134
135 /**
136 * Animates certain properties based on which threshold was passed, and in what direction. The
137 * starting state must also be taken into account because the thresholds mean different things
138 * when going from workspace to overview and vice versa.
139 * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE},
140 * {@link PinchThresholdManager#THRESHOLD_TWO}, or
141 * {@link PinchThresholdManager#THRESHOLD_THREE}
142 * @param startState {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
143 * @param goingTowards {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
Sunny Goyaled268c22016-03-24 15:27:42 -0700144 * Note that this doesn't have to be the opposite of startState;
Tony Wickhamdadb3042016-02-24 11:07:00 -0800145 */
146 public void animateThreshold(float threshold, Workspace.State startState,
147 Workspace.State goingTowards) {
148 if (threshold == PinchThresholdManager.THRESHOLD_ONE) {
149 if (startState == OVERVIEW) {
150 animateOverviewPanelButtons(goingTowards == OVERVIEW);
151 } else if (startState == NORMAL) {
152 animateHotseatAndPageIndicator(goingTowards == NORMAL);
153 animateQsb(goingTowards == NORMAL);
154 }
155 } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
156 if (startState == OVERVIEW) {
157 animateHotseatAndPageIndicator(goingTowards == NORMAL);
158 animateQsb(goingTowards == NORMAL);
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) {
167 mLauncher.showWorkspace(true);
Tony Wickhamf898b972016-04-05 18:36:36 -0700168 mWorkspace.snapToPage(mWorkspace.getCurrentPage());
Tony Wickhamdadb3042016-02-24 11:07:00 -0800169 } else if (startState == NORMAL && goingTowards == OVERVIEW) {
170 mLauncher.showOverviewMode(true);
171 }
172 } else {
173 Log.e(TAG, "Received unknown threshold to animate: " + threshold);
174 }
175 }
176
177 private void setOverviewPanelsAlpha(float alpha, int duration) {
178 mWorkspace.getVisiblePages(mVisiblePageRange);
179 for (int i = mVisiblePageRange[0]; i <= mVisiblePageRange[1]; i++) {
180 View page = mWorkspace.getPageAt(i);
181 if (!mWorkspace.shouldDrawChild(page)) {
182 continue;
183 }
184 if (duration == 0) {
185 ((CellLayout) page).setBackgroundAlpha(alpha);
186 } else {
187 ObjectAnimator.ofFloat(page, "backgroundAlpha", alpha)
188 .setDuration(duration).start();
189 }
190 }
191 }
192
193 private void animateHotseatAndPageIndicator(boolean show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700194 animateShowHideView(INDEX_HOTSEAT, mLauncher.getHotseat(), show);
195 if (mWorkspace.getPageIndicator() != null) {
196 // There aren't page indicators in landscape mode on phones, hence the null check.
197 animateShowHideView(INDEX_PAGE_INDICATOR, mWorkspace.getPageIndicator(), show);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800198 }
199 }
200
201 private void animateQsb(boolean show) {
202 SearchDropTargetBar.State searchBarState = show ? SearchDropTargetBar.State.SEARCH_BAR
203 : SearchDropTargetBar.State.INVISIBLE;
204 mLauncher.getSearchDropTargetBar().animateToState(searchBarState, THRESHOLD_ANIM_DURATION);
205 }
206
207 private void animateOverviewPanelButtons(boolean show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700208 animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800209 }
210
211 private void animateScrim(boolean show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700212 float endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0;
213 startAnimator(INDEX_SCRIM,
214 ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha", endValue),
215 mNormalOverviewTransitionDuration);
216 }
217
218 private void animateShowHideView(int index, final View view, boolean show) {
219 Animator animator = new LauncherViewPropertyAnimator(view).alpha(show ? 1 : 0).withLayer();
Tony Wickhamdadb3042016-02-24 11:07:00 -0800220 if (show) {
Sunny Goyaled268c22016-03-24 15:27:42 -0700221 view.setVisibility(View.VISIBLE);
Tony Wickhamdadb3042016-02-24 11:07:00 -0800222 } else {
Sunny Goyaled268c22016-03-24 15:27:42 -0700223 animator.addListener(new AnimatorListenerAdapter() {
224 @Override
225 public void onAnimationEnd(Animator animation) {
226 view.setVisibility(View.INVISIBLE);
227 }
228 });
Tony Wickhamdadb3042016-02-24 11:07:00 -0800229 }
Sunny Goyaled268c22016-03-24 15:27:42 -0700230 startAnimator(index, animator, THRESHOLD_ANIM_DURATION);
231 }
232
233 private void startAnimator(int index, Animator animator, long duration) {
234 if (mAnimators[index] != null) {
235 mAnimators[index].removeAllListeners();
236 mAnimators[index].cancel();
237 }
238 mAnimators[index] = animator;
239 mAnimators[index].setInterpolator(INTERPOLATOR);
240 mAnimators[index].setDuration(duration).start();
Tony Wickhamdadb3042016-02-24 11:07:00 -0800241 }
242}