1 /* 2 * Copyright (C) 2017 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 androidx.vectordrawable.graphics.drawable; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; 20 21 import android.content.Context; 22 import android.content.res.Resources.NotFoundException; 23 import android.content.res.XmlResourceParser; 24 import android.os.Build; 25 import android.util.AttributeSet; 26 import android.util.Xml; 27 import android.view.animation.AccelerateDecelerateInterpolator; 28 import android.view.animation.AccelerateInterpolator; 29 import android.view.animation.AnimationUtils; 30 import android.view.animation.AnticipateInterpolator; 31 import android.view.animation.AnticipateOvershootInterpolator; 32 import android.view.animation.BounceInterpolator; 33 import android.view.animation.CycleInterpolator; 34 import android.view.animation.DecelerateInterpolator; 35 import android.view.animation.Interpolator; 36 import android.view.animation.LinearInterpolator; 37 import android.view.animation.OvershootInterpolator; 38 39 import androidx.annotation.AnimRes; 40 import androidx.annotation.RestrictTo; 41 import androidx.core.util.ObjectsCompat; 42 import androidx.interpolator.view.animation.FastOutLinearInInterpolator; 43 import androidx.interpolator.view.animation.FastOutSlowInInterpolator; 44 import androidx.interpolator.view.animation.LinearOutSlowInInterpolator; 45 46 import org.jspecify.annotations.NonNull; 47 import org.xmlpull.v1.XmlPullParser; 48 import org.xmlpull.v1.XmlPullParserException; 49 50 import java.io.IOException; 51 52 /** 53 * Defines common utilities for working with animations. 54 */ 55 @RestrictTo(LIBRARY_GROUP_PREFIX) 56 public class AnimationUtilsCompat { 57 /** 58 * Loads an {@link Interpolator} object from a resource 59 * 60 * @param context Application context used to access resources 61 * @param id The resource id of the animation to load 62 * @return The animation object reference by the specified id 63 */ 64 @SuppressWarnings("UnnecessaryInitCause") // requires API 24+ loadInterpolator(@onNull Context context, @AnimRes int id)65 public static @NonNull Interpolator loadInterpolator(@NonNull Context context, @AnimRes int id) 66 throws NotFoundException { 67 // From API 21, we added path Interpolator . 68 if (Build.VERSION.SDK_INT >= 21) { 69 Interpolator interp = AnimationUtils.loadInterpolator(context, id); 70 ObjectsCompat.requireNonNull(interp, "Failed to parse interpolator, no start tag " 71 + "found"); 72 return interp; 73 } 74 75 XmlResourceParser parser = null; 76 try { 77 // Special treatment for the interpolator introduced at API 21. 78 if (id == AndroidResources.FAST_OUT_LINEAR_IN) { 79 return new FastOutLinearInInterpolator(); 80 } else if (id == AndroidResources.FAST_OUT_SLOW_IN) { 81 return new FastOutSlowInInterpolator(); 82 } else if (id == AndroidResources.LINEAR_OUT_SLOW_IN) { 83 return new LinearOutSlowInInterpolator(); 84 } 85 parser = context.getResources().getAnimation(id); 86 return createInterpolatorFromXml(context, parser); 87 } catch (XmlPullParserException ex) { 88 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" 89 + Integer.toHexString(id)); 90 rnf.initCause(ex); 91 throw rnf; 92 } catch (IOException ex) { 93 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" 94 + Integer.toHexString(id)); 95 rnf.initCause(ex); 96 throw rnf; 97 } finally { 98 if (parser != null) parser.close(); 99 } 100 101 } 102 createInterpolatorFromXml(@onNull Context context, @NonNull XmlPullParser parser)103 private static @NonNull Interpolator createInterpolatorFromXml(@NonNull Context context, 104 @NonNull XmlPullParser parser) throws XmlPullParserException, IOException { 105 106 Interpolator interpolator = null; 107 108 // Make sure we are on a start tag. 109 int type; 110 int depth = parser.getDepth(); 111 112 while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 113 && type != XmlPullParser.END_DOCUMENT) { 114 115 if (type != XmlPullParser.START_TAG) { 116 continue; 117 } 118 119 AttributeSet attrs = Xml.asAttributeSet(parser); 120 121 String name = parser.getName(); 122 123 switch (name) { 124 case "linearInterpolator": 125 interpolator = new LinearInterpolator(); 126 break; 127 case "accelerateInterpolator": 128 interpolator = new AccelerateInterpolator(context, attrs); 129 break; 130 case "decelerateInterpolator": 131 interpolator = new DecelerateInterpolator(context, attrs); 132 break; 133 case "accelerateDecelerateInterpolator": 134 interpolator = new AccelerateDecelerateInterpolator(); 135 break; 136 case "cycleInterpolator": 137 interpolator = new CycleInterpolator(context, attrs); 138 break; 139 case "anticipateInterpolator": 140 interpolator = new AnticipateInterpolator(context, attrs); 141 break; 142 case "overshootInterpolator": 143 interpolator = new OvershootInterpolator(context, attrs); 144 break; 145 case "anticipateOvershootInterpolator": 146 interpolator = new AnticipateOvershootInterpolator(context, attrs); 147 break; 148 case "bounceInterpolator": 149 interpolator = new BounceInterpolator(); 150 break; 151 case "pathInterpolator": 152 interpolator = new PathInterpolatorCompat(context, attrs, parser); 153 break; 154 default: 155 throw new RuntimeException("Unknown interpolator name: " + parser.getName()); 156 } 157 } 158 159 if (interpolator == null) { 160 throw new RuntimeException("Failed to parse interpolator, no start tag found"); 161 } 162 163 return interpolator; 164 } 165 AnimationUtilsCompat()166 private AnimationUtilsCompat() { 167 } 168 } 169