/* Copyright (c) 2002-2011 by XMLVM.org * * Project Info: http://www.xmlvm.org * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package android.internal; import java.util.Locale; import android.content.res.Configuration; /** * * Representation of a folders. Besides its name it holds the folder's * components in individual fields to allow fast access. * */ public class Folder { /** * * Interface used to test whether a folder matches a values specified by a * device configuration. Various implementations test the various device * configuration values. * */ public interface Matcher { /** Constant indicating that a test did not match. */ public static final int NO_MATCH = 0; /** Constant indicating that a test matched partially. */ public static final int PARTIAL_MATCH = 1; /** Constant indicating that a test did match. */ public static final int FULL_MATCH = 2; /** * * Tests whether the provided configuration value matches the folder's * value (if specified). * * @param configuration * The configuration to be tested. * * @return NO_MATCH is the provided configuration value does not match, * PARTIAL_MATCH if the folder's configuration only matches * partially (i.e. possible for locales) or FULL_MATCH if both * values are equal. If the folder's value is unspecified it is * also considered not matching. * */ public int matches(Folder folder, Configuration configuration); } /** Matcher used to test the folder's locale. */ public static Matcher LOCALE_MATCHER; /** Matcher used to test the screen size. */ public static Matcher SCREENSIZE_MATCHER; /** Matcher used to test the screen aspect. */ public static Matcher SCREENASPECT_MATCHER; /** Matcher used to test the orientation. */ public static Matcher ORIENTATION_MATCHER; static { LOCALE_MATCHER = new Matcher() { /** * * Tests whether the provided locale matches the folder's locale (if * specified). The comparison in done in two steps. The first tests * for equality which means that both language and country have to * match. If this does not match the language only is considered. If * this matches and the folder's locale country is unspecified it is * considered to be a partial match. * * @param configuration * The configuration providing the locale to be tested. * * @return NO_MATCH is the provided locale does not match, folder's * locale only has PARTIAL_MATCH if the a language specified * which matches the provided locale or FULL_MATCH if both * locales are equal. If the folder's locale is unspecified * it is also considered not matching. * */ @Override public int matches(Folder folder, Configuration configuration) { // Folder's locale unspecified if (folder.locale == null) { return NO_MATCH; } // Locales equal (language and country are equal) if (folder.locale.equals(configuration.locale)) { return FULL_MATCH; } // Test language in case the folder's locale does not include a // country if (folder.locale.getCountry() == null || folder.locale.getCountry().equals("")) { if (folder.locale.getLanguage().equals(configuration.locale.getLanguage())) { return PARTIAL_MATCH; } } // Folder's locale specified but does not match return NO_MATCH; } }; SCREENSIZE_MATCHER = new Matcher() { /** * * Tests whether the provided screen size matches the folder's * screen size. * * @param configuration * The configuration providing the screen layout to be * tested. The size is encoded into an integer value as * specified by Android's Configuration object. * * @return FULL_MATCH if the screen size matches, NO_MATCH * otherwise. If the folder does not specify a screen size * it is considered as not matching. * */ @Override public int matches(Folder folder, Configuration configuration) { int size = folder.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK; if (size == Configuration.SCREENLAYOUT_SIZE_UNDEFINED) { return NO_MATCH; } return size == (configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) ? FULL_MATCH : NO_MATCH; } }; SCREENASPECT_MATCHER = new Matcher() { /** * * Tests whether the provided screen aspect matches the folder's * screen aspect. * * @param configuration * The configuration providing the screen aspect to be * tested. The size is encoded into an integer value as * specified by Android's Configuration object. * * @return FULL_MATCH if the screen aspect matches, NO_MATCH * otherwise. If the folder does not specify a screen aspect * it is considered as not matching. * */ @Override public int matches(Folder folder, Configuration configuration) { int aspect = folder.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK; if (aspect == Configuration.SCREENLAYOUT_LONG_UNDEFINED) { return NO_MATCH; } return aspect == (configuration.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) ? FULL_MATCH : NO_MATCH; } }; ORIENTATION_MATCHER = new Matcher() { /** * * Tests whether the provided orientation matches the folder's * orientation. * * @param configuration * The configuration providing the orientation to be * tested. The orientation is encoded into an integer * value as specified by Android's Configuration object. * * @return FULL_MATCH if the orientation matches, NO_MATCH * otherwise. If the folder does not specify an orienation * it is considered as not matching. * */ @Override public int matches(Folder folder, Configuration configuration) { if (folder.orientation == Configuration.ORIENTATION_UNDEFINED) { return NO_MATCH; } return folder.orientation == configuration.orientation ? FULL_MATCH : NO_MATCH; } }; }; /** The folder's name. */ private String name = null; /** The folder's locale. */ private Locale locale = null; /** The folder's screen layout: resolution and aspect. */ private int screenLayout = 0; /** The folder's orientation. */ private int orientation = 0; /** The folder's density. */ private int density = 0; /** * * Creates a new folder with the given name. The provided name will be used * to initialize the folder's various modifier fields. * * @param name * The name of the folder to be created. * */ public Folder(String name) { init(name); } /** * * Initializes the folder with the given name. The provided name will be * used to initialize the folder's various modifier fields. * * @param name * The name of the folder to be created. * */ private void init(String name) { // Clear all fields this.name = null; this.locale = null; this.screenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED | Configuration.SCREENLAYOUT_LONG_UNDEFINED; this.orientation = Configuration.ORIENTATION_UNDEFINED; this.density = Density.DENSITY_UNDEFINED; // Reinitialize if name is not null if (name != null) { // Initialize name with possibly trainling / removed. this.name = name.replace("/", ""); String[] chunks = splitIntoChunks(name); for (int i = 0; i < chunks.length; i++) { // Initialize locale if (locale == null && isLocale(chunks[i])) { if (chunks[i].length() == 5) { String[] lc = chunks[i].split("_"); locale = new Locale(lc[0], lc[1]); } else { locale = new Locale(chunks[i]); } continue; } // Initialize resolution if ((screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_UNDEFINED && initResolution(chunks[i])) { continue; } // Initialize aspect if ((screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) == Configuration.SCREENLAYOUT_LONG_MASK && initAspect(chunks[i])) { continue; } // Initialize orientation if (orientation == Configuration.ORIENTATION_UNDEFINED && initOrientation(chunks[i])) { continue; } // Initialize density if (density == Density.DENSITY_UNDEFINED && initDensity(chunks[i])) { continue; } // Modifier not supported or unknown // Assert.NOT_IMPLEMENTED(); } } } /** * * Splits the provided folder into chunks - each representing a device * configuration item. * * @param folder * The folder to be split. * * @return An array holding the folders chunks. * */ private String[] splitIntoChunks(String folder) { // Replace -r which indicate a locale region with _ String tmp = folder.replace("-r", "_"); // Remove possibly trailing / tmp = tmp.replace("/", ""); // Split folder name into chungs return tmp.split("-"); } /** * * Tests whether the provided String represents a locale. A string with * length 2 is considered to be a locale or a string with length 5 having a * _ sign at the third position. * * @param str * The string to be tested. * * @return true if the string represents a locale, false otherwise. * */ private boolean isLocale(String str) { if (str.length() == 2) { return true; } if (str.length() == 5 && str.charAt(2) == '_') { return true; } return false; } /** * * Tests whether the provided String represents a resolution. A string * holding one of small, normal or large (case-insensitive) is considered to * be a resolution modifier. If a resolution modifier was found, the * screenLayout field gets initialized. * * @param str * The string to be tested. * * @return true if the string represents a resolution, false otherwise. * */ private boolean initResolution(String str) { if (str.equalsIgnoreCase("small")) { screenLayout |= Configuration.SCREENLAYOUT_SIZE_SMALL; return true; } if (str.equalsIgnoreCase("normal")) { screenLayout |= Configuration.SCREENLAYOUT_SIZE_NORMAL; return true; } if (str.equalsIgnoreCase("large")) { screenLayout |= Configuration.SCREENLAYOUT_SIZE_LARGE; return true; } if (str.equalsIgnoreCase("xlarge")) { screenLayout |= Configuration.SCREENLAYOUT_SIZE_XLARGE; return true; } return false; } /** * * Tests whether the provided String represents an aspect. A string holding * one of long or notlong (case-insensitive) is considered to be an aspect * modifier. If an aspect modifier was found, the screenLayout field gets * initialized. * * @param str * The string to be tested. * * @return true if the string represents an aspect, false otherwise. * */ private boolean initAspect(String str) { if (str.equalsIgnoreCase("long")) { screenLayout |= Configuration.SCREENLAYOUT_LONG_YES; return true; } if (str.equalsIgnoreCase("notlong")) { screenLayout |= Configuration.SCREENLAYOUT_LONG_NO; return true; } return false; } /** * * Tests whether the provided String represents an orientation. A string * holding one of port, land or square (case-insensitive) is considered to * be an orientation modifier. If an orientation modifier was found, the * orientation field gets initialized. * * @param str * The string to be tested. * * @return true if the string represents an orientation, false otherwise. * */ private boolean initOrientation(String str) { if (str.equalsIgnoreCase("port")) { orientation = Configuration.ORIENTATION_PORTRAIT; return true; } if (str.equalsIgnoreCase("land")) { orientation = Configuration.ORIENTATION_LANDSCAPE; return true; } if (str.equalsIgnoreCase("square")) { orientation = Configuration.ORIENTATION_SQUARE; return true; } return false; } /** * * Tests whether the provided String represents a density. A string holding * one of ldpi, mdpi, hdpi or nodpi (case-insensitive) is considered to be a * density modifier. If a density modifier was found, the density field gets * initialized. * * @param str * The string to be tested. * * @return true if the string represents a density, false otherwise. * */ private boolean initDensity(String str) { if (str.equalsIgnoreCase("ldpi")) { density = Density.DENSITY_LOW; return true; } if (str.equalsIgnoreCase("mdpi")) { density = Density.DENSITY_MEDIUM; return true; } if (str.equalsIgnoreCase("hdpi")) { density = Density.DENSITY_HIGH; return true; } if (str.equalsIgnoreCase("nodpi")) { density = Density.DENSITY_NONE; return true; } return false; } /** * * Tests whether this folder contradicts a given device configuration. * * @param configuration * The configuration to test the folder against. * * @return true if the folder contradicts the configuration, false * otherwise. * */ public boolean contradicts(Configuration configuration) { // Test locale: the language first and if specified the country if (locale != null) { if (!locale.getLanguage().equals(configuration.locale.getLanguage())) { return true; } if (locale.getCountry() != null && !locale.getCountry().equals("") && !locale.getCountry().equals(configuration.locale.getCountry())) { return true; } } // Test screen resolution if ((screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) != Configuration.SCREENLAYOUT_SIZE_UNDEFINED && (screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) != (configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)) { return true; } // Test screen aspect if ((screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) != Configuration.SCREENLAYOUT_LONG_UNDEFINED && (screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) != (configuration.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK)) { return true; } // Test orientation if (orientation != Configuration.ORIENTATION_UNDEFINED && orientation != configuration.orientation) { return true; } return false; } public String getName() { return name; } public int getDensity() { return density; } }