Home | History | Annotate | Download | only in configuration
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.common.resources.configuration;
     18 
     19 import com.android.AndroidConstants;
     20 import com.android.resources.Density;
     21 import com.android.resources.ResourceFolderType;
     22 import com.android.resources.ScreenOrientation;
     23 
     24 import java.util.ArrayList;
     25 import java.util.List;
     26 
     27 
     28 /**
     29  * Represents the configuration for Resource Folders. All the properties have a default
     30  * value which means that the property is not set.
     31  */
     32 public final class FolderConfiguration implements Comparable<FolderConfiguration> {
     33 
     34     private final static ResourceQualifier[] DEFAULT_QUALIFIERS;
     35 
     36     static {
     37         // get the default qualifiers.
     38         FolderConfiguration defaultConfig = new FolderConfiguration();
     39         defaultConfig.createDefault();
     40         DEFAULT_QUALIFIERS = defaultConfig.getQualifiers();
     41     }
     42 
     43 
     44     private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT];
     45 
     46     private final static int INDEX_COUNTRY_CODE          = 0;
     47     private final static int INDEX_NETWORK_CODE          = 1;
     48     private final static int INDEX_LANGUAGE              = 2;
     49     private final static int INDEX_REGION                = 3;
     50     private final static int INDEX_SMALLEST_SCREEN_WIDTH = 4;
     51     private final static int INDEX_SCREEN_WIDTH          = 5;
     52     private final static int INDEX_SCREEN_HEIGHT         = 6;
     53     private final static int INDEX_SCREEN_LAYOUT_SIZE    = 7;
     54     private final static int INDEX_SCREEN_RATIO          = 8;
     55     private final static int INDEX_SCREEN_ORIENTATION    = 9;
     56     private final static int INDEX_UI_MODE               = 10;
     57     private final static int INDEX_NIGHT_MODE            = 11;
     58     private final static int INDEX_PIXEL_DENSITY         = 12;
     59     private final static int INDEX_TOUCH_TYPE            = 13;
     60     private final static int INDEX_KEYBOARD_STATE        = 14;
     61     private final static int INDEX_TEXT_INPUT_METHOD     = 15;
     62     private final static int INDEX_NAVIGATION_STATE      = 16;
     63     private final static int INDEX_NAVIGATION_METHOD     = 17;
     64     private final static int INDEX_SCREEN_DIMENSION      = 18;
     65     private final static int INDEX_VERSION               = 19;
     66     private final static int INDEX_COUNT                 = 20;
     67 
     68     /**
     69      * Creates a {@link FolderConfiguration} matching the folder segments.
     70      * @param folderSegments The segments of the folder name. The first segments should contain
     71      * the name of the folder
     72      * @return a FolderConfiguration object, or null if the folder name isn't valid..
     73      */
     74     public static FolderConfiguration getConfig(String[] folderSegments) {
     75         FolderConfiguration config = new FolderConfiguration();
     76 
     77         // we are going to loop through the segments, and match them with the first
     78         // available qualifier. If the segment doesn't match we try with the next qualifier.
     79         // Because the order of the qualifier is fixed, we do not reset the first qualifier
     80         // after each successful segment.
     81         // If we run out of qualifier before processing all the segments, we fail.
     82 
     83         int qualifierIndex = 0;
     84         int qualifierCount = DEFAULT_QUALIFIERS.length;
     85 
     86         for (int i = 1 ; i < folderSegments.length; i++) {
     87             String seg = folderSegments[i];
     88             if (seg.length() > 0) {
     89                 while (qualifierIndex < qualifierCount &&
     90                         DEFAULT_QUALIFIERS[qualifierIndex].checkAndSet(seg, config) == false) {
     91                     qualifierIndex++;
     92                 }
     93 
     94                 // if we reached the end of the qualifier we didn't find a matching qualifier.
     95                 if (qualifierIndex == qualifierCount) {
     96                     return null;
     97                 }
     98 
     99             } else {
    100                 return null;
    101             }
    102         }
    103 
    104         return config;
    105     }
    106 
    107     /**
    108      * Returns the number of {@link ResourceQualifier} that make up a Folder configuration.
    109      */
    110     public static int getQualifierCount() {
    111         return INDEX_COUNT;
    112     }
    113 
    114     /**
    115      * Sets the config from the qualifiers of a given <var>config</var>.
    116      * <p/>This is equivalent to <code>set(config, false)</code>
    117      * @param config the configuration to set
    118      *
    119      * @see #set(FolderConfiguration, boolean)
    120      */
    121     public void set(FolderConfiguration config) {
    122         set(config, false /*nonFakeValuesOnly*/);
    123     }
    124 
    125     /**
    126      * Sets the config from the qualifiers of a given <var>config</var>.
    127      * @param config the configuration to set
    128      * @param nonFakeValuesOnly if set to true this ignore qualifiers for which the
    129      * current value is a fake value.
    130      *
    131      * @see ResourceQualifier#hasFakeValue()
    132      */
    133     public void set(FolderConfiguration config, boolean nonFakeValuesOnly) {
    134         if (config != null) {
    135             for (int i = 0 ; i < INDEX_COUNT ; i++) {
    136                 ResourceQualifier q = config.mQualifiers[i];
    137                 if (nonFakeValuesOnly == false || q == null || q.hasFakeValue() == false) {
    138                     mQualifiers[i] = q;
    139                 }
    140             }
    141         }
    142     }
    143 
    144     /**
    145      * Reset the config.
    146      * <p/>This makes qualifiers at all indices <code>null</code>.
    147      */
    148     public void reset() {
    149         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    150             mQualifiers[i] = null;
    151         }
    152     }
    153 
    154     /**
    155      * Removes the qualifiers from the receiver if they are present (and valid)
    156      * in the given configuration.
    157      */
    158     public void substract(FolderConfiguration config) {
    159         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    160             if (config.mQualifiers[i] != null && config.mQualifiers[i].isValid()) {
    161                 mQualifiers[i] = null;
    162             }
    163         }
    164     }
    165 
    166     /**
    167      * Adds the non-qualifiers from the given config.
    168      * Qualifiers that are null in the given config do not change in the receiver.
    169      */
    170     public void add(FolderConfiguration config) {
    171         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    172             if (config.mQualifiers[i] != null) {
    173                 mQualifiers[i] = config.mQualifiers[i];
    174             }
    175         }
    176     }
    177 
    178     /**
    179      * Returns the first invalid qualifier, or <code>null<code> if they are all valid (or if none
    180      * exists).
    181      */
    182     public ResourceQualifier getInvalidQualifier() {
    183         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    184             if (mQualifiers[i] != null && mQualifiers[i].isValid() == false) {
    185                 return mQualifiers[i];
    186             }
    187         }
    188 
    189         // all allocated qualifiers are valid, we return null.
    190         return null;
    191     }
    192 
    193     /**
    194      * Returns whether the Region qualifier is valid. Region qualifier can only be present if a
    195      * Language qualifier is present as well.
    196      * @return true if the Region qualifier is valid.
    197      */
    198     public boolean checkRegion() {
    199         if (mQualifiers[INDEX_LANGUAGE] == null && mQualifiers[INDEX_REGION] != null) {
    200             return false;
    201         }
    202 
    203         return true;
    204     }
    205 
    206     /**
    207      * Adds a qualifier to the {@link FolderConfiguration}
    208      * @param qualifier the {@link ResourceQualifier} to add.
    209      */
    210     public void addQualifier(ResourceQualifier qualifier) {
    211         if (qualifier instanceof CountryCodeQualifier) {
    212             mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
    213 
    214         } else if (qualifier instanceof NetworkCodeQualifier) {
    215             mQualifiers[INDEX_NETWORK_CODE] = qualifier;
    216 
    217         } else if (qualifier instanceof LanguageQualifier) {
    218             mQualifiers[INDEX_LANGUAGE] = qualifier;
    219 
    220         } else if (qualifier instanceof RegionQualifier) {
    221             mQualifiers[INDEX_REGION] = qualifier;
    222 
    223         } else if (qualifier instanceof SmallestScreenWidthQualifier) {
    224             mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = qualifier;
    225 
    226         } else if (qualifier instanceof ScreenWidthQualifier) {
    227             mQualifiers[INDEX_SCREEN_WIDTH] = qualifier;
    228 
    229         } else if (qualifier instanceof ScreenHeightQualifier) {
    230             mQualifiers[INDEX_SCREEN_HEIGHT] = qualifier;
    231 
    232         } else if (qualifier instanceof ScreenSizeQualifier) {
    233             mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = qualifier;
    234 
    235         } else if (qualifier instanceof ScreenRatioQualifier) {
    236             mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
    237 
    238         } else if (qualifier instanceof ScreenOrientationQualifier) {
    239             mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
    240 
    241         } else if (qualifier instanceof UiModeQualifier) {
    242             mQualifiers[INDEX_UI_MODE] = qualifier;
    243 
    244         } else if (qualifier instanceof NightModeQualifier) {
    245             mQualifiers[INDEX_NIGHT_MODE] = qualifier;
    246 
    247         } else if (qualifier instanceof DensityQualifier) {
    248             mQualifiers[INDEX_PIXEL_DENSITY] = qualifier;
    249 
    250         } else if (qualifier instanceof TouchScreenQualifier) {
    251             mQualifiers[INDEX_TOUCH_TYPE] = qualifier;
    252 
    253         } else if (qualifier instanceof KeyboardStateQualifier) {
    254             mQualifiers[INDEX_KEYBOARD_STATE] = qualifier;
    255 
    256         } else if (qualifier instanceof TextInputMethodQualifier) {
    257             mQualifiers[INDEX_TEXT_INPUT_METHOD] = qualifier;
    258 
    259         } else if (qualifier instanceof NavigationStateQualifier) {
    260             mQualifiers[INDEX_NAVIGATION_STATE] = qualifier;
    261 
    262         } else if (qualifier instanceof NavigationMethodQualifier) {
    263             mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
    264 
    265         } else if (qualifier instanceof ScreenDimensionQualifier) {
    266             mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
    267 
    268         } else if (qualifier instanceof VersionQualifier) {
    269             mQualifiers[INDEX_VERSION] = qualifier;
    270 
    271         }
    272     }
    273 
    274     /**
    275      * Removes a given qualifier from the {@link FolderConfiguration}.
    276      * @param qualifier the {@link ResourceQualifier} to remove.
    277      */
    278     public void removeQualifier(ResourceQualifier qualifier) {
    279         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    280             if (mQualifiers[i] == qualifier) {
    281                 mQualifiers[i] = null;
    282                 return;
    283             }
    284         }
    285     }
    286 
    287     /**
    288      * Returns a qualifier by its index. The total number of qualifiers can be accessed by
    289      * {@link #getQualifierCount()}.
    290      * @param index the index of the qualifier to return.
    291      * @return the qualifier or null if there are none at the index.
    292      */
    293     public ResourceQualifier getQualifier(int index) {
    294         return mQualifiers[index];
    295     }
    296 
    297     public void setCountryCodeQualifier(CountryCodeQualifier qualifier) {
    298         mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
    299     }
    300 
    301     public CountryCodeQualifier getCountryCodeQualifier() {
    302         return (CountryCodeQualifier)mQualifiers[INDEX_COUNTRY_CODE];
    303     }
    304 
    305     public void setNetworkCodeQualifier(NetworkCodeQualifier qualifier) {
    306         mQualifiers[INDEX_NETWORK_CODE] = qualifier;
    307     }
    308 
    309     public NetworkCodeQualifier getNetworkCodeQualifier() {
    310         return (NetworkCodeQualifier)mQualifiers[INDEX_NETWORK_CODE];
    311     }
    312 
    313     public void setLanguageQualifier(LanguageQualifier qualifier) {
    314         mQualifiers[INDEX_LANGUAGE] = qualifier;
    315     }
    316 
    317     public LanguageQualifier getLanguageQualifier() {
    318         return (LanguageQualifier)mQualifiers[INDEX_LANGUAGE];
    319     }
    320 
    321     public void setRegionQualifier(RegionQualifier qualifier) {
    322         mQualifiers[INDEX_REGION] = qualifier;
    323     }
    324 
    325     public RegionQualifier getRegionQualifier() {
    326         return (RegionQualifier)mQualifiers[INDEX_REGION];
    327     }
    328 
    329     public void setSmallestScreenWidthQualifier(SmallestScreenWidthQualifier qualifier) {
    330         mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = qualifier;
    331     }
    332 
    333     public SmallestScreenWidthQualifier getSmallestScreenWidthQualifier() {
    334         return (SmallestScreenWidthQualifier) mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH];
    335     }
    336 
    337     public void setScreenWidthQualifier(ScreenWidthQualifier qualifier) {
    338         mQualifiers[INDEX_SCREEN_WIDTH] = qualifier;
    339     }
    340 
    341     public ScreenWidthQualifier getScreenWidthQualifier() {
    342         return (ScreenWidthQualifier) mQualifiers[INDEX_SCREEN_WIDTH];
    343     }
    344 
    345     public void setScreenHeightQualifier(ScreenHeightQualifier qualifier) {
    346         mQualifiers[INDEX_SCREEN_HEIGHT] = qualifier;
    347     }
    348 
    349     public ScreenHeightQualifier getScreenHeightQualifier() {
    350         return (ScreenHeightQualifier) mQualifiers[INDEX_SCREEN_HEIGHT];
    351     }
    352 
    353     public void setScreenSizeQualifier(ScreenSizeQualifier qualifier) {
    354         mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = qualifier;
    355     }
    356 
    357     public ScreenSizeQualifier getScreenSizeQualifier() {
    358         return (ScreenSizeQualifier)mQualifiers[INDEX_SCREEN_LAYOUT_SIZE];
    359     }
    360 
    361     public void setScreenRatioQualifier(ScreenRatioQualifier qualifier) {
    362         mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
    363     }
    364 
    365     public ScreenRatioQualifier getScreenRatioQualifier() {
    366         return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO];
    367     }
    368 
    369     public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) {
    370         mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
    371     }
    372 
    373     public ScreenOrientationQualifier getScreenOrientationQualifier() {
    374         return (ScreenOrientationQualifier)mQualifiers[INDEX_SCREEN_ORIENTATION];
    375     }
    376 
    377     public void setUiModeQualifier(UiModeQualifier qualifier) {
    378         mQualifiers[INDEX_UI_MODE] = qualifier;
    379     }
    380 
    381     public UiModeQualifier getUiModeQualifier() {
    382         return (UiModeQualifier)mQualifiers[INDEX_UI_MODE];
    383     }
    384 
    385     public void setNightModeQualifier(NightModeQualifier qualifier) {
    386         mQualifiers[INDEX_NIGHT_MODE] = qualifier;
    387     }
    388 
    389     public NightModeQualifier getNightModeQualifier() {
    390         return (NightModeQualifier)mQualifiers[INDEX_NIGHT_MODE];
    391     }
    392 
    393     public void setDensityQualifier(DensityQualifier qualifier) {
    394         mQualifiers[INDEX_PIXEL_DENSITY] = qualifier;
    395     }
    396 
    397     public DensityQualifier getDensityQualifier() {
    398         return (DensityQualifier)mQualifiers[INDEX_PIXEL_DENSITY];
    399     }
    400 
    401     public void setTouchTypeQualifier(TouchScreenQualifier qualifier) {
    402         mQualifiers[INDEX_TOUCH_TYPE] = qualifier;
    403     }
    404 
    405     public TouchScreenQualifier getTouchTypeQualifier() {
    406         return (TouchScreenQualifier)mQualifiers[INDEX_TOUCH_TYPE];
    407     }
    408 
    409     public void setKeyboardStateQualifier(KeyboardStateQualifier qualifier) {
    410         mQualifiers[INDEX_KEYBOARD_STATE] = qualifier;
    411     }
    412 
    413     public KeyboardStateQualifier getKeyboardStateQualifier() {
    414         return (KeyboardStateQualifier)mQualifiers[INDEX_KEYBOARD_STATE];
    415     }
    416 
    417     public void setTextInputMethodQualifier(TextInputMethodQualifier qualifier) {
    418         mQualifiers[INDEX_TEXT_INPUT_METHOD] = qualifier;
    419     }
    420 
    421     public TextInputMethodQualifier getTextInputMethodQualifier() {
    422         return (TextInputMethodQualifier)mQualifiers[INDEX_TEXT_INPUT_METHOD];
    423     }
    424 
    425     public void setNavigationStateQualifier(NavigationStateQualifier qualifier) {
    426         mQualifiers[INDEX_NAVIGATION_STATE] = qualifier;
    427     }
    428 
    429     public NavigationStateQualifier getNavigationStateQualifier() {
    430         return (NavigationStateQualifier)mQualifiers[INDEX_NAVIGATION_STATE];
    431     }
    432 
    433     public void setNavigationMethodQualifier(NavigationMethodQualifier qualifier) {
    434         mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
    435     }
    436 
    437     public NavigationMethodQualifier getNavigationMethodQualifier() {
    438         return (NavigationMethodQualifier)mQualifiers[INDEX_NAVIGATION_METHOD];
    439     }
    440 
    441     public void setScreenDimensionQualifier(ScreenDimensionQualifier qualifier) {
    442         mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
    443     }
    444 
    445     public ScreenDimensionQualifier getScreenDimensionQualifier() {
    446         return (ScreenDimensionQualifier)mQualifiers[INDEX_SCREEN_DIMENSION];
    447     }
    448 
    449     public void setVersionQualifier(VersionQualifier qualifier) {
    450         mQualifiers[INDEX_VERSION] = qualifier;
    451     }
    452 
    453     public VersionQualifier getVersionQualifier() {
    454         return (VersionQualifier)mQualifiers[INDEX_VERSION];
    455     }
    456 
    457     /**
    458      * Updates the {@link SmallestScreenWidthQualifier}, {@link ScreenWidthQualifier}, and
    459      * {@link ScreenHeightQualifier} based on the (required) values of
    460      * {@link ScreenDimensionQualifier} {@link DensityQualifier}, and
    461      * {@link ScreenOrientationQualifier}.
    462      *
    463      * Also the density cannot be {@link Density#NODPI} as it's not valid on a device.
    464      */
    465     public void updateScreenWidthAndHeight() {
    466 
    467         ResourceQualifier sizeQ = mQualifiers[INDEX_SCREEN_DIMENSION];
    468         ResourceQualifier densityQ = mQualifiers[INDEX_PIXEL_DENSITY];
    469         ResourceQualifier orientQ = mQualifiers[INDEX_SCREEN_ORIENTATION];
    470 
    471         if (sizeQ != null && densityQ != null && orientQ != null) {
    472             Density density = ((DensityQualifier) densityQ).getValue();
    473             if (density == Density.NODPI) {
    474                 return;
    475             }
    476 
    477             ScreenOrientation orientation = ((ScreenOrientationQualifier) orientQ).getValue();
    478 
    479             int size1 = ((ScreenDimensionQualifier) sizeQ).getValue1();
    480             int size2 = ((ScreenDimensionQualifier) sizeQ).getValue2();
    481 
    482             // make sure size1 is the biggest (should be the case, but make sure)
    483             if (size1 < size2) {
    484                 int a = size1;
    485                 size1 = size2;
    486                 size2 = a;
    487             }
    488 
    489             // compute the dp. round them up since we want -w480dp to match a 480.5dp screen
    490             int dp1 = (int) Math.ceil(size1 * Density.DEFAULT_DENSITY / density.getDpiValue());
    491             int dp2 = (int) Math.ceil(size2 * Density.DEFAULT_DENSITY / density.getDpiValue());
    492 
    493             setSmallestScreenWidthQualifier(new SmallestScreenWidthQualifier(dp2));
    494 
    495             switch (orientation) {
    496                 case PORTRAIT:
    497                     setScreenWidthQualifier(new ScreenWidthQualifier(dp2));
    498                     setScreenHeightQualifier(new ScreenHeightQualifier(dp1));
    499                     break;
    500                 case LANDSCAPE:
    501                     setScreenWidthQualifier(new ScreenWidthQualifier(dp1));
    502                     setScreenHeightQualifier(new ScreenHeightQualifier(dp2));
    503                     break;
    504                 case SQUARE:
    505                     setScreenWidthQualifier(new ScreenWidthQualifier(dp2));
    506                     setScreenHeightQualifier(new ScreenHeightQualifier(dp2));
    507                     break;
    508             }
    509         }
    510     }
    511 
    512     /**
    513      * Returns whether an object is equals to the receiver.
    514      */
    515     @Override
    516     public boolean equals(Object obj) {
    517         if (obj == this) {
    518             return true;
    519         }
    520 
    521         if (obj instanceof FolderConfiguration) {
    522             FolderConfiguration fc = (FolderConfiguration)obj;
    523             for (int i = 0 ; i < INDEX_COUNT ; i++) {
    524                 ResourceQualifier qualifier = mQualifiers[i];
    525                 ResourceQualifier fcQualifier = fc.mQualifiers[i];
    526                 if (qualifier != null) {
    527                     if (qualifier.equals(fcQualifier) == false) {
    528                         return false;
    529                     }
    530                 } else if (fcQualifier != null) {
    531                     return false;
    532                 }
    533             }
    534 
    535             return true;
    536         }
    537 
    538         return false;
    539     }
    540 
    541     @Override
    542     public int hashCode() {
    543         return toString().hashCode();
    544     }
    545 
    546     /**
    547      * Returns whether the Configuration has only default values.
    548      */
    549     public boolean isDefault() {
    550         for (ResourceQualifier irq : mQualifiers) {
    551             if (irq != null) {
    552                 return false;
    553             }
    554         }
    555 
    556         return true;
    557     }
    558 
    559     /**
    560      * Returns the name of a folder with the configuration.
    561      */
    562     public String getFolderName(ResourceFolderType folder) {
    563         StringBuilder result = new StringBuilder(folder.getName());
    564 
    565         for (ResourceQualifier qualifier : mQualifiers) {
    566             if (qualifier != null) {
    567                 String segment = qualifier.getFolderSegment();
    568                 if (segment != null && segment.length() > 0) {
    569                     result.append(AndroidConstants.RES_QUALIFIER_SEP);
    570                     result.append(segment);
    571                 }
    572             }
    573         }
    574 
    575         return result.toString();
    576     }
    577 
    578     /**
    579      * Returns {@link #toDisplayString()}.
    580      */
    581     @Override
    582     public String toString() {
    583         return toDisplayString();
    584     }
    585 
    586     /**
    587      * Returns a string valid for display purpose.
    588      */
    589     public String toDisplayString() {
    590         if (isDefault()) {
    591             return "default";
    592         }
    593 
    594         StringBuilder result = null;
    595         int index = 0;
    596         ResourceQualifier qualifier = null;
    597 
    598         // pre- language/region qualifiers
    599         while (index < INDEX_LANGUAGE) {
    600             qualifier = mQualifiers[index++];
    601             if (qualifier != null) {
    602                 if (result == null) {
    603                     result = new StringBuilder();
    604                 } else {
    605                     result.append(", "); //$NON-NLS-1$
    606                 }
    607                 result.append(qualifier.getLongDisplayValue());
    608 
    609             }
    610         }
    611 
    612         // process the language/region qualifier in a custom way, if there are both non null.
    613         if (mQualifiers[INDEX_LANGUAGE] != null && mQualifiers[INDEX_REGION] != null) {
    614             String language = mQualifiers[INDEX_LANGUAGE].getLongDisplayValue();
    615             String region = mQualifiers[INDEX_REGION].getLongDisplayValue();
    616 
    617             if (result == null) {
    618                 result = new StringBuilder();
    619             } else {
    620                 result.append(", "); //$NON-NLS-1$
    621             }
    622             result.append(String.format("Locale %s_%s", language, region)); //$NON-NLS-1$
    623 
    624             index += 2;
    625         }
    626 
    627         // post language/region qualifiers.
    628         while (index < INDEX_COUNT) {
    629             qualifier = mQualifiers[index++];
    630             if (qualifier != null) {
    631                 if (result == null) {
    632                     result = new StringBuilder();
    633                 } else {
    634                     result.append(", "); //$NON-NLS-1$
    635                 }
    636                 result.append(qualifier.getLongDisplayValue());
    637 
    638             }
    639         }
    640 
    641         return result == null ? null : result.toString();
    642     }
    643 
    644     @Override
    645     public int compareTo(FolderConfiguration folderConfig) {
    646         // default are always at the top.
    647         if (isDefault()) {
    648             if (folderConfig.isDefault()) {
    649                 return 0;
    650             }
    651             return -1;
    652         }
    653 
    654         // now we compare the qualifiers
    655         for (int i = 0 ; i < INDEX_COUNT; i++) {
    656             ResourceQualifier qualifier1 = mQualifiers[i];
    657             ResourceQualifier qualifier2 = folderConfig.mQualifiers[i];
    658 
    659             if (qualifier1 == null) {
    660                 if (qualifier2 == null) {
    661                     continue;
    662                 } else {
    663                     return -1;
    664                 }
    665             } else {
    666                 if (qualifier2 == null) {
    667                     return 1;
    668                 } else {
    669                     int result = qualifier1.compareTo(qualifier2);
    670 
    671                     if (result == 0) {
    672                         continue;
    673                     }
    674 
    675                     return result;
    676                 }
    677             }
    678         }
    679 
    680         // if we arrive here, all the qualifier matches
    681         return 0;
    682     }
    683 
    684     /**
    685      * Returns the best matching {@link Configurable} for this configuration.
    686      *
    687      * @param configurables the list of {@link Configurable} to choose from.
    688      *
    689      * @return an item from the given list of {@link Configurable} or null.
    690      *
    691      * @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match
    692      */
    693     public Configurable findMatchingConfigurable(List<? extends Configurable> configurables) {
    694         //
    695         // 1: eliminate resources that contradict the reference configuration
    696         // 2: pick next qualifier type
    697         // 3: check if any resources use this qualifier, if no, back to 2, else move on to 4.
    698         // 4: eliminate resources that don't use this qualifier.
    699         // 5: if more than one resource left, go back to 2.
    700         //
    701         // The precedence of the qualifiers is more important than the number of qualifiers that
    702         // exactly match the device.
    703 
    704         // 1: eliminate resources that contradict
    705         ArrayList<Configurable> matchingConfigurables = new ArrayList<Configurable>();
    706         for (int i = 0 ; i < configurables.size(); i++) {
    707             Configurable res = configurables.get(i);
    708 
    709             if (res.getConfiguration().isMatchFor(this)) {
    710                 matchingConfigurables.add(res);
    711             }
    712         }
    713 
    714         // if there is only one match, just take it
    715         if (matchingConfigurables.size() == 1) {
    716             return matchingConfigurables.get(0);
    717         } else if (matchingConfigurables.size() == 0) {
    718             return null;
    719         }
    720 
    721         // 2. Loop on the qualifiers, and eliminate matches
    722         final int count = FolderConfiguration.getQualifierCount();
    723         for (int q = 0 ; q < count ; q++) {
    724             // look to see if one configurable has this qualifier.
    725             // At the same time also record the best match value for the qualifier (if applicable).
    726 
    727             // The reference value, to find the best match.
    728             // Note that this qualifier could be null. In which case any qualifier found in the
    729             // possible match, will all be considered best match.
    730             ResourceQualifier referenceQualifier = getQualifier(q);
    731 
    732             boolean found = false;
    733             ResourceQualifier bestMatch = null; // this is to store the best match.
    734             for (Configurable configurable : matchingConfigurables) {
    735                 ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
    736                 if (qualifier != null) {
    737                     // set the flag.
    738                     found = true;
    739 
    740                     // Now check for a best match. If the reference qualifier is null ,
    741                     // any qualifier is a "best" match (we don't need to record all of them.
    742                     // Instead the non compatible ones are removed below)
    743                     if (referenceQualifier != null) {
    744                         if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
    745                             bestMatch = qualifier;
    746                         }
    747                     }
    748                 }
    749             }
    750 
    751             // 4. If a configurable has a qualifier at the current index, remove all the ones that
    752             // do not have one, or whose qualifier value does not equal the best match found above
    753             // unless there's no reference qualifier, in which case they are all considered
    754             // "best" match.
    755             if (found) {
    756                 for (int i = 0 ; i < matchingConfigurables.size(); ) {
    757                     Configurable configurable = matchingConfigurables.get(i);
    758                     ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
    759 
    760                     if (qualifier == null) {
    761                         // this resources has no qualifier of this type: rejected.
    762                         matchingConfigurables.remove(configurable);
    763                     } else if (referenceQualifier != null && bestMatch != null &&
    764                             bestMatch.equals(qualifier) == false) {
    765                         // there's a reference qualifier and there is a better match for it than
    766                         // this resource, so we reject it.
    767                         matchingConfigurables.remove(configurable);
    768                     } else {
    769                         // looks like we keep this resource, move on to the next one.
    770                         i++;
    771                     }
    772                 }
    773 
    774                 // at this point we may have run out of matching resources before going
    775                 // through all the qualifiers.
    776                 if (matchingConfigurables.size() < 2) {
    777                     break;
    778                 }
    779             }
    780         }
    781 
    782         // Because we accept resources whose configuration have qualifiers where the reference
    783         // configuration doesn't, we can end up with more than one match. In this case, we just
    784         // take the first one.
    785         if (matchingConfigurables.size() == 0) {
    786             return null;
    787         }
    788         return matchingConfigurables.get(0);
    789     }
    790 
    791 
    792     /**
    793      * Returns whether the configuration is a match for the given reference config.
    794      * <p/>A match means that, for each qualifier of this config
    795      * <ul>
    796      * <li>The reference config has no value set
    797      * <li>or, the qualifier of the reference config is a match. Depending on the qualifier type
    798      * this does not mean the same exact value.</li>
    799      * </ul>
    800      * @param referenceConfig The reference configuration to test against.
    801      * @return true if the configuration matches.
    802      */
    803     public boolean isMatchFor(FolderConfiguration referenceConfig) {
    804         if (referenceConfig == null) {
    805             return false;
    806         }
    807 
    808         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    809             ResourceQualifier testQualifier = mQualifiers[i];
    810             ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i];
    811 
    812             // it's only a non match if both qualifiers are non-null, and they don't match.
    813             if (testQualifier != null && referenceQualifier != null &&
    814                         testQualifier.isMatchFor(referenceQualifier) == false) {
    815                 return false;
    816             }
    817         }
    818 
    819         return true;
    820     }
    821 
    822     /**
    823      * Returns the index of the first non null {@link ResourceQualifier} starting at index
    824      * <var>startIndex</var>
    825      * @param startIndex
    826      * @return -1 if no qualifier was found.
    827      */
    828     public int getHighestPriorityQualifier(int startIndex) {
    829         for (int i = startIndex ; i < INDEX_COUNT ; i++) {
    830             if (mQualifiers[i] != null) {
    831                 return i;
    832             }
    833         }
    834 
    835         return -1;
    836     }
    837 
    838     /**
    839      * Create default qualifiers.
    840      * <p/>This creates qualifiers with no values for all indices.
    841      */
    842     public void createDefault() {
    843         mQualifiers[INDEX_COUNTRY_CODE] = new CountryCodeQualifier();
    844         mQualifiers[INDEX_NETWORK_CODE] = new NetworkCodeQualifier();
    845         mQualifiers[INDEX_LANGUAGE] = new LanguageQualifier();
    846         mQualifiers[INDEX_REGION] = new RegionQualifier();
    847         mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = new SmallestScreenWidthQualifier();
    848         mQualifiers[INDEX_SCREEN_WIDTH] = new ScreenWidthQualifier();
    849         mQualifiers[INDEX_SCREEN_HEIGHT] = new ScreenHeightQualifier();
    850         mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = new ScreenSizeQualifier();
    851         mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier();
    852         mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier();
    853         mQualifiers[INDEX_UI_MODE] = new UiModeQualifier();
    854         mQualifiers[INDEX_NIGHT_MODE] = new NightModeQualifier();
    855         mQualifiers[INDEX_PIXEL_DENSITY] = new DensityQualifier();
    856         mQualifiers[INDEX_TOUCH_TYPE] = new TouchScreenQualifier();
    857         mQualifiers[INDEX_KEYBOARD_STATE] = new KeyboardStateQualifier();
    858         mQualifiers[INDEX_TEXT_INPUT_METHOD] = new TextInputMethodQualifier();
    859         mQualifiers[INDEX_NAVIGATION_STATE] = new NavigationStateQualifier();
    860         mQualifiers[INDEX_NAVIGATION_METHOD] = new NavigationMethodQualifier();
    861         mQualifiers[INDEX_SCREEN_DIMENSION] = new ScreenDimensionQualifier();
    862         mQualifiers[INDEX_VERSION] = new VersionQualifier();
    863     }
    864 
    865     /**
    866      * Returns an array of all the non null qualifiers.
    867      */
    868     public ResourceQualifier[] getQualifiers() {
    869         int count = 0;
    870         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    871             if (mQualifiers[i] != null) {
    872                 count++;
    873             }
    874         }
    875 
    876         ResourceQualifier[] array = new ResourceQualifier[count];
    877         int index = 0;
    878         for (int i = 0 ; i < INDEX_COUNT ; i++) {
    879             if (mQualifiers[i] != null) {
    880                 array[index++] = mQualifiers[i];
    881             }
    882         }
    883 
    884         return array;
    885     }
    886 }
    887