summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/PinchAnimationManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/PinchAnimationManager.java')
-rw-r--r--src/com/android/launcher3/PinchAnimationManager.java285
1 files changed, 285 insertions, 0 deletions
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
new file mode 100644
index 000000000..3302cb652
--- /dev/null
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -0,0 +1,285 @@
+package com.android.launcher3;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.View;
+
+import static com.android.launcher3.Workspace.State.NORMAL;
+import static com.android.launcher3.Workspace.State.OVERVIEW;
+
+/**
+ * Manages the animations that play as the user pinches to/from overview mode.
+ *
+ * It will look like this pinching in:
+ * - Workspace scales down
+ * - At some threshold 1, hotseat and QSB fade out (full animation)
+ * - At a later threshold 2, panel buttons fade in and scrim fades in
+ * - At a final threshold 3, snap to overview
+ *
+ * Pinching out:
+ * - Workspace scales up
+ * - At threshold 1, panel buttons fade out
+ * - At threshold 2, hotseat and QSB fade in and scrim fades out
+ * - At threshold 3, snap to workspace
+ *
+ * @see PinchToOverviewListener
+ * @see PinchThresholdManager
+ */
+public class PinchAnimationManager {
+ private static final String TAG = "PinchAnimationManager";
+
+ private static final int THRESHOLD_ANIM_DURATION = 150;
+
+ private Launcher mLauncher;
+ private Workspace mWorkspace;
+
+ private float mOverviewScale;
+ private float mOverviewTranslationY;
+ private int mNormalOverviewTransitionDuration;
+ private final int[] mVisiblePageRange = new int[2];
+ private boolean mIsAnimating;
+
+ // Animators
+ private Animator mShowPageIndicatorAnimator;
+ private Animator mShowHotseatAnimator;
+ private Animator mShowOverviewPanelButtonsAnimator;
+ private Animator mShowScrimAnimator;
+ private Animator mHidePageIndicatorAnimator;
+ private Animator mHideHotseatAnimator;
+ private Animator mHideOverviewPanelButtonsAnimator;
+ private Animator mHideScrimAnimator;
+
+ public PinchAnimationManager(Launcher launcher) {
+ mLauncher = launcher;
+ mWorkspace = launcher.mWorkspace;
+
+ mOverviewScale = mWorkspace.getOverviewModeShrinkFactor();
+ mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY();
+ mNormalOverviewTransitionDuration = mWorkspace.getStateTransitionAnimation()
+ .mOverviewTransitionTime;
+
+ initializeAnimators();
+ }
+
+ private void initializeAnimators() {
+ mShowPageIndicatorAnimator = new LauncherViewPropertyAnimator(
+ mWorkspace.getPageIndicator()).alpha(1f).withLayer();
+ mShowPageIndicatorAnimator.setInterpolator(null);
+
+ mShowHotseatAnimator = new LauncherViewPropertyAnimator(mLauncher.getHotseat())
+ .alpha(1f).withLayer();
+ mShowHotseatAnimator.setInterpolator(null);
+
+ mShowOverviewPanelButtonsAnimator = new LauncherViewPropertyAnimator(
+ mLauncher.getOverviewPanel()).alpha(1f).withLayer();
+ mShowOverviewPanelButtonsAnimator.setInterpolator(null);
+
+ mShowScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha",
+ mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha);
+ mShowScrimAnimator.setInterpolator(null);
+
+ mHidePageIndicatorAnimator = new LauncherViewPropertyAnimator(
+ mWorkspace.getPageIndicator()).alpha(0f).withLayer();
+ mHidePageIndicatorAnimator.setInterpolator(null);
+ mHidePageIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mWorkspace.getPageIndicator() != null) {
+ mWorkspace.getPageIndicator().setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+
+ mHideHotseatAnimator = new LauncherViewPropertyAnimator(mLauncher.getHotseat())
+ .alpha(0f).withLayer();
+ mHideHotseatAnimator.setInterpolator(null);
+ mHideHotseatAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLauncher.getHotseat().setVisibility(View.INVISIBLE);
+ }
+ });
+
+ mHideOverviewPanelButtonsAnimator = new LauncherViewPropertyAnimator(
+ mLauncher.getOverviewPanel()).alpha(0f).withLayer();
+ mHideOverviewPanelButtonsAnimator.setInterpolator(null);
+ mHideOverviewPanelButtonsAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLauncher.getOverviewPanel().setVisibility(View.INVISIBLE);
+ }
+ });
+
+ mHideScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha", 0f);
+ mHideScrimAnimator.setInterpolator(null);
+ }
+
+ public int getNormalOverviewTransitionDuration() {
+ return mNormalOverviewTransitionDuration;
+ }
+
+ /**
+ * Interpolate from {@param currentProgress} to {@param toProgress}, calling
+ * {@link #setAnimationProgress(float)} throughout the duration. If duration is -1,
+ * the default overview transition duration is used.
+ */
+ public void animateToProgress(float currentProgress, float toProgress, int duration,
+ final PinchThresholdManager thresholdManager) {
+ if (duration == -1) {
+ duration = mNormalOverviewTransitionDuration;
+ }
+ ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float pinchProgress = (Float) animation.getAnimatedValue();
+ setAnimationProgress(pinchProgress);
+ thresholdManager.updateAndAnimatePassedThreshold(pinchProgress,
+ PinchAnimationManager.this);
+ }
+ }
+ );
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimating = false;
+ thresholdManager.reset();
+ }
+ });
+ animator.setDuration(duration).start();
+ mIsAnimating = true;
+ }
+
+ public boolean isAnimating() {
+ return mIsAnimating;
+ }
+
+ /**
+ * Animates to the specified progress. This should be called repeatedly throughout the pinch
+ * gesture to run animations that interpolate throughout the gesture.
+ * @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace.
+ */
+ public void setAnimationProgress(float interpolatedProgress) {
+ float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale;
+ float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY;
+ mWorkspace.setScaleX(interpolatedScale);
+ mWorkspace.setScaleY(interpolatedScale);
+ mWorkspace.setTranslationY(interpolatedTranslationY);
+ setOverviewPanelsAlpha(1f - interpolatedProgress, 0);
+
+ // Make sure adjacent pages, except custom content page, are visible while scaling.
+ mWorkspace.setCustomContentVisibility(View.INVISIBLE);
+ mWorkspace.invalidate();
+ }
+
+ /**
+ * Animates certain properties based on which threshold was passed, and in what direction. The
+ * starting state must also be taken into account because the thresholds mean different things
+ * when going from workspace to overview and vice versa.
+ * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE},
+ * {@link PinchThresholdManager#THRESHOLD_TWO}, or
+ * {@link PinchThresholdManager#THRESHOLD_THREE}
+ * @param startState {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
+ * @param goingTowards {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
+ * Note that this doesn't have to be the opposite of startState;
+ */
+ public void animateThreshold(float threshold, Workspace.State startState,
+ Workspace.State goingTowards) {
+ if (threshold == PinchThresholdManager.THRESHOLD_ONE) {
+ if (startState == OVERVIEW) {
+ animateOverviewPanelButtons(goingTowards == OVERVIEW);
+ } else if (startState == NORMAL) {
+ animateHotseatAndPageIndicator(goingTowards == NORMAL);
+ animateQsb(goingTowards == NORMAL);
+ }
+ } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
+ if (startState == OVERVIEW) {
+ animateHotseatAndPageIndicator(goingTowards == NORMAL);
+ animateQsb(goingTowards == NORMAL);
+ animateScrim(goingTowards == OVERVIEW);
+ } else if (startState == NORMAL) {
+ animateOverviewPanelButtons(goingTowards == OVERVIEW);
+ animateScrim(goingTowards == OVERVIEW);
+ }
+ } else if (threshold == PinchThresholdManager.THRESHOLD_THREE) {
+ // Passing threshold 3 ends the pinch and snaps to the new state.
+ if (startState == OVERVIEW && goingTowards == NORMAL) {
+ mLauncher.showWorkspace(true);
+ mWorkspace.snapToPage(mWorkspace.getPageNearestToCenterOfScreen());
+ } else if (startState == NORMAL && goingTowards == OVERVIEW) {
+ mLauncher.showOverviewMode(true);
+ }
+ } else {
+ Log.e(TAG, "Received unknown threshold to animate: " + threshold);
+ }
+ }
+
+ private void setOverviewPanelsAlpha(float alpha, int duration) {
+ mWorkspace.getVisiblePages(mVisiblePageRange);
+ for (int i = mVisiblePageRange[0]; i <= mVisiblePageRange[1]; i++) {
+ View page = mWorkspace.getPageAt(i);
+ if (!mWorkspace.shouldDrawChild(page)) {
+ continue;
+ }
+ if (duration == 0) {
+ ((CellLayout) page).setBackgroundAlpha(alpha);
+ } else {
+ ObjectAnimator.ofFloat(page, "backgroundAlpha", alpha)
+ .setDuration(duration).start();
+ }
+ }
+ }
+
+ private void animateHotseatAndPageIndicator(boolean show) {
+ if (show) {
+ mLauncher.getHotseat().setVisibility(View.VISIBLE);
+ mShowHotseatAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
+ if (mWorkspace.getPageIndicator() != null) {
+ // There aren't page indicators in landscape mode on phones, hence the null check.
+ mWorkspace.getPageIndicator().setVisibility(View.VISIBLE);
+ mShowPageIndicatorAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
+ }
+ } else {
+ mHideHotseatAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
+ if (mWorkspace.getPageIndicator() != null) {
+ // There aren't page indicators in landscape mode on phones, hence the null check.
+ mHidePageIndicatorAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
+ }
+ }
+ }
+
+ private void animateQsb(boolean show) {
+ SearchDropTargetBar.State searchBarState = show ? SearchDropTargetBar.State.SEARCH_BAR
+ : SearchDropTargetBar.State.INVISIBLE;
+ mLauncher.getSearchDropTargetBar().animateToState(searchBarState, THRESHOLD_ANIM_DURATION);
+ }
+
+ private void animateOverviewPanelButtons(boolean show) {
+ if (show) {
+ mLauncher.getOverviewPanel().setVisibility(View.VISIBLE);
+ mShowOverviewPanelButtonsAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
+ } else {
+ mHideOverviewPanelButtonsAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
+ }
+ }
+
+ private void animateScrim(boolean show) {
+ // We reninitialize the animators here so that they have the correct start values.
+ if (show) {
+ mShowScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha",
+ mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha);
+ mShowScrimAnimator.setInterpolator(null);
+ mShowScrimAnimator.setDuration(mNormalOverviewTransitionDuration).start();
+ } else {
+ mHideScrimAnimator.setupStartValues();
+ mHideScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha",
+ 0f);
+ mHideScrimAnimator.setInterpolator(null);
+ mHideScrimAnimator.setDuration(mNormalOverviewTransitionDuration).start();
+ mHideScrimAnimator.setDuration(mNormalOverviewTransitionDuration).start();
+ }
+ }
+}