com.android.ide.common.resources.configuration.FolderConfiguration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdk-common Show documentation
Show all versions of sdk-common Show documentation
sdk-common library used by other Android tools libraries.
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.common.resources.configuration;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.Density;
import com.android.resources.ResourceFolderType;
import com.android.resources.ScreenOrientation;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterators;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
/**
* Represents the configuration for Resource Folders. All the properties have a default
* value which means that the property is not set.
*/
public final class FolderConfiguration implements Comparable {
@NonNull
private static final ResourceQualifier[] DEFAULT_QUALIFIERS;
static {
// get the default qualifiers.
FolderConfiguration defaultConfig = new FolderConfiguration();
defaultConfig.createDefault();
DEFAULT_QUALIFIERS = defaultConfig.getQualifiers();
}
private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT];
private static final int INDEX_COUNTRY_CODE = 0;
private static final int INDEX_NETWORK_CODE = 1;
private static final int INDEX_LANGUAGE = 2;
private static final int INDEX_REGION = 3;
private static final int INDEX_LAYOUT_DIR = 4;
private static final int INDEX_SMALLEST_SCREEN_WIDTH = 5;
private static final int INDEX_SCREEN_WIDTH = 6;
private static final int INDEX_SCREEN_HEIGHT = 7;
private static final int INDEX_SCREEN_LAYOUT_SIZE = 8;
private static final int INDEX_SCREEN_RATIO = 9;
private static final int INDEX_SCREEN_ORIENTATION = 10;
private static final int INDEX_UI_MODE = 11;
private static final int INDEX_NIGHT_MODE = 12;
private static final int INDEX_PIXEL_DENSITY = 13;
private static final int INDEX_TOUCH_TYPE = 14;
private static final int INDEX_KEYBOARD_STATE = 15;
private static final int INDEX_TEXT_INPUT_METHOD = 16;
private static final int INDEX_NAVIGATION_STATE = 17;
private static final int INDEX_NAVIGATION_METHOD = 18;
private static final int INDEX_SCREEN_DIMENSION = 19;
private static final int INDEX_VERSION = 20;
private static final int INDEX_COUNT = 21;
/**
* Creates a {@link FolderConfiguration} matching the folder segments.
* @param folderSegments The segments of the folder name. The first segments should contain
* the name of the folder
* @return a FolderConfiguration object, or null if the folder name isn't valid..
*/
@Nullable
public static FolderConfiguration getConfig(@NonNull String[] folderSegments) {
Iterator iterator = Iterators.forArray(folderSegments);
if (iterator.hasNext()) {
// Skip the first segment: it should be just the base folder, such as "values" or
// "layout"
iterator.next();
}
return getConfigFromQualifiers(iterator);
}
/**
* Creates a {@link FolderConfiguration} matching the folder segments.
* @param folderSegments The segments of the folder name. The first segments should contain
* the name of the folder
* @return a FolderConfiguration object, or null if the folder name isn't valid..
* @see FolderConfiguration#getConfig(String[])
*/
@Nullable
public static FolderConfiguration getConfig(@NonNull Iterable folderSegments) {
Iterator iterator = folderSegments.iterator();
if (iterator.hasNext()) {
// Skip the first segment: it should be just the base folder, such as "values" or
// "layout"
iterator.next();
}
return getConfigFromQualifiers(iterator);
}
/**
* Creates a {@link FolderConfiguration} matching the qualifiers.
*
* @param qualifiers the qualifiers.
*
* @return a FolderConfiguration object, or null if the folder name isn't valid..
*/
@Nullable
public static FolderConfiguration getConfigFromQualifiers(
@NonNull Iterable qualifiers) {
return getConfigFromQualifiers(qualifiers.iterator());
}
/**
* Creates a {@link FolderConfiguration} matching the qualifiers.
*
* @param qualifiers An iterator on the qualifiers.
*
* @return a FolderConfiguration object, or null if the folder name isn't valid..
*/
@Nullable
public static FolderConfiguration getConfigFromQualifiers(
@NonNull Iterator qualifiers) {
FolderConfiguration config = new FolderConfiguration();
// we are going to loop through the segments, and match them with the first
// available qualifier. If the segment doesn't match we try with the next qualifier.
// Because the order of the qualifier is fixed, we do not reset the first qualifier
// after each successful segment.
// If we run out of qualifier before processing all the segments, we fail.
int qualifierIndex = 0;
int qualifierCount = DEFAULT_QUALIFIERS.length;
while (qualifiers.hasNext()) {
String seg = qualifiers.next();
if (!seg.isEmpty()) {
seg = seg.toLowerCase(Locale.US); // no-op if string is already in lower case
while (qualifierIndex < qualifierCount &&
!DEFAULT_QUALIFIERS[qualifierIndex].checkAndSet(seg, config)) {
qualifierIndex++;
}
// if we reached the end of the qualifier we didn't find a matching qualifier.
if (qualifierIndex == qualifierCount) {
return null;
} else {
qualifierIndex++; // already processed this one
}
} else {
return null;
}
}
return config;
}
/**
* Creates a {@link FolderConfiguration} matching the given folder name.
*
* @param folderName the folder name
* @return a FolderConfiguration object, or null if the folder name isn't valid..
*/
@Nullable
public static FolderConfiguration getConfigForFolder(@NonNull String folderName) {
return getConfig(Splitter.on('-').split(folderName));
}
/**
* Returns the number of {@link ResourceQualifier} that make up a Folder configuration.
*/
public static int getQualifierCount() {
return INDEX_COUNT;
}
/**
* Sets the config from the qualifiers of a given config.
* This is equivalent to set(config, false)
* @param config the configuration to set
*
* @see #set(FolderConfiguration, boolean)
*/
public void set(@Nullable FolderConfiguration config) {
set(config, false /*nonFakeValuesOnly*/);
}
/**
* Sets the config from the qualifiers of a given config.
* @param config the configuration to set
* @param nonFakeValuesOnly if set to true this ignore qualifiers for which the
* current value is a fake value.
*
* @see ResourceQualifier#hasFakeValue()
*/
public void set(@Nullable FolderConfiguration config, boolean nonFakeValuesOnly) {
if (config != null) {
for (int i = 0 ; i < INDEX_COUNT ; i++) {
ResourceQualifier q = config.mQualifiers[i];
if (!nonFakeValuesOnly || q == null || !q.hasFakeValue()) {
mQualifiers[i] = q;
}
}
}
}
/**
* Reset the config.
* This makes qualifiers at all indices null
.
*/
public void reset() {
for (int i = 0 ; i < INDEX_COUNT ; i++) {
mQualifiers[i] = null;
}
}
/**
* Removes the qualifiers from the receiver if they are present (and valid)
* in the given configuration.
*/
public void substract(@NonNull FolderConfiguration config) {
for (int i = 0 ; i < INDEX_COUNT ; i++) {
if (config.mQualifiers[i] != null && config.mQualifiers[i].isValid()) {
mQualifiers[i] = null;
}
}
}
/**
* Adds the non-qualifiers from the given config.
* Qualifiers that are null in the given config do not change in the receiver.
*/
public void add(@NonNull FolderConfiguration config) {
for (int i = 0 ; i < INDEX_COUNT ; i++) {
if (config.mQualifiers[i] != null) {
mQualifiers[i] = config.mQualifiers[i];
}
}
}
/**
* Returns the first invalid qualifier, or null if they are all valid (or if none
* exists).
*/
@Nullable
public ResourceQualifier getInvalidQualifier() {
for (int i = 0 ; i < INDEX_COUNT ; i++) {
if (mQualifiers[i] != null && !mQualifiers[i].isValid()) {
return mQualifiers[i];
}
}
// all allocated qualifiers are valid, we return null.
return null;
}
/**
* Returns whether the Region qualifier is valid. Region qualifier can only be present if a
* Language qualifier is present as well.
* @return true if the Region qualifier is valid.
*/
public boolean checkRegion() {
if (mQualifiers[INDEX_LANGUAGE] == null && mQualifiers[INDEX_REGION] != null) {
return false;
}
return true;
}
/**
* Adds a qualifier to the {@link FolderConfiguration}
* @param qualifier the {@link ResourceQualifier} to add.
*/
public void addQualifier(@Nullable ResourceQualifier qualifier) {
if (qualifier instanceof CountryCodeQualifier) {
mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
} else if (qualifier instanceof NetworkCodeQualifier) {
mQualifiers[INDEX_NETWORK_CODE] = qualifier;
} else if (qualifier instanceof LanguageQualifier) {
mQualifiers[INDEX_LANGUAGE] = qualifier;
} else if (qualifier instanceof RegionQualifier) {
mQualifiers[INDEX_REGION] = qualifier;
} else if (qualifier instanceof LayoutDirectionQualifier) {
mQualifiers[INDEX_LAYOUT_DIR] = qualifier;
} else if (qualifier instanceof SmallestScreenWidthQualifier) {
mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = qualifier;
} else if (qualifier instanceof ScreenWidthQualifier) {
mQualifiers[INDEX_SCREEN_WIDTH] = qualifier;
} else if (qualifier instanceof ScreenHeightQualifier) {
mQualifiers[INDEX_SCREEN_HEIGHT] = qualifier;
} else if (qualifier instanceof ScreenSizeQualifier) {
mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = qualifier;
} else if (qualifier instanceof ScreenRatioQualifier) {
mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
} else if (qualifier instanceof ScreenOrientationQualifier) {
mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
} else if (qualifier instanceof UiModeQualifier) {
mQualifiers[INDEX_UI_MODE] = qualifier;
} else if (qualifier instanceof NightModeQualifier) {
mQualifiers[INDEX_NIGHT_MODE] = qualifier;
} else if (qualifier instanceof DensityQualifier) {
mQualifiers[INDEX_PIXEL_DENSITY] = qualifier;
} else if (qualifier instanceof TouchScreenQualifier) {
mQualifiers[INDEX_TOUCH_TYPE] = qualifier;
} else if (qualifier instanceof KeyboardStateQualifier) {
mQualifiers[INDEX_KEYBOARD_STATE] = qualifier;
} else if (qualifier instanceof TextInputMethodQualifier) {
mQualifiers[INDEX_TEXT_INPUT_METHOD] = qualifier;
} else if (qualifier instanceof NavigationStateQualifier) {
mQualifiers[INDEX_NAVIGATION_STATE] = qualifier;
} else if (qualifier instanceof NavigationMethodQualifier) {
mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
} else if (qualifier instanceof ScreenDimensionQualifier) {
mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
} else if (qualifier instanceof VersionQualifier) {
mQualifiers[INDEX_VERSION] = qualifier;
}
}
/**
* Removes a given qualifier from the {@link FolderConfiguration}.
* @param qualifier the {@link ResourceQualifier} to remove.
*/
public void removeQualifier(@NonNull ResourceQualifier qualifier) {
for (int i = 0 ; i < INDEX_COUNT ; i++) {
if (mQualifiers[i] == qualifier) {
mQualifiers[i] = null;
return;
}
}
}
/**
* Returns a qualifier by its index. The total number of qualifiers can be accessed by
* {@link #getQualifierCount()}.
* @param index the index of the qualifier to return.
* @return the qualifier or null if there are none at the index.
*/
@Nullable
public ResourceQualifier getQualifier(int index) {
return mQualifiers[index];
}
public void setCountryCodeQualifier(CountryCodeQualifier qualifier) {
mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
}
@Nullable
public CountryCodeQualifier getCountryCodeQualifier() {
return (CountryCodeQualifier)mQualifiers[INDEX_COUNTRY_CODE];
}
public void setNetworkCodeQualifier(NetworkCodeQualifier qualifier) {
mQualifiers[INDEX_NETWORK_CODE] = qualifier;
}
@Nullable
public NetworkCodeQualifier getNetworkCodeQualifier() {
return (NetworkCodeQualifier)mQualifiers[INDEX_NETWORK_CODE];
}
public void setLanguageQualifier(LanguageQualifier qualifier) {
mQualifiers[INDEX_LANGUAGE] = qualifier;
}
@Nullable
public LanguageQualifier getLanguageQualifier() {
return (LanguageQualifier)mQualifiers[INDEX_LANGUAGE];
}
public void setRegionQualifier(RegionQualifier qualifier) {
mQualifiers[INDEX_REGION] = qualifier;
}
@Nullable
public RegionQualifier getRegionQualifier() {
return (RegionQualifier)mQualifiers[INDEX_REGION];
}
public void setLayoutDirectionQualifier(LayoutDirectionQualifier qualifier) {
mQualifiers[INDEX_LAYOUT_DIR] = qualifier;
}
@Nullable
public LayoutDirectionQualifier getLayoutDirectionQualifier() {
return (LayoutDirectionQualifier)mQualifiers[INDEX_LAYOUT_DIR];
}
public void setSmallestScreenWidthQualifier(SmallestScreenWidthQualifier qualifier) {
mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = qualifier;
}
@Nullable
public SmallestScreenWidthQualifier getSmallestScreenWidthQualifier() {
return (SmallestScreenWidthQualifier) mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH];
}
public void setScreenWidthQualifier(ScreenWidthQualifier qualifier) {
mQualifiers[INDEX_SCREEN_WIDTH] = qualifier;
}
@Nullable
public ScreenWidthQualifier getScreenWidthQualifier() {
return (ScreenWidthQualifier) mQualifiers[INDEX_SCREEN_WIDTH];
}
public void setScreenHeightQualifier(ScreenHeightQualifier qualifier) {
mQualifiers[INDEX_SCREEN_HEIGHT] = qualifier;
}
@Nullable
public ScreenHeightQualifier getScreenHeightQualifier() {
return (ScreenHeightQualifier) mQualifiers[INDEX_SCREEN_HEIGHT];
}
public void setScreenSizeQualifier(ScreenSizeQualifier qualifier) {
mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = qualifier;
}
@Nullable
public ScreenSizeQualifier getScreenSizeQualifier() {
return (ScreenSizeQualifier)mQualifiers[INDEX_SCREEN_LAYOUT_SIZE];
}
public void setScreenRatioQualifier(ScreenRatioQualifier qualifier) {
mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
}
@Nullable
public ScreenRatioQualifier getScreenRatioQualifier() {
return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO];
}
public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) {
mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
}
@Nullable
public ScreenOrientationQualifier getScreenOrientationQualifier() {
return (ScreenOrientationQualifier)mQualifiers[INDEX_SCREEN_ORIENTATION];
}
public void setUiModeQualifier(UiModeQualifier qualifier) {
mQualifiers[INDEX_UI_MODE] = qualifier;
}
@Nullable
public UiModeQualifier getUiModeQualifier() {
return (UiModeQualifier)mQualifiers[INDEX_UI_MODE];
}
public void setNightModeQualifier(NightModeQualifier qualifier) {
mQualifiers[INDEX_NIGHT_MODE] = qualifier;
}
@Nullable
public NightModeQualifier getNightModeQualifier() {
return (NightModeQualifier)mQualifiers[INDEX_NIGHT_MODE];
}
public void setDensityQualifier(DensityQualifier qualifier) {
mQualifiers[INDEX_PIXEL_DENSITY] = qualifier;
}
@Nullable
public DensityQualifier getDensityQualifier() {
return (DensityQualifier)mQualifiers[INDEX_PIXEL_DENSITY];
}
public void setTouchTypeQualifier(TouchScreenQualifier qualifier) {
mQualifiers[INDEX_TOUCH_TYPE] = qualifier;
}
@Nullable
public TouchScreenQualifier getTouchTypeQualifier() {
return (TouchScreenQualifier)mQualifiers[INDEX_TOUCH_TYPE];
}
public void setKeyboardStateQualifier(KeyboardStateQualifier qualifier) {
mQualifiers[INDEX_KEYBOARD_STATE] = qualifier;
}
@Nullable
public KeyboardStateQualifier getKeyboardStateQualifier() {
return (KeyboardStateQualifier)mQualifiers[INDEX_KEYBOARD_STATE];
}
public void setTextInputMethodQualifier(TextInputMethodQualifier qualifier) {
mQualifiers[INDEX_TEXT_INPUT_METHOD] = qualifier;
}
@Nullable
public TextInputMethodQualifier getTextInputMethodQualifier() {
return (TextInputMethodQualifier)mQualifiers[INDEX_TEXT_INPUT_METHOD];
}
public void setNavigationStateQualifier(NavigationStateQualifier qualifier) {
mQualifiers[INDEX_NAVIGATION_STATE] = qualifier;
}
@Nullable
public NavigationStateQualifier getNavigationStateQualifier() {
return (NavigationStateQualifier)mQualifiers[INDEX_NAVIGATION_STATE];
}
public void setNavigationMethodQualifier(NavigationMethodQualifier qualifier) {
mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
}
@Nullable
public NavigationMethodQualifier getNavigationMethodQualifier() {
return (NavigationMethodQualifier)mQualifiers[INDEX_NAVIGATION_METHOD];
}
public void setScreenDimensionQualifier(ScreenDimensionQualifier qualifier) {
mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
}
@Nullable
public ScreenDimensionQualifier getScreenDimensionQualifier() {
return (ScreenDimensionQualifier)mQualifiers[INDEX_SCREEN_DIMENSION];
}
public void setVersionQualifier(VersionQualifier qualifier) {
mQualifiers[INDEX_VERSION] = qualifier;
}
@Nullable
public VersionQualifier getVersionQualifier() {
return (VersionQualifier)mQualifiers[INDEX_VERSION];
}
/**
* Normalize a folder configuration based on the API level of its qualifiers
*/
public void normalize() {
int minSdk = 1;
for (ResourceQualifier qualifier : mQualifiers) {
if (qualifier != null) {
int min = qualifier.since();
if (min > minSdk) {
minSdk = min;
}
}
}
if (minSdk == 1) {
return;
}
if (mQualifiers[INDEX_VERSION] == null ||
((VersionQualifier)mQualifiers[INDEX_VERSION]).getVersion() < minSdk) {
mQualifiers[INDEX_VERSION] = new VersionQualifier(minSdk);
}
}
/**
* Updates the {@link SmallestScreenWidthQualifier}, {@link ScreenWidthQualifier}, and
* {@link ScreenHeightQualifier} based on the (required) values of
* {@link ScreenDimensionQualifier} {@link DensityQualifier}, and
* {@link ScreenOrientationQualifier}.
*
* Also the density cannot be {@link Density#NODPI} as it's not valid on a device.
*/
public void updateScreenWidthAndHeight() {
ResourceQualifier sizeQ = mQualifiers[INDEX_SCREEN_DIMENSION];
ResourceQualifier densityQ = mQualifiers[INDEX_PIXEL_DENSITY];
ResourceQualifier orientQ = mQualifiers[INDEX_SCREEN_ORIENTATION];
if (sizeQ != null && densityQ != null && orientQ != null) {
Density density = ((DensityQualifier) densityQ).getValue();
if (density == Density.NODPI) {
return;
}
ScreenOrientation orientation = ((ScreenOrientationQualifier) orientQ).getValue();
int size1 = ((ScreenDimensionQualifier) sizeQ).getValue1();
int size2 = ((ScreenDimensionQualifier) sizeQ).getValue2();
// make sure size1 is the biggest (should be the case, but make sure)
if (size1 < size2) {
int a = size1;
size1 = size2;
size2 = a;
}
// compute the dp. round them up since we want -w480dp to match a 480.5dp screen
int dp1 = (int) Math.ceil(size1 * Density.DEFAULT_DENSITY / density.getDpiValue());
int dp2 = (int) Math.ceil(size2 * Density.DEFAULT_DENSITY / density.getDpiValue());
setSmallestScreenWidthQualifier(new SmallestScreenWidthQualifier(dp2));
switch (orientation) {
case PORTRAIT:
setScreenWidthQualifier(new ScreenWidthQualifier(dp2));
setScreenHeightQualifier(new ScreenHeightQualifier(dp1));
break;
case LANDSCAPE:
setScreenWidthQualifier(new ScreenWidthQualifier(dp1));
setScreenHeightQualifier(new ScreenHeightQualifier(dp2));
break;
case SQUARE:
setScreenWidthQualifier(new ScreenWidthQualifier(dp2));
setScreenHeightQualifier(new ScreenHeightQualifier(dp2));
break;
}
}
}
/**
* Returns whether an object is equals to the receiver.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof FolderConfiguration) {
FolderConfiguration fc = (FolderConfiguration)obj;
for (int i = 0 ; i < INDEX_COUNT ; i++) {
ResourceQualifier qualifier = mQualifiers[i];
ResourceQualifier fcQualifier = fc.mQualifiers[i];
if (qualifier != null) {
if (!qualifier.equals(fcQualifier)) {
return false;
}
} else if (fcQualifier != null) {
return false;
}
}
return true;
}
return false;
}
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Returns whether the Configuration has only default values.
*/
public boolean isDefault() {
for (ResourceQualifier irq : mQualifiers) {
if (irq != null) {
return false;
}
}
return true;
}
/**
* Returns the name of a folder with the configuration.
*/
@NonNull
public String getFolderName(@NonNull ResourceFolderType folder) {
StringBuilder result = new StringBuilder(folder.getName());
for (ResourceQualifier qualifier : mQualifiers) {
if (qualifier != null) {
String segment = qualifier.getFolderSegment();
if (segment != null && !segment.isEmpty()) {
result.append(SdkConstants.RES_QUALIFIER_SEP);
result.append(segment);
}
}
}
return result.toString();
}
/**
* Returns the folder configuration as a unique key
*/
@NonNull
public String getUniqueKey() {
StringBuilder result = new StringBuilder(100);
for (ResourceQualifier qualifier : mQualifiers) {
if (qualifier != null) {
String segment = qualifier.getFolderSegment();
if (segment != null && !segment.isEmpty()) {
result.append(SdkConstants.RES_QUALIFIER_SEP);
result.append(segment);
}
}
}
return result.toString();
}
/**
* Returns {@link #toDisplayString()}.
*/
@NonNull
@Override
public String toString() {
return toDisplayString();
}
/**
* Returns a string valid for display purpose.
*/
@NonNull
public String toDisplayString() {
if (isDefault()) {
return "default";
}
StringBuilder result = null;
int index = 0;
ResourceQualifier qualifier;
// pre- language/region qualifiers
while (index < INDEX_LANGUAGE) {
qualifier = mQualifiers[index++];
if (qualifier != null) {
if (result == null) {
result = new StringBuilder();
} else {
result.append(", "); //$NON-NLS-1$
}
result.append(qualifier.getLongDisplayValue());
}
}
// process the language/region qualifier in a custom way, if there are both non null.
if (mQualifiers[INDEX_LANGUAGE] != null && mQualifiers[INDEX_REGION] != null) {
String language = mQualifiers[INDEX_LANGUAGE].getLongDisplayValue();
String region = mQualifiers[INDEX_REGION].getLongDisplayValue();
if (result == null) {
result = new StringBuilder();
} else {
result.append(", "); //$NON-NLS-1$
}
result.append(String.format("Locale %s_%s", language, region)); //$NON-NLS-1$
index += 2;
}
// post language/region qualifiers.
while (index < INDEX_COUNT) {
qualifier = mQualifiers[index++];
if (qualifier != null) {
if (result == null) {
result = new StringBuilder();
} else {
result.append(", "); //$NON-NLS-1$
}
result.append(qualifier.getLongDisplayValue());
}
}
return result == null ? "" : result.toString();
}
/**
* Returns a string for display purposes which uses only the short names of the qualifiers
*/
@NonNull
public String toShortDisplayString() {
if (isDefault()) {
return "default";
}
StringBuilder result = new StringBuilder(100);
int index = 0;
// pre- language/region qualifiers
while (index < INDEX_COUNT) {
ResourceQualifier qualifier = mQualifiers[index++];
if (qualifier != null) {
if (result.length() > 0) {
result.append(',');
}
result.append(qualifier.getShortDisplayValue());
}
}
return result.toString();
}
@Override
public int compareTo(@NonNull FolderConfiguration folderConfig) {
// default are always at the top.
if (isDefault()) {
if (folderConfig.isDefault()) {
return 0;
}
return -1;
}
// now we compare the qualifiers
for (int i = 0 ; i < INDEX_COUNT; i++) {
ResourceQualifier qualifier1 = mQualifiers[i];
ResourceQualifier qualifier2 = folderConfig.mQualifiers[i];
if (qualifier1 == null) {
if (qualifier2 != null) {
return -1;
}
} else {
if (qualifier2 == null) {
return 1;
} else {
int result = qualifier1.compareTo(qualifier2);
if (result == 0) {
continue;
}
return result;
}
}
}
// if we arrive here, all the qualifier matches
return 0;
}
/**
* Returns the best matching {@link Configurable} for this configuration.
*
* @param configurables the list of {@link Configurable} to choose from.
*
* @return an item from the given list of {@link Configurable} or null.
*
* See http://d.android.com/guide/topics/resources/resources-i18n.html#best-match
*/
@Nullable
public Configurable findMatchingConfigurable(@Nullable List extends Configurable> configurables) {
// Because we skip qualifiers where reference configuration doesn't have a valid qualifier,
// we can end up with more than one match. In this case, we just take the first one.
List matches = findMatchingConfigurables(configurables);
return matches.isEmpty() ? null : matches.get(0);
}
/**
* Tries to eliminate as many {@link Configurable}s as possible. It skips the
* {@link ResourceQualifier} if it's not valid and assumes that all resources match it.
*
* @param configurables the list of {@code Configurable} to choose from.
*
* @return a list of items from the above list. This may be empty.
*/
@NonNull
public List findMatchingConfigurables(
@Nullable List extends Configurable> configurables) {
if (configurables == null) {
return Collections.emptyList();
}
//
// 1: eliminate resources that contradict the reference configuration
// 2: pick next qualifier type
// 3: check if any resources use this qualifier, if no, back to 2, else move on to 4.
// 4: eliminate resources that don't use this qualifier.
// 5: if more than one resource left, go back to 2.
//
// The precedence of the qualifiers is more important than the number of qualifiers that
// exactly match the device.
// 1: eliminate resources that contradict
ArrayList matchingConfigurables = new ArrayList();
for (Configurable res : configurables) {
final FolderConfiguration configuration = res.getConfiguration();
if (configuration != null && configuration.isMatchFor(this)) {
matchingConfigurables.add(res);
}
}
// if there is at most one match, just take it
if (matchingConfigurables.size() < 2) {
return matchingConfigurables;
}
// 2. Loop on the qualifiers, and eliminate matches
final int count = getQualifierCount();
for (int q = 0 ; q < count ; q++) {
// look to see if one configurable has this qualifier.
// At the same time also record the best match value for the qualifier (if applicable).
// The reference value, to find the best match.
// Note that this qualifier could be null. In which case any qualifier found in the
// possible match, will all be considered best match.
ResourceQualifier referenceQualifier = getQualifier(q);
// If referenceQualifier is null, we don't eliminate resources based on it.
if (referenceQualifier == null) {
continue;
}
boolean found = false;
ResourceQualifier bestMatch = null; // this is to store the best match.
for (Configurable configurable : matchingConfigurables) {
ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
if (qualifier != null) {
// set the flag.
found = true;
// Now check for a best match. If the reference qualifier is null ,
// any qualifier is a "best" match (we don't need to record all of them.
// Instead the non compatible ones are removed below)
if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
bestMatch = qualifier;
}
}
}
// 4. If a configurable has a qualifier at the current index, remove all the ones that
// do not have one, or whose qualifier value does not equal the best match found above
// unless there's no reference qualifier, in which case they are all considered
// "best" match.
if (found) {
for (int i = 0 ; i < matchingConfigurables.size(); ) {
Configurable configurable = matchingConfigurables.get(i);
ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
if (qualifier == null) {
// this resources has no qualifier of this type: rejected.
matchingConfigurables.remove(configurable);
} else if (bestMatch != null && !bestMatch.equals(qualifier)) {
// there's a reference qualifier and there is a better match for it than
// this resource, so we reject it.
matchingConfigurables.remove(configurable);
} else {
// looks like we keep this resource, move on to the next one.
//noinspection AssignmentToForLoopParameter
i++;
}
}
// at this point we may have run out of matching resources before going
// through all the qualifiers.
if (matchingConfigurables.size() < 2) {
break;
}
}
}
// We've exhausted all the qualifiers. If we still have matching ones left, return all.
return matchingConfigurables;
}
/**
* Returns whether the configuration is a match for the given reference config.
* A match means that, for each qualifier of this config
*
* - The reference config has no value set
*
- or, the qualifier of the reference config is a match. Depending on the qualifier type
* this does not mean the same exact value.
*
* @param referenceConfig The reference configuration to test against.
* @return true if the configuration matches.
*/
public boolean isMatchFor(@Nullable FolderConfiguration referenceConfig) {
if (referenceConfig == null) {
return false;
}
for (int i = 0 ; i < INDEX_COUNT ; i++) {
ResourceQualifier testQualifier = mQualifiers[i];
ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i];
// it's only a non match if both qualifiers are non-null, and they don't match.
if (testQualifier != null && referenceQualifier != null &&
!testQualifier.isMatchFor(referenceQualifier)) {
return false;
}
}
return true;
}
/**
* Returns the index of the first non null {@link ResourceQualifier} starting at index
* startIndex
* @param startIndex
* @return -1 if no qualifier was found.
*/
public int getHighestPriorityQualifier(int startIndex) {
for (int i = startIndex ; i < INDEX_COUNT ; i++) {
if (mQualifiers[i] != null) {
return i;
}
}
return -1;
}
/**
* Create default qualifiers.
* This creates qualifiers with no values for all indices.
*/
public void createDefault() {
mQualifiers[INDEX_COUNTRY_CODE] = new CountryCodeQualifier();
mQualifiers[INDEX_NETWORK_CODE] = new NetworkCodeQualifier();
mQualifiers[INDEX_LANGUAGE] = new LanguageQualifier();
mQualifiers[INDEX_REGION] = new RegionQualifier();
mQualifiers[INDEX_LAYOUT_DIR] = new LayoutDirectionQualifier();
mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = new SmallestScreenWidthQualifier();
mQualifiers[INDEX_SCREEN_WIDTH] = new ScreenWidthQualifier();
mQualifiers[INDEX_SCREEN_HEIGHT] = new ScreenHeightQualifier();
mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = new ScreenSizeQualifier();
mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier();
mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier();
mQualifiers[INDEX_UI_MODE] = new UiModeQualifier();
mQualifiers[INDEX_NIGHT_MODE] = new NightModeQualifier();
mQualifiers[INDEX_PIXEL_DENSITY] = new DensityQualifier();
mQualifiers[INDEX_TOUCH_TYPE] = new TouchScreenQualifier();
mQualifiers[INDEX_KEYBOARD_STATE] = new KeyboardStateQualifier();
mQualifiers[INDEX_TEXT_INPUT_METHOD] = new TextInputMethodQualifier();
mQualifiers[INDEX_NAVIGATION_STATE] = new NavigationStateQualifier();
mQualifiers[INDEX_NAVIGATION_METHOD] = new NavigationMethodQualifier();
mQualifiers[INDEX_SCREEN_DIMENSION] = new ScreenDimensionQualifier();
mQualifiers[INDEX_VERSION] = new VersionQualifier();
}
/**
* Returns an array of all the non null qualifiers.
*/
@NonNull
public ResourceQualifier[] getQualifiers() {
int count = 0;
for (int i = 0 ; i < INDEX_COUNT ; i++) {
if (mQualifiers[i] != null) {
count++;
}
}
ResourceQualifier[] array = new ResourceQualifier[count];
int index = 0;
for (int i = 0 ; i < INDEX_COUNT ; i++) {
if (mQualifiers[i] != null) {
array[index++] = mQualifiers[i];
}
}
return array;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy