Implement overshoot and squish motion in all apps open
Bug: 187475924
Bug: 183062683
Test: manual
Change-Id: I33b6c647c45ff467c6d49cf3796f92ca366ab3f1
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3267a5d..65c4c80 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -84,7 +84,7 @@
<dimen name="fastscroll_end_margin">-26dp</dimen>
<!-- All Apps -->
- <dimen name="all_apps_open_vertical_translate">300dp</dimen>
+ <dimen name="all_apps_open_vertical_translate">320dp</dimen>
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
<dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5ecdca6..7b67807 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -905,7 +905,7 @@
} else {
mOverlayManager.onActivityStopped(this);
}
-
+ hideKeyboard();
logStopAndResume(false /* isResume */);
mAppWidgetHost.setActivityStarted(false);
NotificationListener.removeNotificationsChangedListener();
@@ -1459,7 +1459,7 @@
&& AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this, intent);
-
+ hideKeyboard();
if (isActionMain) {
if (!internalStateHandled) {
// In all these cases, only animate if we're already on home
@@ -1481,9 +1481,6 @@
}
}
- // Handle HOME_INTENT
- hideKeyboard();
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent(internalStateHandled);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 47236b6..d9c8c96 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -45,6 +45,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.ColorUtils;
import androidx.core.os.BuildCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
@@ -79,10 +80,11 @@
Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener,
ScrimView.ScrimDrawingController {
- private static final float FLING_VELOCITY_MULTIPLIER = 1000f;
+ public static final float PULL_MULTIPLIER = .02f;
+ public static final float FLING_VELOCITY_MULTIPLIER = 2000f;
// Starts the springs after at least 25% of the animation has passed.
- private static final float FLING_ANIMATION_THRESHOLD = 0.25f;
+ public static final float FLING_ANIMATION_THRESHOLD = 0.25f;
private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -100,6 +102,7 @@
private AllAppsPagedView mViewPager;
protected FloatingHeaderView mHeader;
+ private float mHeaderTop;
private WorkModeSwitch mWorkModeSwitch;
@@ -530,7 +533,7 @@
return view.getGlobalVisibleRect(new Rect());
}
- // Used by tests only
+ @VisibleForTesting
public boolean isPersonalTabVisible() {
return isDescendantViewVisible(R.id.tab_personal);
}
@@ -582,6 +585,7 @@
mAH[i].padding.top = padding;
mAH[i].applyPadding();
}
+ mHeaderTop = mHeader.getTop();
}
public void setLastSearchQuery(String query) {
@@ -636,14 +640,42 @@
public void onAnimationUpdate(ValueAnimator valueAnimator) {
if (shouldSpring
&& valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
- absorbSwipeUpVelocity(Math.abs(
- Math.round(velocity * FLING_VELOCITY_MULTIPLIER)));
+ absorbSwipeUpVelocity(Math.max(100, Math.abs(
+ Math.round(velocity * FLING_VELOCITY_MULTIPLIER))));
+ // calculate the velocity of using the not user controlled interpolator
+ // of when the container reach the end.
shouldSpring = false;
}
}
});
}
+ public void onPull(float deltaDistance, float displacement) {
+ absorbPullDeltaDistance(PULL_MULTIPLIER * deltaDistance,
+ PULL_MULTIPLIER * displacement);
+ // ideally, this should be done using EdgeEffect.onPush to create squish effect.
+ // However, until such method is available, launcher to simulate the onPush method.
+ mHeader.setTranslationY(-.5f * mHeaderTop * deltaDistance);
+ getRecyclerViewContainer().setTranslationY(-mHeaderTop * deltaDistance);
+ }
+
+ public void onRelease() {
+ ValueAnimator anim1 = ValueAnimator.ofFloat(1f, 0f);
+ final float floatingHeaderHeight = getFloatingHeaderView().getTranslationY();
+ final float recyclerViewHeight = getRecyclerViewContainer().getTranslationY();
+ anim1.setDuration(200);
+ anim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ getFloatingHeaderView().setTranslationY(
+ ((float) valueAnimator.getAnimatedValue()) * floatingHeaderHeight);
+ getRecyclerViewContainer().setTranslationY(
+ ((float) valueAnimator.getAnimatedValue()) * recyclerViewHeight);
+ }
+ });
+ anim1.start();
+ super.onRelease();
+ }
@Override
public void getDrawingRect(Rect outRect) {
super.getDrawingRect(outRect);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 90f2d7c..75f3149 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -17,10 +17,7 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -158,9 +155,8 @@
return;
}
- Interpolator interpolator = toState.equals(ALL_APPS)
- ? (config.userControlled ? ACCEL_2 : ACCEL_0_75) :
- (config.userControlled ? DEACCEL_2 : DEACCEL);
+ // need to decide depending on the release velocity
+ Interpolator interpolator = (config.userControlled ? LINEAR : DEACCEL_1_7);
Animator anim = createSpringAnimation(mProgress, targetProgress);
anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index d092f11..a086635 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -56,9 +56,8 @@
protected final AnimatorListener mClearStateOnCancelListener =
newCancelListener(this::clearState);
+ private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
- private boolean mNoIntercept;
- private boolean mIsLogContainerSet;
protected int mStartContainerType;
protected LauncherState mStartState;
@@ -67,12 +66,14 @@
protected AnimatorPlaybackController mCurrentAnimation;
protected boolean mGoingBetweenStates = true;
+ private boolean mNoIntercept;
+ private boolean mIsLogContainerSet;
private float mStartProgress;
// Ratio of transition process [0, 1] to drag displacement (px)
private float mProgressMultiplier;
private float mDisplacementShift;
private boolean mCanBlockFling;
- private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
+ private boolean mAllAppsOvershootStarted;
public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
mLauncher = l;
@@ -216,8 +217,15 @@
mFlingBlockCheck.blockFling();
}
}
+ if (mToState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+ mAllAppsOvershootStarted = true;
+ // 1f, value when all apps container hit the top
+ mLauncher.getAppsView().onPull(progress - 1f, progress - 1f);
+ }
+
} else {
mFlingBlockCheck.onEvent();
+
}
return true;
@@ -325,8 +333,15 @@
anim.setFloatValues(startProgress, endProgress);
updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
mCurrentAnimation.dispatchOnStart();
- if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
- mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
+ if (targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+ if (mAllAppsOvershootStarted) {
+
+ mLauncher.getAppsView().onRelease();
+ mAllAppsOvershootStarted = false;
+
+ } else {
+ mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
+ }
}
anim.start();
}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 9701389..8342d3e 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -32,6 +32,7 @@
*/
public class SpringRelativeLayout extends RelativeLayout {
+ // fixed edge at the time force is applied
private final EdgeEffect mEdgeGlowTop;
private final EdgeEffect mEdgeGlowBottom;
@@ -87,6 +88,15 @@
invalidate();
}
+ protected void absorbPullDeltaDistance(float deltaDistance, float displacement) {
+ mEdgeGlowBottom.onPull(deltaDistance, displacement);
+ invalidate();
+ }
+
+ protected void onRelease() {
+ mEdgeGlowBottom.onRelease();
+ }
+
public EdgeEffectFactory createEdgeEffectFactory() {
return new ProxyEdgeEffectFactory();
}