1 /* 2 * Copyright (C) 2007 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 android.view.animation; 18 19 import org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 22 import android.content.Context; 23 import android.content.res.Resources; 24 import android.content.res.Resources.Theme; 25 import android.content.res.XmlResourceParser; 26 import android.content.res.Resources.NotFoundException; 27 import android.util.AttributeSet; 28 import android.util.Xml; 29 import android.os.SystemClock; 30 31 import java.io.IOException; 32 33 /** 34 * Defines common utilities for working with animations. 35 * 36 */ 37 public class AnimationUtils { 38 39 /** 40 * These flags are used when parsing AnimatorSet objects 41 */ 42 private static final int TOGETHER = 0; 43 private static final int SEQUENTIALLY = 1; 44 45 46 /** 47 * Returns the current animation time in milliseconds. This time should be used when invoking 48 * {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more 49 * information about the different available clocks. The clock used by this method is 50 * <em>not</em> the "wall" clock (it is not {@link System#currentTimeMillis}). 51 * 52 * @return the current animation time in milliseconds 53 * 54 * @see android.os.SystemClock 55 */ currentAnimationTimeMillis()56 public static long currentAnimationTimeMillis() { 57 return SystemClock.uptimeMillis(); 58 } 59 60 /** 61 * Loads an {@link Animation} object from a resource 62 * 63 * @param context Application context used to access resources 64 * @param id The resource id of the animation to load 65 * @return The animation object reference by the specified id 66 * @throws NotFoundException when the animation cannot be loaded 67 */ loadAnimation(Context context, int id)68 public static Animation loadAnimation(Context context, int id) 69 throws NotFoundException { 70 71 XmlResourceParser parser = null; 72 try { 73 parser = context.getResources().getAnimation(id); 74 return createAnimationFromXml(context, parser); 75 } catch (XmlPullParserException ex) { 76 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 77 Integer.toHexString(id)); 78 rnf.initCause(ex); 79 throw rnf; 80 } catch (IOException ex) { 81 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 82 Integer.toHexString(id)); 83 rnf.initCause(ex); 84 throw rnf; 85 } finally { 86 if (parser != null) parser.close(); 87 } 88 } 89 createAnimationFromXml(Context c, XmlPullParser parser)90 private static Animation createAnimationFromXml(Context c, XmlPullParser parser) 91 throws XmlPullParserException, IOException { 92 93 return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser)); 94 } 95 createAnimationFromXml(Context c, XmlPullParser parser, AnimationSet parent, AttributeSet attrs)96 private static Animation createAnimationFromXml(Context c, XmlPullParser parser, 97 AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException { 98 99 Animation anim = null; 100 101 // Make sure we are on a start tag. 102 int type; 103 int depth = parser.getDepth(); 104 105 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 106 && type != XmlPullParser.END_DOCUMENT) { 107 108 if (type != XmlPullParser.START_TAG) { 109 continue; 110 } 111 112 String name = parser.getName(); 113 114 if (name.equals("set")) { 115 anim = new AnimationSet(c, attrs); 116 createAnimationFromXml(c, parser, (AnimationSet)anim, attrs); 117 } else if (name.equals("alpha")) { 118 anim = new AlphaAnimation(c, attrs); 119 } else if (name.equals("scale")) { 120 anim = new ScaleAnimation(c, attrs); 121 } else if (name.equals("rotate")) { 122 anim = new RotateAnimation(c, attrs); 123 } else if (name.equals("translate")) { 124 anim = new TranslateAnimation(c, attrs); 125 } else { 126 throw new RuntimeException("Unknown animation name: " + parser.getName()); 127 } 128 129 if (parent != null) { 130 parent.addAnimation(anim); 131 } 132 } 133 134 return anim; 135 136 } 137 138 /** 139 * Loads a {@link LayoutAnimationController} object from a resource 140 * 141 * @param context Application context used to access resources 142 * @param id The resource id of the animation to load 143 * @return The animation object reference by the specified id 144 * @throws NotFoundException when the layout animation controller cannot be loaded 145 */ loadLayoutAnimation(Context context, int id)146 public static LayoutAnimationController loadLayoutAnimation(Context context, int id) 147 throws NotFoundException { 148 149 XmlResourceParser parser = null; 150 try { 151 parser = context.getResources().getAnimation(id); 152 return createLayoutAnimationFromXml(context, parser); 153 } catch (XmlPullParserException ex) { 154 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 155 Integer.toHexString(id)); 156 rnf.initCause(ex); 157 throw rnf; 158 } catch (IOException ex) { 159 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 160 Integer.toHexString(id)); 161 rnf.initCause(ex); 162 throw rnf; 163 } finally { 164 if (parser != null) parser.close(); 165 } 166 } 167 createLayoutAnimationFromXml(Context c, XmlPullParser parser)168 private static LayoutAnimationController createLayoutAnimationFromXml(Context c, 169 XmlPullParser parser) throws XmlPullParserException, IOException { 170 171 return createLayoutAnimationFromXml(c, parser, Xml.asAttributeSet(parser)); 172 } 173 createLayoutAnimationFromXml(Context c, XmlPullParser parser, AttributeSet attrs)174 private static LayoutAnimationController createLayoutAnimationFromXml(Context c, 175 XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { 176 177 LayoutAnimationController controller = null; 178 179 int type; 180 int depth = parser.getDepth(); 181 182 while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 183 && type != XmlPullParser.END_DOCUMENT) { 184 185 if (type != XmlPullParser.START_TAG) { 186 continue; 187 } 188 189 String name = parser.getName(); 190 191 if ("layoutAnimation".equals(name)) { 192 controller = new LayoutAnimationController(c, attrs); 193 } else if ("gridLayoutAnimation".equals(name)) { 194 controller = new GridLayoutAnimationController(c, attrs); 195 } else { 196 throw new RuntimeException("Unknown layout animation name: " + name); 197 } 198 } 199 200 return controller; 201 } 202 203 /** 204 * Make an animation for objects becoming visible. Uses a slide and fade 205 * effect. 206 * 207 * @param c Context for loading resources 208 * @param fromLeft is the object to be animated coming from the left 209 * @return The new animation 210 */ makeInAnimation(Context c, boolean fromLeft)211 public static Animation makeInAnimation(Context c, boolean fromLeft) { 212 Animation a; 213 if (fromLeft) { 214 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_left); 215 } else { 216 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_right); 217 } 218 219 a.setInterpolator(new DecelerateInterpolator()); 220 a.setStartTime(currentAnimationTimeMillis()); 221 return a; 222 } 223 224 /** 225 * Make an animation for objects becoming invisible. Uses a slide and fade 226 * effect. 227 * 228 * @param c Context for loading resources 229 * @param toRight is the object to be animated exiting to the right 230 * @return The new animation 231 */ makeOutAnimation(Context c, boolean toRight)232 public static Animation makeOutAnimation(Context c, boolean toRight) { 233 Animation a; 234 if (toRight) { 235 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_right); 236 } else { 237 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left); 238 } 239 240 a.setInterpolator(new AccelerateInterpolator()); 241 a.setStartTime(currentAnimationTimeMillis()); 242 return a; 243 } 244 245 246 /** 247 * Make an animation for objects becoming visible. Uses a slide up and fade 248 * effect. 249 * 250 * @param c Context for loading resources 251 * @return The new animation 252 */ makeInChildBottomAnimation(Context c)253 public static Animation makeInChildBottomAnimation(Context c) { 254 Animation a; 255 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_child_bottom); 256 a.setInterpolator(new AccelerateInterpolator()); 257 a.setStartTime(currentAnimationTimeMillis()); 258 return a; 259 } 260 261 /** 262 * Loads an {@link Interpolator} object from a resource 263 * 264 * @param context Application context used to access resources 265 * @param id The resource id of the animation to load 266 * @return The animation object reference by the specified id 267 * @throws NotFoundException 268 */ loadInterpolator(Context context, int id)269 public static Interpolator loadInterpolator(Context context, int id) throws NotFoundException { 270 XmlResourceParser parser = null; 271 try { 272 parser = context.getResources().getAnimation(id); 273 return createInterpolatorFromXml(context.getResources(), context.getTheme(), parser); 274 } catch (XmlPullParserException ex) { 275 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 276 Integer.toHexString(id)); 277 rnf.initCause(ex); 278 throw rnf; 279 } catch (IOException ex) { 280 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 281 Integer.toHexString(id)); 282 rnf.initCause(ex); 283 throw rnf; 284 } finally { 285 if (parser != null) parser.close(); 286 } 287 288 } 289 290 /** 291 * Loads an {@link Interpolator} object from a resource 292 * 293 * @param res The resources 294 * @param id The resource id of the animation to load 295 * @return The interpolator object reference by the specified id 296 * @throws NotFoundException 297 * @hide 298 */ loadInterpolator(Resources res, Theme theme, int id)299 public static Interpolator loadInterpolator(Resources res, Theme theme, int id) throws NotFoundException { 300 XmlResourceParser parser = null; 301 try { 302 parser = res.getAnimation(id); 303 return createInterpolatorFromXml(res, theme, parser); 304 } catch (XmlPullParserException ex) { 305 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 306 Integer.toHexString(id)); 307 rnf.initCause(ex); 308 throw rnf; 309 } catch (IOException ex) { 310 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 311 Integer.toHexString(id)); 312 rnf.initCause(ex); 313 throw rnf; 314 } finally { 315 if (parser != null) 316 parser.close(); 317 } 318 319 } 320 createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser)321 private static Interpolator createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser) 322 throws XmlPullParserException, IOException { 323 324 BaseInterpolator interpolator = null; 325 326 // Make sure we are on a start tag. 327 int type; 328 int depth = parser.getDepth(); 329 330 while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 331 && type != XmlPullParser.END_DOCUMENT) { 332 333 if (type != XmlPullParser.START_TAG) { 334 continue; 335 } 336 337 AttributeSet attrs = Xml.asAttributeSet(parser); 338 339 String name = parser.getName(); 340 341 if (name.equals("linearInterpolator")) { 342 interpolator = new LinearInterpolator(); 343 } else if (name.equals("accelerateInterpolator")) { 344 interpolator = new AccelerateInterpolator(res, theme, attrs); 345 } else if (name.equals("decelerateInterpolator")) { 346 interpolator = new DecelerateInterpolator(res, theme, attrs); 347 } else if (name.equals("accelerateDecelerateInterpolator")) { 348 interpolator = new AccelerateDecelerateInterpolator(); 349 } else if (name.equals("cycleInterpolator")) { 350 interpolator = new CycleInterpolator(res, theme, attrs); 351 } else if (name.equals("anticipateInterpolator")) { 352 interpolator = new AnticipateInterpolator(res, theme, attrs); 353 } else if (name.equals("overshootInterpolator")) { 354 interpolator = new OvershootInterpolator(res, theme, attrs); 355 } else if (name.equals("anticipateOvershootInterpolator")) { 356 interpolator = new AnticipateOvershootInterpolator(res, theme, attrs); 357 } else if (name.equals("bounceInterpolator")) { 358 interpolator = new BounceInterpolator(); 359 } else if (name.equals("pathInterpolator")) { 360 interpolator = new PathInterpolator(res, theme, attrs); 361 } else { 362 throw new RuntimeException("Unknown interpolator name: " + parser.getName()); 363 } 364 } 365 return interpolator; 366 } 367 } 368