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 17 package com.android.inputmethod.keyboard.internal; 18 19 import android.content.res.TypedArray; 20 import android.util.Log; 21 22 import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException; 23 import com.android.inputmethod.latin.R; 24 25 import org.xmlpull.v1.XmlPullParser; 26 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 30 public class KeyStyles { 31 private static final String TAG = "KeyStyles"; 32 private static final boolean DEBUG = false; 33 34 private final HashMap<String, DeclaredKeyStyle> mStyles = 35 new HashMap<String, DeclaredKeyStyle>(); 36 private static final KeyStyle EMPTY_KEY_STYLE = new EmptyKeyStyle(); 37 38 public interface KeyStyle { 39 public CharSequence[] getTextArray(TypedArray a, int index); 40 public CharSequence getText(TypedArray a, int index); 41 public int getInt(TypedArray a, int index, int defaultValue); 42 public int getFlag(TypedArray a, int index, int defaultValue); 43 public boolean getBoolean(TypedArray a, int index, boolean defaultValue); 44 } 45 46 /* package */ static class EmptyKeyStyle implements KeyStyle { 47 private EmptyKeyStyle() { 48 // Nothing to do. 49 } 50 51 @Override 52 public CharSequence[] getTextArray(TypedArray a, int index) { 53 return parseTextArray(a, index); 54 } 55 56 @Override 57 public CharSequence getText(TypedArray a, int index) { 58 return a.getText(index); 59 } 60 61 @Override 62 public int getInt(TypedArray a, int index, int defaultValue) { 63 return a.getInt(index, defaultValue); 64 } 65 66 @Override 67 public int getFlag(TypedArray a, int index, int defaultValue) { 68 return a.getInt(index, defaultValue); 69 } 70 71 @Override 72 public boolean getBoolean(TypedArray a, int index, boolean defaultValue) { 73 return a.getBoolean(index, defaultValue); 74 } 75 76 protected static CharSequence[] parseTextArray(TypedArray a, int index) { 77 if (!a.hasValue(index)) 78 return null; 79 final CharSequence text = a.getText(index); 80 return parseCsvText(text); 81 } 82 83 /* package */ static CharSequence[] parseCsvText(CharSequence text) { 84 final int size = text.length(); 85 if (size == 0) return null; 86 if (size == 1) return new CharSequence[] { text }; 87 final StringBuilder sb = new StringBuilder(); 88 ArrayList<CharSequence> list = null; 89 int start = 0; 90 for (int pos = 0; pos < size; pos++) { 91 final char c = text.charAt(pos); 92 if (c == ',') { 93 if (list == null) list = new ArrayList<CharSequence>(); 94 if (sb.length() == 0) { 95 list.add(text.subSequence(start, pos)); 96 } else { 97 list.add(sb.toString()); 98 sb.setLength(0); 99 } 100 start = pos + 1; 101 continue; 102 } else if (c == '\\') { 103 if (start == pos) { 104 // Skip escape character at the beginning of the value. 105 start++; 106 pos++; 107 } else { 108 if (start < pos && sb.length() == 0) 109 sb.append(text.subSequence(start, pos)); 110 pos++; 111 if (pos < size) 112 sb.append(text.charAt(pos)); 113 } 114 } else if (sb.length() > 0) { 115 sb.append(c); 116 } 117 } 118 if (list == null) { 119 return new CharSequence[] { sb.length() > 0 ? sb : text.subSequence(start, size) }; 120 } else { 121 list.add(sb.length() > 0 ? sb : text.subSequence(start, size)); 122 return list.toArray(new CharSequence[list.size()]); 123 } 124 } 125 } 126 127 private static class DeclaredKeyStyle extends EmptyKeyStyle { 128 private final HashMap<Integer, Object> mAttributes = new HashMap<Integer, Object>(); 129 130 @Override 131 public CharSequence[] getTextArray(TypedArray a, int index) { 132 return a.hasValue(index) 133 ? super.getTextArray(a, index) : (CharSequence[])mAttributes.get(index); 134 } 135 136 @Override 137 public CharSequence getText(TypedArray a, int index) { 138 return a.hasValue(index) 139 ? super.getText(a, index) : (CharSequence)mAttributes.get(index); 140 } 141 142 @Override 143 public int getInt(TypedArray a, int index, int defaultValue) { 144 final Integer value = (Integer)mAttributes.get(index); 145 return super.getInt(a, index, (value != null) ? value : defaultValue); 146 } 147 148 @Override 149 public int getFlag(TypedArray a, int index, int defaultValue) { 150 final Integer value = (Integer)mAttributes.get(index); 151 return super.getFlag(a, index, defaultValue) | (value != null ? value : 0); 152 } 153 154 @Override 155 public boolean getBoolean(TypedArray a, int index, boolean defaultValue) { 156 final Boolean value = (Boolean)mAttributes.get(index); 157 return super.getBoolean(a, index, (value != null) ? value : defaultValue); 158 } 159 160 private DeclaredKeyStyle() { 161 super(); 162 } 163 164 private void parseKeyStyleAttributes(TypedArray keyAttr) { 165 // TODO: Currently not all Key attributes can be declared as style. 166 readInt(keyAttr, R.styleable.Keyboard_Key_code); 167 readText(keyAttr, R.styleable.Keyboard_Key_keyLabel); 168 readText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); 169 readText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); 170 readTextArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); 171 readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption); 172 readInt(keyAttr, R.styleable.Keyboard_Key_keyIcon); 173 readInt(keyAttr, R.styleable.Keyboard_Key_keyIconPreview); 174 readInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted); 175 readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn); 176 readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType); 177 readBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable); 178 readBoolean(keyAttr, R.styleable.Keyboard_Key_enabled); 179 } 180 181 private void readText(TypedArray a, int index) { 182 if (a.hasValue(index)) 183 mAttributes.put(index, a.getText(index)); 184 } 185 186 private void readInt(TypedArray a, int index) { 187 if (a.hasValue(index)) 188 mAttributes.put(index, a.getInt(index, 0)); 189 } 190 191 private void readFlag(TypedArray a, int index) { 192 final Integer value = (Integer)mAttributes.get(index); 193 if (a.hasValue(index)) 194 mAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0)); 195 } 196 197 private void readBoolean(TypedArray a, int index) { 198 if (a.hasValue(index)) 199 mAttributes.put(index, a.getBoolean(index, false)); 200 } 201 202 private void readTextArray(TypedArray a, int index) { 203 final CharSequence[] value = parseTextArray(a, index); 204 if (value != null) 205 mAttributes.put(index, value); 206 } 207 208 private void addParent(DeclaredKeyStyle parentStyle) { 209 mAttributes.putAll(parentStyle.mAttributes); 210 } 211 } 212 213 public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs, 214 XmlPullParser parser) { 215 final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName); 216 if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />", 217 KeyboardBuilder.TAG_KEY_STYLE, styleName)); 218 if (mStyles.containsKey(styleName)) 219 throw new ParseException("duplicate key style declared: " + styleName, parser); 220 221 final DeclaredKeyStyle style = new DeclaredKeyStyle(); 222 if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) { 223 final String parentStyle = keyStyleAttr.getString( 224 R.styleable.Keyboard_KeyStyle_parentStyle); 225 final DeclaredKeyStyle parent = mStyles.get(parentStyle); 226 if (parent == null) 227 throw new ParseException("Unknown parentStyle " + parentStyle, parser); 228 style.addParent(parent); 229 } 230 style.parseKeyStyleAttributes(keyAttrs); 231 mStyles.put(styleName, style); 232 } 233 234 public KeyStyle getKeyStyle(String styleName) { 235 return mStyles.get(styleName); 236 } 237 238 public KeyStyle getEmptyKeyStyle() { 239 return EMPTY_KEY_STYLE; 240 } 241 } 242