Home | History | Annotate | Download | only in compat
      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 {
     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 
     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 
     62         public PathInterpolatorBase(float controlX, float controlY) {
     63             this(createQuad(controlX, controlY));
     64         }
     65 
     66         public PathInterpolatorBase(float controlX1, float controlY1,
     67                                     float controlX2, float controlY2) {
     68             this(createCubic(controlX1, controlY1, controlX2, controlY2));
     69         }
     70 
     71         @Override
     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 
    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 
    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