• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ResourceUtils()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];
sBuildKeyValues.put(key, value)61             sBuildKeyValues.put(key, value);
62             keyValuePairs.add(key + '=' + value);
63         }
64         sBuildKeyValuesDebugString = "[" + TextUtils.join(" ", keyValuePairs) + "]";
65     }
66 
getDeviceOverrideValue(final Resources res, final int overrideResId)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
findConstantForKeyValuePairs(final HashMap<String, String> keyValuePairs, final String[] conditionConstantArray)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 
fulfillsCondition(final HashMap<String,String> keyValuePairs, final String condition)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
findDefaultConstant(final String[] conditionConstantArray)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 
isValidFraction(final float fraction)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.
isValidDimensionPixelSize(final int dimension)188     public static boolean isValidDimensionPixelSize(final int dimension) {
189         return dimension > 0;
190     }
191 
192     // {@link Resources#getDimensionPixelOffset(int)} may return zero pixel offset.
isValidDimensionPixelOffset(final int dimension)193     public static boolean isValidDimensionPixelOffset(final int dimension) {
194         return dimension >= 0;
195     }
196 
getFraction(final TypedArray a, final int index, final float defValue)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 
getFraction(final TypedArray a, final int index)205     public static float getFraction(final TypedArray a, final int index) {
206         return getFraction(a, index, UNDEFINED_RATIO);
207     }
208 
getDimensionPixelSize(final TypedArray a, final int index)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 
getDimensionOrFraction(final TypedArray a, final int index, final int base, final float defValue)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 
getEnumValue(final TypedArray a, final int index, final int defValue)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 
isFractionValue(final TypedValue v)242     public static boolean isFractionValue(final TypedValue v) {
243         return v.type == TypedValue.TYPE_FRACTION;
244     }
245 
isDimensionValue(final TypedValue v)246     public static boolean isDimensionValue(final TypedValue v) {
247         return v.type == TypedValue.TYPE_DIMENSION;
248     }
249 
isIntegerValue(final TypedValue v)250     public static boolean isIntegerValue(final TypedValue v) {
251         return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
252     }
253 
isStringValue(final TypedValue v)254     public static boolean isStringValue(final TypedValue v) {
255         return v.type == TypedValue.TYPE_STRING;
256     }
257 }
258