Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2012 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.inputmethod.latin;
     18 
     19 import android.content.res.Resources;
     20 import android.content.res.TypedArray;
     21 import android.os.Build;
     22 import android.text.TextUtils;
     23 import android.util.Log;
     24 import android.util.TypedValue;
     25 
     26 import com.android.inputmethod.annotations.UsedForTesting;
     27 
     28 import java.util.ArrayList;
     29 import java.util.HashMap;
     30 
     31 public final class ResourceUtils {
     32     private static final String TAG = ResourceUtils.class.getSimpleName();
     33 
     34     public static final float UNDEFINED_RATIO = -1.0f;
     35     public static final int UNDEFINED_DIMENSION = -1;
     36 
     37     private ResourceUtils() {
     38         // This utility class is not publicly instantiable.
     39     }
     40 
     41     private static final HashMap<String, String> sDeviceOverrideValueMap =
     42             CollectionUtils.newHashMap();
     43 
     44     private static final String[] BUILD_KEYS_AND_VALUES = {
     45         "HARDWARE", Build.HARDWARE,
     46         "MODEL", Build.MODEL,
     47         "BRAND", Build.BRAND,
     48         "MANUFACTURER", Build.MANUFACTURER
     49     };
     50     private static final HashMap<String, String> sBuildKeyValues;
     51     private static final String sBuildKeyValuesDebugString;
     52 
     53     static {
     54         sBuildKeyValues = CollectionUtils.newHashMap();
     55         final ArrayList<String> keyValuePairs = CollectionUtils.newArrayList();
     56         final int keyCount = BUILD_KEYS_AND_VALUES.length / 2;
     57         for (int i = 0; i < keyCount; i++) {
     58             final int index = i * 2;
     59             final String key = BUILD_KEYS_AND_VALUES[index];
     60             final String value = BUILD_KEYS_AND_VALUES[index + 1];
     61             sBuildKeyValues.put(key, value);
     62             keyValuePairs.add(key + '=' + value);
     63         }
     64         sBuildKeyValuesDebugString = "[" + TextUtils.join(" ", keyValuePairs) + "]";
     65     }
     66 
     67     public static String getDeviceOverrideValue(final Resources res, final int overrideResId) {
     68         final int orientation = res.getConfiguration().orientation;
     69         final String key = overrideResId + "-" + orientation;
     70         if (sDeviceOverrideValueMap.containsKey(key)) {
     71             return sDeviceOverrideValueMap.get(key);
     72         }
     73 
     74         final String[] overrideArray = res.getStringArray(overrideResId);
     75         final String overrideValue = findConstantForKeyValuePairs(sBuildKeyValues, overrideArray);
     76         // The overrideValue might be an empty string.
     77         if (overrideValue != null) {
     78             Log.i(TAG, "Find override value:"
     79                     + " resource="+ res.getResourceEntryName(overrideResId)
     80                     + " build=" + sBuildKeyValuesDebugString
     81                     + " override=" + overrideValue);
     82             sDeviceOverrideValueMap.put(key, overrideValue);
     83             return overrideValue;
     84         }
     85 
     86         final String defaultValue = findDefaultConstant(overrideArray);
     87         // The defaultValue might be an empty string.
     88         if (defaultValue == null) {
     89             Log.w(TAG, "Couldn't find override value nor default value:"
     90                     + " resource="+ res.getResourceEntryName(overrideResId)
     91                     + " build=" + sBuildKeyValuesDebugString);
     92         } else {
     93             Log.i(TAG, "Found default value:"
     94                     + " resource="+ res.getResourceEntryName(overrideResId)
     95                     + " build=" + sBuildKeyValuesDebugString
     96                     + " default=" + defaultValue);
     97         }
     98         sDeviceOverrideValueMap.put(key, defaultValue);
     99         return defaultValue;
    100     }
    101 
    102     /**
    103      * Find the condition that fulfills specified key value pairs from an array of
    104      * "condition,constant", and return the corresponding string constant. A condition is
    105      * "pattern1[:pattern2...] (or an empty string for the default). A pattern is
    106      * "key=regexp_value" string. The condition matches only if all patterns of the condition
    107      * are true for the specified key value pairs.
    108      *
    109      * For example, "condition,constant" has the following format.
    110      * (See {@link ResourceUtilsTests#testFindConstantForKeyValuePairsRegexp()})
    111      *  - HARDWARE=mako,constantForNexus4
    112      *  - MODEL=Nexus 4:MANUFACTURER=LGE,constantForNexus4
    113      *  - ,defaultConstant
    114      *
    115      * @param keyValuePairs attributes to be used to look for a matched condition.
    116      * @param conditionConstantArray an array of "condition,constant" elements to be searched.
    117      * @return the constant part of the matched "condition,constant" element. Returns null if no
    118      * condition matches.
    119      */
    120     @UsedForTesting
    121     static String findConstantForKeyValuePairs(final HashMap<String, String> keyValuePairs,
    122             final String[] conditionConstantArray) {
    123         if (conditionConstantArray == null || keyValuePairs == null) {
    124             return null;
    125         }
    126         for (final String conditionConstant : conditionConstantArray) {
    127             final int posComma = conditionConstant.indexOf(',');
    128             if (posComma < 0) {
    129                 throw new RuntimeException("Array element has no comma: " + conditionConstant);
    130             }
    131             final String condition = conditionConstant.substring(0, posComma);
    132             if (condition.isEmpty()) {
    133                 // Default condition. The default condition should be searched by
    134                 // {@link #findConstantForDefault(String[])}.
    135                 continue;
    136             }
    137             if (fulfillsCondition(keyValuePairs, condition)) {
    138                 return conditionConstant.substring(posComma + 1);
    139             }
    140         }
    141         return null;
    142     }
    143 
    144     private static boolean fulfillsCondition(final HashMap<String,String> keyValuePairs,
    145             final String condition) {
    146         final String[] patterns = condition.split(":");
    147         // Check all patterns in a condition are true
    148         for (final String pattern : patterns) {
    149             final int posEqual = pattern.indexOf('=');
    150             if (posEqual < 0) {
    151                 throw new RuntimeException("Pattern has no '=': " + condition);
    152             }
    153             final String key = pattern.substring(0, posEqual);
    154             final String value = keyValuePairs.get(key);
    155             if (value == null) {
    156                 throw new RuntimeException("Found unknown key: " + condition);
    157             }
    158             final String patternRegexpValue = pattern.substring(posEqual + 1);
    159             if (!value.matches(patternRegexpValue)) {
    160                 return false;
    161             }
    162         }
    163         return true;
    164     }
    165 
    166     @UsedForTesting
    167     static String findDefaultConstant(final String[] conditionConstantArray) {
    168         if (conditionConstantArray == null) {
    169             return null;
    170         }
    171         for (final String condition : conditionConstantArray) {
    172             final int posComma = condition.indexOf(',');
    173             if (posComma < 0) {
    174                 throw new RuntimeException("Array element has no comma: " + condition);
    175             }
    176             if (posComma == 0) { // condition is empty.
    177                 return condition.substring(posComma + 1);
    178             }
    179         }
    180         return null;
    181     }
    182 
    183     public static boolean isValidFraction(final float fraction) {
    184         return fraction >= 0.0f;
    185     }
    186 
    187     // {@link Resources#getDimensionPixelSize(int)} returns at least one pixel size.
    188     public static boolean isValidDimensionPixelSize(final int dimension) {
    189         return dimension > 0;
    190     }
    191 
    192     // {@link Resources#getDimensionPixelOffset(int)} may return zero pixel offset.
    193     public static boolean isValidDimensionPixelOffset(final int dimension) {
    194         return dimension >= 0;
    195     }
    196 
    197     public static float getFraction(final TypedArray a, final int index, final float defValue) {
    198         final TypedValue value = a.peekValue(index);
    199         if (value == null || !isFractionValue(value)) {
    200             return defValue;
    201         }
    202         return a.getFraction(index, 1, 1, defValue);
    203     }
    204 
    205     public static float getFraction(final TypedArray a, final int index) {
    206         return getFraction(a, index, UNDEFINED_RATIO);
    207     }
    208 
    209     public static int getDimensionPixelSize(final TypedArray a, final int index) {
    210         final TypedValue value = a.peekValue(index);
    211         if (value == null || !isDimensionValue(value)) {
    212             return ResourceUtils.UNDEFINED_DIMENSION;
    213         }
    214         return a.getDimensionPixelSize(index, ResourceUtils.UNDEFINED_DIMENSION);
    215     }
    216 
    217     public static float getDimensionOrFraction(final TypedArray a, final int index, final int base,
    218             final float defValue) {
    219         final TypedValue value = a.peekValue(index);
    220         if (value == null) {
    221             return defValue;
    222         }
    223         if (isFractionValue(value)) {
    224             return a.getFraction(index, base, base, defValue);
    225         } else if (isDimensionValue(value)) {
    226             return a.getDimension(index, defValue);
    227         }
    228         return defValue;
    229     }
    230 
    231     public static int getEnumValue(final TypedArray a, final int index, final int defValue) {
    232         final TypedValue value = a.peekValue(index);
    233         if (value == null) {
    234             return defValue;
    235         }
    236         if (isIntegerValue(value)) {
    237             return a.getInt(index, defValue);
    238         }
    239         return defValue;
    240     }
    241 
    242     public static boolean isFractionValue(final TypedValue v) {
    243         return v.type == TypedValue.TYPE_FRACTION;
    244     }
    245 
    246     public static boolean isDimensionValue(final TypedValue v) {
    247         return v.type == TypedValue.TYPE_DIMENSION;
    248     }
    249 
    250     public static boolean isIntegerValue(final TypedValue v) {
    251         return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
    252     }
    253 
    254     public static boolean isStringValue(final TypedValue v) {
    255         return v.type == TypedValue.TYPE_STRING;
    256     }
    257 }
    258