diff options
9 files changed, 312 insertions, 364 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b06081b81..775dad2c8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -117,6 +117,7 @@ import com.android.launcher3.popup.BaseActionPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.shortcuts.DeepShortcutManager; +import com.android.launcher3.states.AllAppsState; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -202,9 +203,7 @@ public class Launcher extends BaseActivity // Type: SparseArray<Parcelable> private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; - static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; - - @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; + private LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -431,6 +430,10 @@ public class Launcher extends BaseActivity recreate(); } + public LauncherStateTransitionAnimation getStateTransition() { + return mStateTransitionAnimation; + } + protected void overrideTheme(boolean isDark, boolean supportsDarkText) { if (isDark) { setTheme(R.style.LauncherThemeDark); @@ -2441,8 +2444,7 @@ public class Launcher extends BaseActivity boolean changed = !isInState(LauncherState.NORMAL); if (changed || mAllAppsController.isTransitioning()) { mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace( - LauncherState.NORMAL, animated, onCompleteRunnable); + mStateTransitionAnimation.goToState(LauncherState.NORMAL, animated, onCompleteRunnable); // Set focus to the AppsCustomize button if (mAllAppsButton != null) { @@ -2483,8 +2485,7 @@ public class Launcher extends BaseActivity }; } mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace( - LauncherState.OVERVIEW, animated, postAnimRunnable); + mStateTransitionAnimation.goToState(LauncherState.OVERVIEW, animated, postAnimRunnable); // If animated from long press, then don't allow any of the controller in the drag // layer to intercept any remaining touch. @@ -2499,8 +2500,6 @@ public class Launcher extends BaseActivity // TODO: calling method should use the return value so that when {@code false} is returned // the workspace transition doesn't fall into invalid state. public boolean showAppsView(boolean animated) { - markAppsViewShown(); - if (!(isInState(LauncherState.NORMAL) || (isInState(LauncherState.ALL_APPS) && mAllAppsController.isTransitioning()))) { return false; @@ -2512,7 +2511,7 @@ public class Launcher extends BaseActivity mExitSpringLoadedModeRunnable = null; } - mStateTransitionAnimation.startAnimationToAllApps(animated); + mStateTransitionAnimation.goToState(LauncherState.ALL_APPS, animated, null); // Change the state *after* we've called all the transition code AbstractFloatingView.closeAllOpenViews(this); @@ -2529,10 +2528,7 @@ public class Launcher extends BaseActivity if (isInState(LauncherState.SPRING_LOADED)) { return; } - - mStateTransitionAnimation.startAnimationToWorkspace( - LauncherState.SPRING_LOADED, true /* animated */, - null /* onCompleteRunnable */); + mStateTransitionAnimation.goToState(LauncherState.SPRING_LOADED, true, null); } public void exitSpringLoadedDragMode(int delay) { @@ -3307,15 +3303,9 @@ public class Launcher extends BaseActivity return mRotationEnabled; } - private void markAppsViewShown() { - if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) { - return; - } - mSharedPrefs.edit().putBoolean(APPS_VIEW_SHOWN, true).apply(); - } - private boolean shouldShowDiscoveryBounce() { - return isInState(LauncherState.NORMAL) && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) + return isInState(LauncherState.NORMAL) + && !mSharedPrefs.getBoolean(AllAppsState.APPS_VIEW_SHOWN, false) && !UserManagerCompat.getInstance(this).isDemoUser(); } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 0ac27e538..9d01ed82d 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -20,6 +20,9 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; +import android.view.View; + +import com.android.launcher3.states.AllAppsState; import com.android.launcher3.states.OverviewState; import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -28,7 +31,7 @@ import java.util.Arrays; /** - * Various states for launcher + * Base state for various states used for the Launcher */ public class LauncherState { @@ -37,14 +40,14 @@ public class LauncherState { protected static final int FLAG_HIDE_HOTSEAT = 1 << 2; protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; protected static final int FLAG_DO_NOT_RESTORE = 1 << 4; + protected static final int FLAG_HAS_SPRING = 1 << 5; private static final LauncherState[] sAllStates = new LauncherState[4]; public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, - 0, FLAG_DO_NOT_RESTORE); + 0, 1f, FLAG_DO_NOT_RESTORE); - public static final LauncherState ALL_APPS = new LauncherState(1, ContainerType.ALLAPPS, - ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY); + public static final LauncherState ALL_APPS = new AllAppsState(1); public static final LauncherState SPRING_LOADED = new SpringLoadedState(2); @@ -73,12 +76,25 @@ public class LauncherState { */ public final int workspaceAccessibilityFlag; - // Properties related to state transition animation. + /** + * Properties related to state transition animation + * + * @see WorkspaceStateTransitionAnimation + */ public final boolean hasScrim; public final boolean hideHotseat; public final int transitionDuration; - public LauncherState(int id, int containerType, int transitionDuration, int flags) { + /** + * Fraction shift in the vertical translation UI and related properties + * + * @see com.android.launcher3.allapps.AllAppsTransitionController + */ + public final float verticalProgress; + public final boolean hasVerticalSpring; + + public LauncherState(int id, int containerType, int transitionDuration, float verticalProgress, + int flags) { this.containerType = containerType; this.transitionDuration = transitionDuration; @@ -90,6 +106,9 @@ public class LauncherState { : IMPORTANT_FOR_ACCESSIBILITY_AUTO; this.doNotRestore = (flags & FLAG_DO_NOT_RESTORE) != 0; + this.verticalProgress = verticalProgress; + this.hasVerticalSpring = (flags & FLAG_HAS_SPRING) != 0; + this.ordinal = id; sAllStates[id] = this; } @@ -105,4 +124,8 @@ public class LauncherState { public void onStateEnabled(Launcher launcher) { } public void onStateDisabled(Launcher launcher) { } + + public View getFinalFocus(Launcher launcher) { + return launcher.getWorkspace(); + } } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 0e6cead0c..eec8b31c4 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -19,14 +19,13 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.util.Log; +import android.os.Handler; +import android.os.Looper; import android.view.View; -import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationLayerSet; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.util.Thunk; +import com.android.launcher3.anim.AnimationSuccessListener; /** * TODO: figure out what kind of tests we can write for this @@ -74,223 +73,75 @@ public class LauncherStateTransitionAnimation { public static final String TAG = "LSTAnimation"; private final AnimationConfig mConfig = new AnimationConfig(); - @Thunk Launcher mLauncher; - @Thunk AnimatorSet mCurrentAnimation; - AllAppsTransitionController mAllAppsController; + private final Handler mUiHandler; + private final Launcher mLauncher; + private final AllAppsTransitionController mAllAppsController; - public LauncherStateTransitionAnimation(Launcher l, AllAppsTransitionController allAppsController) { + public LauncherStateTransitionAnimation( + Launcher l, AllAppsTransitionController allAppsController) { + mUiHandler = new Handler(Looper.getMainLooper()); mLauncher = l; mAllAppsController = allAppsController; } - /** - * Starts an animation to the apps view. - */ - public void startAnimationToAllApps(boolean animated) { - final AllAppsContainerView toView = mLauncher.getAppsView(); - - // If for some reason our views aren't initialized, don't animate - animated = animated && (toView != null); - - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - - final AnimationLayerSet layerViews = new AnimationLayerSet(); - + public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) { // Cancel the current animation - cancelAnimation(); - - if (!animated) { - mLauncher.getWorkspace().setState(LauncherState.ALL_APPS); - - mAllAppsController.finishPullUp(); - toView.setTranslationX(0.0f); - toView.setTranslationY(0.0f); - toView.setScaleX(1.0f); - toView.setScaleY(1.0f); - toView.setAlpha(1.0f); - toView.setVisibility(View.VISIBLE); - - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - return; - } - - if (!FeatureFlags.LAUNCHER3_PHYSICS) { - // We are animating the content view alpha, so ensure we have a layer for it. - layerViews.addView(toView); - } - - animation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - cleanupAnimation(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - } - }); - mConfig.reset(); - mAllAppsController.animateToAllApps(animation, mConfig); - mLauncher.getWorkspace().setStateWithAnimation(LauncherState.ALL_APPS, - layerViews, animation, mConfig); - - Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); - mCurrentAnimation = animation; - mCurrentAnimation.addListener(layerViews); - if (mConfig.shouldPost) { - toView.post(startAnimRunnable); - } else { - startAnimRunnable.run(); - } - } - - /** - * Starts an animation to the workspace from the current overlay view. - */ - public void startAnimationToWorkspace(final LauncherState toWorkspaceState, - final boolean animated, final Runnable onCompleteRunnable) { - if (toWorkspaceState != LauncherState.NORMAL && - toWorkspaceState != LauncherState.SPRING_LOADED && - toWorkspaceState != LauncherState.OVERVIEW) { - Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); - } - - if (mLauncher.isInState(LauncherState.ALL_APPS) || mAllAppsController.isTransitioning()) { - startAnimationToWorkspaceFromAllApps(mLauncher.getWorkspace().getState(), - toWorkspaceState, animated, onCompleteRunnable); - } else { - startAnimationToNewWorkspaceState(toWorkspaceState, animated, onCompleteRunnable); - } - } - - /** - * Starts an animation to the workspace from the apps view. - */ - private void startAnimationToWorkspaceFromAllApps(final LauncherState fromWorkspaceState, - final LauncherState toWorkspaceState, boolean animated, - final Runnable onCompleteRunnable) { - final AllAppsContainerView fromView = mLauncher.getAppsView(); - // If for some reason our views aren't initialized, don't animate - animated = animated & (fromView != null); - - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - final AnimationLayerSet layerViews = new AnimationLayerSet(); - - // Cancel the current animation - cancelAnimation(); if (!animated) { - if (fromWorkspaceState == LauncherState.ALL_APPS) { - mAllAppsController.finishPullDown(); - } - fromView.setVisibility(View.GONE); - mLauncher.getWorkspace().setState(toWorkspaceState); + mAllAppsController.setFinalProgress(state.verticalProgress); + mLauncher.getWorkspace().setState(state); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - // Run any queued runnables + // Run any queued runnable if (onCompleteRunnable != null) { onCompleteRunnable.run(); } return; } - animation.addListener(new AnimatorListenerAdapter() { - boolean canceled = false; - @Override - public void onAnimationCancel(Animator animation) { - canceled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (canceled) return; - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - - cleanupAnimation(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - } - - }); - - mConfig.reset(); - mAllAppsController.animateToWorkspace(animation, mConfig); - mLauncher.getWorkspace().setStateWithAnimation(toWorkspaceState, layerViews, animation, - mConfig); - - Runnable startAnimRunnable = new StartAnimRunnable(animation, mLauncher.getWorkspace()); - mCurrentAnimation = animation; - mCurrentAnimation.addListener(layerViews); + AnimatorSet animation = createAnimationToNewWorkspace(state, onCompleteRunnable); + Runnable runnable = new StartAnimRunnable(animation, state.getFinalFocus(mLauncher)); if (mConfig.shouldPost) { - fromView.post(startAnimRunnable); + mUiHandler.post(runnable); } else { - startAnimRunnable.run(); + runnable.run(); } } - /** - * Starts an animation to the workspace from another workspace state, e.g. normal to overview. - */ - private void startAnimationToNewWorkspaceState( - final LauncherState toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { - // Cancel the current animation - cancelAnimation(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - - if (!animated) { - mLauncher.getWorkspace().setState(toWorkspaceState); - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - return; - } - - final AnimatorSet animation = - createAnimationToNewWorkspace(toWorkspaceState, onCompleteRunnable); - mLauncher.getWorkspace().post(new StartAnimRunnable(animation, null)); - mCurrentAnimation = animation; - } - protected AnimatorSet createAnimationToNewWorkspace(LauncherState state, final Runnable onCompleteRunnable) { - cancelAnimation(); + mConfig.reset(); - final AnimationLayerSet layerViews = new AnimationLayerSet(); final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - mConfig.reset(); - mLauncher.getWorkspace().setStateWithAnimation(state, layerViews, animation, mConfig); + final AnimationLayerSet layerViews = new AnimationLayerSet(); + + mAllAppsController.animateToFinalProgress(state.verticalProgress, + state.hasVerticalSpring, animation, mConfig); + mLauncher.getWorkspace().setStateWithAnimation(state, + layerViews, animation, mConfig); - animation.addListener(new AnimatorListenerAdapter() { + animation.addListener(layerViews); + animation.addListener(new AnimationSuccessListener() { @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationSuccess(Animator animator) { // Run any queued runnables if (onCompleteRunnable != null) { onCompleteRunnable.run(); } - // This can hold unnecessary references to views. - cleanupAnimation(); + mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); } }); - animation.addListener(layerViews); - return animation; + mConfig.setAnimation(animation); + return mConfig.mCurrentAnimation; } /** * Cancels the current animation. */ - private void cancelAnimation() { - if (mCurrentAnimation != null) { - mCurrentAnimation.setDuration(0); - mCurrentAnimation.cancel(); - mCurrentAnimation = null; - } - } - - @Thunk void cleanupAnimation() { - mCurrentAnimation = null; + public void cancelAnimation() { + mConfig.reset(); } private class StartAnimRunnable implements Runnable { @@ -305,7 +156,7 @@ public class LauncherStateTransitionAnimation { @Override public void run() { - if (mCurrentAnimation != mAnim) { + if (mConfig.mCurrentAnimation != mAnim) { return; } if (mViewToFocus != null) { @@ -315,14 +166,21 @@ public class LauncherStateTransitionAnimation { } } - public static class AnimationConfig { + public static class AnimationConfig extends AnimatorListenerAdapter { public boolean shouldPost; private long mOverriddenDuration = -1; + private AnimatorSet mCurrentAnimation; public void reset() { shouldPost = false; mOverriddenDuration = -1; + + if (mCurrentAnimation != null) { + mCurrentAnimation.setDuration(0); + mCurrentAnimation.cancel(); + mCurrentAnimation = null; + } } public void overrideDuration(long duration) { @@ -332,5 +190,17 @@ public class LauncherStateTransitionAnimation { public long getDuration(long defaultDuration) { return mOverriddenDuration >= 0 ? mOverriddenDuration : defaultDuration; } + + @Override + public void onAnimationEnd(Animator animation) { + if (mCurrentAnimation == animation) { + mCurrentAnimation = null; + } + } + + public void setAnimation(AnimatorSet animation) { + mCurrentAnimation = animation; + mCurrentAnimation.addListener(this); + } } } diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index 47113c938..407f0b59d 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -107,7 +107,7 @@ public class PinchToOverviewListener mToState = mLauncher.isInState(LauncherState.OVERVIEW) ? LauncherState.NORMAL : LauncherState.OVERVIEW; - mCurrentAnimation = mLauncher.mStateTransitionAnimation + mCurrentAnimation = mLauncher.getStateTransition() .createAnimationToNewWorkspace(mToState, this); mPinchStarted = true; mCurrentScale = 1; diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 9247c54f7..35dfa8122 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -4,10 +4,11 @@ import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.support.animation.SpringAnimation; import android.support.v4.view.animation.FastOutSlowInInterpolator; +import android.util.Property; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateInterpolator; @@ -22,12 +23,13 @@ import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.SpringAnimationHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; @@ -46,6 +48,25 @@ import com.android.launcher3.util.TouchController; public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener, SearchUiManager.OnScrollRangeChangeListener { + private static final Property<AllAppsTransitionController, Float> PROGRESS = + new Property<AllAppsTransitionController, Float>(Float.class, "progress") { + + @Override + public Float get(AllAppsTransitionController controller) { + return controller.mProgress; + } + + @Override + public void set(AllAppsTransitionController controller, Float progress) { + controller.setProgress(progress); + } + }; + + // Spring values used when the user has not flung all apps. + private static final float SPRING_MAX_RELEASE_VELOCITY = 10000; + // The delay (as a % of the animation duration) to start the springs. + private static final float SPRING_DELAY = 0.3f; + private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f); private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f); private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator(); @@ -61,11 +82,8 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect private AllAppsCaretController mCaretController; - private float mStatusBarHeight; - private final Launcher mLauncher; private final SwipeDetector mDetector; - private final ArgbEvaluator mEvaluator; private final boolean mIsDarkTheme; // Animation in this class is controlled by a single variable {@link mProgress}. @@ -87,7 +105,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect private long mAnimationDuration; - private AnimatorSet mCurrentAnimation; private boolean mNoIntercept; private boolean mTouchEventStartedOnHotseat; @@ -105,7 +122,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mShiftRange = DEFAULT_SHIFT_RANGE; mProgress = 1f; - mEvaluator = new ArgbEvaluator(); mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); } @@ -177,10 +193,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect @Override public void onDragStart(boolean start) { mCaretController.onDragStart(); - cancelAnimation(); - mCurrentAnimation = LauncherAnimUtils.createAnimatorSet(); + mLauncher.getStateTransition().cancelAnimation(); + cancelDiscoveryAnimation(); mShiftStart = mAppsView.getTranslationY(); - preparePull(start); + onProgressAnimationStart(); if (hasSpringAnimationHandler()) { mSpringAnimationHandler.skipToEnd(); } @@ -263,16 +279,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect return mDetector.isDraggingOrSettling(); } - /** - * @param start {@code true} if start of new drag. - */ - public void preparePull(boolean start) { - if (start) { - // Initialize values that should not change until #onDragEnd - mStatusBarHeight = mLauncher.getDragLayer().getInsets().top; - mHotseat.setVisibility(View.VISIBLE); - mAppsView.setVisibility(View.VISIBLE); - } + private void onProgressAnimationStart() { + // Initialize values that should not change until #onDragEnd + mHotseat.setVisibility(View.VISIBLE); + mAppsView.setVisibility(View.VISIBLE); } private void updateLightStatusBar(float shift) { @@ -296,7 +306,13 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } /** - * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace + * Note this method should not be called outside this class. This is public because it is used + * in xml-based animations which also handle updating the appropriate UI. + * + * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace + * + * @see #setFinalProgress(float) + * @see #animateToFinalProgress(float, boolean, AnimatorSet, AnimationConfig) */ public void setProgress(float progress) { float shiftPrevious = mProgress * mShiftRange; @@ -344,74 +360,77 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange); } - public void animateToAllApps(AnimatorSet animationOut, AnimationConfig outConfig) { - outConfig.shouldPost = true; - if (animationOut == null) { + /** + * Sets the vertical transition progress to {@param progress} and updates all the dependent UI + * accordingly. + */ + public void setFinalProgress(float progress) { + setProgress(progress); + onProgressAnimationEnd(); + } + + /** + * Creates an animation which updates the vertical transition progress and updates all the + * dependent UI using various animation events + * + * @param progress the final vertical progress at the end of the animation + * @param addSpring should there be an addition spring animation for the sub-views + * @param animationOut the target AnimatorSet where this animation should be added + * @param outConfig an in/out configuration which can be shared with other animations + */ + public void animateToFinalProgress(float progress, boolean addSpring, + AnimatorSet animationOut, AnimationConfig outConfig) { + if (Float.compare(mProgress, progress) == 0) { + // Fail fast + onProgressAnimationEnd(); return; } + + outConfig.shouldPost = true; Interpolator interpolator; if (mDetector.isIdleState()) { - preparePull(true); mAnimationDuration = LauncherAnimUtils.ALL_APPS_TRANSITION_MS; mShiftStart = mAppsView.getTranslationY(); interpolator = mFastOutSlowInInterpolator; } else { mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity)); interpolator = mScrollInterpolator; - float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange; - if (nextFrameProgress >= 0f) { - mProgress = nextFrameProgress; - } + mProgress = Utilities.boundToRange( + mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange, 0f, 1f); outConfig.shouldPost = false; } outConfig.overrideDuration(mAnimationDuration); - ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", - mProgress, 0f); - driftAndAlpha.setDuration(mAnimationDuration); - driftAndAlpha.setInterpolator(interpolator); - animationOut.play(driftAndAlpha); - - animationOut.addListener(new AnimatorListenerAdapter() { - // Spring values used when the user has not flung all apps. - private final float MAX_RELEASE_VELOCITY = 10000; - // The delay (as a % of the animation duration) to start the springs. - private final float DELAY = 0.3f; - - boolean canceled = false; - + ObjectAnimator anim = ObjectAnimator.ofFloat(this, PROGRESS, mProgress, progress); + anim.setDuration(mAnimationDuration); + anim.setInterpolator(interpolator); + anim.addListener(new AnimationSuccessListener() { @Override - public void onAnimationCancel(Animator animation) { - canceled = true; + public void onAnimationSuccess(Animator animator) { + onProgressAnimationEnd(); } @Override public void onAnimationStart(Animator animation) { - // Add springs for cases where the user has not flung. - // ie. clicking on the caret, releasing all apps so it snaps up. - mAppsView.postDelayed(new Runnable() { - @Override - public void run() { - if (!canceled && !mSpringAnimationHandler.isRunning()) { - float velocity = mProgress * MAX_RELEASE_VELOCITY; - mSpringAnimationHandler.animateToPositionWithVelocity(0, 1, velocity); - } - } - }, (long) (mAnimationDuration * DELAY)); + onProgressAnimationStart(); } + }); - @Override - public void onAnimationEnd(Animator animation) { - if (canceled) { - return; - } else { - finishPullUp(); - cleanUpAnimation(); - mDetector.finishedScrolling(); + animationOut.play(anim); + if (addSpring) { + ValueAnimator springAnim = ValueAnimator.ofFloat(0, 1); + springAnim.setDuration((long) (mAnimationDuration * SPRING_DELAY)); + springAnim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (!mSpringAnimationHandler.isRunning()) { + float velocity = mProgress * SPRING_MAX_RELEASE_VELOCITY; + mSpringAnimationHandler.animateToPositionWithVelocity(0, 1, velocity); + } } - } - }); - mCurrentAnimation = animationOut; + }); + animationOut.play(anim); + } } public void showDiscoveryBounce() { @@ -425,12 +444,12 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect @Override public void onAnimationStart(Animator animator) { mIsTranslateWithoutWorkspace = true; - preparePull(true); + onProgressAnimationStart(); } @Override public void onAnimationEnd(Animator animator) { - finishPullDown(); + onProgressAnimationEnd(); mDiscoBounceAnimation = null; mIsTranslateWithoutWorkspace = false; } @@ -447,83 +466,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect }); } - public void animateToWorkspace(AnimatorSet animationOut, AnimationConfig outconfig) { - outconfig.shouldPost = true; - if (animationOut == null) { - return; - } - Interpolator interpolator; - if (mDetector.isIdleState()) { - preparePull(true); - mAnimationDuration = LauncherAnimUtils.ALL_APPS_TRANSITION_MS; - mShiftStart = mAppsView.getTranslationY(); - interpolator = mFastOutSlowInInterpolator; - } else { - mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity)); - interpolator = mScrollInterpolator; - float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange; - if (nextFrameProgress <= 1f) { - mProgress = nextFrameProgress; - } - outconfig.shouldPost = false; - } - - outconfig.overrideDuration(mAnimationDuration); - ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", - mProgress, 1f); - driftAndAlpha.setDuration(mAnimationDuration); - driftAndAlpha.setInterpolator(interpolator); - animationOut.play(driftAndAlpha); - - animationOut.addListener(new AnimatorListenerAdapter() { - boolean canceled = false; - - @Override - public void onAnimationCancel(Animator animation) { - canceled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (canceled) { - return; - } else { - finishPullDown(); - cleanUpAnimation(); - mDetector.finishedScrolling(); - } - } - }); - mCurrentAnimation = animationOut; - } - - public void finishPullUp() { - mHotseat.setVisibility(View.INVISIBLE); - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.remove(mSearchSpring); - mSpringAnimationHandler.reset(); - } - setProgress(0f); - } - - public void finishPullDown() { - mAppsView.setVisibility(View.INVISIBLE); - mHotseat.setVisibility(View.VISIBLE); - mAppsView.reset(); - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.reset(); - } - setProgress(1f); - } - - private void cancelAnimation() { - if (mCurrentAnimation != null) { - mCurrentAnimation.cancel(); - mCurrentAnimation = null; - } - cancelDiscoveryAnimation(); - } - public void cancelDiscoveryAnimation() { if (mDiscoBounceAnimation == null) { return; @@ -532,10 +474,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mDiscoBounceAnimation = null; } - private void cleanUpAnimation() { - mCurrentAnimation = null; - } - public void setupViews(AllAppsContainerView appsView, Hotseat hotseat, Workspace workspace) { mAppsView = appsView; mHotseat = hotseat; @@ -557,4 +495,27 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mShiftRange = scrollRange; setProgress(mProgress); } + + /** + * Set the final view states based on the progress. + * TODO: This logic should go in {@link LauncherState} + */ + private void onProgressAnimationEnd() { + if (Float.compare(mProgress, 1f) == 0) { + mAppsView.setVisibility(View.INVISIBLE); + mHotseat.setVisibility(View.VISIBLE); + mAppsView.reset(); + } else if (Float.compare(mProgress, 0f) == 0) { + mHotseat.setVisibility(View.INVISIBLE); + mAppsView.setVisibility(View.VISIBLE); + } + if (hasSpringAnimationHandler()) { + mSpringAnimationHandler.remove(mSearchSpring); + mSpringAnimationHandler.reset(); + } + + // TODO: This call should no longer be needed once caret stops animating. + setProgress(mProgress); + mDetector.finishedScrolling(); + } } diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java new file mode 100644 index 000000000..feebc6c5d --- /dev/null +++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 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.anim; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; + +/** + * Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations + */ +public abstract class AnimationSuccessListener extends AnimatorListenerAdapter { + + private boolean mCancelled = false; + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancelled) { + onAnimationSuccess(animation); + } + } + + public abstract void onAnimationSuccess(Animator animator); +} diff --git a/src/com/android/launcher3/states/AllAppsState.java b/src/com/android/launcher3/states/AllAppsState.java new file mode 100644 index 000000000..9922d999c --- /dev/null +++ b/src/com/android/launcher3/states/AllAppsState.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 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.states; + +import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; + +import android.view.View; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; + +/** + * Definition for AllApps state + */ +public class AllAppsState extends LauncherState { + + public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; + + private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_HAS_SPRING; + + public AllAppsState(int id) { + super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS); + } + + @Override + public void onStateEnabled(Launcher launcher) { + if (!launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)) { + launcher.getSharedPrefs().edit().putBoolean(APPS_VIEW_SHOWN, true).apply(); + } + } + + @Override + public View getFinalFocus(Launcher launcher) { + return launcher.getAppsView(); + } +} diff --git a/src/com/android/launcher3/states/OverviewState.java b/src/com/android/launcher3/states/OverviewState.java index 911cec62f..57f023cc6 100644 --- a/src/com/android/launcher3/states/OverviewState.java +++ b/src/com/android/launcher3/states/OverviewState.java @@ -19,6 +19,7 @@ import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.Utilities.isAccessibilityEnabled; import android.graphics.Rect; +import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import com.android.launcher3.DeviceProfile; @@ -39,7 +40,7 @@ public class OverviewState extends LauncherState { FLAG_DO_NOT_RESTORE; public OverviewState(int id) { - super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS); + super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS); } @Override @@ -75,4 +76,9 @@ public class OverviewState extends LauncherState { public void onStateDisabled(Launcher launcher) { launcher.getWorkspace().setPageRearrangeEnabled(false); } + + @Override + public View getFinalFocus(Launcher launcher) { + return launcher.getOverviewPanel(); + } } diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java index f60ef0ca1..3864e3a85 100644 --- a/src/com/android/launcher3/states/SpringLoadedState.java +++ b/src/com/android/launcher3/states/SpringLoadedState.java @@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_M import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.os.Handler; +import android.view.View; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InstallShortcutReceiver; @@ -41,7 +42,7 @@ public class SpringLoadedState extends LauncherState { private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500; public SpringLoadedState(int id) { - super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS); + super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, 1f, STATE_FLAGS); } @Override @@ -105,4 +106,9 @@ public class SpringLoadedState extends LauncherState { InstallShortcutReceiver.disableAndFlushInstallQueue( InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher); } + + @Override + public View getFinalFocus(Launcher launcher) { + return null; + } } |