1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.phone.common.compat;
18 
19 import android.graphics.Path;
20 import android.graphics.PathMeasure;
21 import android.os.Build;
22 import android.view.animation.Interpolator;
23 import android.view.animation.PathInterpolator;
24 
25 public class PathInterpolatorCompat {
create(float controlX1, float controlY1, float controlX2, float controlY2)26     public static Interpolator create(float controlX1, float controlY1,
27             float controlX2, float controlY2) {
28         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
29             return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
30         }
31         return new PathInterpolatorBase(controlX1, controlY1, controlX2, controlY2);
32     }
33 
34     private static class PathInterpolatorBase implements Interpolator {
35         /**
36          * Governs the accuracy of the approximation of the {@link Path}.
37          */
38         private static final float PRECISION = 0.002f;
39 
40         private final float[] mX;
41         private final float[] mY;
42 
PathInterpolatorBase(Path path)43         public PathInterpolatorBase(Path path) {
44             final PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */);
45 
46             final float pathLength = pathMeasure.getLength();
47             final int numPoints = (int) (pathLength / PRECISION) + 1;
48 
49             mX = new float[numPoints];
50             mY = new float[numPoints];
51 
52             final float[] position = new float[2];
53             for (int i = 0; i < numPoints; ++i) {
54                 final float distance = (i * pathLength) / (numPoints - 1);
55                 pathMeasure.getPosTan(distance, position, null /* tangent */);
56 
57                 mX[i] = position[0];
58                 mY[i] = position[1];
59             }
60         }
61 
PathInterpolatorBase(float controlX, float controlY)62         public PathInterpolatorBase(float controlX, float controlY) {
63             this(createQuad(controlX, controlY));
64         }
65 
PathInterpolatorBase(float controlX1, float controlY1, float controlX2, float controlY2)66         public PathInterpolatorBase(float controlX1, float controlY1,
67                                     float controlX2, float controlY2) {
68             this(createCubic(controlX1, controlY1, controlX2, controlY2));
69         }
70 
71         @Override
getInterpolation(float t)72         public float getInterpolation(float t) {
73             if (t <= 0.0f) {
74                 return 0.0f;
75             } else if (t >= 1.0f) {
76                 return 1.0f;
77             }
78 
79             // Do a binary search for the correct x to interpolate between.
80             int startIndex = 0;
81             int endIndex = mX.length - 1;
82             while (endIndex - startIndex > 1) {
83                 int midIndex = (startIndex + endIndex) / 2;
84                 if (t < mX[midIndex]) {
85                     endIndex = midIndex;
86                 } else {
87                     startIndex = midIndex;
88                 }
89             }
90 
91             final float xRange = mX[endIndex] - mX[startIndex];
92             if (xRange == 0) {
93                 return mY[startIndex];
94             }
95 
96             final float tInRange = t - mX[startIndex];
97             final float fraction = tInRange / xRange;
98 
99             final float startY = mY[startIndex];
100             final float endY = mY[endIndex];
101 
102             return startY + (fraction * (endY - startY));
103         }
104 
createQuad(float controlX, float controlY)105         private static Path createQuad(float controlX, float controlY) {
106             final Path path = new Path();
107             path.moveTo(0.0f, 0.0f);
108             path.quadTo(controlX, controlY, 1.0f, 1.0f);
109             return path;
110         }
111 
createCubic(float controlX1, float controlY1, float controlX2, float controlY2)112         private static Path createCubic(float controlX1, float controlY1,
113                                         float controlX2, float controlY2) {
114             final Path path = new Path();
115             path.moveTo(0.0f, 0.0f);
116             path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f);
117             return path;
118         }
119     }
120 
121 }
122 
123