summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/android/launcher3/Launcher.java34
-rw-r--r--src/com/android/launcher3/LauncherState.java35
-rw-r--r--src/com/android/launcher3/LauncherStateTransitionAnimation.java236
-rw-r--r--src/com/android/launcher3/PinchToOverviewListener.java2
-rw-r--r--src/com/android/launcher3/allapps/AllAppsTransitionController.java261
-rw-r--r--src/com/android/launcher3/anim/AnimationSuccessListener.java42
-rw-r--r--src/com/android/launcher3/states/AllAppsState.java50
-rw-r--r--src/com/android/launcher3/states/OverviewState.java8
-rw-r--r--src/com/android/launcher3/states/SpringLoadedState.java8
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;
+ }
}