1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.inputmethod.keyboard.internal;
18
19import android.content.res.TypedArray;
20import android.util.Log;
21
22import com.android.inputmethod.keyboard.Keyboard;
23import com.android.inputmethod.latin.R;
24import com.android.inputmethod.latin.XmlParseUtils;
25
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlPullParserException;
28
29import java.util.HashMap;
30
31public class KeyStyles {
32    private static final String TAG = KeyStyles.class.getSimpleName();
33    private static final boolean DEBUG = false;
34
35    final HashMap<String, KeyStyle> mStyles = new HashMap<String, KeyStyle>();
36
37    final KeyboardTextsSet mTextsSet;
38    private final KeyStyle mEmptyKeyStyle;
39    private static final String EMPTY_STYLE_NAME = "<empty>";
40
41    public KeyStyles(KeyboardTextsSet textsSet) {
42        mTextsSet = textsSet;
43        mEmptyKeyStyle = new EmptyKeyStyle();
44        mStyles.put(EMPTY_STYLE_NAME, mEmptyKeyStyle);
45    }
46
47    public abstract class KeyStyle {
48        public abstract String[] getStringArray(TypedArray a, int index);
49        public abstract String getString(TypedArray a, int index);
50        public abstract int getInt(TypedArray a, int index, int defaultValue);
51        public abstract int getFlag(TypedArray a, int index);
52
53        protected String parseString(TypedArray a, int index) {
54            if (a.hasValue(index)) {
55                return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
56            }
57            return null;
58        }
59
60        protected String[] parseStringArray(TypedArray a, int index) {
61            if (a.hasValue(index)) {
62                return KeySpecParser.parseCsvString(a.getString(index), mTextsSet);
63            }
64            return null;
65        }
66    }
67
68    class EmptyKeyStyle extends KeyStyle {
69        @Override
70        public String[] getStringArray(TypedArray a, int index) {
71            return parseStringArray(a, index);
72        }
73
74        @Override
75        public String getString(TypedArray a, int index) {
76            return parseString(a, index);
77        }
78
79        @Override
80        public int getInt(TypedArray a, int index, int defaultValue) {
81            return a.getInt(index, defaultValue);
82        }
83
84        @Override
85        public int getFlag(TypedArray a, int index) {
86            return a.getInt(index, 0);
87        }
88    }
89
90    private class DeclaredKeyStyle extends KeyStyle {
91        private final String mParentStyleName;
92        private final HashMap<Integer, Object> mStyleAttributes = new HashMap<Integer, Object>();
93
94        public DeclaredKeyStyle(String parentStyleName) {
95            mParentStyleName = parentStyleName;
96        }
97
98        @Override
99        public String[] getStringArray(TypedArray a, int index) {
100            if (a.hasValue(index)) {
101                return parseStringArray(a, index);
102            }
103            if (mStyleAttributes.containsKey(index)) {
104                return (String[])mStyleAttributes.get(index);
105            }
106            final KeyStyle parentStyle = mStyles.get(mParentStyleName);
107            return parentStyle.getStringArray(a, index);
108        }
109
110        @Override
111        public String getString(TypedArray a, int index) {
112            if (a.hasValue(index)) {
113                return parseString(a, index);
114            }
115            if (mStyleAttributes.containsKey(index)) {
116                return (String)mStyleAttributes.get(index);
117            }
118            final KeyStyle parentStyle = mStyles.get(mParentStyleName);
119            return parentStyle.getString(a, index);
120        }
121
122        @Override
123        public int getInt(TypedArray a, int index, int defaultValue) {
124            if (a.hasValue(index)) {
125                return a.getInt(index, defaultValue);
126            }
127            if (mStyleAttributes.containsKey(index)) {
128                return (Integer)mStyleAttributes.get(index);
129            }
130            final KeyStyle parentStyle = mStyles.get(mParentStyleName);
131            return parentStyle.getInt(a, index, defaultValue);
132        }
133
134        @Override
135        public int getFlag(TypedArray a, int index) {
136            int value = a.getInt(index, 0);
137            if (mStyleAttributes.containsKey(index)) {
138                value |= (Integer)mStyleAttributes.get(index);
139            }
140            final KeyStyle parentStyle = mStyles.get(mParentStyleName);
141            return value | parentStyle.getFlag(a, index);
142        }
143
144        void readKeyAttributes(TypedArray keyAttr) {
145            // TODO: Currently not all Key attributes can be declared as style.
146            readString(keyAttr, R.styleable.Keyboard_Key_code);
147            readString(keyAttr, R.styleable.Keyboard_Key_altCode);
148            readString(keyAttr, R.styleable.Keyboard_Key_keyLabel);
149            readString(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
150            readString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
151            readStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
152            readStringArray(keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys);
153            readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags);
154            readString(keyAttr, R.styleable.Keyboard_Key_keyIcon);
155            readString(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled);
156            readString(keyAttr, R.styleable.Keyboard_Key_keyIconPreview);
157            readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn);
158            readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType);
159            readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
160        }
161
162        private void readString(TypedArray a, int index) {
163            if (a.hasValue(index)) {
164                mStyleAttributes.put(index, parseString(a, index));
165            }
166        }
167
168        private void readInt(TypedArray a, int index) {
169            if (a.hasValue(index)) {
170                mStyleAttributes.put(index, a.getInt(index, 0));
171            }
172        }
173
174        private void readFlag(TypedArray a, int index) {
175            if (a.hasValue(index)) {
176                final Integer value = (Integer)mStyleAttributes.get(index);
177                mStyleAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0));
178            }
179        }
180
181        private void readStringArray(TypedArray a, int index) {
182            if (a.hasValue(index)) {
183                mStyleAttributes.put(index, parseStringArray(a, index));
184            }
185        }
186    }
187
188    public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs,
189            XmlPullParser parser) throws XmlPullParserException {
190        final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
191        if (DEBUG) {
192            Log.d(TAG, String.format("<%s styleName=%s />",
193                    Keyboard.Builder.TAG_KEY_STYLE, styleName));
194            if (mStyles.containsKey(styleName)) {
195                Log.d(TAG, "key-style " + styleName + " is overridden at "
196                        + parser.getPositionDescription());
197            }
198        }
199
200        String parentStyleName = EMPTY_STYLE_NAME;
201        if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) {
202            parentStyleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_parentStyle);
203            if (!mStyles.containsKey(parentStyleName)) {
204                throw new XmlParseUtils.ParseException(
205                        "Unknown parentStyle " + parentStyleName, parser);
206            }
207        }
208        final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName);
209        style.readKeyAttributes(keyAttrs);
210        mStyles.put(styleName, style);
211    }
212
213    public KeyStyle getKeyStyle(TypedArray keyAttr, XmlPullParser parser)
214            throws XmlParseUtils.ParseException {
215        if (!keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) {
216            return mEmptyKeyStyle;
217        }
218        final String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle);
219        if (!mStyles.containsKey(styleName)) {
220            throw new XmlParseUtils.ParseException("Unknown key style: " + styleName, parser);
221        }
222        return mStyles.get(styleName);
223    }
224}
225