Using edge effect to trigger spring animation for all apps.
Bug: 72811152
Bug: 72059944
Change-Id: Ied7b51caa2fb48a2fda126d59e4eaf6a35edded3
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
index 541c6bb..a55edfe 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -143,11 +142,6 @@
}
@Override
- protected void initSprings() {
- mSpringHandlers = new SpringAnimationHandler[0];
- }
-
- @Override
protected float getShiftRange() {
return getShiftRange(mLauncher);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index 2695054..c8d75dc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
import android.animation.Animator;
@@ -27,7 +26,6 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.support.animation.SpringAnimation;
import android.util.Log;
import android.view.MotionEvent;
@@ -38,11 +36,9 @@
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -52,8 +48,6 @@
import com.android.launcher3.util.TouchController;
import com.android.quickstep.TouchInteractionService;
-import java.util.ArrayList;
-
/**
* Handles vertical touch gesture on the DragLayer
*/
@@ -112,8 +106,6 @@
// Ratio of transition process [0, 1] to drag displacement (px)
private float mProgressMultiplier;
- private SpringAnimationHandler[] mSpringHandlers;
-
public TwoStepSwipeController(Launcher l) {
mLauncher = l;
mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
@@ -156,29 +148,6 @@
}
}
- private void initSprings() {
- AllAppsContainerView appsView = mLauncher.getAppsView();
-
- SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
- if (handler == null) {
- mSpringHandlers = new SpringAnimationHandler[0];
- return;
- }
-
- ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
- handlers.add(handler);
-
- SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
- if (searchSpring != null) {
- SpringAnimationHandler searchHandler =
- new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
- searchHandler.add(searchSpring, true /* setDefaultValues */);
- handlers.add(searchHandler);
- }
-
- mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
- }
-
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -214,10 +183,6 @@
mDetector.setDetectableScrollConditions(
directionsToDetectScroll, ignoreSlopWhenSettling);
-
- if (mSpringHandlers == null) {
- initSprings();
- }
}
if (mNoIntercept) {
@@ -230,9 +195,6 @@
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.addMovement(ev);
- }
return mDetector.onTouchEvent(ev);
}
@@ -283,10 +245,6 @@
mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
updatePauseDetectorRangeFlag();
}
-
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.skipToEnd();
- }
}
private float getShiftRange() {
@@ -329,13 +287,6 @@
targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
}
- if (fling && targetState == ALL_APPS) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
- h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
- }
- }
-
float endProgress;
if (mDragPauseDetector.isTriggered() && targetState == NORMAL) {
diff --git a/res/layout/all_apps_rv_layout.xml b/res/layout/all_apps_rv_layout.xml
index 3c19f8c..c353b36 100644
--- a/res/layout/all_apps_rv_layout.xml
+++ b/res/layout/all_apps_rv_layout.xml
@@ -22,5 +22,4 @@
android:layout_below="@id/search_container_all_apps"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
- android:focusable="true"
- android:overScrollMode="never" />
+ android:focusable="true" />
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 26922ad..2cd9d1b 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -36,7 +36,6 @@
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.widget.RelativeLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.DeviceProfile;
@@ -50,7 +49,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
@@ -61,11 +59,12 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BottomUserEducationView;
import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.SpringRelativeLayout;
/**
* The all apps view container.
*/
-public class AllAppsContainerView extends RelativeLayout implements DragSource,
+public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
OnLongClickListener, Insettable, OnDeviceProfileChangeListener {
private final Launcher mLauncher;
@@ -119,6 +118,10 @@
// Attach a scrim to be drawn behind all-apps and hotseat
new ColorScrim(this, Themes.getAttrColor(context, R.attr.allAppsScrimColor), DEACCEL_2)
.attach();
+
+ addSpringView(R.id.all_apps_header);
+ addSpringView(R.id.apps_list_view);
+ addSpringView(R.id.all_apps_tabs_view_pager);
}
public AllAppsStore getAppsStore() {
@@ -324,10 +327,6 @@
}
}
- public SpringAnimationHandler getSpringAnimationHandler() {
- return mUsingTabs ? null : mAH[AdapterHolder.MAIN].animationHandler;
- }
-
private void rebindAdapters(boolean showTabs) {
rebindAdapters(showTabs, false /* force */);
}
@@ -470,7 +469,6 @@
public final AllAppsGridAdapter adapter;
final LinearLayoutManager layoutManager;
- final SpringAnimationHandler animationHandler;
final AlphabeticalAppsList appsList;
final Rect padding = new Rect();
AllAppsRecyclerView recyclerView;
@@ -481,22 +479,19 @@
adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
AllAppsContainerView.this, true);
appsList.setAdapter(adapter);
- animationHandler = adapter.getSpringAnimationHandler();
layoutManager = adapter.getLayoutManager();
}
void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) {
appsList.updateItemFilter(matcher);
recyclerView = (AllAppsRecyclerView) rv;
+ recyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
recyclerView.setApps(appsList, mUsingTabs);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
// No animations will occur when changes occur to the items in this RecyclerView.
recyclerView.setItemAnimator(null);
- if (FeatureFlags.LAUNCHER3_PHYSICS && animationHandler != null) {
- recyclerView.setSpringAnimationHandler(animationHandler);
- }
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(recyclerView);
recyclerView.addItemDecoration(focusedItemDecorator);
adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index a61521c..b9443fd 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.support.animation.DynamicAnimation;
-import android.support.animation.SpringAnimation;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -38,11 +36,8 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -71,7 +66,6 @@
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
- public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON;
public interface BindViewCallback {
@@ -195,8 +189,6 @@
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
- private final SpringAnimationHandler<ViewHolder> mSpringAnimationHandler;
-
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
iconClickListener, View.OnLongClickListener iconLongClickListener, boolean springAnim) {
Resources res = launcher.getResources();
@@ -209,21 +201,11 @@
mLayoutInflater = LayoutInflater.from(launcher);
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
- if (FeatureFlags.LAUNCHER3_PHYSICS && springAnim) {
- mSpringAnimationHandler = new SpringAnimationHandler<>(
- SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
- } else {
- mSpringAnimationHandler = null;
- }
mAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
mGridLayoutMgr.setSpanCount(mAppsPerRow);
}
- public SpringAnimationHandler getSpringAnimationHandler() {
- return mSpringAnimationHandler;
- }
-
public static boolean isDividerViewType(int viewType) {
return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
}
@@ -344,22 +326,6 @@
}
@Override
- public void onViewAttachedToWindow(ViewHolder holder) {
- int type = holder.getItemViewType();
- if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- mSpringAnimationHandler.add(holder.itemView, holder);
- }
- }
-
- @Override
- public void onViewDetachedFromWindow(ViewHolder holder) {
- int type = holder.getItemViewType();
- if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- mSpringAnimationHandler.remove(holder.itemView);
- }
- }
-
- @Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
@@ -376,104 +342,4 @@
return item.viewType;
}
- /**
- * Helper class to set the SpringAnimation values for an item in the adapter.
- */
- private class AllAppsSpringAnimationFactory
- implements SpringAnimationHandler.AnimationFactory<ViewHolder> {
- private static final float DEFAULT_MAX_VALUE_PX = 100;
- private static final float DEFAULT_MIN_VALUE_PX = -DEFAULT_MAX_VALUE_PX;
-
- // Damping ratio range is [0, 1]
- private static final float SPRING_DAMPING_RATIO = 0.55f;
-
- // Stiffness is a non-negative number.
- private static final float MIN_SPRING_STIFFNESS = 580f;
- private static final float MAX_SPRING_STIFFNESS = 900f;
-
- // The amount by which each adjacent rows' stiffness will differ.
- private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
-
- // The percentage by which we multiply each row to create the row factor.
- private static final float ROW_PERCENTAGE = 0.3f;
-
- @Override
- public SpringAnimation initialize(ViewHolder vh) {
- return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
- }
-
- /**
- * @param spring A new or recycled SpringAnimation.
- * @param vh The ViewHolder that {@param spring} is related to.
- */
- @Override
- public void update(SpringAnimation spring, ViewHolder vh) {
- int appPosition = vh.getAdapterPosition();
- int col = appPosition % mAppsPerRow;
- int row = appPosition / mAppsPerRow;
-
- int numTotalRows = mApps.getNumAppRows() - 1; // zero-based count
- if (row > (numTotalRows / 2)) {
- // Mirror the rows so that the top row acts the same as the bottom row.
- row = Math.abs(numTotalRows - row);
- }
-
- calculateSpringValues(spring, row, col);
- }
-
- @Override
- public void setDefaultValues(SpringAnimation spring) {
- calculateSpringValues(spring, 0, mAppsPerRow / 2);
- }
-
- /**
- * We manipulate the stiffness, min, and max values based on the items distance to the
- * first row and the items distance to the center column to create the ^-shaped motion
- * effect.
- */
- private void calculateSpringValues(SpringAnimation spring, int row, int col) {
- float rowFactor = (1 + row) * ROW_PERCENTAGE;
- float colFactor = getColumnFactor(col, mAppsPerRow);
-
- float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
- float maxValue = DEFAULT_MAX_VALUE_PX * (rowFactor + colFactor);
-
- float stiffness = Utilities.boundToRange(
- MAX_SPRING_STIFFNESS - (row * ROW_STIFFNESS_COEFFICIENT),
- MIN_SPRING_STIFFNESS,
- MAX_SPRING_STIFFNESS);
-
- spring.setMinValue(minValue)
- .setMaxValue(maxValue)
- .getSpring()
- .setStiffness(stiffness)
- .setDampingRatio(SPRING_DAMPING_RATIO);
- }
-
- /**
- * Increase the column factor as the distance increases between the column and the center
- * column(s).
- */
- private float getColumnFactor(int col, int numCols) {
- float centerColumn = numCols / 2;
- int distanceToCenter = (int) Math.abs(col - centerColumn);
-
- boolean evenNumberOfColumns = numCols % 2 == 0;
- if (evenNumberOfColumns && col < centerColumn) {
- distanceToCenter -= 1;
- }
-
- float factor = 0;
- while (distanceToCenter > 0) {
- if (distanceToCenter == 1) {
- factor += 0.2f;
- } else {
- factor += 0.1f;
- }
- --distanceToCenter;
- }
-
- return factor;
- }
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 4a393c9..a7447b7 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -17,14 +17,12 @@
import static android.view.View.MeasureSpec.UNSPECIFIED;
-import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
-import android.util.Property;
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
@@ -35,12 +33,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
-import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -64,23 +58,6 @@
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
- private SpringAnimationHandler mSpringAnimationHandler;
- private OverScrollHelper mOverScrollHelper;
- private SwipeDetector mPullDetector;
- private float mContentTranslationY = 0;
- public static final Property<AllAppsRecyclerView, Float> CONTENT_TRANS_Y =
- new Property<AllAppsRecyclerView, Float>(Float.class, "appsRecyclerViewContentTransY") {
- @Override
- public Float get(AllAppsRecyclerView allAppsRecyclerView) {
- return allAppsRecyclerView.getContentTranslationY();
- }
-
- @Override
- public void set(AllAppsRecyclerView allAppsRecyclerView, Float y) {
- allAppsRecyclerView.setContentTranslationY(y);
- }
- };
-
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -99,30 +76,9 @@
Resources res = getResources();
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
-
- mOverScrollHelper = new OverScrollHelper();
- mPullDetector = new SwipeDetector(getContext(), mOverScrollHelper, SwipeDetector.VERTICAL);
- mPullDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
-
mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
}
- public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
- if (FeatureFlags.LAUNCHER3_PHYSICS) {
- mSpringAnimationHandler = springAnimationHandler;
- addOnScrollListener(new SpringMotionOnScrollListener());
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent e) {
- mPullDetector.onTouchEvent(e);
- if (FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null) {
- mSpringAnimationHandler.addMovement(e);
- }
- return super.onTouchEvent(e);
- }
-
/**
* Sets the list of apps in this view, used to determine the fastscroll position.
*/
@@ -170,26 +126,6 @@
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- canvas.translate(0, mContentTranslationY);
- super.dispatchDraw(canvas);
- canvas.translate(0, -mContentTranslationY);
- }
-
- public float getContentTranslationY() {
- return mContentTranslationY;
- }
-
- /**
- * Use this method instead of calling {@link #setTranslationY(float)}} directly to avoid drawing
- * on top of other Views.
- */
- public void setContentTranslationY(float y) {
- mContentTranslationY = y;
- invalidate();
- }
-
- @Override
protected boolean verifyDrawable(Drawable who) {
return who == mEmptySearchBackground || super.verifyDrawable(who);
}
@@ -231,8 +167,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
- mPullDetector.onTouchEvent(e);
- boolean result = super.onInterceptTouchEvent(e) || mOverScrollHelper.isInOverScroll();
+ boolean result = super.onInterceptTouchEvent(e);
if (!result && e.getAction() == MotionEvent.ACTION_DOWN
&& mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.setHotspot(e.getX(), e.getY());
@@ -480,114 +415,4 @@
y + mEmptySearchBackground.getIntrinsicHeight());
}
- private class SpringMotionOnScrollListener extends RecyclerView.OnScrollListener {
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (mOverScrollHelper.isInOverScroll()) {
- // OverScroll will handle animating the springs.
- return;
- }
-
- // We only start the spring animation when we hit the top/bottom, to ensure
- // that all of the animations start at the same time.
- if (dy < 0 && !canScrollVertically(-1)) {
- mSpringAnimationHandler.animateToFinalPosition(0, 1);
- } else if (dy > 0 && !canScrollVertically(1)) {
- mSpringAnimationHandler.animateToFinalPosition(0, -1);
- }
- }
- }
-
- private class OverScrollHelper implements SwipeDetector.Listener {
-
- private static final float MAX_RELEASE_VELOCITY = 5000; // px / s
- private static final float MAX_OVERSCROLL_PERCENTAGE = 0.07f;
-
- private boolean mIsInOverScroll;
-
- // We use this value to calculate the actual amount the user has overscrolled.
- private float mFirstDisplacement = 0;
-
- private boolean mAlreadyScrollingUp;
- private int mFirstScrollYOnScrollUp;
-
- @Override
- public void onDragStart(boolean start) {
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- boolean isScrollingUp = displacement > 0;
- if (isScrollingUp) {
- if (!mAlreadyScrollingUp) {
- mFirstScrollYOnScrollUp = getCurrentScrollY();
- mAlreadyScrollingUp = true;
- }
- } else {
- mAlreadyScrollingUp = false;
- }
-
- // Only enter overscroll if the user is interacting with the RecyclerView directly
- // and if one of the following criteria are met:
- // - User scrolls down when they're already at the bottom.
- // - User starts scrolling up, hits the top, and continues scrolling up.
- boolean wasInOverScroll = mIsInOverScroll;
- mIsInOverScroll = !mScrollbar.isDraggingThumb() &&
- ((!canScrollVertically(1) && displacement < 0) ||
- (!canScrollVertically(-1) && isScrollingUp && mFirstScrollYOnScrollUp != 0));
-
- if (wasInOverScroll && !mIsInOverScroll) {
- // Exit overscroll. This can happen when the user is in overscroll and then
- // scrolls the opposite way.
- reset(false /* shouldSpring */);
- } else if (mIsInOverScroll) {
- if (Float.compare(mFirstDisplacement, 0) == 0) {
- // Because users can scroll before entering overscroll, we need to
- // subtract the amount where the user was not in overscroll.
- mFirstDisplacement = displacement;
- }
- float overscrollY = displacement - mFirstDisplacement;
- setContentTranslationY(getDampedOverScroll(overscrollY));
- }
-
- return mIsInOverScroll;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- reset(mIsInOverScroll /* shouldSpring */);
- }
-
- private void reset(boolean shouldSpring) {
- float y = getContentTranslationY();
- if (Float.compare(y, 0) != 0) {
- if (mSpringAnimationHandler != null && shouldSpring) {
- // We calculate our own velocity to give the springs the desired effect.
- float velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY;
- // We want to negate the velocity because we are moving to 0 from -1 due to the
- // downward motion. (y-axis -1 is above 0).
- mSpringAnimationHandler.animateToPositionWithVelocity(0, -1, -velocity);
- }
-
- ObjectAnimator.ofFloat(AllAppsRecyclerView.this,
- AllAppsRecyclerView.CONTENT_TRANS_Y, 0)
- .setDuration(100)
- .start();
- }
- mIsInOverScroll = false;
- mFirstDisplacement = 0;
- mFirstScrollYOnScrollUp = 0;
- mAlreadyScrollingUp = false;
- }
-
- public boolean isInOverScroll() {
- return mIsInOverScroll;
- }
-
- private float getDampedOverScroll(float y) {
- return OverScroll.dampedScroll(y, getHeight());
- }
- }
-
}
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
deleted file mode 100644
index 29a2430..0000000
--- a/src/com/android/launcher3/anim/SpringAnimationHandler.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * 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.support.animation.FloatPropertyCompat;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
-import android.support.annotation.IntDef;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-
-import com.android.launcher3.R;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * Handler class that manages springs for a set of views that should all move based on the same
- * {@link MotionEvent}s.
- *
- * Supports setting either X or Y velocity on the list of springs added to this handler.
- */
-public class SpringAnimationHandler<T> {
-
- private static final String TAG = "SpringAnimationHandler";
- private static final boolean DEBUG = false;
-
- private static final float VELOCITY_DAMPING_FACTOR = 0.175f;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({Y_DIRECTION, X_DIRECTION})
- public @interface Direction {}
- public static final int Y_DIRECTION = 0;
- public static final int X_DIRECTION = 1;
- private int mVelocityDirection;
-
- private VelocityTracker mVelocityTracker;
- private float mCurrentVelocity = 0;
- private boolean mShouldComputeVelocity = false;
-
- private AnimationFactory<T> mAnimationFactory;
-
- private ArrayList<SpringAnimation> mAnimations = new ArrayList<>();
-
- /**
- * @param direction Either {@link #X_DIRECTION} or {@link #Y_DIRECTION}.
- * Determines which direction we use to calculate and set the velocity.
- * @param factory The AnimationFactory is responsible for initializing and updating the
- * SpringAnimations added to this class.
- */
- public SpringAnimationHandler(@Direction int direction, AnimationFactory<T> factory) {
- mVelocityDirection = direction;
- mAnimationFactory = factory;
- }
-
- /**
- * Adds a spring to the list of springs handled by this class.
- * @param spring The new spring to be added.
- * @param setDefaultValues If True, sets the spring to the default
- * {@link AnimationFactory} values.
- */
- public void add(SpringAnimation spring, boolean setDefaultValues) {
- if (setDefaultValues) {
- mAnimationFactory.setDefaultValues(spring);
- }
- spring.setStartVelocity(mCurrentVelocity);
- mAnimations.add(spring);
- }
-
- public AnimationFactory<T> getFactory() {
- return mAnimationFactory;
- }
-
- /**
- * Adds a new or recycled animation to the list of springs handled by this class.
- *
- * @param view The view the spring is attached to.
- * @param object Used to initialize and update the spring.
- */
- public void add(View view, T object) {
- SpringAnimation spring = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
- if (spring == null) {
- spring = mAnimationFactory.initialize(object);
- view.setTag(R.id.spring_animation_tag, spring);
- }
- mAnimationFactory.update(spring, object);
- add(spring, false /* setDefaultValues */);
- }
-
- /**
- * Stops and removes the spring attached to {@param view}.
- */
- public void remove(View view) {
- remove((SpringAnimation) view.getTag(R.id.spring_animation_tag));
- }
-
- public void remove(SpringAnimation animation) {
- if (animation.canSkipToEnd()) {
- animation.skipToEnd();
- }
- mAnimations.remove(animation);
- }
-
- public void addMovement(MotionEvent event) {
- int action = event.getActionMasked();
- if (DEBUG) Log.d(TAG, "addMovement#action=" + action);
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_DOWN:
- reset();
- break;
- }
-
- getVelocityTracker().addMovement(event);
- mShouldComputeVelocity = true;
- }
-
- public void animateToFinalPosition(float position, int startValue) {
- animateToFinalPosition(position, startValue, mShouldComputeVelocity);
- }
-
- /**
- * @param position The final animation position.
- * @param startValue < 0 if scrolling from start to end; > 0 if scrolling from end to start
- * The magnitude of the number changes how the spring will move.
- * @param setVelocity If true, we set the velocity to {@link #mCurrentVelocity} before
- * starting the animation.
- */
- private void animateToFinalPosition(float position, int startValue, boolean setVelocity) {
- if (DEBUG) {
- Log.d(TAG, "animateToFinalPosition#position=" + position + ", startValue=" + startValue);
- }
-
- if (mShouldComputeVelocity) {
- mCurrentVelocity = computeVelocity();
- }
-
- int size = mAnimations.size();
- for (int i = 0; i < size; ++i) {
- mAnimations.get(i).setStartValue(startValue);
- if (setVelocity) {
- mAnimations.get(i).setStartVelocity(mCurrentVelocity);
- }
- mAnimations.get(i).animateToFinalPosition(position);
- }
-
- reset();
- }
-
- /**
- * Similar to {@link #animateToFinalPosition(float, int)}, but used in cases where we want to
- * manually set the velocity.
- */
- public void animateToPositionWithVelocity(float position, int startValue, float velocity) {
- if (DEBUG) {
- Log.d(TAG, "animateToPosition#pos=" + position + ", start=" + startValue
- + ", velocity=" + velocity);
- }
-
- mCurrentVelocity = velocity;
- mShouldComputeVelocity = false;
- animateToFinalPosition(position, startValue, true);
- }
-
-
- public boolean isRunning() {
- // All the animations run at the same time so we can just check the first one.
- return !mAnimations.isEmpty() && mAnimations.get(0).isRunning();
- }
-
- public void skipToEnd() {
- if (DEBUG) Log.d(TAG, "setStartVelocity#skipToEnd");
- if (DEBUG) Log.v(TAG, "setStartVelocity#skipToEnd", new Exception());
-
- int size = mAnimations.size();
- for (int i = 0; i < size; ++i) {
- if (mAnimations.get(i).canSkipToEnd()) {
- mAnimations.get(i).skipToEnd();
- }
- }
- }
-
- public void reset() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mCurrentVelocity = 0;
- mShouldComputeVelocity = false;
- }
-
-
- private float computeVelocity() {
- getVelocityTracker().computeCurrentVelocity(1000 /* millis */);
-
- float velocity = isVerticalDirection()
- ? getVelocityTracker().getYVelocity()
- : getVelocityTracker().getXVelocity();
- velocity *= VELOCITY_DAMPING_FACTOR;
-
- if (DEBUG) Log.d(TAG, "computeVelocity=" + velocity);
- return velocity;
- }
-
- private boolean isVerticalDirection() {
- return mVelocityDirection == Y_DIRECTION;
- }
-
- private VelocityTracker getVelocityTracker() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- return mVelocityTracker;
- }
-
- /**
- * This interface is used to initialize and update the SpringAnimations added to the
- * {@link SpringAnimationHandler}.
- *
- * @param <T> The object that each SpringAnimation is attached to.
- */
- public interface AnimationFactory<T> {
-
- /**
- * Initializes a new Spring for {@param object}.
- */
- SpringAnimation initialize(T object);
-
- /**
- * Updates the value of {@param spring} based on {@param object}.
- */
- void update(SpringAnimation spring, T object);
-
- /**
- * Sets the factory default values for the given {@param spring}.
- */
- void setDefaultValues(SpringAnimation spring);
- }
-
- /**
- * Helper method to create a new SpringAnimation for {@param view}.
- */
- public static SpringAnimation forView(View view, FloatPropertyCompat property, float finalPos) {
- SpringAnimation spring = new SpringAnimation(view, property, finalPos);
- spring.setSpring(new SpringForce(finalPos));
- return spring;
- }
-
-}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index e494bea..28645dc 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -35,8 +35,6 @@
public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
// When enabled the promise icon is visible in all apps while installation an app.
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
- // When enabled allows use of physics based motions in the Launcher.
- public static final boolean LAUNCHER3_PHYSICS = true;
// When enabled allows use of spring motions on the icons.
public static final boolean LAUNCHER3_SPRING_ICONS = true;
diff --git a/src/com/android/launcher3/util/VerticalSwipeController.java b/src/com/android/launcher3/util/VerticalSwipeController.java
index a647378..ae5bfd5 100644
--- a/src/com/android/launcher3/util/VerticalSwipeController.java
+++ b/src/com/android/launcher3/util/VerticalSwipeController.java
@@ -18,12 +18,10 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.support.animation.SpringAnimation;
import android.util.Log;
import android.view.MotionEvent;
@@ -31,13 +29,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.touch.SwipeDetector.Direction;
-import java.util.ArrayList;
/**
* Handles vertical touch gesture on the DragLayer allowing transitioning from
@@ -68,8 +63,6 @@
// Ratio of transition process [0, 1] to drag displacement (px)
private float mProgressMultiplier;
- protected SpringAnimationHandler[] mSpringHandlers;
-
public VerticalSwipeController(Launcher l, LauncherState baseState) {
this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL);
}
@@ -104,29 +97,6 @@
}
}
- protected void initSprings() {
- AllAppsContainerView appsView = mLauncher.getAppsView();
-
- SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
- if (handler == null) {
- mSpringHandlers = new SpringAnimationHandler[0];
- return;
- }
-
- ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
- handlers.add(handler);
-
- SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
- if (searchSpring != null) {
- SpringAnimationHandler searchHandler =
- new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
- searchHandler.add(searchSpring, true /* setDefaultValues */);
- handlers.add(searchHandler);
- }
-
- mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
- }
-
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -155,10 +125,6 @@
mDetector.setDetectableScrollConditions(
directionsToDetectScroll, ignoreSlopWhenSettling);
-
- if (mSpringHandlers == null) {
- initSprings();
- }
}
if (mNoIntercept) {
@@ -173,9 +139,6 @@
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.addMovement(ev);
- }
return mDetector.onTouchEvent(ev);
}
@@ -198,10 +161,6 @@
mCurrentAnimation.pause();
mStartProgress = mCurrentAnimation.getProgressFraction();
}
-
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.skipToEnd();
- }
}
protected boolean isTransitionFlipped() {
@@ -244,13 +203,6 @@
}
}
- if (fling && targetState == mTargetState) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
- h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
- }
- }
-
mCurrentAnimation.setEndAction(() -> {
mLauncher.getStateManager().goToState(targetState, false);
onTransitionComplete(fling, targetState == mToState);
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
new file mode 100644
index 0000000..090b3e6
--- /dev/null
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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.views;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.support.animation.FloatPropertyCompat;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.EdgeEffectFactory;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.View;
+import android.widget.EdgeEffect;
+import android.widget.RelativeLayout;
+
+import static android.support.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+import static android.support.animation.SpringForce.STIFFNESS_LOW;
+import static android.support.animation.SpringForce.STIFFNESS_MEDIUM;
+
+public class SpringRelativeLayout extends RelativeLayout {
+
+ private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2;
+ private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY;
+ private static final float VELOCITY_MULTIPLIER = 0.3f;
+
+ private static final FloatPropertyCompat<SpringRelativeLayout> DAMPED_SCROLL =
+ new FloatPropertyCompat<SpringRelativeLayout>("value") {
+
+ @Override
+ public float getValue(SpringRelativeLayout object) {
+ return object.mDampedScrollShift;
+ }
+
+ @Override
+ public void setValue(SpringRelativeLayout object, float value) {
+ object.setDampedScrollShift(value);
+ }
+ };
+
+ private final SparseBooleanArray mSpringViews = new SparseBooleanArray();
+ private final SpringAnimation mSpring;
+
+ private float mDampedScrollShift = 0;
+
+ public SpringRelativeLayout(Context context) {
+ this(context, null);
+ }
+
+ public SpringRelativeLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0);
+ mSpring.setSpring(new SpringForce(0)
+ .setStiffness(STIFFNESS)
+ .setDampingRatio(DAMPING_RATIO));
+ }
+
+ public void addSpringView(int id) {
+ mSpringViews.put(id, true);
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
+ canvas.translate(0, mDampedScrollShift);
+ boolean result = super.drawChild(canvas, child, drawingTime);
+ canvas.translate(0, -mDampedScrollShift);
+ return result;
+ }
+ return super.drawChild(canvas, child, drawingTime);
+ }
+
+ private void setDampedScrollShift(float shift) {
+ if (shift != mDampedScrollShift) {
+ mDampedScrollShift = shift;
+ invalidate();
+ }
+ }
+
+ private void finishScrollWithVelocity(float velocity) {
+ mSpring.setStartVelocity(velocity);
+ mSpring.setStartValue(mDampedScrollShift);
+ mSpring.start();
+ }
+
+ public EdgeEffectFactory createEdgeEffectFactory() {
+ return new SpringEdgeEffectFactory();
+ }
+
+ private class SpringEdgeEffectFactory extends EdgeEffectFactory {
+
+ @NonNull @Override
+ protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
+ switch (direction) {
+ case DIRECTION_TOP:
+ return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER);
+ case DIRECTION_BOTTOM:
+ return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER);
+ }
+ return super.createEdgeEffect(view, direction);
+ }
+ }
+
+ private class SpringEdgeEffect extends EdgeEffect {
+
+ private final float mVelocityMultiplier;
+
+ private float mDistance;
+
+ public SpringEdgeEffect(Context context, float velocityMultiplier) {
+ super(context);
+ mVelocityMultiplier = velocityMultiplier;
+ }
+
+ @Override
+ public boolean draw(Canvas canvas) {
+ return false;
+ }
+
+ @Override
+ public void onAbsorb(int velocity) {
+ finishScrollWithVelocity(velocity * mVelocityMultiplier);
+ }
+
+ @Override
+ public void onPull(float deltaDistance, float displacement) {
+ mDistance += deltaDistance * (mVelocityMultiplier / 3f);
+ setDampedScrollShift(mDistance * getHeight());
+ }
+
+ @Override
+ public void onRelease() {
+ mDistance = 0;
+ finishScrollWithVelocity(0);
+ }
+ }
+}
\ No newline at end of file