1 package com.xtremelabs.robolectric.shadows;
2 
3 import android.content.res.AssetManager;
4 import android.content.res.Configuration;
5 import android.content.res.Resources;
6 import android.content.res.TypedArray;
7 import android.graphics.BitmapFactory;
8 import android.graphics.drawable.BitmapDrawable;
9 import android.graphics.drawable.Drawable;
10 import android.graphics.drawable.NinePatchDrawable;
11 import android.util.AttributeSet;
12 import android.util.DisplayMetrics;
13 import android.util.TypedValue;
14 import android.view.Display;
15 import com.xtremelabs.robolectric.Robolectric;
16 import com.xtremelabs.robolectric.internal.Implementation;
17 import com.xtremelabs.robolectric.internal.Implements;
18 import com.xtremelabs.robolectric.internal.RealObject;
19 import com.xtremelabs.robolectric.res.ResourceExtractor;
20 import com.xtremelabs.robolectric.res.ResourceLoader;
21 
22 import java.io.InputStream;
23 import java.util.HashMap;
24 import java.util.Locale;
25 
26 import static com.xtremelabs.robolectric.Robolectric.newInstanceOf;
27 import static com.xtremelabs.robolectric.Robolectric.shadowOf;
28 
29 /**
30  * Shadow of {@code Resources} that simulates the loading of resources
31  *
32  * @see com.xtremelabs.robolectric.RobolectricTestRunner#RobolectricTestRunner(Class, String, String)
33  */
34 @SuppressWarnings({"UnusedDeclaration"})
35 @Implements(Resources.class)
36 public class ShadowResources {
37     private float density = 1.0f;
38     Configuration configuration = null;
39     private DisplayMetrics displayMetrics;
40     private Display display;
41 
bind(Resources resources, ResourceLoader resourceLoader)42     static Resources bind(Resources resources, ResourceLoader resourceLoader) {
43         ShadowResources shadowResources = shadowOf(resources);
44         if (shadowResources.resourceLoader != null) throw new RuntimeException("ResourceLoader already set!");
45         shadowResources.resourceLoader = resourceLoader;
46         return resources;
47     }
48 
49     @RealObject
50     Resources realResources;
51     private ResourceLoader resourceLoader;
52 
53     @Implementation
getIdentifier(String name, String defType, String defPackage)54     public int getIdentifier(String name, String defType, String defPackage) {
55         Integer index = 0;
56 
57         ResourceExtractor resourceExtractor = resourceLoader.getResourceExtractor();
58 
59         index = resourceExtractor.getResourceId(defType + "/" + name);
60         if (index == null) {
61             return 0;
62         }
63         return index;
64     }
65 
66     @Implementation
getColor(int id)67     public int getColor(int id) throws Resources.NotFoundException {
68         return resourceLoader.getColorValue(id);
69     }
70 
71     @Implementation
getConfiguration()72     public Configuration getConfiguration() {
73         if (configuration == null) {
74             configuration = new Configuration();
75             configuration.setToDefaults();
76         }
77         if (configuration.locale == null) {
78             configuration.locale = Locale.getDefault();
79         }
80         return configuration;
81     }
82 
83     @Implementation
getString(int id)84     public String getString(int id) throws Resources.NotFoundException {
85         return resourceLoader.getStringValue(id);
86     }
87 
88     @Implementation
getString(int id, Object... formatArgs)89     public String getString(int id, Object... formatArgs) throws Resources.NotFoundException {
90         String raw = getString(id);
91         return String.format(Locale.ENGLISH, raw, formatArgs);
92     }
93 
94     @Implementation
getQuantityString(int id, int quantity, Object... formatArgs)95     public String getQuantityString(int id, int quantity, Object... formatArgs) throws Resources.NotFoundException {
96         String raw = getQuantityString(id, quantity);
97         return String.format(Locale.ENGLISH, raw, formatArgs);
98     }
99 
100     @Implementation
getQuantityString(int id, int quantity)101     public String getQuantityString(int id, int quantity) throws Resources.NotFoundException {
102         return resourceLoader.getPluralStringValue(id, quantity);
103     }
104 
105     @Implementation
openRawResource(int id)106     public InputStream openRawResource(int id) throws Resources.NotFoundException {
107         return resourceLoader.getRawValue(id);
108     }
109 
110     @Implementation
getStringArray(int id)111     public String[] getStringArray(int id) throws Resources.NotFoundException {
112         String[] arrayValue = resourceLoader.getStringArrayValue(id);
113         if (arrayValue == null) {
114             throw new Resources.NotFoundException();
115         }
116         return arrayValue;
117     }
118 
119     @Implementation
getTextArray(int id)120     public CharSequence[] getTextArray(int id) throws Resources.NotFoundException {
121         return getStringArray(id);
122     }
123 
124     @Implementation
getText(int id)125     public CharSequence getText(int id) throws Resources.NotFoundException {
126         return getString(id);
127     }
128 
setDensity(float density)129     public void setDensity(float density) {
130         this.density = density;
131     }
132 
setDisplay(Display display)133     public void setDisplay(Display display) {
134         this.display = display;
135         displayMetrics = null;
136     }
137 
138     @Implementation
getDisplayMetrics()139     public DisplayMetrics getDisplayMetrics() {
140         if (displayMetrics == null) {
141             if (display == null) {
142                 display = Robolectric.newInstanceOf(Display.class);
143             }
144 
145             displayMetrics = new DisplayMetrics();
146             display.getMetrics(displayMetrics);
147         }
148         displayMetrics.density = this.density;
149         return displayMetrics;
150     }
151 
152     @Implementation
getDrawable(int drawableResourceId)153     public Drawable getDrawable(int drawableResourceId) throws Resources.NotFoundException {
154 
155         ResourceLoader resLoader = Robolectric.shadowOf(Robolectric.application).getResourceLoader();
156 
157         Drawable xmlDrawable = resLoader.getXmlDrawable(drawableResourceId);
158         if (xmlDrawable != null) {
159             return xmlDrawable;
160         }
161 
162         Drawable animDrawable = resLoader.getAnimDrawable(drawableResourceId);
163         if (animDrawable != null) {
164             return animDrawable;
165         }
166 
167         Drawable colorDrawable = resLoader.getColorDrawable(drawableResourceId);
168         if (colorDrawable != null) {
169             return colorDrawable;
170         }
171 
172         if (resLoader.isNinePatchDrawable(drawableResourceId)) {
173         	return new NinePatchDrawable(realResources, null);
174         }
175 
176         return new BitmapDrawable(realResources, BitmapFactory.decodeResource(realResources, drawableResourceId));
177     }
178 
179     @Implementation
getDimension(int id)180     public float getDimension(int id) throws Resources.NotFoundException {
181         return resourceLoader.getDimenValue(id);
182     }
183 
184     @Implementation
getInteger(int id)185     public int getInteger(int id) throws Resources.NotFoundException {
186     	return resourceLoader.getIntegerValue( id );
187     }
188 
189     @Implementation
getDimensionPixelSize(int id)190     public int getDimensionPixelSize(int id) throws Resources.NotFoundException {
191         // The int value returned from here is probably going to be handed to TextView.setTextSize(),
192         // which takes a float. Avoid int-to-float conversion errors by returning a value generated from this
193         // resource ID but which isn't too big (resource values in R.java are all greater than 0x7f000000).
194 
195         return (int) getDimension(id);
196     }
197 
198     @Implementation
getDimensionPixelOffset(int id)199     public int getDimensionPixelOffset(int id) throws Resources.NotFoundException {
200         return (int) getDimension(id);
201     }
202 
203     @Implementation
getAssets()204     public AssetManager getAssets() {
205         return ShadowAssetManager.bind(Robolectric.newInstanceOf(AssetManager.class), resourceLoader);
206     }
207 
208     @Implementation
newTheme()209     public final android.content.res.Resources.Theme newTheme() {
210         return newInstanceOf(Resources.Theme.class);
211     }
212 
213     @Implements(Resources.Theme.class)
214     public static class ShadowTheme {
215         HashMap<Integer, TypedValue> attrMap = new HashMap<Integer, TypedValue>();
216 
217         @Implementation
obtainStyledAttributes(int[] attrs)218         public TypedArray obtainStyledAttributes(int[] attrs) {
219             return obtainStyledAttributes(0, attrs);
220         }
221 
222         @Implementation
obtainStyledAttributes(int resid, int[] attrs)223         public TypedArray obtainStyledAttributes(int resid, int[] attrs) throws android.content.res.Resources.NotFoundException {
224             return obtainStyledAttributes(null, attrs, 0, 0);
225         }
226 
227         @Implementation
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)228         public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
229             return newInstanceOf(TypedArray.class);
230         }
231 
232         @Implementation
resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs)233         public boolean resolveAttribute (int resid, TypedValue outValue, boolean resolveRefs) {
234             TypedValue foundValue = attrMap.get(resid);
235             if (foundValue != null) {
236                 outValue.setTo(foundValue);
237                 return true;
238             }
239             return false;
240         }
241 
setAttribue(int attrId, TypedValue value)242         public void setAttribue(int attrId, TypedValue value) {
243             attrMap.put(attrId, value);
244         }
245     }
246 }
247