diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java index 541c6bbc61..a55edfe4e7 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; 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; @@ -142,11 +141,6 @@ public class EdgeSwipeController extends VerticalSwipeController implements } } - @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 2695054c23..c8d75dc6f6 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.ALL_APPS; 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.AnimatorListenerAdapter; 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; 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.FloatRange; 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 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter // 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 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter } } - private void initSprings() { - AllAppsContainerView appsView = mLauncher.getAppsView(); - - SpringAnimationHandler handler = appsView.getSpringAnimationHandler(); - if (handler == null) { - mSpringHandlers = new SpringAnimationHandler[0]; - return; - } - - ArrayList 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 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter mDetector.setDetectableScrollConditions( directionsToDetectScroll, ignoreSlopWhenSettling); - - if (mSpringHandlers == null) { - initSprings(); - } } if (mNoIntercept) { @@ -230,9 +195,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter @Override public boolean onControllerTouchEvent(MotionEvent ev) { - for (SpringAnimationHandler h : mSpringHandlers) { - h.addMovement(ev); - } return mDetector.onTouchEvent(ev); } @@ -283,10 +245,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING); updatePauseDetectorRangeFlag(); } - - for (SpringAnimationHandler h : mSpringHandlers) { - h.skipToEnd(); - } } private float getShiftRange() { @@ -329,13 +287,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter 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 3c19f8c5b2..c353b361cf 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 26922ad8d7..2cd9d1bb8f 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -36,7 +36,6 @@ import android.view.MotionEvent; 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.ItemInfo; 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.ItemInfoMatcher; 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 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource, // 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 class AllAppsContainerView extends RelativeLayout implements DragSource, } } - public SpringAnimationHandler getSpringAnimationHandler() { - return mUsingTabs ? null : mAH[AdapterHolder.MAIN].animationHandler; - } - private void rebindAdapters(boolean showTabs) { rebindAdapters(showTabs, false /* force */); } @@ -470,7 +469,6 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource, public final AllAppsGridAdapter adapter; final LinearLayoutManager layoutManager; - final SpringAnimationHandler animationHandler; final AlphabeticalAppsList appsList; final Rect padding = new Rect(); AllAppsRecyclerView recyclerView; @@ -481,22 +479,19 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource, 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 a61521c769..b9443fd73a 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -18,8 +18,6 @@ package com.android.launcher3.allapps; 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.AppInfo; 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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter mSpringAnimationHandler; - public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener, boolean springAnim) { Resources res = launcher.getResources(); @@ -209,21 +201,11 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter( - 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); } @@ -343,22 +325,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter { - 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 4a393c9b9c..a7447b7b8c 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -17,14 +17,12 @@ package com.android.launcher3.allapps; 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.ItemInfo; 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 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine private AllAppsBackgroundDrawable mEmptySearchBackground; private int mEmptySearchBackgroundTopOffset; - private SpringAnimationHandler mSpringAnimationHandler; - private OverScrollHelper mOverScrollHelper; - private SwipeDetector mPullDetector; - private float mContentTranslationY = 0; - public static final Property CONTENT_TRANS_Y = - new Property(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 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine 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. */ @@ -169,26 +125,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine super.onDraw(c); } - @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 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine @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 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine 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 29a2430a56..0000000000 --- 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 { - - 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 mAnimationFactory; - - private ArrayList 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 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 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 The object that each SpringAnimation is attached to. - */ - public interface AnimationFactory { - - /** - * 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 e494beacd2..28645dc73f 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -35,8 +35,6 @@ abstract class BaseFlags { 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 a64737865d..ae5bfd581f 100644 --- a/src/com/android/launcher3/util/VerticalSwipeController.java +++ b/src/com/android/launcher3/util/VerticalSwipeController.java @@ -18,12 +18,10 @@ package com.android.launcher3.util; 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.AbstractFloatingView; 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 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter // 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 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter } } - protected void initSprings() { - AllAppsContainerView appsView = mLauncher.getAppsView(); - - SpringAnimationHandler handler = appsView.getSpringAnimationHandler(); - if (handler == null) { - mSpringHandlers = new SpringAnimationHandler[0]; - return; - } - - ArrayList 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 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter mDetector.setDetectableScrollConditions( directionsToDetectScroll, ignoreSlopWhenSettling); - - if (mSpringHandlers == null) { - initSprings(); - } } if (mNoIntercept) { @@ -173,9 +139,6 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter @Override public boolean onControllerTouchEvent(MotionEvent ev) { - for (SpringAnimationHandler h : mSpringHandlers) { - h.addMovement(ev); - } return mDetector.onTouchEvent(ev); } @@ -198,10 +161,6 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter mCurrentAnimation.pause(); mStartProgress = mCurrentAnimation.getProgressFraction(); } - - for (SpringAnimationHandler h : mSpringHandlers) { - h.skipToEnd(); - } } protected boolean isTransitionFlipped() { @@ -244,13 +203,6 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter } } - 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 0000000000..090b3e6b96 --- /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 DAMPED_SCROLL = + new FloatPropertyCompat("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