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