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