• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.SdkConstants;
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();
defaultConfig.createDefault()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      */
getConfig(String[] folderSegments)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      */
getQualifierCount()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      */
set(FolderConfiguration config)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      */
set(FolderConfiguration config, boolean nonFakeValuesOnly)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      */
reset()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      */
substract(FolderConfiguration config)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      */
add(FolderConfiguration config)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      */
getInvalidQualifier()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      */
checkRegion()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      */
addQualifier(ResourceQualifier qualifier)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      */
removeQualifier(ResourceQualifier qualifier)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      */
getQualifier(int index)293     public ResourceQualifier getQualifier(int index) {
294         return mQualifiers[index];
295     }
296 
setCountryCodeQualifier(CountryCodeQualifier qualifier)297     public void setCountryCodeQualifier(CountryCodeQualifier qualifier) {
298         mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
299     }
300 
getCountryCodeQualifier()301     public CountryCodeQualifier getCountryCodeQualifier() {
302         return (CountryCodeQualifier)mQualifiers[INDEX_COUNTRY_CODE];
303     }
304 
setNetworkCodeQualifier(NetworkCodeQualifier qualifier)305     public void setNetworkCodeQualifier(NetworkCodeQualifier qualifier) {
306         mQualifiers[INDEX_NETWORK_CODE] = qualifier;
307     }
308 
getNetworkCodeQualifier()309     public NetworkCodeQualifier getNetworkCodeQualifier() {
310         return (NetworkCodeQualifier)mQualifiers[INDEX_NETWORK_CODE];
311     }
312 
setLanguageQualifier(LanguageQualifier qualifier)313     public void setLanguageQualifier(LanguageQualifier qualifier) {
314         mQualifiers[INDEX_LANGUAGE] = qualifier;
315     }
316 
getLanguageQualifier()317     public LanguageQualifier getLanguageQualifier() {
318         return (LanguageQualifier)mQualifiers[INDEX_LANGUAGE];
319     }
320 
setRegionQualifier(RegionQualifier qualifier)321     public void setRegionQualifier(RegionQualifier qualifier) {
322         mQualifiers[INDEX_REGION] = qualifier;
323     }
324 
getRegionQualifier()325     public RegionQualifier getRegionQualifier() {
326         return (RegionQualifier)mQualifiers[INDEX_REGION];
327     }
328 
setSmallestScreenWidthQualifier(SmallestScreenWidthQualifier qualifier)329     public void setSmallestScreenWidthQualifier(SmallestScreenWidthQualifier qualifier) {
330         mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = qualifier;
331     }
332 
getSmallestScreenWidthQualifier()333     public SmallestScreenWidthQualifier getSmallestScreenWidthQualifier() {
334         return (SmallestScreenWidthQualifier) mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH];
335     }
336 
setScreenWidthQualifier(ScreenWidthQualifier qualifier)337     public void setScreenWidthQualifier(ScreenWidthQualifier qualifier) {
338         mQualifiers[INDEX_SCREEN_WIDTH] = qualifier;
339     }
340 
getScreenWidthQualifier()341     public ScreenWidthQualifier getScreenWidthQualifier() {
342         return (ScreenWidthQualifier) mQualifiers[INDEX_SCREEN_WIDTH];
343     }
344 
setScreenHeightQualifier(ScreenHeightQualifier qualifier)345     public void setScreenHeightQualifier(ScreenHeightQualifier qualifier) {
346         mQualifiers[INDEX_SCREEN_HEIGHT] = qualifier;
347     }
348 
getScreenHeightQualifier()349     public ScreenHeightQualifier getScreenHeightQualifier() {
350         return (ScreenHeightQualifier) mQualifiers[INDEX_SCREEN_HEIGHT];
351     }
352 
setScreenSizeQualifier(ScreenSizeQualifier qualifier)353     public void setScreenSizeQualifier(ScreenSizeQualifier qualifier) {
354         mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = qualifier;
355     }
356 
getScreenSizeQualifier()357     public ScreenSizeQualifier getScreenSizeQualifier() {
358         return (ScreenSizeQualifier)mQualifiers[INDEX_SCREEN_LAYOUT_SIZE];
359     }
360 
setScreenRatioQualifier(ScreenRatioQualifier qualifier)361     public void setScreenRatioQualifier(ScreenRatioQualifier qualifier) {
362         mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
363     }
364 
getScreenRatioQualifier()365     public ScreenRatioQualifier getScreenRatioQualifier() {
366         return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO];
367     }
368 
setScreenOrientationQualifier(ScreenOrientationQualifier qualifier)369     public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) {
370         mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
371     }
372 
getScreenOrientationQualifier()373     public ScreenOrientationQualifier getScreenOrientationQualifier() {
374         return (ScreenOrientationQualifier)mQualifiers[INDEX_SCREEN_ORIENTATION];
375     }
376 
setUiModeQualifier(UiModeQualifier qualifier)377     public void setUiModeQualifier(UiModeQualifier qualifier) {
378         mQualifiers[INDEX_UI_MODE] = qualifier;
379     }
380 
getUiModeQualifier()381     public UiModeQualifier getUiModeQualifier() {
382         return (UiModeQualifier)mQualifiers[INDEX_UI_MODE];
383     }
384 
setNightModeQualifier(NightModeQualifier qualifier)385     public void setNightModeQualifier(NightModeQualifier qualifier) {
386         mQualifiers[INDEX_NIGHT_MODE] = qualifier;
387     }
388 
getNightModeQualifier()389     public NightModeQualifier getNightModeQualifier() {
390         return (NightModeQualifier)mQualifiers[INDEX_NIGHT_MODE];
391     }
392 
setDensityQualifier(DensityQualifier qualifier)393     public void setDensityQualifier(DensityQualifier qualifier) {
394         mQualifiers[INDEX_PIXEL_DENSITY] = qualifier;
395     }
396 
getDensityQualifier()397     public DensityQualifier getDensityQualifier() {
398         return (DensityQualifier)mQualifiers[INDEX_PIXEL_DENSITY];
399     }
400 
setTouchTypeQualifier(TouchScreenQualifier qualifier)401     public void setTouchTypeQualifier(TouchScreenQualifier qualifier) {
402         mQualifiers[INDEX_TOUCH_TYPE] = qualifier;
403     }
404 
getTouchTypeQualifier()405     public TouchScreenQualifier getTouchTypeQualifier() {
406         return (TouchScreenQualifier)mQualifiers[INDEX_TOUCH_TYPE];
407     }
408 
setKeyboardStateQualifier(KeyboardStateQualifier qualifier)409     public void setKeyboardStateQualifier(KeyboardStateQualifier qualifier) {
410         mQualifiers[INDEX_KEYBOARD_STATE] = qualifier;
411     }
412 
getKeyboardStateQualifier()413     public KeyboardStateQualifier getKeyboardStateQualifier() {
414         return (KeyboardStateQualifier)mQualifiers[INDEX_KEYBOARD_STATE];
415     }
416 
setTextInputMethodQualifier(TextInputMethodQualifier qualifier)417     public void setTextInputMethodQualifier(TextInputMethodQualifier qualifier) {
418         mQualifiers[INDEX_TEXT_INPUT_METHOD] = qualifier;
419     }
420 
getTextInputMethodQualifier()421     public TextInputMethodQualifier getTextInputMethodQualifier() {
422         return (TextInputMethodQualifier)mQualifiers[INDEX_TEXT_INPUT_METHOD];
423     }
424 
setNavigationStateQualifier(NavigationStateQualifier qualifier)425     public void setNavigationStateQualifier(NavigationStateQualifier qualifier) {
426         mQualifiers[INDEX_NAVIGATION_STATE] = qualifier;
427     }
428 
getNavigationStateQualifier()429     public NavigationStateQualifier getNavigationStateQualifier() {
430         return (NavigationStateQualifier)mQualifiers[INDEX_NAVIGATION_STATE];
431     }
432 
setNavigationMethodQualifier(NavigationMethodQualifier qualifier)433     public void setNavigationMethodQualifier(NavigationMethodQualifier qualifier) {
434         mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
435     }
436 
getNavigationMethodQualifier()437     public NavigationMethodQualifier getNavigationMethodQualifier() {
438         return (NavigationMethodQualifier)mQualifiers[INDEX_NAVIGATION_METHOD];
439     }
440 
setScreenDimensionQualifier(ScreenDimensionQualifier qualifier)441     public void setScreenDimensionQualifier(ScreenDimensionQualifier qualifier) {
442         mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
443     }
444 
getScreenDimensionQualifier()445     public ScreenDimensionQualifier getScreenDimensionQualifier() {
446         return (ScreenDimensionQualifier)mQualifiers[INDEX_SCREEN_DIMENSION];
447     }
448 
setVersionQualifier(VersionQualifier qualifier)449     public void setVersionQualifier(VersionQualifier qualifier) {
450         mQualifiers[INDEX_VERSION] = qualifier;
451     }
452 
getVersionQualifier()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      */
updateScreenWidthAndHeight()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
equals(Object obj)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
hashCode()542     public int hashCode() {
543         return toString().hashCode();
544     }
545 
546     /**
547      * Returns whether the Configuration has only default values.
548      */
isDefault()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      */
getFolderName(ResourceFolderType folder)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(SdkConstants.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
toString()582     public String toString() {
583         return toDisplayString();
584     }
585 
586     /**
587      * Returns a string valid for display purpose.
588      */
toDisplayString()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
compareTo(FolderConfiguration folderConfig)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      */
findMatchingConfigurable(List<? extends Configurable> configurables)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      */
isMatchFor(FolderConfiguration referenceConfig)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      */
getHighestPriorityQualifier(int startIndex)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      */
createDefault()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      */
getQualifiers()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