102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette/* 202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Copyright (C) 2015 The Android Open Source Project 302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License"); 502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * you may not use this file except in compliance with the License. 602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * You may obtain a copy of the License at 702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * http://www.apache.org/licenses/LICENSE-2.0 902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 1002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Unless required by applicable law or agreed to in writing, software 1102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS, 1202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * See the License for the specific language governing permissions and 1402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * limitations under the License. 1502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette */ 1602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 1702fc5fef36357467eba22a0ee250a96734daf791Alan Viverettepackage android.graphics.drawable; 1802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 1902fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport org.xmlpull.v1.XmlPullParser; 2002fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport org.xmlpull.v1.XmlPullParserException; 2102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 2202fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.annotation.DrawableRes; 2302fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.annotation.NonNull; 2402fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.annotation.Nullable; 2502fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.content.Context; 2602fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.content.res.Resources; 2702fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.content.res.Resources.Theme; 2802fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.util.AttributeSet; 2902fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport android.view.InflateException; 3002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 3102fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport java.io.IOException; 3202fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport java.lang.reflect.Constructor; 3302fc5fef36357467eba22a0ee250a96734daf791Alan Viveretteimport java.util.HashMap; 3402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 3502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette/** 3602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Instantiates a drawable XML file into its corresponding 3702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * {@link android.graphics.drawable.Drawable} objects. 3802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * <p> 3902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * For performance reasons, inflation relies heavily on pre-processing of 4002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * XML files that is done at build time. Therefore, it is not currently possible 4102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * to use this inflater with an XmlPullParser over a plain XML file at runtime; 4202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * it only works with an XmlPullParser returned from a compiled resource (R. 4302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * <em>something</em> file.) 4402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 4502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @hide Pending API finalization. 4602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette */ 4702fc5fef36357467eba22a0ee250a96734daf791Alan Viverettepublic final class DrawableInflater { 4802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette private static final HashMap<String, Constructor<? extends Drawable>> CONSTRUCTOR_MAP = 4902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette new HashMap<>(); 5002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 5102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette private final Resources mRes; 5202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette private final ClassLoader mClassLoader; 5302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 5402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette /** 5502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Loads the drawable resource with the specified identifier. 5602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 5702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param context the context in which the drawable should be loaded 5802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param id the identifier of the drawable resource 5902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @return a drawable, or {@code null} if the drawable failed to load 6002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette */ 6102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @Nullable 6202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette public static Drawable loadDrawable(@NonNull Context context, @DrawableRes int id) { 6302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return loadDrawable(context.getResources(), context.getTheme(), id); 6402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 6502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 6602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette /** 6702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Loads the drawable resource with the specified identifier. 6802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 6902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param resources the resources from which the drawable should be loaded 7002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param theme the theme against which the drawable should be inflated 7102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param id the identifier of the drawable resource 7202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @return a drawable, or {@code null} if the drawable failed to load 7302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette */ 7402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @Nullable 7502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette public static Drawable loadDrawable( 7602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @NonNull Resources resources, @Nullable Theme theme, @DrawableRes int id) { 7702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return resources.getDrawable(id, theme); 7802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 7902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 8002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette /** 8102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Constructs a new drawable inflater using the specified resources and 8202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * class loader. 8302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 8402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param res the resources used to resolve resource identifiers 8502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param classLoader the class loader used to load custom drawables 8602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @hide 8702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette */ 8802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette public DrawableInflater(@NonNull Resources res, @NonNull ClassLoader classLoader) { 8902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette mRes = res; 9002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette mClassLoader = classLoader; 9102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 9202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 9302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette /** 9402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Inflates a drawable from inside an XML document using an optional 9502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * {@link Theme}. 9602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * <p> 9702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * This method should be called on a parser positioned at a tag in an XML 9802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * document defining a drawable resource. It will attempt to create a 9902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * Drawable from the tag at the current position. 10002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 10102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param name the name of the tag at the current position 10202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param parser an XML parser positioned at the drawable tag 10302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param attrs an attribute set that wraps the parser 10402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @param theme the theme against which the drawable should be inflated, or 10502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * {@code null} to not inflate against a theme 10602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @return a drawable 10702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * 10802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @throws XmlPullParserException 10902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette * @throws IOException 11002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette */ 11102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @NonNull 11202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette public Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser, 11302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @NonNull AttributeSet attrs, @Nullable Theme theme) 11402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette throws XmlPullParserException, IOException { 115caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette // Inner classes must be referenced as Outer$Inner, but XML tag names 116caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette // can't contain $, so the <drawable> tag allows developers to specify 117caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette // the class in an attribute. We'll still run it through inflateFromTag 118caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette // to stay consistent with how LayoutInflater works. 119caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette if (name.equals("drawable")) { 120caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette name = attrs.getAttributeValue(null, "class"); 121caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette if (name == null) { 122caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette throw new InflateException("<drawable> tag must specify class attribute"); 123caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette } 124caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette } 125caca720b176a0dab0a43a20496e676687e8d78f7Alan Viverette 12602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette Drawable drawable = inflateFromTag(name); 12702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette if (drawable == null) { 12802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette drawable = inflateFromClass(name); 12902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 13002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette drawable.inflate(mRes, parser, attrs, theme); 13102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return drawable; 13202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 13302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 13402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @NonNull 13502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @SuppressWarnings("deprecation") 13602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette private Drawable inflateFromTag(@NonNull String name) { 13702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette switch (name) { 13802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "selector": 13902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new StateListDrawable(); 14002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "animated-selector": 14102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new AnimatedStateListDrawable(); 14202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "level-list": 14302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new LevelListDrawable(); 14402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "layer-list": 14502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new LayerDrawable(); 14602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "transition": 14702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new TransitionDrawable(); 14802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "ripple": 14902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new RippleDrawable(); 15002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "color": 15102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new ColorDrawable(); 15202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "shape": 15302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new GradientDrawable(); 15402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "vector": 15502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new VectorDrawable(); 15602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "animated-vector": 15702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new AnimatedVectorDrawable(); 15802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "scale": 15902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new ScaleDrawable(); 16002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "clip": 16102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new ClipDrawable(); 16202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "rotate": 16302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new RotateDrawable(); 16402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "animated-rotate": 16502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new AnimatedRotateDrawable(); 16602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "animation-list": 16702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new AnimationDrawable(); 16802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "inset": 16902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new InsetDrawable(); 17002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "bitmap": 17102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new BitmapDrawable(); 17202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette case "nine-patch": 17302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return new NinePatchDrawable(); 17402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette default: 17502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return null; 17602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 17702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 17802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette 17902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette @NonNull 18002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette private Drawable inflateFromClass(@NonNull String className) { 18102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette try { 18202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette Constructor<? extends Drawable> constructor; 18302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette synchronized (CONSTRUCTOR_MAP) { 18402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette constructor = CONSTRUCTOR_MAP.get(className); 18502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette if (constructor == null) { 18602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette final Class<? extends Drawable> clazz = 18702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette mClassLoader.loadClass(className).asSubclass(Drawable.class); 18802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette constructor = clazz.getConstructor(); 18902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette CONSTRUCTOR_MAP.put(className, constructor); 19002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 19102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 19202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette return constructor.newInstance(); 19302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } catch (NoSuchMethodException e) { 19402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette final InflateException ie = new InflateException( 19502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette "Error inflating class " + className); 19602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette ie.initCause(e); 19702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette throw ie; 19802fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } catch (ClassCastException e) { 19902fc5fef36357467eba22a0ee250a96734daf791Alan Viverette // If loaded class is not a Drawable subclass. 20002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette final InflateException ie = new InflateException( 20102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette "Class is not a Drawable " + className); 20202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette ie.initCause(e); 20302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette throw ie; 20402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } catch (ClassNotFoundException e) { 20502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette // If loadClass fails, we should propagate the exception. 206c725e8d5ea04ffb5821e00db1c6b45cfe2130e05Alan Viverette final InflateException ie = new InflateException( 207c725e8d5ea04ffb5821e00db1c6b45cfe2130e05Alan Viverette "Class not found " + className); 208c725e8d5ea04ffb5821e00db1c6b45cfe2130e05Alan Viverette ie.initCause(e); 209c725e8d5ea04ffb5821e00db1c6b45cfe2130e05Alan Viverette throw ie; 21002fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } catch (Exception e) { 21102fc5fef36357467eba22a0ee250a96734daf791Alan Viverette final InflateException ie = new InflateException( 21202fc5fef36357467eba22a0ee250a96734daf791Alan Viverette "Error inflating class " + className); 21302fc5fef36357467eba22a0ee250a96734daf791Alan Viverette ie.initCause(e); 21402fc5fef36357467eba22a0ee250a96734daf791Alan Viverette throw ie; 21502fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 21602fc5fef36357467eba22a0ee250a96734daf791Alan Viverette } 21702fc5fef36357467eba22a0ee250a96734daf791Alan Viverette} 218