diff --git a/packages/SystemUI/src/com/android/systemui/qs/PathInterpolatorBuilder.java b/packages/SystemUI/src/com/android/systemui/qs/PathInterpolatorBuilder.java new file mode 100644 index 0000000000000..b8cb92a1619a6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/PathInterpolatorBuilder.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2016 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.systemui.qs; + +import android.graphics.Path; +import android.view.animation.BaseInterpolator; +import android.view.animation.Interpolator; + +public class PathInterpolatorBuilder { + + // This governs how accurate the approximation of the Path is. + private static final float PRECISION = 0.002f; + + private float[] mX; // x coordinates in the line + private float[] mY; // y coordinates in the line + private float[] mDist; // Cumulative percentage length of the line + + public PathInterpolatorBuilder(Path path) { + initPath(path); + } + + public PathInterpolatorBuilder(float controlX, float controlY) { + initQuad(controlX, controlY); + } + + public PathInterpolatorBuilder(float controlX1, float controlY1, float controlX2, + float controlY2) { + initCubic(controlX1, controlY1, controlX2, controlY2); + } + + private void initQuad(float controlX, float controlY) { + Path path = new Path(); + path.moveTo(0, 0); + path.quadTo(controlX, controlY, 1f, 1f); + initPath(path); + } + + private void initCubic(float x1, float y1, float x2, float y2) { + Path path = new Path(); + path.moveTo(0, 0); + path.cubicTo(x1, y1, x2, y2, 1f, 1f); + initPath(path); + } + + private void initPath(Path path) { + float[] pointComponents = path.approximate(PRECISION); + + int numPoints = pointComponents.length / 3; + if (pointComponents[1] != 0 || pointComponents[2] != 0 + || pointComponents[pointComponents.length - 2] != 1 + || pointComponents[pointComponents.length - 1] != 1) { + throw new IllegalArgumentException("The Path must start at (0,0) and end at (1,1)"); + } + + mX = new float[numPoints]; + mY = new float[numPoints]; + mDist = new float[numPoints]; + float prevX = 0; + float prevFraction = 0; + int componentIndex = 0; + for (int i = 0; i < numPoints; i++) { + float fraction = pointComponents[componentIndex++]; + float x = pointComponents[componentIndex++]; + float y = pointComponents[componentIndex++]; + if (fraction == prevFraction && x != prevX) { + throw new IllegalArgumentException( + "The Path cannot have discontinuity in the X axis."); + } + if (x < prevX) { + throw new IllegalArgumentException("The Path cannot loop back on itself."); + } + mX[i] = x; + mY[i] = y; + if (i > 0) { + float dx = mX[i] - mX[i - 1]; + float dy = mY[i] - mY[i - 1]; + float dist = (float) Math.sqrt(dx * dx + dy * dy); + mDist[i] = mDist[i - 1] + dist; + } + prevX = x; + prevFraction = fraction; + } + // Scale down dist to 0-1. + float max = mDist[mDist.length - 1]; + for (int i = 0; i < numPoints; i++) { + mDist[i] /= max; + } + } + + public Interpolator getXInterpolator() { + return new PathInterpolator(mDist, mX); + } + + public Interpolator getYInterpolator() { + return new PathInterpolator(mDist, mY); + } + + private static class PathInterpolator extends BaseInterpolator { + private final float[] mX; // x coordinates in the line + private final float[] mY; // y coordinates in the line + + private PathInterpolator(float[] xs, float[] ys) { + mX = xs; + mY = ys; + } + + @Override + public float getInterpolation(float t) { + if (t <= 0) { + return 0; + } else if (t >= 1) { + return 1; + } + // Do a binary search for the correct x to interpolate between. + int startIndex = 0; + int endIndex = mX.length - 1; + + while (endIndex - startIndex > 1) { + int midIndex = (startIndex + endIndex) / 2; + if (t < mX[midIndex]) { + endIndex = midIndex; + } else { + startIndex = midIndex; + } + } + + float xRange = mX[endIndex] - mX[startIndex]; + if (xRange == 0) { + return mY[startIndex]; + } + + float tInRange = t - mX[startIndex]; + float fraction = tInRange / xRange; + + float startY = mY[startIndex]; + float endY = mY[endIndex]; + return startY + (fraction * (endY - startY)); + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 3d455993a2215..b611ba3efc359 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -53,7 +53,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private boolean mOnFirstPage = true; private TouchAnimator mFirstPageAnimator; private TouchAnimator mFirstPageDelayedAnimator; - private TouchAnimator mTranslationAnimator; + private TouchAnimator mTranslationXAnimator; + private TouchAnimator mTranslationYAnimator; private TouchAnimator mNonfirstPageAnimator; private boolean mOnKeyguard; @@ -189,10 +190,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha 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(); + PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, 0, 1); + translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator()); + translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator()); + mTranslationXAnimator = translationXBuilder.build(); + mTranslationYAnimator = translationYBuilder.build(); } mNonfirstPageAnimator = new TouchAnimator.Builder() .addFloat(mQuickQsPanel, "alpha", 1, 0) @@ -230,7 +232,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mQuickQsPanel.setAlpha(1); mFirstPageAnimator.setPosition(position); mFirstPageDelayedAnimator.setPosition(position); - mTranslationAnimator.setPosition(position); + mTranslationXAnimator.setPosition(position); + mTranslationYAnimator.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 db1724570476b..37f2528205f70 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java @@ -14,11 +14,8 @@ 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; @@ -141,19 +138,6 @@ public class TouchAnimator { 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); @@ -241,10 +225,6 @@ public class TouchAnimator { public static KeyframeSet ofFloat(Property property, float... values) { return new FloatKeyframeSet((Property) property, values); } - - public static KeyframeSet ofPath(Property xProp, Property yProp, Path path) { - return new PathKeyframeSet<>(xProp, yProp, path); - } } private static class FloatKeyframeSet extends KeyframeSet { @@ -282,31 +262,4 @@ public class TouchAnimator { mProperty.set((T) target, (int) (firstFloat + (secondFloat - firstFloat) * amount)); } } - - private static class PathKeyframeSet extends KeyframeSet { - private final Property mXProp; - private final Property mYProp; - private final Path mPath; - private final PathMeasure mPathMeasure; - private final float mLength; - private final float[] mPos; - - public PathKeyframeSet(Property xProp, Property 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 targets = (Pair) target; - mPathMeasure.getPosTan(amount * mLength, mPos, null); - mXProp.set((T) targets.first, mPos[0]); - mYProp.set((T) targets.second, mPos[1]); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index d50e67aaefbef..553d7f85c8163 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -281,7 +281,7 @@ public class NotificationPanelView extends PanelView implements if (lp.width != panelWidth) { lp.width = panelWidth; lp.gravity = panelGravity; - mQsContainer.setLayoutParams(lp); + mQsDensityContainer.setLayoutParams(lp); mQsContainer.post(mUpdateHeader); }