1 /*
2  * Copyright (C) 2010 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.keyboard.internal;
18 
19 import android.content.res.TypedArray;
20 import android.util.Log;
21 import android.util.SparseArray;
22 
23 import com.android.inputmethod.latin.R;
24 import com.android.inputmethod.latin.utils.XmlParseUtils;
25 
26 import org.xmlpull.v1.XmlPullParser;
27 import org.xmlpull.v1.XmlPullParserException;
28 
29 import java.util.Arrays;
30 import java.util.HashMap;
31 
32 import javax.annotation.Nonnull;
33 import javax.annotation.Nullable;
34 
35 public final class KeyStylesSet {
36     private static final String TAG = KeyStylesSet.class.getSimpleName();
37     private static final boolean DEBUG = false;
38 
39     @Nonnull
40     private final HashMap<String, KeyStyle> mStyles = new HashMap<>();
41 
42     @Nonnull
43     private final KeyboardTextsSet mTextsSet;
44     @Nonnull
45     private final KeyStyle mEmptyKeyStyle;
46     @Nonnull
47     private static final String EMPTY_STYLE_NAME = "<empty>";
48 
KeyStylesSet(@onnull final KeyboardTextsSet textsSet)49     public KeyStylesSet(@Nonnull final KeyboardTextsSet textsSet) {
50         mTextsSet = textsSet;
51         mEmptyKeyStyle = new EmptyKeyStyle(textsSet);
52         mStyles.put(EMPTY_STYLE_NAME, mEmptyKeyStyle);
53     }
54 
55     private static final class EmptyKeyStyle extends KeyStyle {
EmptyKeyStyle(@onnull final KeyboardTextsSet textsSet)56         EmptyKeyStyle(@Nonnull final KeyboardTextsSet textsSet) {
57             super(textsSet);
58         }
59 
60         @Override
61         @Nullable
getStringArray(final TypedArray a, final int index)62         public String[] getStringArray(final TypedArray a, final int index) {
63             return parseStringArray(a, index);
64         }
65 
66         @Override
67         @Nullable
getString(final TypedArray a, final int index)68         public String getString(final TypedArray a, final int index) {
69             return parseString(a, index);
70         }
71 
72         @Override
getInt(final TypedArray a, final int index, final int defaultValue)73         public int getInt(final TypedArray a, final int index, final int defaultValue) {
74             return a.getInt(index, defaultValue);
75         }
76 
77         @Override
getFlags(final TypedArray a, final int index)78         public int getFlags(final TypedArray a, final int index) {
79             return a.getInt(index, 0);
80         }
81     }
82 
83     private static final class DeclaredKeyStyle extends KeyStyle {
84         private final HashMap<String, KeyStyle> mStyles;
85         private final String mParentStyleName;
86         private final SparseArray<Object> mStyleAttributes = new SparseArray<>();
87 
DeclaredKeyStyle(@onnull final String parentStyleName, @Nonnull final KeyboardTextsSet textsSet, @Nonnull final HashMap<String, KeyStyle> styles)88         public DeclaredKeyStyle(@Nonnull final String parentStyleName,
89                 @Nonnull final KeyboardTextsSet textsSet,
90                 @Nonnull final HashMap<String, KeyStyle> styles) {
91             super(textsSet);
92             mParentStyleName = parentStyleName;
93             mStyles = styles;
94         }
95 
96         @Override
97         @Nullable
getStringArray(final TypedArray a, final int index)98         public String[] getStringArray(final TypedArray a, final int index) {
99             if (a.hasValue(index)) {
100                 return parseStringArray(a, index);
101             }
102             final Object value = mStyleAttributes.get(index);
103             if (value != null) {
104                 final String[] array = (String[])value;
105                 return Arrays.copyOf(array, array.length);
106             }
107             final KeyStyle parentStyle = mStyles.get(mParentStyleName);
108             return parentStyle.getStringArray(a, index);
109         }
110 
111         @Override
112         @Nullable
getString(final TypedArray a, final int index)113         public String getString(final TypedArray a, final int index) {
114             if (a.hasValue(index)) {
115                 return parseString(a, index);
116             }
117             final Object value = mStyleAttributes.get(index);
118             if (value != null) {
119                 return (String)value;
120             }
121             final KeyStyle parentStyle = mStyles.get(mParentStyleName);
122             return parentStyle.getString(a, index);
123         }
124 
125         @Override
getInt(final TypedArray a, final int index, final int defaultValue)126         public int getInt(final TypedArray a, final int index, final int defaultValue) {
127             if (a.hasValue(index)) {
128                 return a.getInt(index, defaultValue);
129             }
130             final Object value = mStyleAttributes.get(index);
131             if (value != null) {
132                 return (Integer)value;
133             }
134             final KeyStyle parentStyle = mStyles.get(mParentStyleName);
135             return parentStyle.getInt(a, index, defaultValue);
136         }
137 
138         @Override
getFlags(final TypedArray a, final int index)139         public int getFlags(final TypedArray a, final int index) {
140             final int parentFlags = mStyles.get(mParentStyleName).getFlags(a, index);
141             final Integer value = (Integer)mStyleAttributes.get(index);
142             final int styleFlags = (value != null) ? value : 0;
143             final int flags = a.getInt(index, 0);
144             return flags | styleFlags | parentFlags;
145         }
146 
readKeyAttributes(final TypedArray keyAttr)147         public void readKeyAttributes(final TypedArray keyAttr) {
148             // TODO: Currently not all Key attributes can be declared as style.
149             readString(keyAttr, R.styleable.Keyboard_Key_altCode);
150             readString(keyAttr, R.styleable.Keyboard_Key_keySpec);
151             readString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
152             readStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
153             readStringArray(keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys);
154             readFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags);
155             readString(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled);
156             readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn);
157             readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType);
158             readFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
159         }
160 
readString(final TypedArray a, final int index)161         private void readString(final TypedArray a, final int index) {
162             if (a.hasValue(index)) {
163                 mStyleAttributes.put(index, parseString(a, index));
164             }
165         }
166 
readInt(final TypedArray a, final int index)167         private void readInt(final TypedArray a, final int index) {
168             if (a.hasValue(index)) {
169                 mStyleAttributes.put(index, a.getInt(index, 0));
170             }
171         }
172 
readFlags(final TypedArray a, final int index)173         private void readFlags(final TypedArray a, final int index) {
174             if (a.hasValue(index)) {
175                 final Integer value = (Integer)mStyleAttributes.get(index);
176                 final int styleFlags = value != null ? value : 0;
177                 mStyleAttributes.put(index, a.getInt(index, 0) | styleFlags);
178             }
179         }
180 
readStringArray(final TypedArray a, final int index)181         private void readStringArray(final TypedArray a, final int index) {
182             if (a.hasValue(index)) {
183                 mStyleAttributes.put(index, parseStringArray(a, index));
184             }
185         }
186     }
187 
parseKeyStyleAttributes(final TypedArray keyStyleAttr, final TypedArray keyAttrs, final XmlPullParser parser)188     public void parseKeyStyleAttributes(final TypedArray keyStyleAttr, final TypedArray keyAttrs,
189             final XmlPullParser parser) throws XmlPullParserException {
190         final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
191         if (styleName == null) {
192             throw new XmlParseUtils.ParseException(
193                     KeyboardBuilder.TAG_KEY_STYLE + " has no styleName attribute", parser);
194         }
195         if (DEBUG) {
196             Log.d(TAG, String.format("<%s styleName=%s />",
197                     KeyboardBuilder.TAG_KEY_STYLE, styleName));
198             if (mStyles.containsKey(styleName)) {
199                 Log.d(TAG, KeyboardBuilder.TAG_KEY_STYLE + " " + styleName + " is overridden at "
200                         + parser.getPositionDescription());
201             }
202         }
203 
204         final String parentStyleInAttr = keyStyleAttr.getString(
205                 R.styleable.Keyboard_KeyStyle_parentStyle);
206         if (parentStyleInAttr != null && !mStyles.containsKey(parentStyleInAttr)) {
207             throw new XmlParseUtils.ParseException(
208                     "Unknown parentStyle " + parentStyleInAttr, parser);
209         }
210         final String parentStyleName = (parentStyleInAttr == null) ? EMPTY_STYLE_NAME
211                 : parentStyleInAttr;
212         final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName, mTextsSet, mStyles);
213         style.readKeyAttributes(keyAttrs);
214         mStyles.put(styleName, style);
215     }
216 
217     @Nonnull
getKeyStyle(final TypedArray keyAttr, final XmlPullParser parser)218     public KeyStyle getKeyStyle(final TypedArray keyAttr, final XmlPullParser parser)
219             throws XmlParseUtils.ParseException {
220         final String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle);
221         if (styleName == null) {
222             return mEmptyKeyStyle;
223         }
224         final KeyStyle style = mStyles.get(styleName);
225         if (style == null) {
226             throw new XmlParseUtils.ParseException("Unknown key style: " + styleName, parser);
227         }
228         return style;
229     }
230 }
231