From dc61c4d41468a8beced2863de1b863b340cf49ba Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 20 Apr 2015 18:26:57 -0700 Subject: Refactoring to single path for overview mode. Change-Id: I590a813c6f031342d75c3a6c3e9c9afda1808f2e --- src/com/android/launcher3/Launcher.java | 42 +- src/com/android/launcher3/LauncherClings.java | 2 +- .../LauncherStateTransitionAnimation.java | 43 +- src/com/android/launcher3/PagedView.java | 4 +- src/com/android/launcher3/SearchDropTargetBar.java | 123 ++--- src/com/android/launcher3/Workspace.java | 532 ++------------------- .../WorkspaceStateTransitionAnimation.java | 519 ++++++++++++++++++++ 7 files changed, 652 insertions(+), 613 deletions(-) create mode 100644 src/com/android/launcher3/WorkspaceStateTransitionAnimation.java (limited to 'src') diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2bfb29ef6..7364a9f20 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -214,7 +214,6 @@ public class Launcher extends Activity /** The different states that Launcher can be in. */ enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }; @Thunk State mState = State.WORKSPACE; - @Thunk AnimatorSet mStateAnimation; @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -528,7 +527,8 @@ public class Launcher extends Activity @Override public void dismissAllApps() { - showWorkspace(true); + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, null, + false /* notifyLauncherCallbacks */); } }); return true; @@ -785,7 +785,7 @@ public class Launcher extends Activity return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(false); + showWorkspace(false); } return; } @@ -1220,7 +1220,6 @@ public class Launcher extends Activity return false; } - public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks, String description) { mWorkspace.addToCustomContentPage(customContent, callbacks, description); @@ -2480,7 +2479,7 @@ public class Launcher extends Activity } else if (isWidgetsViewVisible()) { showOverviewMode(true); } else if (mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(true); + showWorkspace(true); } else if (mWorkspace.getOpenFolder() != null) { Folder openFolder = mWorkspace.getOpenFolder(); if (openFolder.isEditingName()) { @@ -2523,14 +2522,14 @@ public class Launcher extends Activity if (v instanceof Workspace) { if (mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(true); + showWorkspace(true); } return; } if (v instanceof CellLayout) { if (mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v), true); + showWorkspace(mWorkspace.indexOfChild(v), true); } } @@ -3178,7 +3177,9 @@ public class Launcher extends Activity if (v instanceof Workspace) { if (!mWorkspace.isInOverviewMode()) { - if (mWorkspace.enterOverviewMode()) { + + if (!mWorkspace.isTouchActive()) { + showOverviewMode(true); mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); return true; @@ -3211,7 +3212,7 @@ public class Launcher extends Activity if (mWorkspace.isInOverviewMode()) { mWorkspace.startReordering(v); } else { - mWorkspace.enterOverviewMode(); + showOverviewMode(true); } } else { final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank( @@ -3302,17 +3303,28 @@ public class Launcher extends Activity } protected void showWorkspace(boolean animated) { - showWorkspace(animated, null); + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null, + true); + } + + protected void showWorkspace(boolean animated, Runnable onCompleteRunnable) { + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, + onCompleteRunnable, true); + } + + protected void showWorkspace(int snapToPage, boolean animated) { + showWorkspace(snapToPage, animated, null, true); } - void showWorkspace(boolean animated, Runnable onCompleteRunnable) { + void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable, + boolean notifyLauncherCallbacks) { boolean changed = mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL; if (changed) { boolean wasInSpringLoadedMode = (mState != State.WORKSPACE); mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL, - animated, onCompleteRunnable); + snapToPage, animated, onCompleteRunnable); // Show the search bar (only animate if we were showing the drop target bar in spring // loaded mode) @@ -3345,7 +3357,8 @@ public class Launcher extends Activity void showOverviewMode(boolean animated) { mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW, - animated, null /* onCompleteRunnable */); + WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, + null /* onCompleteRunnable */); mState = State.WORKSPACE; onWorkspaceShown(animated); } @@ -3419,7 +3432,8 @@ public class Launcher extends Activity } mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED, - true /* animated */, null /* onCompleteRunnable */); + WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */, + null /* onCompleteRunnable */); mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; } diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index 2ce8b1c59..a2c56a91c 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -126,7 +126,7 @@ class LauncherClings implements OnClickListener { @Override public boolean onLongClick(View v) { - mLauncher.getWorkspace().enterOverviewMode(); + mLauncher.showOverviewMode(true); dismissLongPressCling(); return true; } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 8ba5c60f3..f91cfa07b 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -23,7 +23,6 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.content.res.Resources; -import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewAnimationUtils; @@ -174,7 +173,8 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), animated, false /* hideSearchBar */, cb); + toView.getRevealView(), animated, + !mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb); } /** @@ -207,8 +207,8 @@ public class LauncherStateTransitionAnimation { * Starts and animation to the workspace from the current overlay view. */ public void startAnimationToWorkspace(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { if (toWorkspaceState != Workspace.State.NORMAL && toWorkspaceState != Workspace.State.SPRING_LOADED && toWorkspaceState != Workspace.State.OVERVIEW) { @@ -216,11 +216,11 @@ public class LauncherStateTransitionAnimation { } if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { - startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, animated, - onCompleteRunnable); + startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage, + animated, onCompleteRunnable); } else { - startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, animated, - onCompleteRunnable); + startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage, + animated, onCompleteRunnable); } } @@ -249,8 +249,8 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( - toWorkspaceState, animated, layerViews); + Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation( + toWorkspaceState, -1, animated, layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); @@ -424,8 +424,8 @@ public class LauncherStateTransitionAnimation { * Starts and animation to the workspace from the apps view. */ private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { AppsContainerView appsView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { int[] mAllAppsToPanelDelta; @@ -477,16 +477,17 @@ public class LauncherStateTransitionAnimation { }; } }; - startAnimationToWorkspaceFromOverlay(toWorkspaceState, appsView, appsView.getContentView(), - appsView.getRevealView(), animated, onCompleteRunnable, cb); + startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView, + appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable, + cb); } /** * Starts and animation to the workspace from the widgets view. */ private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); final Resources res = mLauncher.getResources(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @@ -514,7 +515,7 @@ public class LauncherStateTransitionAnimation { }; } }; - startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView, + startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView, widgetsView.getContentView(), widgetsView.getRevealView(), animated, onCompleteRunnable, cb); } @@ -523,8 +524,8 @@ public class LauncherStateTransitionAnimation { * Creates and starts a new animation to the workspace. */ private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, - final View fromView, final View contentView, final View revealView, - final boolean animated, final Runnable onCompleteRunnable, + final int toWorkspacePage, final View fromView, final View contentView, + final View revealView, final boolean animated, final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); @@ -545,8 +546,8 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( - toWorkspaceState, animated, layerViews); + Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation( + toWorkspaceState, toWorkspacePage, animated, layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 88295c084..a4593ecb4 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1696,11 +1696,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return OVERSCROLL_DAMP_FACTOR * f; } - protected void enableFreeScroll() { + public void enableFreeScroll() { setEnableFreeScroll(true); } - protected void disableFreeScroll() { + public void disableFreeScroll() { setEnableFreeScroll(false); } diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index a8dcd0f06..af8bc7580 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -33,18 +33,16 @@ import android.widget.FrameLayout; */ public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener { - private static final int sTransitionInDuration = 200; - private static final int sTransitionOutDuration = 175; + private static final int TRANSITION_DURATION = 200; - private ObjectAnimator mDropTargetBarAnim; - private ValueAnimator mQSBSearchBarAnim; + private ObjectAnimator mShowDropTargetBarAnim; + private ValueAnimator mHideSearchBarAnim; private static final AccelerateInterpolator sAccelerateInterpolator = new AccelerateInterpolator(); private boolean mIsSearchBarHidden; private View mQSBSearchBar; private View mDropTargetBar; - private int mBarHeight; private boolean mDeferOnDragEnd = false; // Drop targets @@ -52,8 +50,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D private ButtonDropTarget mDeleteDropTarget; private ButtonDropTarget mUninstallDropTarget; - private boolean mEnableDropDownDropTargets; - public SearchDropTargetBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -82,17 +78,12 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D public void setQsbSearchBar(View qsb) { mQSBSearchBar = qsb; if (mQSBSearchBar != null) { - if (mEnableDropDownDropTargets) { - mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "translationY", 0, - -mBarHeight); - } else { - mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f); - } - setupAnimation(mQSBSearchBarAnim, mQSBSearchBar); + mHideSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f); + setupAnimation(mHideSearchBarAnim, mQSBSearchBar); } else { // Create a no-op animation of the search bar is null - mQSBSearchBarAnim = ValueAnimator.ofFloat(0, 0); - mQSBSearchBarAnim.setDuration(sTransitionInDuration); + mHideSearchBarAnim = ValueAnimator.ofFloat(0, 0); + mHideSearchBarAnim.setDuration(TRANSITION_DURATION); } } @@ -106,7 +97,7 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D private void setupAnimation(ValueAnimator anim, final View v) { anim.setInterpolator(sAccelerateInterpolator); - anim.setDuration(sTransitionInDuration); + anim.setDuration(TRANSITION_DURATION); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -131,76 +122,74 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D mDeleteDropTarget.setSearchDropTargetBar(this); mUninstallDropTarget.setSearchDropTargetBar(this); - mEnableDropDownDropTargets = - getResources().getBoolean(R.bool.config_useDropTargetDownTransition); - // Create the various fade animations - if (mEnableDropDownDropTargets) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mBarHeight = grid.searchBarSpaceHeightPx; - mDropTargetBar.setTranslationY(-mBarHeight); - mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "translationY", - -mBarHeight, 0f); - - } else { - mDropTargetBar.setAlpha(0f); - mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f); - } - setupAnimation(mDropTargetBarAnim, mDropTargetBar); + mDropTargetBar.setAlpha(0f); + mShowDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f); + setupAnimation(mShowDropTargetBarAnim, mDropTargetBar); } + /** + * Finishes all the animations on the search and drop target bars. + */ public void finishAnimations() { prepareStartAnimation(mDropTargetBar); - mDropTargetBarAnim.reverse(); + mShowDropTargetBarAnim.reverse(); prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.reverse(); + mHideSearchBarAnim.reverse(); } - /* - * Shows and hides the search bar. + /** + * Shows the search bar. */ public void showSearchBar(boolean animated) { - boolean needToCancelOngoingAnimation = mQSBSearchBarAnim.isRunning() && !animated; - if (!mIsSearchBarHidden && !needToCancelOngoingAnimation) return; + if (!mIsSearchBarHidden) return; if (animated) { prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.reverse(); + mHideSearchBarAnim.reverse(); } else { - mQSBSearchBarAnim.cancel(); - if (mQSBSearchBar != null && mEnableDropDownDropTargets) { - mQSBSearchBar.setTranslationY(0); - } else if (mQSBSearchBar != null) { + mHideSearchBarAnim.cancel(); + if (mQSBSearchBar != null) { mQSBSearchBar.setAlpha(1f); } } mIsSearchBarHidden = false; } + + /** + * Hides the search bar. We only use this for clings. + */ public void hideSearchBar(boolean animated) { - boolean needToCancelOngoingAnimation = mQSBSearchBarAnim.isRunning() && !animated; - if (mIsSearchBarHidden && !needToCancelOngoingAnimation) return; + if (mIsSearchBarHidden) return; if (animated) { prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.start(); + mHideSearchBarAnim.start(); } else { - mQSBSearchBarAnim.cancel(); - if (mQSBSearchBar != null && mEnableDropDownDropTargets) { - mQSBSearchBar.setTranslationY(-mBarHeight); - } else if (mQSBSearchBar != null) { + mHideSearchBarAnim.cancel(); + if (mQSBSearchBar != null) { mQSBSearchBar.setAlpha(0f); } } mIsSearchBarHidden = true; } - /* - * Gets various transition durations. + /** + * Shows the drop target bar. */ - public int getTransitionInDuration() { - return sTransitionInDuration; + public void showDeleteTarget() { + // Animate out the QSB search bar, and animate in the drop target bar + prepareStartAnimation(mDropTargetBar); + mShowDropTargetBarAnim.start(); + hideSearchBar(true); } - public int getTransitionOutDuration() { - return sTransitionOutDuration; + + /** + * Hides the drop target bar. + */ + public void hideDeleteTarget() { + // Restore the QSB search bar, and animate out the drop target bar + prepareStartAnimation(mDropTargetBar); + mShowDropTargetBarAnim.reverse(); + showSearchBar(true); } /* @@ -211,26 +200,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D showDeleteTarget(); } - public void showDeleteTarget() { - // Animate out the QSB search bar, and animate in the drop target bar - prepareStartAnimation(mDropTargetBar); - mDropTargetBarAnim.start(); - if (!mIsSearchBarHidden) { - prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.start(); - } - } - - public void hideDeleteTarget() { - // Restore the QSB search bar, and animate out the drop target bar - prepareStartAnimation(mDropTargetBar); - mDropTargetBarAnim.reverse(); - if (!mIsSearchBarHidden) { - prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.reverse(); - } - } - public void deferOnDragEnd() { mDeferOnDragEnd = true; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6b03e3100..9e680fb82 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -17,15 +17,10 @@ package com.android.launcher3; import android.animation.Animator; -import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; import android.app.WallpaperManager; import android.appwidget.AppWidgetHostView; @@ -99,12 +94,9 @@ public class Workspace extends SmoothPagedView protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; protected static final int FADE_EMPTY_SCREEN_DURATION = 150; - private static final int BACKGROUND_FADE_OUT_DURATION = 350; private static final int ADJACENT_SCREEN_DROP_DURATION = 300; private static final int FLING_THRESHOLD_VELOCITY = 500; - private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; - static final boolean MAP_NO_RECURSE = false; static final boolean MAP_RECURSE = true; @@ -113,10 +105,6 @@ public class Workspace extends SmoothPagedView private ObjectAnimator mChildrenOutlineFadeOutAnimation; private float mChildrenOutlineAlpha = 0; - // These properties refer to the background protection gradient used for AllApps and Customize - private ValueAnimator mBackgroundFadeInAnimation; - private ValueAnimator mBackgroundFadeOutAnimation; - private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200; private long mTouchDownTime = -1; private long mCustomContentShowTime = -1; @@ -129,7 +117,6 @@ public class Workspace extends SmoothPagedView private int mDefaultPage; private ShortcutAndWidgetContainer mDragSourceInternal; - @Thunk static boolean sAccessibilityEnabled; // The screen id used for the empty screen always present to the right. final static long EXTRA_EMPTY_SCREEN_ID = -201; @@ -272,14 +259,7 @@ public class Workspace extends SmoothPagedView private float mSavedTranslationX; private float mCurrentScale; - private float mNewScale; - @Thunk float[] mOldBackgroundAlphas; - private float[] mOldAlphas; - @Thunk float[] mNewBackgroundAlphas; - private float[] mNewAlphas; - private int mLastChildCount = -1; private float mTransitionProgress; - @Thunk Animator mStateAnimator = null; float mOverScrollEffect = 0f; @@ -294,6 +274,9 @@ public class Workspace extends SmoothPagedView boolean mShouldSendPageSettled; int mLastOverlaySroll = 0; + // Handles workspace state transitions + private WorkspaceStateTransitionAnimation mStateTransitionAnimation; + private final Runnable mBindPages = new Runnable() { @Override public void run() { @@ -329,6 +312,7 @@ public class Workspace extends SmoothPagedView setDataIsReady(); mLauncher = (Launcher) context; + mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); final Resources res = getResources(); mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid(). getDeviceProfile().shouldFadeAdjacentWorkspaceScreens(); @@ -1583,37 +1567,6 @@ public class Workspace extends SmoothPagedView return mChildrenOutlineAlpha; } - private void animateBackgroundGradient(float finalAlpha, boolean animated) { - final DragLayer dragLayer = mLauncher.getDragLayer(); - - if (mBackgroundFadeInAnimation != null) { - mBackgroundFadeInAnimation.cancel(); - mBackgroundFadeInAnimation = null; - } - if (mBackgroundFadeOutAnimation != null) { - mBackgroundFadeOutAnimation.cancel(); - mBackgroundFadeOutAnimation = null; - } - float startAlpha = dragLayer.getBackgroundAlpha(); - if (finalAlpha != startAlpha) { - if (animated) { - mBackgroundFadeOutAnimation = - LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha); - mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() { - public void onAnimationUpdate(ValueAnimator animation) { - dragLayer.setBackgroundAlpha( - ((Float)animation.getAnimatedValue()).floatValue()); - } - }); - mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); - mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); - mBackgroundFadeOutAnimation.start(); - } else { - dragLayer.setBackgroundAlpha(finalAlpha); - } - } - } - float backgroundAlphaInterpolator(float r) { float pivotA = 0.1f; float pivotB = 0.4f; @@ -1737,7 +1690,7 @@ public class Workspace extends SmoothPagedView OnClickListener listener = new OnClickListener() { @Override public void onClick(View arg0) { - enterOverviewMode(); + mLauncher.showOverviewMode(true); } }; return listener; @@ -1797,9 +1750,6 @@ public class Workspace extends SmoothPagedView getPageIndicator().setOnClickListener(listener); } } - AccessibilityManager am = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - sAccessibilityEnabled = am.isEnabled(); // Update wallpaper dimensions if they were changed since last onResume // (we also always set the wallpaper dimensions in the constructor) @@ -1969,64 +1919,6 @@ public class Workspace extends SmoothPagedView position[0], position[1], 0, null); } - /* - * This interpolator emulates the rate at which the perceived scale of an object changes - * as its distance from a camera increases. When this interpolator is applied to a scale - * animation on a view, it evokes the sense that the object is shrinking due to moving away - * from the camera. - */ - static class ZInterpolator implements TimeInterpolator { - private float focalLength; - - public ZInterpolator(float foc) { - focalLength = foc; - } - - public float getInterpolation(float input) { - return (1.0f - focalLength / (focalLength + input)) / - (1.0f - focalLength / (focalLength + 1.0f)); - } - } - - /* - * The exact reverse of ZInterpolator. - */ - static class InverseZInterpolator implements TimeInterpolator { - private ZInterpolator zInterpolator; - public InverseZInterpolator(float foc) { - zInterpolator = new ZInterpolator(foc); - } - public float getInterpolation(float input) { - return 1 - zInterpolator.getInterpolation(1 - input); - } - } - - /* - * ZInterpolator compounded with an ease-out. - */ - static class ZoomOutInterpolator implements TimeInterpolator { - private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f); - private final ZInterpolator zInterpolator = new ZInterpolator(0.13f); - - public float getInterpolation(float input) { - return decelerate.getInterpolation(zInterpolator.getInterpolation(input)); - } - } - - /* - * InvereZInterpolator compounded with an ease-out. - */ - static class ZoomInInterpolator implements TimeInterpolator { - private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f); - private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f); - - public float getInterpolation(float input) { - return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input)); - } - } - - private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); - /* * * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we @@ -2090,21 +1982,6 @@ public class Workspace extends SmoothPagedView dragLayer.clearAllResizeFrames(); } - private void initAnimationArrays() { - final int childCount = getChildCount(); - if (mLastChildCount == childCount) return; - - mOldBackgroundAlphas = new float[childCount]; - mOldAlphas = new float[childCount]; - mNewBackgroundAlphas = new float[childCount]; - mNewAlphas = new float[childCount]; - } - - Animator getChangeStateAnimation(final State state, boolean animated, - HashMap layerViews) { - return getChangeStateAnimation(state, animated, 0, -1, layerViews); - } - @Override protected void getFreeScrollPageRange(int[] range) { getOverviewModePages(range); @@ -2151,41 +2028,6 @@ public class Workspace extends SmoothPagedView return mState == State.OVERVIEW; } - public boolean enterOverviewMode() { - if (mTouchState != TOUCH_STATE_REST) { - return false; - } - enableOverviewMode(true, -1, true); - return true; - } - - public void exitOverviewMode(boolean animated) { - exitOverviewMode(-1, animated); - } - - public void exitOverviewMode(int snapPage, boolean animated) { - enableOverviewMode(false, snapPage, animated); - } - - private void enableOverviewMode(boolean enable, int snapPage, boolean animated) { - State finalState = Workspace.State.OVERVIEW; - if (!enable) { - finalState = Workspace.State.NORMAL; - } - - Animator workspaceAnim = getChangeStateAnimation(finalState, animated, 0, snapPage); - if (workspaceAnim != null) { - onTransitionPrepare(); - workspaceAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator arg0) { - onTransitionEnd(); - } - }); - workspaceAnim.start(); - } - } - int getOverviewModeTranslationY() { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -2208,10 +2050,22 @@ public class Workspace extends SmoothPagedView } } - private void setState(State state) { - mState = state; + /** + * Sets the current workspace {@link State}, returning an animation transitioning the workspace + * to that new state. + */ + public Animator setStateWithAnimation(State toState, int toPage, boolean animated, + HashMap layerViews) { + // Create the animation to the new state + Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(getState(), + toState, toPage, animated, layerViews); + + // Update the current state + mState = toState; updateInteractionForState(); updateAccessibilityFlags(); + + return workspaceAnim; } State getState() { @@ -2225,321 +2079,15 @@ public class Workspace extends SmoothPagedView setImportantForAccessibility(accessible); } - private static final int HIDE_WORKSPACE_DURATION = 100; - - Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) { - return getChangeStateAnimation(state, animated, delay, snapPage, null); - } - - Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage, - HashMap layerViews) { - if (mState == state) { - return null; - } - - // Initialize animation arrays for the first time if necessary - initAnimationArrays(); - - AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null; - - // We only want a single instance of a workspace animation to be running at once, so - // we cancel any incomplete transition. - if (mStateAnimator != null) { - mStateAnimator.cancel(); - } - mStateAnimator = anim; - - final State oldState = mState; - final boolean oldStateIsNormal = (oldState == State.NORMAL); - final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED); - final boolean oldStateIsNormalHidden = (oldState == State.NORMAL_HIDDEN); - final boolean oldStateIsOverviewHidden = (oldState == State.OVERVIEW_HIDDEN); - final boolean oldStateIsOverview = (oldState == State.OVERVIEW); - setState(state); - final boolean stateIsNormal = (state == State.NORMAL); - final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED); - final boolean stateIsNormalHidden = (state == State.NORMAL_HIDDEN); - final boolean stateIsOverviewHidden = (state == State.OVERVIEW_HIDDEN); - final boolean stateIsOverview = (state == State.OVERVIEW); - float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f; - float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; - float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; - // We keep the search bar visible on the workspace and in AllApps now - boolean showSearchBar = stateIsNormal || - (mLauncher.isAllAppsSearchOverridden() && stateIsNormalHidden); - float finalSearchBarAlpha = showSearchBar ? 1f : 0f; - float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? - getOverviewModeTranslationY() : 0; - - boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); - boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); - boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); - boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview); - boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal); - - mNewScale = 1.0f; - - if (oldStateIsOverview) { - disableFreeScroll(); - } else if (stateIsOverview) { - enableFreeScroll(); - } - - if (state != State.NORMAL) { - if (stateIsSpringLoaded) { - mNewScale = mSpringLoadedShrinkFactor; - } else if (stateIsOverview || stateIsOverviewHidden) { - mNewScale = mOverviewModeShrinkFactor; - } - } - - final int duration; - if (workspaceToAllApps || overviewToAllApps) { - duration = HIDE_WORKSPACE_DURATION; //getResources().getInteger(R.integer.config_workspaceUnshrinkTime); - } else if (workspaceToOverview || overviewToWorkspace) { - duration = getResources().getInteger(R.integer.config_overviewTransitionTime); - } else { - duration = getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime); - } - - if (snapPage == -1) { - snapPage = getPageNearestToCenterOfScreen(); - } - snapToPage(snapPage, duration, mZoomInInterpolator); - - for (int i = 0; i < getChildCount(); i++) { - final CellLayout cl = (CellLayout) getChildAt(i); - boolean isCurrentPage = (i == snapPage); - float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); - float finalAlpha; - if (stateIsNormalHidden || stateIsOverviewHidden) { - finalAlpha = 0f; - } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) { - finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f; - } else { - finalAlpha = 1f; - } - - // If we are animating to/from the small state, then hide the side pages and fade the - // current page in - if (!mIsSwitchingState) { - if (workspaceToAllApps || allAppsToWorkspace) { - if (allAppsToWorkspace && isCurrentPage) { - initialAlpha = 0f; - } else if (!isCurrentPage) { - initialAlpha = finalAlpha = 0f; - } - cl.setShortcutAndWidgetAlpha(initialAlpha); - } - } - - mOldAlphas[i] = initialAlpha; - mNewAlphas[i] = finalAlpha; - if (animated) { - mOldBackgroundAlphas[i] = cl.getBackgroundAlpha(); - mNewBackgroundAlphas[i] = finalBackgroundAlpha; - } else { - cl.setBackgroundAlpha(finalBackgroundAlpha); - cl.setShortcutAndWidgetAlpha(finalAlpha); - } - } - - final View searchBar = mLauncher.getOrCreateQsbBar(); - final View overviewPanel = mLauncher.getOverviewPanel(); - final View hotseat = mLauncher.getHotseat(); - final View pageIndicator = getPageIndicator(); - if (animated) { - LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this); - scale.scaleX(mNewScale) - .scaleY(mNewScale) - .translationY(finalWorkspaceTranslationY) - .setDuration(duration) - .setInterpolator(mZoomInInterpolator); - anim.play(scale); - for (int index = 0; index < getChildCount(); index++) { - final int i = index; - final CellLayout cl = (CellLayout) getChildAt(i); - float currentAlpha = cl.getShortcutsAndWidgets().getAlpha(); - if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) { - cl.setBackgroundAlpha(mNewBackgroundAlphas[i]); - cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); - } else { - if (layerViews != null) { - layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER); - } - if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { - LauncherViewPropertyAnimator alphaAnim = - new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); - alphaAnim.alpha(mNewAlphas[i]) - .setDuration(duration) - .setInterpolator(mZoomInInterpolator); - anim.play(alphaAnim); - } - if (mOldBackgroundAlphas[i] != 0 || - mNewBackgroundAlphas[i] != 0) { - ValueAnimator bgAnim = - LauncherAnimUtils.ofFloat(cl, 0f, 1f); - bgAnim.setInterpolator(mZoomInInterpolator); - bgAnim.setDuration(duration); - bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { - public void onAnimationUpdate(float a, float b) { - cl.setBackgroundAlpha( - a * mOldBackgroundAlphas[i] + - b * mNewBackgroundAlphas[i]); - } - }); - anim.play(bgAnim); - } - } - } - Animator pageIndicatorAlpha = null; - if (pageIndicator != null) { - pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator) - .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); - pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator)); - } else { - // create a dummy animation so we don't need to do null checks later - pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0); - } - - LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) - .alpha(finalHotseatAndPageIndicatorAlpha); - hotseatAlpha.addListener(new AlphaUpdateListener(hotseat)); - - LauncherViewPropertyAnimator overviewPanelAlpha = - new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha); - overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel)); - - // For animation optimations, we may need to provide the Launcher transition - // with a set of views on which to force build layers in certain scenarios. - hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null); - overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (layerViews != null) { - // If layerViews is not null, we add these views, and indicate that - // the caller can manage layer state. - layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - } else { - // Otherwise let the animator handle layer management. - hotseatAlpha.withLayer(); - overviewPanelAlpha.withLayer(); - } - - if (workspaceToOverview) { - pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2)); - hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); - overviewPanelAlpha.setInterpolator(null); - } else if (overviewToWorkspace) { - pageIndicatorAlpha.setInterpolator(null); - hotseatAlpha.setInterpolator(null); - overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); - } - - overviewPanelAlpha.setDuration(duration); - pageIndicatorAlpha.setDuration(duration); - hotseatAlpha.setDuration(duration); - - if (searchBar != null) { - LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar) - .alpha(finalSearchBarAlpha); - searchBarAlpha.addListener(new AlphaUpdateListener(searchBar)); - searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (layerViews != null) { - // If layerViews is not null, we add these views, and indicate that - // the caller can manage layer state. - layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - } else { - // Otherwise let the animator handle layer management. - searchBarAlpha.withLayer(); - } - searchBarAlpha.setDuration(duration); - anim.play(searchBarAlpha); - } - - anim.play(overviewPanelAlpha); - anim.play(hotseatAlpha); - anim.play(pageIndicatorAlpha); - anim.setStartDelay(delay); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mStateAnimator = null; - } - }); - } else { - overviewPanel.setAlpha(finalOverviewPanelAlpha); - AlphaUpdateListener.updateVisibility(overviewPanel); - hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha); - AlphaUpdateListener.updateVisibility(hotseat); - if (pageIndicator != null) { - pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha); - AlphaUpdateListener.updateVisibility(pageIndicator); - } - if (searchBar != null) { - searchBar.setAlpha(finalSearchBarAlpha); - AlphaUpdateListener.updateVisibility(searchBar); - } - updateCustomContentVisibility(); - setScaleX(mNewScale); - setScaleY(mNewScale); - setTranslationY(finalWorkspaceTranslationY); - } - - if (stateIsNormal) { - animateBackgroundGradient(0f, animated); - } else { - animateBackgroundGradient(getResources().getInteger( - R.integer.config_workspaceScrimAlpha) / 100f, animated); - } - return anim; - } - - static class AlphaUpdateListener implements AnimatorUpdateListener, AnimatorListener { - View view; - public AlphaUpdateListener(View v) { - view = v; - } - - @Override - public void onAnimationUpdate(ValueAnimator arg0) { - updateVisibility(view); - } - - public static void updateVisibility(View view) { - // We want to avoid the extra layout pass by setting the views to GONE unless - // accessibility is on, in which case not setting them to GONE causes a glitch. - int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE; - if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { - view.setVisibility(invisibleState); - } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD - && view.getVisibility() != VISIBLE) { - view.setVisibility(VISIBLE); - } - } - - @Override - public void onAnimationCancel(Animator arg0) { - } - - @Override - public void onAnimationEnd(Animator arg0) { - updateVisibility(view); - } - - @Override - public void onAnimationRepeat(Animator arg0) { - } - - @Override - public void onAnimationStart(Animator arg0) { - // We want the views to be visible for animation, so fade-in/out is visible - view.setVisibility(VISIBLE); - } - } - @Override public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - onTransitionPrepare(); + mIsSwitchingState = true; + + // Invalidate here to ensure that the pages are rendered during the state change transition. + invalidate(); + + updateChildrenLayersEnabled(false); + hideCustomContentIfNecessary(); } @Override @@ -2553,17 +2101,9 @@ public class Workspace extends SmoothPagedView @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - onTransitionEnd(); - } - - private void onTransitionPrepare() { - mIsSwitchingState = true; - - // Invalidate here to ensure that the pages are rendered during the state change transition. - invalidate(); - + mIsSwitchingState = false; updateChildrenLayersEnabled(false); - hideCustomContentIfNecessary(); + showCustomContentIfNecessary(); } void updateCustomContentVisibility() { @@ -2589,12 +2129,6 @@ public class Workspace extends SmoothPagedView } } - @Thunk void onTransitionEnd() { - mIsSwitchingState = false; - updateChildrenLayersEnabled(false); - showCustomContentIfNecessary(); - } - @Override public View getContent() { return this; @@ -3106,7 +2640,7 @@ public class Workspace extends SmoothPagedView } } - int snapScreen = -1; + int snapScreen = WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE; boolean resizeOnDrop = false; if (d.dragSource != this) { final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0], @@ -3267,7 +2801,9 @@ public class Workspace extends SmoothPagedView animateWidgetDrop(info, parent, d.dragView, onCompleteRunnable, animationType, cell, false); } else { - int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION; + int duration = snapScreen < 0 ? + WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE : + ADJACENT_SCREEN_DROP_DURATION; mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration, onCompleteRunnable, this); } @@ -4165,8 +3701,8 @@ public class Workspace extends SmoothPagedView public void setFinalTransitionTransform(CellLayout layout) { if (isSwitchingState()) { mCurrentScale = getScaleX(); - setScaleX(mNewScale); - setScaleY(mNewScale); + setScaleX(mStateTransitionAnimation.getFinalScale()); + setScaleY(mStateTransitionAnimation.getFinalScale()); } } public void resetTransitionTransform(CellLayout layout) { diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java new file mode 100644 index 000000000..a0cedeb63 --- /dev/null +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.view.View; +import android.view.accessibility.AccessibilityManager; +import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.util.Thunk; + +import java.util.HashMap; + +/** + * A convenience class to update a view's visibility state after an alpha animation. + */ +class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener { + private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; + + private View mView; + private boolean mAccessibilityEnabled; + + public AlphaUpdateListener(View v, boolean accessibilityEnabled) { + mView = v; + mAccessibilityEnabled = accessibilityEnabled; + } + + @Override + public void onAnimationUpdate(ValueAnimator arg0) { + updateVisibility(mView, mAccessibilityEnabled); + } + + public static void updateVisibility(View view, boolean accessibilityEnabled) { + // We want to avoid the extra layout pass by setting the views to GONE unless + // accessibility is on, in which case not setting them to GONE causes a glitch. + int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE; + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { + view.setVisibility(invisibleState); + } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD + && view.getVisibility() != View.VISIBLE) { + view.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationEnd(Animator arg0) { + updateVisibility(mView, mAccessibilityEnabled); + } + + @Override + public void onAnimationStart(Animator arg0) { + // We want the views to be visible for animation, so fade-in/out is visible + mView.setVisibility(View.VISIBLE); + } +} + +/** + * This interpolator emulates the rate at which the perceived scale of an object changes + * as its distance from a camera increases. When this interpolator is applied to a scale + * animation on a view, it evokes the sense that the object is shrinking due to moving away + * from the camera. + */ +class ZInterpolator implements TimeInterpolator { + private float focalLength; + + public ZInterpolator(float foc) { + focalLength = foc; + } + + public float getInterpolation(float input) { + return (1.0f - focalLength / (focalLength + input)) / + (1.0f - focalLength / (focalLength + 1.0f)); + } +} + +/** + * The exact reverse of ZInterpolator. + */ +class InverseZInterpolator implements TimeInterpolator { + private ZInterpolator zInterpolator; + public InverseZInterpolator(float foc) { + zInterpolator = new ZInterpolator(foc); + } + public float getInterpolation(float input) { + return 1 - zInterpolator.getInterpolation(1 - input); + } +} + +/** + * InverseZInterpolator compounded with an ease-out. + */ +class ZoomInInterpolator implements TimeInterpolator { + private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f); + private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f); + + public float getInterpolation(float input) { + return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input)); + } +} + +/** + * Manages the animations between each of the workspace states. + */ +public class WorkspaceStateTransitionAnimation { + + public static final String TAG = "WorkspaceStateTransitionAnimation"; + + public static final int SCROLL_TO_CURRENT_PAGE = -1; + @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350; + + final @Thunk Launcher mLauncher; + final @Thunk Workspace mWorkspace; + + @Thunk AnimatorSet mStateAnimator; + @Thunk float[] mOldBackgroundAlphas; + @Thunk float[] mOldAlphas; + @Thunk float[] mNewBackgroundAlphas; + @Thunk float[] mNewAlphas; + @Thunk int mLastChildCount = -1; + + @Thunk float mCurrentScale; + @Thunk float mNewScale; + + @Thunk final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); + + // These properties refer to the background protection gradient used for AllApps and Customize + @Thunk ValueAnimator mBackgroundFadeInAnimation; + @Thunk ValueAnimator mBackgroundFadeOutAnimation; + + @Thunk float mSpringLoadedShrinkFactor; + @Thunk float mOverviewModeShrinkFactor; + @Thunk float mWorkspaceScrimAlpha; + @Thunk int mAllAppsTransitionTime; + @Thunk int mOverviewTransitionTime; + @Thunk int mOverlayTransitionTime; + @Thunk boolean mWorkspaceFadeInAdjacentScreens; + + public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) { + mLauncher = launcher; + mWorkspace = workspace; + + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + Resources res = launcher.getResources(); + mAllAppsTransitionTime = res.getInteger(R.integer.config_workspaceUnshrinkTime); + mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime); + mOverlayTransitionTime = res.getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime); + mSpringLoadedShrinkFactor = + res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f; + mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f; + mOverviewModeShrinkFactor = grid.getOverviewModeScale(); + mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); + } + + public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState, + int toPage, boolean animated, + HashMap layerViews) { + getAnimation(fromState, toState, toPage, animated, layerViews); + return mStateAnimator; + } + + public float getFinalScale() { + return mNewScale; + } + + /** + * Starts a transition animation for the workspace. + */ + private void getAnimation(final Workspace.State fromState, final Workspace.State toState, + int toPage, final boolean animated, + final HashMap layerViews) { + AccessibilityManager am = (AccessibilityManager) + mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); + boolean accessibilityEnabled = am.isEnabled(); + + // Reinitialize animation arrays for the current workspace state + reinitializeAnimationArrays(); + + // Cancel existing workspace animations and create a new animator set if requested + cancelAnimation(); + if (animated) { + mStateAnimator = LauncherAnimUtils.createAnimatorSet(); + } + + // Update the workspace state + final boolean oldStateIsNormal = (fromState == Workspace.State.NORMAL); + final boolean oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED); + final boolean oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN); + final boolean oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN); + final boolean oldStateIsOverview = (fromState == Workspace.State.OVERVIEW); + + final boolean stateIsNormal = (toState == Workspace.State.NORMAL); + final boolean stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED); + final boolean stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN); + final boolean stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN); + final boolean stateIsOverview = (toState == Workspace.State.OVERVIEW); + + final boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); + final boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); + final boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); + final boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview); + final boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal); + + float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f; + float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; + float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; + // We keep the search bar visible on the workspace and in AllApps now + boolean showSearchBar = stateIsNormal || + (mLauncher.isAllAppsSearchOverridden() && stateIsNormalHidden); + float finalSearchBarAlpha = showSearchBar ? 1f : 0f; + float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? + mWorkspace.getOverviewModeTranslationY() : 0; + + final int childCount = mWorkspace.getChildCount(); + final int customPageCount = mWorkspace.numCustomPages(); + + mNewScale = 1.0f; + + if (oldStateIsOverview) { + mWorkspace.disableFreeScroll(); + } else if (stateIsOverview) { + mWorkspace.enableFreeScroll(); + } + + if (!stateIsNormal) { + if (stateIsSpringLoaded) { + mNewScale = mSpringLoadedShrinkFactor; + } else if (stateIsOverview || stateIsOverviewHidden) { + mNewScale = mOverviewModeShrinkFactor; + } + } + + final int duration; + if (workspaceToAllApps || overviewToAllApps) { + duration = mAllAppsTransitionTime; + } else if (workspaceToOverview || overviewToWorkspace) { + duration = mOverviewTransitionTime; + } else { + duration = mOverlayTransitionTime; + } + + if (toPage == SCROLL_TO_CURRENT_PAGE) { + toPage = mWorkspace.getPageNearestToCenterOfScreen(); + } + mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator); + + for (int i = 0; i < childCount; i++) { + final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); + boolean isCurrentPage = (i == toPage); + float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); + float finalAlpha; + if (stateIsNormalHidden || stateIsOverviewHidden) { + finalAlpha = 0f; + } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) { + finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f; + } else { + finalAlpha = 1f; + } + + // If we are animating to/from the small state, then hide the side pages and fade the + // current page in + if (!mWorkspace.isSwitchingState()) { + if (workspaceToAllApps || allAppsToWorkspace) { + if (allAppsToWorkspace && isCurrentPage) { + initialAlpha = 0f; + } else if (!isCurrentPage) { + initialAlpha = finalAlpha = 0f; + } + cl.setShortcutAndWidgetAlpha(initialAlpha); + } + } + + mOldAlphas[i] = initialAlpha; + mNewAlphas[i] = finalAlpha; + if (animated) { + mOldBackgroundAlphas[i] = cl.getBackgroundAlpha(); + mNewBackgroundAlphas[i] = finalBackgroundAlpha; + } else { + cl.setBackgroundAlpha(finalBackgroundAlpha); + cl.setShortcutAndWidgetAlpha(finalAlpha); + } + } + + final View searchBar = mLauncher.getOrCreateQsbBar(); + final View overviewPanel = mLauncher.getOverviewPanel(); + final View hotseat = mLauncher.getHotseat(); + final View pageIndicator = mWorkspace.getPageIndicator(); + if (animated) { + LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mWorkspace); + scale.scaleX(mNewScale) + .scaleY(mNewScale) + .translationY(finalWorkspaceTranslationY) + .setDuration(duration) + .setInterpolator(mZoomInInterpolator); + mStateAnimator.play(scale); + for (int index = 0; index < childCount; index++) { + final int i = index; + final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); + float currentAlpha = cl.getShortcutsAndWidgets().getAlpha(); + if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) { + cl.setBackgroundAlpha(mNewBackgroundAlphas[i]); + cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); + } else { + if (layerViews != null) { + layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER); + } + if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { + LauncherViewPropertyAnimator alphaAnim = + new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); + alphaAnim.alpha(mNewAlphas[i]) + .setDuration(duration) + .setInterpolator(mZoomInInterpolator); + mStateAnimator.play(alphaAnim); + } + if (mOldBackgroundAlphas[i] != 0 || + mNewBackgroundAlphas[i] != 0) { + ValueAnimator bgAnim = + LauncherAnimUtils.ofFloat(cl, 0f, 1f); + bgAnim.setInterpolator(mZoomInInterpolator); + bgAnim.setDuration(duration); + bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { + public void onAnimationUpdate(float a, float b) { + cl.setBackgroundAlpha( + a * mOldBackgroundAlphas[i] + + b * mNewBackgroundAlphas[i]); + } + }); + mStateAnimator.play(bgAnim); + } + } + } + Animator pageIndicatorAlpha = null; + if (pageIndicator != null) { + pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator) + .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); + pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator, + accessibilityEnabled)); + } else { + // create a dummy animation so we don't need to do null checks later + pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0); + } + + LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) + .alpha(finalHotseatAndPageIndicatorAlpha); + hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled)); + + LauncherViewPropertyAnimator overviewPanelAlpha = + new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha); + overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel, + accessibilityEnabled)); + + // For animation optimations, we may need to provide the Launcher transition + // with a set of views on which to force build layers in certain scenarios. + hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null); + overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (layerViews != null) { + // If layerViews is not null, we add these views, and indicate that + // the caller can manage layer state. + layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + } else { + // Otherwise let the animator handle layer management. + hotseatAlpha.withLayer(); + overviewPanelAlpha.withLayer(); + } + + if (workspaceToOverview) { + pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2)); + hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); + overviewPanelAlpha.setInterpolator(null); + } else if (overviewToWorkspace) { + pageIndicatorAlpha.setInterpolator(null); + hotseatAlpha.setInterpolator(null); + overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); + } + + overviewPanelAlpha.setDuration(duration); + pageIndicatorAlpha.setDuration(duration); + hotseatAlpha.setDuration(duration); + + // TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the + // bar has no idea that it is hidden, and this has no idea what state the bar is + // actually in. + if (searchBar != null) { + LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar) + .alpha(finalSearchBarAlpha); + searchBarAlpha.addListener(new AlphaUpdateListener(searchBar, accessibilityEnabled)); + searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (layerViews != null) { + // If layerViews is not null, we add these views, and indicate that + // the caller can manage layer state. + layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + } else { + // Otherwise let the animator handle layer management. + searchBarAlpha.withLayer(); + } + searchBarAlpha.setDuration(duration); + mStateAnimator.play(searchBarAlpha); + } + + mStateAnimator.play(overviewPanelAlpha); + mStateAnimator.play(hotseatAlpha); + mStateAnimator.play(pageIndicatorAlpha); + mStateAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mStateAnimator = null; + } + }); + } else { + overviewPanel.setAlpha(finalOverviewPanelAlpha); + AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled); + hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha); + AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled); + if (pageIndicator != null) { + pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha); + AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled); + } + if (searchBar != null) { + searchBar.setAlpha(finalSearchBarAlpha); + AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); + } + mWorkspace.updateCustomContentVisibility(); + mWorkspace.setScaleX(mNewScale); + mWorkspace.setScaleY(mNewScale); + mWorkspace.setTranslationY(finalWorkspaceTranslationY); + } + + if (stateIsNormal) { + animateBackgroundGradient(0f, animated); + } else { + animateBackgroundGradient(mWorkspaceScrimAlpha, animated); + } + } + + /** + * Reinitializes the arrays that we need for the animations on each page. + */ + private void reinitializeAnimationArrays() { + final int childCount = mWorkspace.getChildCount(); + if (mLastChildCount == childCount) return; + + mOldBackgroundAlphas = new float[childCount]; + mOldAlphas = new float[childCount]; + mNewBackgroundAlphas = new float[childCount]; + mNewAlphas = new float[childCount]; + } + + /** + * Animates the background scrim. + * TODO(winsonc): Is there a better place for this? + * + * @param finalAlpha the final alpha for the background scrim + * @param animated whether or not to set the background alpha immediately + */ + private void animateBackgroundGradient(float finalAlpha, boolean animated) { + // Cancel any running background animations + cancelAnimator(mBackgroundFadeInAnimation); + cancelAnimator(mBackgroundFadeOutAnimation); + + final DragLayer dragLayer = mLauncher.getDragLayer(); + final float startAlpha = dragLayer.getBackgroundAlpha(); + if (finalAlpha != startAlpha) { + if (animated) { + mBackgroundFadeOutAnimation = + LauncherAnimUtils.ofFloat(mWorkspace, startAlpha, finalAlpha); + mBackgroundFadeOutAnimation.addUpdateListener( + new ValueAnimator.AnimatorUpdateListener() { + public void onAnimationUpdate(ValueAnimator animation) { + dragLayer.setBackgroundAlpha( + ((Float)animation.getAnimatedValue()).floatValue()); + } + }); + mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); + mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); + mBackgroundFadeOutAnimation.start(); + } else { + dragLayer.setBackgroundAlpha(finalAlpha); + } + } + } + + /** + * Cancels the current animation. + */ + private void cancelAnimation() { + cancelAnimator(mStateAnimator); + mStateAnimator = null; + } + + /** + * Cancels the specified animation. + */ + private void cancelAnimator(Animator animator) { + if (animator != null) { + animator.setDuration(0); + animator.cancel(); + } + } +} \ No newline at end of file -- cgit v1.2.3