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 public int compareTo(FolderConfiguration folderConfig) { 645 // default are always at the top. 646 if (isDefault()) { 647 if (folderConfig.isDefault()) { 648 return 0; 649 } 650 return -1; 651 } 652 653 // now we compare the qualifiers 654 for (int i = 0 ; i < INDEX_COUNT; i++) { 655 ResourceQualifier qualifier1 = mQualifiers[i]; 656 ResourceQualifier qualifier2 = folderConfig.mQualifiers[i]; 657 658 if (qualifier1 == null) { 659 if (qualifier2 == null) { 660 continue; 661 } else { 662 return -1; 663 } 664 } else { 665 if (qualifier2 == null) { 666 return 1; 667 } else { 668 int result = qualifier1.compareTo(qualifier2); 669 670 if (result == 0) { 671 continue; 672 } 673 674 return result; 675 } 676 } 677 } 678 679 // if we arrive here, all the qualifier matches 680 return 0; 681 } 682 683 /** 684 * Returns the best matching {@link Configurable} for this configuration. 685 * 686 * @param configurables the list of {@link Configurable} to choose from. 687 * 688 * @return an item from the given list of {@link Configurable} or null. 689 * 690 * @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match 691 */ 692 public Configurable findMatchingConfigurable(List<? extends Configurable> configurables) { 693 // 694 // 1: eliminate resources that contradict the reference configuration 695 // 2: pick next qualifier type 696 // 3: check if any resources use this qualifier, if no, back to 2, else move on to 4. 697 // 4: eliminate resources that don't use this qualifier. 698 // 5: if more than one resource left, go back to 2. 699 // 700 // The precedence of the qualifiers is more important than the number of qualifiers that 701 // exactly match the device. 702 703 // 1: eliminate resources that contradict 704 ArrayList<Configurable> matchingConfigurables = new ArrayList<Configurable>(); 705 for (int i = 0 ; i < configurables.size(); i++) { 706 Configurable res = configurables.get(i); 707 708 if (res.getConfiguration().isMatchFor(this)) { 709 matchingConfigurables.add(res); 710 } 711 } 712 713 // if there is only one match, just take it 714 if (matchingConfigurables.size() == 1) { 715 return matchingConfigurables.get(0); 716 } else if (matchingConfigurables.size() == 0) { 717 return null; 718 } 719 720 // 2. Loop on the qualifiers, and eliminate matches 721 final int count = FolderConfiguration.getQualifierCount(); 722 for (int q = 0 ; q < count ; q++) { 723 // look to see if one configurable has this qualifier. 724 // At the same time also record the best match value for the qualifier (if applicable). 725 726 // The reference value, to find the best match. 727 // Note that this qualifier could be null. In which case any qualifier found in the 728 // possible match, will all be considered best match. 729 ResourceQualifier referenceQualifier = getQualifier(q); 730 731 boolean found = false; 732 ResourceQualifier bestMatch = null; // this is to store the best match. 733 for (Configurable configurable : matchingConfigurables) { 734 ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q); 735 if (qualifier != null) { 736 // set the flag. 737 found = true; 738 739 // Now check for a best match. If the reference qualifier is null , 740 // any qualifier is a "best" match (we don't need to record all of them. 741 // Instead the non compatible ones are removed below) 742 if (referenceQualifier != null) { 743 if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) { 744 bestMatch = qualifier; 745 } 746 } 747 } 748 } 749 750 // 4. If a configurable has a qualifier at the current index, remove all the ones that 751 // do not have one, or whose qualifier value does not equal the best match found above 752 // unless there's no reference qualifier, in which case they are all considered 753 // "best" match. 754 if (found) { 755 for (int i = 0 ; i < matchingConfigurables.size(); ) { 756 Configurable configurable = matchingConfigurables.get(i); 757 ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q); 758 759 if (qualifier == null) { 760 // this resources has no qualifier of this type: rejected. 761 matchingConfigurables.remove(configurable); 762 } else if (referenceQualifier != null && bestMatch != null && 763 bestMatch.equals(qualifier) == false) { 764 // there's a reference qualifier and there is a better match for it than 765 // this resource, so we reject it. 766 matchingConfigurables.remove(configurable); 767 } else { 768 // looks like we keep this resource, move on to the next one. 769 i++; 770 } 771 } 772 773 // at this point we may have run out of matching resources before going 774 // through all the qualifiers. 775 if (matchingConfigurables.size() < 2) { 776 break; 777 } 778 } 779 } 780 781 // Because we accept resources whose configuration have qualifiers where the reference 782 // configuration doesn't, we can end up with more than one match. In this case, we just 783 // take the first one. 784 if (matchingConfigurables.size() == 0) { 785 return null; 786 } 787 return matchingConfigurables.get(0); 788 } 789 790 791 /** 792 * Returns whether the configuration is a match for the given reference config. 793 * <p/>A match means that, for each qualifier of this config 794 * <ul> 795 * <li>The reference config has no value set 796 * <li>or, the qualifier of the reference config is a match. Depending on the qualifier type 797 * this does not mean the same exact value.</li> 798 * </ul> 799 * @param referenceConfig The reference configuration to test against. 800 * @return true if the configuration matches. 801 */ 802 public boolean isMatchFor(FolderConfiguration referenceConfig) { 803 if (referenceConfig == null) { 804 return false; 805 } 806 807 for (int i = 0 ; i < INDEX_COUNT ; i++) { 808 ResourceQualifier testQualifier = mQualifiers[i]; 809 ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i]; 810 811 // it's only a non match if both qualifiers are non-null, and they don't match. 812 if (testQualifier != null && referenceQualifier != null && 813 testQualifier.isMatchFor(referenceQualifier) == false) { 814 return false; 815 } 816 } 817 818 return true; 819 } 820 821 /** 822 * Returns the index of the first non null {@link ResourceQualifier} starting at index 823 * <var>startIndex</var> 824 * @param startIndex 825 * @return -1 if no qualifier was found. 826 */ 827 public int getHighestPriorityQualifier(int startIndex) { 828 for (int i = startIndex ; i < INDEX_COUNT ; i++) { 829 if (mQualifiers[i] != null) { 830 return i; 831 } 832 } 833 834 return -1; 835 } 836 837 /** 838 * Create default qualifiers. 839 * <p/>This creates qualifiers with no values for all indices. 840 */ 841 public void createDefault() { 842 mQualifiers[INDEX_COUNTRY_CODE] = new CountryCodeQualifier(); 843 mQualifiers[INDEX_NETWORK_CODE] = new NetworkCodeQualifier(); 844 mQualifiers[INDEX_LANGUAGE] = new LanguageQualifier(); 845 mQualifiers[INDEX_REGION] = new RegionQualifier(); 846 mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = new SmallestScreenWidthQualifier(); 847 mQualifiers[INDEX_SCREEN_WIDTH] = new ScreenWidthQualifier(); 848 mQualifiers[INDEX_SCREEN_HEIGHT] = new ScreenHeightQualifier(); 849 mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = new ScreenSizeQualifier(); 850 mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier(); 851 mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier(); 852 mQualifiers[INDEX_UI_MODE] = new UiModeQualifier(); 853 mQualifiers[INDEX_NIGHT_MODE] = new NightModeQualifier(); 854 mQualifiers[INDEX_PIXEL_DENSITY] = new DensityQualifier(); 855 mQualifiers[INDEX_TOUCH_TYPE] = new TouchScreenQualifier(); 856 mQualifiers[INDEX_KEYBOARD_STATE] = new KeyboardStateQualifier(); 857 mQualifiers[INDEX_TEXT_INPUT_METHOD] = new TextInputMethodQualifier(); 858 mQualifiers[INDEX_NAVIGATION_STATE] = new NavigationStateQualifier(); 859 mQualifiers[INDEX_NAVIGATION_METHOD] = new NavigationMethodQualifier(); 860 mQualifiers[INDEX_SCREEN_DIMENSION] = new ScreenDimensionQualifier(); 861 mQualifiers[INDEX_VERSION] = new VersionQualifier(); 862 } 863 864 /** 865 * Returns an array of all the non null qualifiers. 866 */ 867 public ResourceQualifier[] getQualifiers() { 868 int count = 0; 869 for (int i = 0 ; i < INDEX_COUNT ; i++) { 870 if (mQualifiers[i] != null) { 871 count++; 872 } 873 } 874 875 ResourceQualifier[] array = new ResourceQualifier[count]; 876 int index = 0; 877 for (int i = 0 ; i < INDEX_COUNT ; i++) { 878 if (mQualifiers[i] != null) { 879 array[index++] = mQualifiers[i]; 880 } 881 } 882 883 return array; 884 } 885 } 886