ResourcesCompat.java revision 543fd2946ded1593b28553879e74ca4393eddd2e
1/* 2 * Copyright (C) 2014 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 17package android.support.v4.content.res; 18 19import static android.os.Build.VERSION.SDK_INT; 20 21import android.content.Context; 22import android.content.res.ColorStateList; 23import android.content.res.Resources; 24import android.content.res.Resources.NotFoundException; 25import android.content.res.Resources.Theme; 26import android.content.res.XmlResourceParser; 27import android.graphics.Typeface; 28import android.graphics.drawable.Drawable; 29import android.support.annotation.ColorInt; 30import android.support.annotation.ColorRes; 31import android.support.annotation.DrawableRes; 32import android.support.annotation.FontRes; 33import android.support.annotation.NonNull; 34import android.support.annotation.Nullable; 35import android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry; 36import android.support.v4.graphics.TypefaceCompat; 37import android.support.v4.os.BuildCompat; 38import android.util.Log; 39import android.util.TypedValue; 40 41import org.xmlpull.v1.XmlPullParserException; 42 43import java.io.IOException; 44 45/** 46 * Helper for accessing features in {@link android.content.res.Resources} 47 * introduced after API level 4 in a backwards compatible fashion. 48 */ 49public final class ResourcesCompat { 50 private static final String TAG = "ResourcesCompat"; 51 52 /** 53 * Return a drawable object associated with a particular resource ID and 54 * styled for the specified theme. Various types of objects will be 55 * returned depending on the underlying resource -- for example, a solid 56 * color, PNG image, scalable image, etc. 57 * <p> 58 * Prior to API level 21, the theme will not be applied and this method 59 * simply calls through to {@link Resources#getDrawable(int)}. 60 * 61 * @param id The desired resource identifier, as generated by the aapt 62 * tool. This integer encodes the package, type, and resource 63 * entry. The value 0 is an invalid identifier. 64 * @param theme The theme used to style the drawable attributes, may be 65 * {@code null}. 66 * @return Drawable An object that can be used to draw this resource. 67 * @throws NotFoundException Throws NotFoundException if the given ID does 68 * not exist. 69 */ 70 @Nullable 71 @SuppressWarnings("deprecation") 72 public static Drawable getDrawable(@NonNull Resources res, @DrawableRes int id, 73 @Nullable Theme theme) throws NotFoundException { 74 if (SDK_INT >= 21) { 75 return res.getDrawable(id, theme); 76 } else { 77 return res.getDrawable(id); 78 } 79 } 80 81 82 /** 83 * Return a drawable object associated with a particular resource ID for 84 * the given screen density in DPI and styled for the specified theme. 85 * <p> 86 * Prior to API level 15, the theme and density will not be applied and 87 * this method simply calls through to {@link Resources#getDrawable(int)}. 88 * <p> 89 * Prior to API level 21, the theme will not be applied and this method 90 * calls through to Resources#getDrawableForDensity(int, int). 91 * 92 * @param id The desired resource identifier, as generated by the aapt 93 * tool. This integer encodes the package, type, and resource 94 * entry. The value 0 is an invalid identifier. 95 * @param density The desired screen density indicated by the resource as 96 * found in {@link android.util.DisplayMetrics}. 97 * @param theme The theme used to style the drawable attributes, may be 98 * {@code null}. 99 * @return Drawable An object that can be used to draw this resource. 100 * @throws NotFoundException Throws NotFoundException if the given ID does 101 * not exist. 102 */ 103 @Nullable 104 @SuppressWarnings("deprecation") 105 public static Drawable getDrawableForDensity(@NonNull Resources res, @DrawableRes int id, 106 int density, @Nullable Theme theme) throws NotFoundException { 107 if (SDK_INT >= 21) { 108 return res.getDrawableForDensity(id, density, theme); 109 } else if (SDK_INT >= 15) { 110 return res.getDrawableForDensity(id, density); 111 } else { 112 return res.getDrawable(id); 113 } 114 } 115 116 /** 117 * Returns a themed color integer associated with a particular resource ID. 118 * If the resource holds a complex {@link ColorStateList}, then the default 119 * color from the set is returned. 120 * <p> 121 * Prior to API level 23, the theme will not be applied and this method 122 * calls through to {@link Resources#getColor(int)}. 123 * 124 * @param id The desired resource identifier, as generated by the aapt 125 * tool. This integer encodes the package, type, and resource 126 * entry. The value 0 is an invalid identifier. 127 * @param theme The theme used to style the color attributes, may be 128 * {@code null}. 129 * @return A single color value in the form {@code 0xAARRGGBB}. 130 * @throws NotFoundException Throws NotFoundException if the given ID does 131 * not exist. 132 */ 133 @ColorInt 134 @SuppressWarnings("deprecation") 135 public static int getColor(@NonNull Resources res, @ColorRes int id, @Nullable Theme theme) 136 throws NotFoundException { 137 if (SDK_INT >= 23) { 138 return res.getColor(id, theme); 139 } else { 140 return res.getColor(id); 141 } 142 } 143 144 /** 145 * Returns a themed color state list associated with a particular resource 146 * ID. The resource may contain either a single raw color value or a 147 * complex {@link ColorStateList} holding multiple possible colors. 148 * <p> 149 * Prior to API level 23, the theme will not be applied and this method 150 * calls through to {@link Resources#getColorStateList(int)}. 151 * 152 * @param id The desired resource identifier of a {@link ColorStateList}, 153 * as generated by the aapt tool. This integer encodes the 154 * package, type, and resource entry. The value 0 is an invalid 155 * identifier. 156 * @param theme The theme used to style the color attributes, may be 157 * {@code null}. 158 * @return A themed ColorStateList object containing either a single solid 159 * color or multiple colors that can be selected based on a state. 160 * @throws NotFoundException Throws NotFoundException if the given ID does 161 * not exist. 162 */ 163 @Nullable 164 @SuppressWarnings("deprecation") 165 public static ColorStateList getColorStateList(@NonNull Resources res, @ColorRes int id, 166 @Nullable Theme theme) throws NotFoundException { 167 if (SDK_INT >= 23) { 168 return res.getColorStateList(id, theme); 169 } else { 170 return res.getColorStateList(id); 171 } 172 } 173 174 /** 175 * Returns a font Typeface associated with a particular resource ID. 176 * <p> 177 * Prior to API level 23, font resources with more than one font in a family will only load the 178 * first font in that family. 179 * 180 * @param context A context to retrieve the Resources from. 181 * @param id The desired resource identifier of a {@link Typeface}, 182 * as generated by the aapt tool. This integer encodes the 183 * package, type, and resource entry. The value 0 is an invalid 184 * identifier. 185 * @return A font Typeface object. 186 * @throws NotFoundException Throws NotFoundException if the given ID does 187 * not exist. 188 */ 189 @Nullable 190 public static Typeface getFont(@NonNull Context context, @FontRes int id) 191 throws NotFoundException { 192 if (context.isRestricted()) { 193 return null; 194 } 195 if (BuildCompat.isAtLeastO()) { 196 // Use framework support 197 return context.getResources().getFont(id); 198 } else { 199 return loadFont(context, id); 200 } 201 } 202 203 private static Typeface loadFont(@NonNull Context context, int id) { 204 final TypedValue value = new TypedValue(); 205 final Resources resources = context.getResources(); 206 resources.getValue(id, value, true); 207 Typeface typeface = loadFont(context, resources, value, id); 208 if (typeface != null) { 209 return typeface; 210 } 211 throw new NotFoundException("Font resource ID #0x" 212 + Integer.toHexString(id)); 213 } 214 215 private static Typeface loadFont(@NonNull Context context, Resources wrapper, TypedValue value, 216 int id) { 217 if (value.string == null) { 218 throw new NotFoundException("Resource \"" + wrapper.getResourceName(id) + "\" (" 219 + Integer.toHexString(id) + ") is not a Font: " + value); 220 } 221 222 final String file = value.string.toString(); 223 Typeface cached = TypefaceCompat.findFromCache(wrapper, id, file); 224 if (cached != null) { 225 return cached; 226 } 227 228 try { 229 if (file.toLowerCase().endsWith(".xml")) { 230 final XmlResourceParser rp = wrapper.getXml(id); 231 final FamilyResourceEntry familyEntry = 232 FontResourcesParserCompat.parse(rp, wrapper); 233 if (familyEntry == null) { 234 Log.e(TAG, "Failed to find font-family tag"); 235 return null; 236 } 237 return TypefaceCompat.createFromResources(context, familyEntry, wrapper, id, file); 238 } 239 return TypefaceCompat.createFromResources(context, wrapper, id, file); 240 } catch (XmlPullParserException e) { 241 Log.e(TAG, "Failed to parse xml resource " + file, e); 242 } catch (IOException e) { 243 Log.e(TAG, "Failed to read xml resource " + file, e); 244 } 245 return null; 246 } 247 248 private ResourcesCompat() {} 249} 250