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