Merge "Swipe up to go home, swipe and hold to go to overview" into ub-launcher3-master
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 9bbe57a..da4a3de 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -15,17 +15,11 @@
*/
package com.android.quickstep;
-import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.systemui.shared.system.ActivityManagerWrapper
.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.PackageManagerWrapper
- .ACTION_PREFERRED_ACTIVITY_CHANGED;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -33,15 +27,9 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Build;
-import android.os.PatternMatcher;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
@@ -56,21 +44,16 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
-import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
-import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.TransformedRect;
import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.TransformedRect;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.TransactionCompat;
-import java.util.ArrayList;
-
/**
* Helper class to handle various atomic commands for switching between Overview.
*/
@@ -85,100 +68,16 @@
private final ActivityManagerWrapper mAM;
private final RecentsModel mRecentsModel;
private final MainThreadExecutor mMainThreadExecutor;
- private final ComponentName mMyHomeComponent;
-
- private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- initOverviewTargets();
- }
- };
- private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- initOverviewTargets();
- }
- };
- private String mUpdateRegisteredPackage;
-
- public Intent overviewIntent;
- public ComponentName overviewComponent;
- private ActivityControlHelper mActivityControlHelper;
+ private final OverviewComponentObserver mOverviewComponentObserver;
private long mLastToggleTime;
- public OverviewCommandHelper(Context context) {
+ public OverviewCommandHelper(Context context, OverviewComponentObserver observer) {
mContext = context;
mAM = ActivityManagerWrapper.getInstance();
mMainThreadExecutor = new MainThreadExecutor();
mRecentsModel = RecentsModel.INSTANCE.get(mContext);
-
- Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(mContext.getPackageName());
- ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
- mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
-
- mContext.registerReceiver(mUserPreferenceChangeReceiver,
- new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
- initOverviewTargets();
- }
-
- private void initOverviewTargets() {
- ComponentName defaultHome = PackageManagerWrapper.getInstance()
- .getHomeActivities(new ArrayList<>());
-
- final String overviewIntentCategory;
- if (defaultHome == null || mMyHomeComponent.equals(defaultHome)) {
- // User default home is same as out home app. Use Overview integrated in Launcher.
- overviewComponent = mMyHomeComponent;
- mActivityControlHelper = new LauncherActivityControllerHelper();
- overviewIntentCategory = Intent.CATEGORY_HOME;
-
- if (mUpdateRegisteredPackage != null) {
- // Remove any update listener as we don't care about other packages.
- mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
- mUpdateRegisteredPackage = null;
- }
- } else {
- // The default home app is a different launcher. Use the fallback Overview instead.
- overviewComponent = new ComponentName(mContext, RecentsActivity.class);
- mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
- overviewIntentCategory = Intent.CATEGORY_DEFAULT;
-
- // User's default home app can change as a result of package updates of this app (such
- // as uninstalling the app or removing the "Launcher" feature in an update).
- // Listen for package updates of this app (and remove any previously attached
- // package listener).
- if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
- if (mUpdateRegisteredPackage != null) {
- mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
- }
-
- mUpdateRegisteredPackage = defaultHome.getPackageName();
- IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
- updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
- updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
- updateReceiver.addDataScheme("package");
- updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
- PatternMatcher.PATTERN_LITERAL);
- mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
- }
- }
-
- overviewIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(overviewIntentCategory)
- .setComponent(overviewComponent)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
-
- public void onDestroy() {
- mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
-
- if (mUpdateRegisteredPackage != null) {
- mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
- mUpdateRegisteredPackage = null;
- }
+ mOverviewComponentObserver = observer;
}
public void onOverviewToggle() {
@@ -200,10 +99,6 @@
UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
}
- public ActivityControlHelper getActivityControlHelper() {
- return mActivityControlHelper;
- }
-
private class ShowRecentsCommand extends RecentsActivityCommand {
@Override
@@ -225,7 +120,7 @@
private boolean mUserEventLogged;
public RecentsActivityCommand() {
- mHelper = getActivityControlHelper();
+ mHelper = mOverviewComponentObserver.getActivityControlHelper();
mCreateTime = SystemClock.elapsedRealtime();
mRunningTaskId = RecentsModel.getRunningTaskId();
@@ -242,8 +137,10 @@
// Start overview
if (!mHelper.switchToRecentsIfVisible(true)) {
mListener = mHelper.createActivityInitListener(this::onActivityReady);
- mListener.registerAndStartActivity(overviewIntent, this::createWindowAnimation,
- mContext, mMainThreadExecutor.getHandler(), RECENTS_LAUNCH_DURATION);
+ mListener.registerAndStartActivity(
+ mOverviewComponentObserver.getOverviewIntent(),
+ this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(),
+ RECENTS_LAUNCH_DURATION);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
new file mode 100644
index 0000000..e119e53
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 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.quickstep;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+
+import static com.android.systemui.shared.system.PackageManagerWrapper
+ .ACTION_PREFERRED_ACTIVITY_CHANGED;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
+import android.os.PatternMatcher;
+
+import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
+import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+import java.util.ArrayList;
+
+/**
+ * Class to keep track of the current overview component based off user preferences and app updates
+ * and provide callers the relevant classes.
+ */
+public final class OverviewComponentObserver {
+ private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateOverviewTargets();
+ }
+ };
+ private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateOverviewTargets();
+ }
+ };
+ private final Context mContext;
+ private final ComponentName mMyHomeComponent;
+ private String mUpdateRegisteredPackage;
+ private ActivityControlHelper mActivityControlHelper;
+ private Intent mOverviewIntent;
+
+ public OverviewComponentObserver(Context context) {
+ mContext = context;
+
+ Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(mContext.getPackageName());
+ ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
+ mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
+
+ mContext.registerReceiver(mUserPreferenceChangeReceiver,
+ new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
+ updateOverviewTargets();
+ }
+
+ /**
+ * Update overview intent and {@link ActivityControlHelper} based off the current launcher home
+ * component.
+ */
+ private void updateOverviewTargets() {
+ ComponentName defaultHome = PackageManagerWrapper.getInstance()
+ .getHomeActivities(new ArrayList<>());
+
+ final String overviewIntentCategory;
+ ComponentName overviewComponent;
+ if (defaultHome == null || mMyHomeComponent.equals(defaultHome)) {
+ // User default home is same as out home app. Use Overview integrated in Launcher.
+ overviewComponent = mMyHomeComponent;
+ mActivityControlHelper = new LauncherActivityControllerHelper();
+ overviewIntentCategory = Intent.CATEGORY_HOME;
+
+ if (mUpdateRegisteredPackage != null) {
+ // Remove any update listener as we don't care about other packages.
+ mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+ mUpdateRegisteredPackage = null;
+ }
+ } else {
+ // The default home app is a different launcher. Use the fallback Overview instead.
+ overviewComponent = new ComponentName(mContext, RecentsActivity.class);
+ mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
+ overviewIntentCategory = Intent.CATEGORY_DEFAULT;
+
+ // User's default home app can change as a result of package updates of this app (such
+ // as uninstalling the app or removing the "Launcher" feature in an update).
+ // Listen for package updates of this app (and remove any previously attached
+ // package listener).
+ if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
+ if (mUpdateRegisteredPackage != null) {
+ mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+ }
+
+ mUpdateRegisteredPackage = defaultHome.getPackageName();
+ IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
+ updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
+ updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
+ updateReceiver.addDataScheme("package");
+ updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
+ PatternMatcher.PATTERN_LITERAL);
+ mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
+ }
+ }
+
+ mOverviewIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(overviewIntentCategory)
+ .setComponent(overviewComponent)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ /**
+ * Clean up any registered receivers.
+ */
+ public void onDestroy() {
+ mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
+
+ if (mUpdateRegisteredPackage != null) {
+ mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+ mUpdateRegisteredPackage = null;
+ }
+ }
+
+ /**
+ * Get the current intent for going to the overview activity.
+ *
+ * @return the overview intent
+ */
+ public Intent getOverviewIntent() {
+ return mOverviewIntent;
+ }
+
+ /**
+ * Get the current activity control helper for managing interactions to the overview activity.
+ *
+ * @return the current activity control helper
+ */
+ public ActivityControlHelper getActivityControlHelper() {
+ return mActivityControlHelper;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 8b6867f..164c9bd 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -183,6 +183,7 @@
private MainThreadExecutor mMainThreadExecutor;
private ISystemUiProxy mISystemUiProxy;
private OverviewCommandHelper mOverviewCommandHelper;
+ private OverviewComponentObserver mOverviewComponentObserver;
private OverviewInteractionState mOverviewInteractionState;
private OverviewCallbacks mOverviewCallbacks;
private TaskOverlayFactory mTaskOverlayFactory;
@@ -198,7 +199,8 @@
mAM = ActivityManagerWrapper.getInstance();
mRecentsModel = RecentsModel.INSTANCE.get(this);
mMainThreadExecutor = new MainThreadExecutor();
- mOverviewCommandHelper = new OverviewCommandHelper(this);
+ mOverviewComponentObserver = new OverviewComponentObserver(this);
+ mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
mMainThreadChoreographer = Choreographer.getInstance();
mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP);
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
@@ -218,7 +220,7 @@
@Override
public void onDestroy() {
mInputConsumer.unregisterInputConsumer();
- mOverviewCommandHelper.onDestroy();
+ mOverviewComponentObserver.onDestroy();
sConnected = false;
super.onDestroy();
}
@@ -250,21 +252,22 @@
if (runningTaskInfo == null && !forceToLauncher) {
return TouchConsumer.NO_OP;
} else if (forceToLauncher ||
- mOverviewCommandHelper.getActivityControlHelper().isResumed()) {
+ mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
return OverviewTouchConsumer.newInstance(
- mOverviewCommandHelper.getActivityControlHelper(), false, mTouchInteractionLog);
+ mOverviewComponentObserver.getActivityControlHelper(), false,
+ mTouchInteractionLog);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
- mOverviewCommandHelper.getActivityControlHelper().isInLiveTileMode()) {
+ mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) {
return OverviewTouchConsumer.newInstance(
- mOverviewCommandHelper.getActivityControlHelper(), false, mTouchInteractionLog,
- false /* waitForWindowAvailable */);
+ mOverviewComponentObserver.getActivityControlHelper(), false,
+ mTouchInteractionLog, false /* waitForWindowAvailable */);
} else {
if (tracker == null) {
tracker = VelocityTracker.obtain();
}
return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
- mOverviewCommandHelper.overviewIntent,
- mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor,
+ mOverviewComponentObserver.getOverviewIntent(),
+ mOverviewComponentObserver.getActivityControlHelper(), mMainThreadExecutor,
mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
mTaskOverlayFactory, mInputConsumer, tracker, mTouchInteractionLog);
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9470635..a6b3a19 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
import android.animation.LayoutTransition;
@@ -25,6 +26,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
import android.provider.Settings;
@@ -121,6 +123,8 @@
protected boolean mIsPageInTransition = false;
+ protected float mSpringOverScrollX;
+
protected boolean mWasInOverscroll = false;
protected int mUnboundedScrollX;
@@ -349,6 +353,11 @@
boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
+
+ if (!isXBeforeFirstPage && !isXAfterLastPage) {
+ mSpringOverScrollX = 0;
+ }
+
if (isXBeforeFirstPage) {
super.scrollTo(mIsRtl ? mMaxScrollX : 0, y);
if (mAllowOverScroll) {
@@ -988,12 +997,35 @@
}
}
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mScroller.isSpringing() && mSpringOverScrollX != 0) {
+ int saveCount = canvas.save();
+
+ canvas.translate(-mSpringOverScrollX, 0);
+ super.dispatchDraw(canvas);
+
+ canvas.restoreToCount(saveCount);
+ } else {
+ super.dispatchDraw(canvas);
+ }
+ }
+
protected void dampedOverScroll(int amount) {
- if (amount == 0) return;
+ mSpringOverScrollX = amount;
+ if (amount == 0) {
+ return;
+ }
int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth());
+ mSpringOverScrollX = overScrollAmount;
+ if (mScroller.isSpringing()) {
+ invalidate();
+ return;
+ }
+
if (amount < 0) {
- super.scrollTo(overScrollAmount, getScrollY());
+ super.scrollTo(amount, getScrollY());
} else {
super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY());
}
@@ -1001,6 +1033,12 @@
}
protected void overScroll(int amount) {
+ mSpringOverScrollX = amount;
+ if (mScroller.isSpringing()) {
+ invalidate();
+ return;
+ }
+
if (amount == 0) return;
if (mFreeScroll && !mScroller.isFinished()) {
@@ -1372,7 +1410,12 @@
// interpolator at zero, ie. 5. We use 4 to make it a little slower.
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- return snapToPage(whichPage, delta, duration);
+ if (QUICKSTEP_SPRINGS.get()) {
+ return snapToPage(whichPage, delta, duration, false, null,
+ velocity * Math.signum(newX - getUnboundedScrollX()), true);
+ } else {
+ return snapToPage(whichPage, delta, duration);
+ }
}
public boolean snapToPage(int whichPage) {
@@ -1397,15 +1440,15 @@
int newX = getScrollForPage(whichPage);
final int delta = newX - getUnboundedScrollX();
- return snapToPage(whichPage, delta, duration, immediate, interpolator);
+ return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false);
}
protected boolean snapToPage(int whichPage, int delta, int duration) {
- return snapToPage(whichPage, delta, duration, false, null);
+ return snapToPage(whichPage, delta, duration, false, null, 0, false);
}
protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
- TimeInterpolator interpolator) {
+ TimeInterpolator interpolator, float velocity, boolean spring) {
if (mFirstLayout) {
setCurrentPage(whichPage);
return false;
@@ -1441,7 +1484,11 @@
mScroller.setInterpolator(mDefaultInterpolator);
}
- mScroller.startScroll(getUnboundedScrollX(), delta, duration);
+ if (spring && QUICKSTEP_SPRINGS.get()) {
+ mScroller.startScrollSpring(getUnboundedScrollX(), delta, duration, velocity);
+ } else {
+ mScroller.startScroll(getUnboundedScrollX(), delta, duration);
+ }
updatePageIndicator();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index eb26961..3438a26 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -996,7 +996,7 @@
@Override
protected void overScroll(int amount) {
- boolean shouldScrollOverlay = mLauncherOverlay != null &&
+ boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 4be8bbc..6ad69d7 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -95,9 +95,8 @@
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
- public static final ToggleableGlobalSettingsFlag ENABLE_TASK_STABILIZER
- = new ToggleableGlobalSettingsFlag("ENABLE_TASK_STABILIZER", false,
- "Stable task list across fast task switches");
+ public static final TogglableFlag ENABLE_TASK_STABILIZER = new TogglableFlag(
+ "ENABLE_TASK_STABILIZER", false, "Stable task list across fast task switches");
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
false, "Enable springs for quickstep animations");
diff --git a/src/com/android/launcher3/util/OverScroller.java b/src/com/android/launcher3/util/OverScroller.java
index d697ece..fc8a138 100644
--- a/src/com/android/launcher3/util/OverScroller.java
+++ b/src/com/android/launcher3/util/OverScroller.java
@@ -26,6 +26,11 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
/**
* Based on {@link android.widget.OverScroller} supporting only 1-d scrolling and with more
* customization options.
@@ -196,6 +201,9 @@
switch (mMode) {
case SCROLL_MODE:
+ if (isSpringing()) {
+ return true;
+ }
long time = AnimationUtils.currentAnimationTimeMillis();
// Any scroller can be used for time, since they were started
// together in scroll mode. We use X here.
@@ -254,6 +262,22 @@
}
/**
+ * Start scrolling using a spring by providing a starting point and the distance to travel.
+ *
+ * @param start Starting scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param delta Distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param duration Duration of the scroll in milliseconds.
+ * @param velocity The starting velocity for the spring in px per ms.
+ */
+ public void startScrollSpring(int start, int delta, int duration, float velocity) {
+ mMode = SCROLL_MODE;
+ mScroller.mState = mScroller.SPRING;
+ mScroller.startScroll(start, delta, duration, velocity);
+ }
+
+ /**
* Call this when you want to 'spring back' into a valid coordinate range.
*
* @param start Starting X coordinate
@@ -354,6 +378,10 @@
return (int) (time - mScroller.mStartTime);
}
+ public boolean isSpringing() {
+ return mScroller.mState == SplineOverScroller.SPRING && !isFinished();
+ }
+
static class SplineOverScroller {
// Initial position
private int mStart;
@@ -397,6 +425,8 @@
// Current state of the animation.
private int mState = SPLINE;
+ private SpringAnimation mSpring;
+
// Constant gravity value, used in the deceleration phase.
private static final float GRAVITY = 2000.0f;
@@ -417,6 +447,20 @@
private static final int SPLINE = 0;
private static final int CUBIC = 1;
private static final int BALLISTIC = 2;
+ private static final int SPRING = 3;
+
+ private static final FloatPropertyCompat<SplineOverScroller> SPRING_PROPERTY =
+ new FloatPropertyCompat<SplineOverScroller>("splineOverScrollerSpring") {
+ @Override
+ public float getValue(SplineOverScroller scroller) {
+ return scroller.mCurrentPosition;
+ }
+
+ @Override
+ public void setValue(SplineOverScroller scroller, float value) {
+ scroller.mCurrentPosition = (int) value;
+ }
+ };
static {
float x_min = 0.0f;
@@ -465,6 +509,9 @@
}
void updateScroll(float q) {
+ if (mState == SPRING) {
+ return;
+ }
mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
}
@@ -495,6 +542,10 @@
}
void startScroll(int start, int distance, int duration) {
+ startScroll(start, distance, duration, 0);
+ }
+
+ void startScroll(int start, int distance, int duration, float velocity) {
mFinished = false;
mCurrentPosition = mStart = start;
@@ -503,12 +554,31 @@
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = duration;
+ if (mState == SPRING) {
+ if (mSpring != null) {
+ mSpring.cancel();
+ }
+ mSpring = new SpringAnimation(this, SPRING_PROPERTY);
+
+ mSpring.setSpring(new SpringForce(mFinal)
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
+ mSpring.setStartVelocity(velocity);
+ mSpring.animateToFinalPosition(mFinal);
+ mSpring.addEndListener((animation, canceled, value, velocity1) -> {
+ finish();
+ mState = SPLINE;
+ mSpring = null;
+ });
+ }
// Unused
mDeceleration = 0.0f;
mVelocity = 0;
}
void finish() {
+ if (mSpring != null && mSpring.isRunning()) mSpring.cancel();
+
mCurrentPosition = mFinal;
// Not reset since WebView relies on this value for fast fling.
// TODO: restore when WebView uses the fast fling implemented in this class.
@@ -518,6 +588,9 @@
void setFinalPosition(int position) {
mFinal = position;
+ if (mState == SPRING && mSpring != null) {
+ mSpring.animateToFinalPosition(mFinal);
+ }
mSplineDistance = mFinal - mStart;
mFinished = false;
}
@@ -722,6 +795,10 @@
* reached.
*/
boolean update() {
+ if (mState == SPRING) {
+ return mFinished;
+ }
+
final long time = AnimationUtils.currentAnimationTimeMillis();
final long currentTime = time - mStartTime;