1package com.xtremelabs.robolectric.shadows;
2
3import android.content.res.AssetManager;
4import android.content.res.Configuration;
5import android.content.res.Resources;
6import android.content.res.TypedArray;
7import android.graphics.BitmapFactory;
8import android.graphics.drawable.BitmapDrawable;
9import android.graphics.drawable.Drawable;
10import android.graphics.drawable.NinePatchDrawable;
11import android.util.AttributeSet;
12import android.util.DisplayMetrics;
13import android.util.TypedValue;
14import android.view.Display;
15import com.xtremelabs.robolectric.Robolectric;
16import com.xtremelabs.robolectric.internal.Implementation;
17import com.xtremelabs.robolectric.internal.Implements;
18import com.xtremelabs.robolectric.internal.RealObject;
19import com.xtremelabs.robolectric.res.ResourceExtractor;
20import com.xtremelabs.robolectric.res.ResourceLoader;
21
22import java.io.InputStream;
23import java.util.HashMap;
24import java.util.Locale;
25
26import static com.xtremelabs.robolectric.Robolectric.newInstanceOf;
27import 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)
36public class ShadowResources {
37    private float density = 1.0f;
38    Configuration configuration = null;
39    private DisplayMetrics displayMetrics;
40    private Display display;
41
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
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
67    public int getColor(int id) throws Resources.NotFoundException {
68        return resourceLoader.getColorValue(id);
69    }
70
71    @Implementation
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
84    public String getString(int id) throws Resources.NotFoundException {
85        return resourceLoader.getStringValue(id);
86    }
87
88    @Implementation
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
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
101    public String getQuantityString(int id, int quantity) throws Resources.NotFoundException {
102        return resourceLoader.getPluralStringValue(id, quantity);
103    }
104
105    @Implementation
106    public InputStream openRawResource(int id) throws Resources.NotFoundException {
107        return resourceLoader.getRawValue(id);
108    }
109
110    @Implementation
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
120    public CharSequence[] getTextArray(int id) throws Resources.NotFoundException {
121        return getStringArray(id);
122    }
123
124    @Implementation
125    public CharSequence getText(int id) throws Resources.NotFoundException {
126        return getString(id);
127    }
128
129    public void setDensity(float density) {
130        this.density = density;
131    }
132
133    public void setDisplay(Display display) {
134        this.display = display;
135        displayMetrics = null;
136    }
137
138    @Implementation
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
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
180    public float getDimension(int id) throws Resources.NotFoundException {
181        return resourceLoader.getDimenValue(id);
182    }
183
184    @Implementation
185    public int getInteger(int id) throws Resources.NotFoundException {
186    	return resourceLoader.getIntegerValue( id );
187    }
188
189    @Implementation
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
199    public int getDimensionPixelOffset(int id) throws Resources.NotFoundException {
200        return (int) getDimension(id);
201    }
202
203    @Implementation
204    public AssetManager getAssets() {
205        return ShadowAssetManager.bind(Robolectric.newInstanceOf(AssetManager.class), resourceLoader);
206    }
207
208    @Implementation
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
218        public TypedArray obtainStyledAttributes(int[] attrs) {
219            return obtainStyledAttributes(0, attrs);
220        }
221
222        @Implementation
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
228        public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
229            return newInstanceOf(TypedArray.class);
230        }
231
232        @Implementation
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
242        public void setAttribue(int attrId, TypedValue value) {
243            attrMap.put(attrId, value);
244        }
245    }
246}
247