• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.android;
2 
3 import static android.content.res.Configuration.DENSITY_DPI_ANY;
4 import static android.content.res.Configuration.DENSITY_DPI_NONE;
5 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
6 
7 import android.content.res.Configuration;
8 import android.os.Build;
9 import android.os.Build.VERSION_CODES;
10 import android.os.LocaleList;
11 import android.text.TextUtils;
12 import android.util.DisplayMetrics;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Locale;
16 import org.robolectric.RuntimeEnvironment;
17 
18 // adapted from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/core/java/android/content/res/Configuration.java
19 public class ConfigurationV25 {
20 
localesToResourceQualifier(List<Locale> locs)21   private static String localesToResourceQualifier(List<Locale> locs) {
22     final StringBuilder sb = new StringBuilder();
23     for (int i = 0; i < locs.size(); i++) {
24       final Locale loc = locs.get(i);
25       final int l = loc.getLanguage().length();
26       if (l == 0) {
27         continue;
28       }
29       final int s = loc.getScript().length();
30       final int c = loc.getCountry().length();
31       final int v = loc.getVariant().length();
32       // We ignore locale extensions, since they are not supported by AAPT
33 
34       if (sb.length() != 0) {
35         sb.append(",");
36       }
37       if (l == 2 && s == 0 && (c == 0 || c == 2) && v == 0) {
38         // Traditional locale format: xx or xx-rYY
39         sb.append(loc.getLanguage());
40         if (c == 2) {
41           sb.append("-r").append(loc.getCountry());
42         }
43       } else {
44         sb.append("b+");
45         sb.append(loc.getLanguage());
46         if (s != 0) {
47           sb.append("+");
48           sb.append(loc.getScript());
49         }
50         if (c != 0) {
51           sb.append("+");
52           sb.append(loc.getCountry());
53         }
54         if (v != 0) {
55           sb.append("+");
56           sb.append(loc.getVariant());
57         }
58       }
59     }
60     return sb.toString();
61   }
62 
63 
64   /**
65    * Returns a string representation of the configuration that can be parsed
66    * by build tools (like AAPT).
67    *
68    * @hide
69    */
resourceQualifierString(Configuration config, DisplayMetrics displayMetrics)70   public static String resourceQualifierString(Configuration config, DisplayMetrics displayMetrics) {
71     return resourceQualifierString(config, displayMetrics, true);
72   }
73 
resourceQualifierString(Configuration config, DisplayMetrics displayMetrics, boolean includeSdk)74   public static String resourceQualifierString(Configuration config, DisplayMetrics displayMetrics, boolean includeSdk) {
75     ArrayList<String> parts = new ArrayList<String>();
76 
77     if (config.mcc != 0) {
78       parts.add("mcc" + config.mcc);
79       if (config.mnc != 0) {
80         parts.add("mnc" + config.mnc);
81       }
82     }
83 
84     List<Locale> locales = getLocales(config);
85     if (!locales.isEmpty()) {
86       final String resourceQualifier = localesToResourceQualifier(locales);
87       if (!resourceQualifier.isEmpty()) {
88         parts.add(resourceQualifier);
89       }
90     }
91 
92     switch (config.screenLayout & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) {
93       case Configuration.SCREENLAYOUT_LAYOUTDIR_LTR:
94         parts.add("ldltr");
95         break;
96       case Configuration.SCREENLAYOUT_LAYOUTDIR_RTL:
97         parts.add("ldrtl");
98         break;
99       default:
100         break;
101     }
102 
103     if (config.smallestScreenWidthDp != 0) {
104       parts.add("sw" + config.smallestScreenWidthDp + "dp");
105     }
106 
107     if (config.screenWidthDp != 0) {
108       parts.add("w" + config.screenWidthDp + "dp");
109     }
110 
111     if (config.screenHeightDp != 0) {
112       parts.add("h" + config.screenHeightDp + "dp");
113     }
114 
115     switch (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) {
116       case Configuration.SCREENLAYOUT_SIZE_SMALL:
117         parts.add("small");
118         break;
119       case Configuration.SCREENLAYOUT_SIZE_NORMAL:
120         parts.add("normal");
121         break;
122       case Configuration.SCREENLAYOUT_SIZE_LARGE:
123         parts.add("large");
124         break;
125       case Configuration.SCREENLAYOUT_SIZE_XLARGE:
126         parts.add("xlarge");
127         break;
128       default:
129         break;
130     }
131 
132     switch (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) {
133       case Configuration.SCREENLAYOUT_LONG_YES:
134         parts.add("long");
135         break;
136       case Configuration.SCREENLAYOUT_LONG_NO:
137         parts.add("notlong");
138         break;
139       default:
140         break;
141     }
142 
143     switch (config.screenLayout & Configuration.SCREENLAYOUT_ROUND_MASK) {
144       case Configuration.SCREENLAYOUT_ROUND_YES:
145         parts.add("round");
146         break;
147       case Configuration.SCREENLAYOUT_ROUND_NO:
148         parts.add("notround");
149         break;
150       default:
151         break;
152     }
153 
154     if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.O) {
155       switch (config.colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK) {
156         case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES:
157           parts.add("widecg");
158           break;
159         case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO:
160           parts.add("nowidecg");
161           break;
162         default:
163           break;
164       }
165 
166       switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) {
167         case Configuration.COLOR_MODE_HDR_YES:
168           parts.add("highdr");
169           break;
170         case Configuration.COLOR_MODE_HDR_NO:
171           parts.add("lowdr");
172           break;
173         default:
174           break;
175       }
176     }
177 
178     switch (config.orientation) {
179       case Configuration.ORIENTATION_LANDSCAPE:
180         parts.add("land");
181         break;
182       case Configuration.ORIENTATION_PORTRAIT:
183         parts.add("port");
184         break;
185       default:
186         break;
187     }
188 
189     switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) {
190       case Configuration.UI_MODE_TYPE_APPLIANCE:
191         parts.add("appliance");
192         break;
193       case Configuration.UI_MODE_TYPE_DESK:
194         parts.add("desk");
195         break;
196       case Configuration.UI_MODE_TYPE_TELEVISION:
197         parts.add("television");
198         break;
199       case Configuration.UI_MODE_TYPE_CAR:
200         parts.add("car");
201         break;
202       case Configuration.UI_MODE_TYPE_WATCH:
203         parts.add("watch");
204         break;
205       case Configuration.UI_MODE_TYPE_VR_HEADSET:
206         parts.add("vrheadset");
207         break;
208       case Configuration.UI_MODE_TYPE_NORMAL:
209       default:
210         break;
211     }
212 
213     switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) {
214       case Configuration.UI_MODE_NIGHT_YES:
215         parts.add("night");
216         break;
217       case Configuration.UI_MODE_NIGHT_NO:
218         parts.add("notnight");
219         break;
220       default:
221         break;
222     }
223 
224     int densityDpi;
225     if (RuntimeEnvironment.getApiLevel() > VERSION_CODES.JELLY_BEAN) {
226       densityDpi = config.densityDpi;
227     } else {
228       densityDpi = displayMetrics.densityDpi;
229     }
230 
231     switch (densityDpi) {
232       case DENSITY_DPI_UNDEFINED:
233         break;
234       case 120:
235         parts.add("ldpi");
236         break;
237       case 160:
238         parts.add("mdpi");
239         break;
240       case 213:
241         parts.add("tvdpi");
242         break;
243       case 240:
244         parts.add("hdpi");
245         break;
246       case 320:
247         parts.add("xhdpi");
248         break;
249       case 480:
250         parts.add("xxhdpi");
251         break;
252       case 640:
253         parts.add("xxxhdpi");
254         break;
255       case DENSITY_DPI_ANY:
256         parts.add("anydpi");
257         break;
258       case DENSITY_DPI_NONE:
259         parts.add("nodpi");
260         break;
261       default:
262         parts.add(densityDpi + "dpi");
263         break;
264     }
265 
266     switch (config.touchscreen) {
267       case Configuration.TOUCHSCREEN_NOTOUCH:
268         parts.add("notouch");
269         break;
270       case Configuration.TOUCHSCREEN_FINGER:
271         parts.add("finger");
272         break;
273       default:
274         break;
275     }
276 
277     switch (config.keyboardHidden) {
278       case Configuration.KEYBOARDHIDDEN_NO:
279         parts.add("keysexposed");
280         break;
281       case Configuration.KEYBOARDHIDDEN_YES:
282         parts.add("keyshidden");
283         break;
284       case Configuration.KEYBOARDHIDDEN_SOFT:
285         parts.add("keyssoft");
286         break;
287       default:
288         break;
289     }
290 
291     switch (config.keyboard) {
292       case Configuration.KEYBOARD_NOKEYS:
293         parts.add("nokeys");
294         break;
295       case Configuration.KEYBOARD_QWERTY:
296         parts.add("qwerty");
297         break;
298       case Configuration.KEYBOARD_12KEY:
299         parts.add("12key");
300         break;
301       default:
302         break;
303     }
304 
305     switch (config.navigationHidden) {
306       case Configuration.NAVIGATIONHIDDEN_NO:
307         parts.add("navexposed");
308         break;
309       case Configuration.NAVIGATIONHIDDEN_YES:
310         parts.add("navhidden");
311         break;
312       default:
313         break;
314     }
315 
316     switch (config.navigation) {
317       case Configuration.NAVIGATION_NONAV:
318         parts.add("nonav");
319         break;
320       case Configuration.NAVIGATION_DPAD:
321         parts.add("dpad");
322         break;
323       case Configuration.NAVIGATION_TRACKBALL:
324         parts.add("trackball");
325         break;
326       case Configuration.NAVIGATION_WHEEL:
327         parts.add("wheel");
328         break;
329       default:
330         break;
331     }
332 
333     if (includeSdk) {
334       parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
335     }
336 
337     return TextUtils.join("-", parts);
338   }
339 
getLocales(Configuration config)340   private static List<Locale> getLocales(Configuration config) {
341     List<Locale> locales = new ArrayList<>();
342     if (RuntimeEnvironment.getApiLevel() > Build.VERSION_CODES.M) {
343       LocaleList localeList = config.getLocales();
344       for (int i = 0; i < localeList.size(); i++) {
345         locales.add(localeList.get(i));
346       }
347     } else if (config.locale != null) {
348       locales.add(config.locale);
349     }
350     return locales;
351   }
352 }
353