Home | History | Annotate | Download | only in android
      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 
     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    */
     70   public static String resourceQualifierString(Configuration config, DisplayMetrics displayMetrics) {
     71     return resourceQualifierString(config, displayMetrics, true);
     72   }
     73 
     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 
    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