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