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