Work on QS animation curves

Bug: 27201532
Change-Id: Ib59b12c57f60c7fc08fbf2b3e63238c6d56efaf1
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 6137349..fcc7b24 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,11 +14,11 @@
 
 package com.android.systemui.qs;
 
+import android.graphics.Path;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnLayoutChangeListener;
-import android.view.animation.PathInterpolator;
 import android.widget.TextView;
 import com.android.systemui.qs.PagedTileLayout.PageListener;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -40,9 +40,6 @@
     private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
     private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
 
-    public static final PathInterpolator TRANSLATION_Y_INTERPOLATOR =
-            new PathInterpolator(.1f, .3f, 1, 1);
-
     public static final float EXPANDED_TILE_DELAY = .7f;
 
     private final ArrayList<View> mAllViews = new ArrayList<>();
@@ -56,7 +53,7 @@
     private boolean mOnFirstPage = true;
     private TouchAnimator mFirstPageAnimator;
     private TouchAnimator mFirstPageDelayedAnimator;
-    private TouchAnimator mTranslationYAnimator;
+    private TouchAnimator mTranslationAnimator;
     private TouchAnimator mNonfirstPageAnimator;
 
     private boolean mOnKeyguard;
@@ -129,6 +126,7 @@
 
     private void updateAnimators() {
         TouchAnimator.Builder firstPageBuilder = new Builder();
+        TouchAnimator.Builder translationXBuilder = new Builder();
         TouchAnimator.Builder translationYBuilder = new Builder();
         TouchAnimator.Builder firstPageDelayedBuilder = new Builder();
         Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
@@ -138,7 +136,6 @@
         int lastYDiff = 0;
         firstPageDelayedBuilder.setStartDelay(EXPANDED_TILE_DELAY);
         firstPageBuilder.setListener(this);
-        translationYBuilder.setInterpolator(TRANSLATION_Y_INTERPOLATOR);
         // Fade in the tiles/labels as we reach the final position.
         firstPageDelayedBuilder.addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1);
         mAllViews.clear();
@@ -158,7 +155,7 @@
                 final int yDiff = loc2[1] - loc1[1];
                 lastYDiff = yDiff;
                 // Move the quick tile right from its location to the new one.
-                firstPageBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
+                translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
                 translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
 
                 // Counteract the parent translation on the tile. So we have a static base to
@@ -167,7 +164,7 @@
 
                 // Move the real tile's label from the quick tile position to its final
                 // location.
-                firstPageBuilder.addFloat(label, "translationX", -xDiff, 0);
+                translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
                 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
 
                 mTopFiveQs.add(tileIcon);
@@ -188,7 +185,13 @@
         if (mAllowFancy) {
             mFirstPageAnimator = firstPageBuilder.build();
             mFirstPageDelayedAnimator = firstPageDelayedBuilder.build();
-            mTranslationYAnimator = translationYBuilder.build();
+            Path path = new Path();
+            path.moveTo(0, 0);
+            path.cubicTo(0, 0, 0, 1, 1, 1);
+            mTranslationAnimator = new TouchAnimator.Builder()
+                    .addPath(translationXBuilder.build(), translationYBuilder.build(),
+                            "position", "position", path)
+                    .build();
         }
         mNonfirstPageAnimator = new TouchAnimator.Builder()
                 .addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -226,7 +229,7 @@
             mQuickQsPanel.setAlpha(1);
             mFirstPageAnimator.setPosition(position);
             mFirstPageDelayedAnimator.setPosition(position);
-            mTranslationYAnimator.setPosition(position);
+            mTranslationAnimator.setPosition(position);
         } else {
             mNonfirstPageAnimator.setPosition(position);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 35ade58..db17245 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -14,7 +14,11 @@
 
 package com.android.systemui.qs;
 
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.util.FloatProperty;
 import android.util.MathUtils;
+import android.util.Pair;
 import android.util.Property;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -74,6 +78,19 @@
         }
     }
 
+    private static final FloatProperty<TouchAnimator> POSITION =
+            new FloatProperty<TouchAnimator>("position") {
+        @Override
+        public void setValue(TouchAnimator touchAnimator, float value) {
+            touchAnimator.setPosition(value);
+        }
+
+        @Override
+        public Float get(TouchAnimator touchAnimator) {
+            return touchAnimator.mLastT;
+        }
+    };
+
     public static class ListenerAdapter implements Listener {
         @Override
         public void onAnimationAtStart() { }
@@ -124,6 +141,19 @@
             return this;
         }
 
+        public Builder addPath(Object target, String xProp, String yProp,
+                Path path) {
+            return addPath(target, target, xProp, yProp, path);
+        }
+
+        public Builder addPath(Object xTarget, Object yTarget, String xProp, String yProp,
+                Path path) {
+            add(new Pair<>(xTarget, yTarget),
+                    KeyframeSet.ofPath(getProperty(xTarget, xProp, float.class),
+                    getProperty(yTarget, yProp, float.class), path));
+            return this;
+        }
+
         private void add(Object target, KeyframeSet keyframeSet) {
             mTargets.add(target);
             mValues.add(keyframeSet);
@@ -152,6 +182,9 @@
                         return View.SCALE_Y;
                 }
             }
+            if (target instanceof TouchAnimator && "position".equals(property)) {
+                return POSITION;
+            }
             return Property.of(target.getClass(), cls, property);
         }
 
@@ -208,6 +241,10 @@
         public static KeyframeSet ofFloat(Property property, float... values) {
             return new FloatKeyframeSet((Property<?, Float>) property, values);
         }
+
+        public static KeyframeSet ofPath(Property xProp, Property yProp, Path path) {
+            return new PathKeyframeSet<>(xProp, yProp, path);
+        }
     }
 
     private static class FloatKeyframeSet<T> extends KeyframeSet {
@@ -245,4 +282,31 @@
             mProperty.set((T) target, (int) (firstFloat + (secondFloat - firstFloat) * amount));
         }
     }
+
+    private static class PathKeyframeSet<T> extends KeyframeSet {
+        private final Property<T, Float> mXProp;
+        private final Property<T, Float> mYProp;
+        private final Path mPath;
+        private final PathMeasure mPathMeasure;
+        private final float mLength;
+        private final float[] mPos;
+
+        public PathKeyframeSet(Property<T, Float> xProp, Property<T, Float> yProp, Path path) {
+            super(2);
+            mXProp = xProp;
+            mYProp = yProp;
+            mPath = path;
+            mPathMeasure = new PathMeasure(mPath, false);
+            mLength = mPathMeasure.getLength();
+            mPos = new float[2];
+        }
+
+        @Override
+        protected void interpolate(int index, float amount, Object target) {
+            Pair<Object, Object> targets = (Pair<Object, Object>) target;
+            mPathMeasure.getPosTan(amount * mLength, mPos, null);
+            mXProp.set((T) targets.first, mPos[0]);
+            mYProp.set((T) targets.second, mPos[1]);
+        }
+    }
 }