summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Miranda <jonmiranda@google.com>2017-04-20 12:07:38 -0700
committerJon Miranda <jonmiranda@google.com>2017-05-23 10:02:51 -0700
commit4e11c2738a346fe2859c2d03e3133671e5da49ec (patch)
tree5570fa051502b27134263092c4030bb4102c040b
parent09273b676e9659890651459c11bda73d67e925c0 (diff)
downloadandroid_packages_apps_Trebuchet-4e11c2738a346fe2859c2d03e3133671e5da49ec.tar.gz
android_packages_apps_Trebuchet-4e11c2738a346fe2859c2d03e3133671e5da49ec.tar.bz2
android_packages_apps_Trebuchet-4e11c2738a346fe2859c2d03e3133671e5da49ec.zip
Add physics motion to items in all apps.
Motion applied to: - Icons - Prediction icons - Prediction divider Bug: 38349031 Change-Id: I376e6e39080c8c80463a0ce8b104b05e4d576f17
-rw-r--r--Android.mk3
-rw-r--r--res/layout/all_apps.xml1
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java47
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java39
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java16
-rw-r--r--src/com/android/launcher3/allapps/AllAppsTransitionController.java23
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java7
-rw-r--r--src/com/android/launcher3/anim/SpringAnimationHandler.java247
-rw-r--r--src_flags/com/android/launcher3/config/FeatureFlags.java2
9 files changed, 379 insertions, 6 deletions
diff --git a/Android.mk b/Android.mk
index 6cb40c51a..c8a53d23d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,7 +26,8 @@ LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
android-support-v7-recyclerview \
- android-support-v7-palette
+ android-support-v7-palette \
+ android-support-dynamic-animation
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index f3539dcfa..09b965544 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -51,6 +51,7 @@
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|top"
android:clipToPadding="false"
+ android:overScrollMode="never"
android:descendantFocusability="afterDescendants"
android:focusable="true"
android:paddingStart="@dimen/container_fastscroll_thumb_max_width"
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index d9ee2c55a..a399d748a 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,6 +20,8 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.InsetDrawable;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -42,6 +44,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
@@ -63,7 +66,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
private final Launcher mLauncher;
private final AlphabeticalAppsList mApps;
private final AllAppsGridAdapter mAdapter;
- private final RecyclerView.LayoutManager mLayoutManager;
+ private final LinearLayoutManager mLayoutManager;
private AllAppsRecyclerView mAppsRecyclerView;
private SearchUiManager mSearchUiManager;
@@ -74,6 +77,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
+ private SpringAnimationHandler mSpringAnimationHandler;
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -87,7 +92,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mLauncher = Launcher.getLauncher(context);
mApps = new AlphabeticalAppsList(context);
- mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
+ mSpringAnimationHandler = new SpringAnimationHandler(SpringAnimationHandler.Y_DIRECTION);
+ mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this,
+ mSpringAnimationHandler);
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mSearchQueryBuilder = new SpannableStringBuilder();
@@ -227,6 +234,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
+ if (FeatureFlags.LAUNCHER3_PHYSICS) {
+ mAppsRecyclerView.setSpringAnimationHandler(mSpringAnimationHandler);
+ mAppsRecyclerView.addOnScrollListener(new SpringMotionOnScrollListener());
+ }
mSearchContainer = findViewById(R.id.search_container);
mSearchUiManager = (SearchUiManager) mSearchContainer;
@@ -404,4 +415,36 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
}
}
}
+
+ public SpringAnimationHandler getSpringAnimationHandler() {
+ return mSpringAnimationHandler;
+ }
+
+ public class SpringMotionOnScrollListener extends RecyclerView.OnScrollListener {
+
+ private int mScrollState = RecyclerView.SCROLL_STATE_IDLE;
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
+ mSpringAnimationHandler.skipToEnd();
+ return;
+ }
+
+ int first = mLayoutManager.findFirstVisibleItemPosition();
+ int last = mLayoutManager.findLastVisibleItemPosition();
+
+ // We only show the spring animation when at the top or bottom, so we wait until the
+ // first or last row is visible to ensure that all animations run in sync.
+ if (first == 0 || last >= mAdapter.getItemCount() - mAdapter.getNumAppsPerRow()) {
+ mSpringAnimationHandler.animateToFinalPosition(0);
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ mScrollState = newState;
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index cfd04e2e0..d3d23ca24 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -19,6 +19,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Point;
+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,6 +39,8 @@ import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.discovery.AppDiscoveryItemView;
import com.android.launcher3.util.PackageManagerHelper;
@@ -80,6 +83,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
| VIEW_TYPE_PREDICTION_ICON;
public static final int VIEW_TYPE_MASK_CONTENT = VIEW_TYPE_MASK_ICON
| VIEW_TYPE_DISCOVERY_ITEM;
+ public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON
+ | VIEW_TYPE_PREDICTION_DIVIDER;
public interface BindViewCallback {
@@ -90,6 +95,12 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
* ViewHolder for each icon.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
+
+ /**
+ * Springs used for items where isViewType(viewType, VIEW_TYPE_MASK_HAS_SPRINGS) is true.
+ */
+ private SpringAnimation spring;
+
public ViewHolder(View v) {
super(v);
}
@@ -202,8 +213,11 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
+ private SpringAnimationHandler mSpringAnimationHandler;
+
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
- iconClickListener, View.OnLongClickListener iconLongClickListener) {
+ iconClickListener, View.OnLongClickListener iconLongClickListener,
+ SpringAnimationHandler springAnimationHandler) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
@@ -214,6 +228,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mLayoutInflater = LayoutInflater.from(launcher);
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
+ mSpringAnimationHandler = springAnimationHandler;
}
public static boolean isDividerViewType(int viewType) {
@@ -236,6 +251,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mGridLayoutMgr.setSpanCount(appsPerRow);
}
+ public int getNumAppsPerRow() {
+ return mAppsPerRow;
+ }
+
public void setIconFocusListener(OnFocusChangeListener focusListener) {
mIconFocusListener = focusListener;
}
@@ -327,7 +346,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.applyFromApplicationInfo(info);
- icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
break;
case VIEW_TYPE_DISCOVERY_ITEM:
AppDiscoveryAppInfo appDiscoveryAppInfo = (AppDiscoveryAppInfo)
@@ -365,6 +383,23 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
}
@Override
+ public void onViewAttachedToWindow(ViewHolder holder) {
+ int type = holder.getItemViewType();
+ if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
+ holder.spring = mSpringAnimationHandler.add(holder.itemView,
+ holder.getAdapterPosition(), mApps, mAppsPerRow, holder.spring);
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(ViewHolder holder) {
+ int type = holder.getItemViewType();
+ if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
+ holder.spring = mSpringAnimationHandler.remove(holder.spring);
+ }
+ }
+
+ @Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 16b2bd1fc..d76abccd3 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -28,8 +28,8 @@ import android.view.View;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
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.userevent.nano.LauncherLogProto.ContainerType;
@@ -53,6 +53,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
+ private SpringAnimationHandler mSpringAnimationHandler;
+
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -75,6 +77,18 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
R.dimen.all_apps_empty_search_bg_top_offset);
}
+ public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
+ mSpringAnimationHandler = springAnimationHandler;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent 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.
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 7c6ff5120..dd0d23810 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -22,6 +22,7 @@ import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.graphics.GradientView;
@@ -99,6 +100,8 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
private GradientView mGradientView;
private ScrimView mScrimView;
+ private SpringAnimationHandler mSpringAnimationHandler;
+
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
mDetector = new VerticalPullDetector(l);
@@ -161,6 +164,9 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.addMovement(ev);
+ }
return mDetector.onTouchEvent(ev);
}
@@ -179,6 +185,9 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
mCurrentAnimation = LauncherAnimUtils.createAnimatorSet();
mShiftStart = mAppsView.getTranslationY();
preparePull(start);
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.skipToEnd();
+ }
}
@Override
@@ -214,6 +223,9 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
mLauncher.showAppsView(true /* animated */,
false /* updatePredictedApps */,
false /* focusSearchBar */);
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.animateToFinalPosition(0);
+ }
} else {
calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
mLauncher.showWorkspace(true);
@@ -498,6 +510,9 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
public void finishPullUp() {
mHotseat.setVisibility(View.INVISIBLE);
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.reset();
+ }
setProgress(0f);
}
@@ -506,6 +521,9 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
mHotseat.setBackgroundTransparent(false /* transparent */);
mHotseat.setVisibility(View.VISIBLE);
mAppsView.reset();
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.reset();
+ }
setProgress(1f);
}
@@ -537,6 +555,11 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
mCaretController = new AllAppsCaretController(
mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher);
mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
+ mSpringAnimationHandler = mAppsView.getSpringAnimationHandler();
+ }
+
+ private boolean hasSpringAnimationHandler() {
+ return FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null;
}
@Override
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 047441985..b84c6276a 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -228,6 +228,13 @@ public class AlphabeticalAppsList {
}
/**
+ * Returns the predicted apps.
+ */
+ public List<AppInfo> getPredictedApps() {
+ return mPredictedApps;
+ }
+
+ /**
* Returns fast scroller sections of all the current filtered applications.
*/
public List<FastScrollSectionInfo> getFastScrollerSections() {
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
new file mode 100644
index 000000000..5792127d0
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java
@@ -0,0 +1,247 @@
+/*
+ * 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.DynamicAnimation;
+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.Utilities;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+
+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 using physics for X or Y translations.
+ */
+public class SpringAnimationHandler {
+
+ private static final String TAG = "SpringAnimationHandler";
+ private static final boolean DEBUG = false;
+
+ private static final float DEFAULT_MAX_VALUE = 100;
+ private static final float DEFAULT_MIN_VALUE = -DEFAULT_MAX_VALUE;
+
+ private static final float SPRING_DAMPING_RATIO = 0.55f;
+ private static final float MIN_SPRING_STIFFNESS = 580f;
+ private static final float MAX_SPRING_STIFFNESS = 900f;
+
+ @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 mDirection;
+
+ private VelocityTracker mVelocityTracker;
+ private float mCurrentVelocity = 0;
+ private boolean mShouldComputeVelocity = false;
+
+ private ArrayList<SpringAnimation> mAnimations = new ArrayList<>();
+
+ public SpringAnimationHandler(@Direction int direction) {
+ mDirection = direction;
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+
+ public SpringAnimation add(View view, int position, AlphabeticalAppsList apps, int appsPerRow,
+ SpringAnimation recycle) {
+ int numPredictedApps = Math.min(appsPerRow, apps.getPredictedApps().size());
+ int appPosition = getAppPosition(position, numPredictedApps, appsPerRow);
+
+ int col = appPosition % appsPerRow;
+ int row = appPosition / appsPerRow;
+
+ int numTotalRows = apps.getNumAppRows() - 1; // zero offset
+ if (row > (numTotalRows / 2)) {
+ // Mirror the rows so that the top row acts the same as the bottom row.
+ row = Math.abs(numTotalRows - row);
+ }
+
+ // 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.
+ float rowFactor = (1 + row) * 0.5f;
+ float colFactor = getColumnFactor(col, appsPerRow);
+
+ float minValue = DEFAULT_MIN_VALUE * (rowFactor + colFactor);
+ float maxValue = DEFAULT_MAX_VALUE * (rowFactor + colFactor);
+
+ float stiffness = Utilities.boundToRange(MAX_SPRING_STIFFNESS - (row * 50f),
+ MIN_SPRING_STIFFNESS, MAX_SPRING_STIFFNESS);
+
+ SpringAnimation animation = (recycle != null ? recycle : createSpringAnimation(view))
+ .setStartVelocity(mCurrentVelocity)
+ .setMinValue(minValue)
+ .setMaxValue(maxValue);
+ animation.getSpring().setStiffness(stiffness);
+
+ mAnimations.add(animation);
+ return animation;
+ }
+
+ public SpringAnimation remove(SpringAnimation animation) {
+ animation.skipToEnd();
+ mAnimations.remove(animation);
+ return 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) {
+ if (DEBUG) Log.d(TAG, "animateToFinalPosition#computeVelocity=" + mShouldComputeVelocity);
+
+ if (mShouldComputeVelocity) {
+ computeVelocity();
+ setStartVelocity(mCurrentVelocity);
+ }
+
+ int size = mAnimations.size();
+ for (int i = 0; i < size; ++i) {
+ mAnimations.get(i).animateToFinalPosition(position);
+ }
+
+ reset();
+ }
+
+ 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) {
+ mAnimations.get(i).skipToEnd();
+ }
+ }
+
+ public void reset() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ mCurrentVelocity = 0;
+ }
+
+ private void setStartVelocity(float velocity) {
+ int size = mAnimations.size();
+ for (int i = 0; i < size; ++i) {
+ mAnimations.get(i).setStartVelocity(velocity);
+ }
+ }
+
+ private void computeVelocity() {
+ getVelocityTracker().computeCurrentVelocity(300);
+
+ mCurrentVelocity = isVerticalDirection()
+ ? getVelocityTracker().getYVelocity()
+ : getVelocityTracker().getXVelocity();
+ mShouldComputeVelocity = false;
+
+ if (DEBUG) Log.d(TAG, "computeVelocity=" + mCurrentVelocity);
+ }
+
+ private boolean isVerticalDirection() {
+ return mDirection == Y_DIRECTION;
+ }
+
+ private SpringAnimation createSpringAnimation(View view) {
+ DynamicAnimation.ViewProperty property = isVerticalDirection()
+ ? DynamicAnimation.TRANSLATION_Y
+ : DynamicAnimation.TRANSLATION_X;
+
+ return new SpringAnimation(view, property, 0)
+ .setStartValue(1f)
+ .setSpring(new SpringForce(0)
+ .setDampingRatio(SPRING_DAMPING_RATIO));
+ }
+
+ /**
+ * @return The app position is the position of the app in the Adapter if we ignored all other
+ * view types.
+ *
+ * ie. The first predicted app is at position 0, and the first app of all apps is
+ * at {@param appsPerRow}.
+ */
+ private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
+ int appPosition = position;
+ int numDividerViews = 1 + (numPredictedApps == 0 ? 0 : 1);
+
+ int allAppsStartAt = numDividerViews + numPredictedApps;
+ if (numDividerViews == 1 || position < allAppsStartAt) {
+ appPosition -= 1;
+ } else {
+ // We cannot assume that the predicted row will always be full.
+ int numPredictedAppsOffset = appsPerRow - numPredictedApps;
+ appPosition = position + numPredictedAppsOffset - numDividerViews;
+ }
+
+ return appPosition;
+ }
+
+ /**
+ * 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;
+ }
+
+ private VelocityTracker getVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ return mVelocityTracker;
+ }
+}
diff --git a/src_flags/com/android/launcher3/config/FeatureFlags.java b/src_flags/com/android/launcher3/config/FeatureFlags.java
index c0184fa91..2f97a2ba9 100644
--- a/src_flags/com/android/launcher3/config/FeatureFlags.java
+++ b/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -40,6 +40,8 @@ public final class FeatureFlags {
public static boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = true;
// When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps
public static boolean LAUNCHER3_GRADIENT_ALL_APPS = false;
+ // When enabled allows use of physics based motions in the Launcher.
+ public static boolean LAUNCHER3_PHYSICS = false;
// Feature flag to enable moving the QSB on the 0th screen of the workspace.
public static final boolean QSB_ON_FIRST_SCREEN = true;