InputMethodSubtype.java revision 4f31353cb3b00c77c9420ef27ec949fd570ede3b
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 17package android.view.inputmethod; 18 19import android.content.Context; 20import android.content.pm.ApplicationInfo; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.text.TextUtils; 24import android.util.Slog; 25 26import java.util.ArrayList; 27import java.util.Arrays; 28import java.util.HashMap; 29import java.util.HashSet; 30import java.util.List; 31 32/** 33 * This class is used to specify meta information of a subtype contained in an input method. 34 * Subtype can describe locale (e.g. en_US, fr_FR...) and mode (e.g. voice, keyboard...), and is 35 * used for IME switch and settings. The input method subtype allows the system to bring up the 36 * specified subtype of the designated input method directly. 37 */ 38public final class InputMethodSubtype implements Parcelable { 39 private static final String TAG = InputMethodSubtype.class.getSimpleName(); 40 private static final String EXTRA_VALUE_PAIR_SEPARATOR = ","; 41 private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "="; 42 43 private final boolean mIsAuxiliary; 44 private final int mSubtypeHashCode; 45 private final int mSubtypeIconResId; 46 private final int mSubtypeNameResId; 47 private final String mSubtypeLocale; 48 private final String mSubtypeMode; 49 private final String mSubtypeExtraValue; 50 private HashMap<String, String> mExtraValueHashMapCache; 51 52 /** 53 * Constructor 54 * @param nameId The name of the subtype 55 * @param iconId The icon of the subtype 56 * @param locale The locale supported by the subtype 57 * @param modeId The mode supported by the subtype 58 * @param extraValue The extra value of the subtype 59 */ 60 InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue) { 61 this(nameId, iconId, locale, mode, extraValue, false); 62 } 63 64 /** 65 * Constructor 66 * @param nameId The name of the subtype 67 * @param iconId The icon of the subtype 68 * @param locale The locale supported by the subtype 69 * @param modeId The mode supported by the subtype 70 * @param extraValue The extra value of the subtype 71 * @param isAuxiliary true when this subtype is one shot subtype. 72 */ 73 InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue, 74 boolean isAuxiliary) { 75 mSubtypeNameResId = nameId; 76 mSubtypeIconResId = iconId; 77 mSubtypeLocale = locale != null ? locale : ""; 78 mSubtypeMode = mode != null ? mode : ""; 79 mSubtypeExtraValue = extraValue != null ? extraValue : ""; 80 mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue); 81 mIsAuxiliary = isAuxiliary; 82 } 83 84 InputMethodSubtype(Parcel source) { 85 String s; 86 mSubtypeNameResId = source.readInt(); 87 mSubtypeIconResId = source.readInt(); 88 s = source.readString(); 89 mSubtypeLocale = s != null ? s : ""; 90 s = source.readString(); 91 mSubtypeMode = s != null ? s : ""; 92 s = source.readString(); 93 mSubtypeExtraValue = s != null ? s : ""; 94 mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue); 95 mIsAuxiliary = (source.readInt() == 1); 96 } 97 98 /** 99 * @return the name of the subtype 100 */ 101 public int getNameResId() { 102 return mSubtypeNameResId; 103 } 104 105 /** 106 * @return the icon of the subtype 107 */ 108 public int getIconResId() { 109 return mSubtypeIconResId; 110 } 111 112 /** 113 * @return the locale of the subtype 114 */ 115 public String getLocale() { 116 return mSubtypeLocale; 117 } 118 119 /** 120 * @return the mode of the subtype 121 */ 122 public String getMode() { 123 return mSubtypeMode; 124 } 125 126 /** 127 * @return the extra value of the subtype 128 */ 129 public String getExtraValue() { 130 return mSubtypeExtraValue; 131 } 132 133 /** 134 * @return true if this subtype is one shot subtype. One shot subtype will not be shown in the 135 * ime switch list when this subtype is implicitly enabled. The framework will never 136 * switch the current ime to this subtype by switchToLastInputMethod in InputMethodManager. 137 */ 138 public boolean isAuxiliary() { 139 return mIsAuxiliary; 140 } 141 142 /** 143 * @param context Context will be used for getting Locale and PackageManager. 144 * @param packageName The package name of the IME 145 * @param appInfo The application info of the IME 146 * @return a display name for this subtype. The string resource of the label (mSubtypeNameResId) 147 * can have only one %s in it. If there is, the %s part will be replaced with the locale's 148 * display name by the formatter. If there is not, this method simply returns the string 149 * specified by mSubtypeNameResId. If mSubtypeNameResId is not specified (== 0), it's up to the 150 * framework to generate an appropriate display name. 151 */ 152 public CharSequence getDisplayName( 153 Context context, String packageName, ApplicationInfo appInfo) { 154 final String locale = context.getResources().getConfiguration().locale.getDisplayName(); 155 if (mSubtypeNameResId == 0) { 156 return locale; 157 } 158 final String subtypeName = context.getPackageManager().getText( 159 packageName, mSubtypeNameResId, appInfo).toString(); 160 if (!TextUtils.isEmpty(subtypeName)) { 161 return String.format(subtypeName, locale); 162 } else { 163 return locale; 164 } 165 } 166 167 private HashMap<String, String> getExtraValueHashMap() { 168 if (mExtraValueHashMapCache == null) { 169 mExtraValueHashMapCache = new HashMap<String, String>(); 170 final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR); 171 final int N = pairs.length; 172 for (int i = 0; i < N; ++i) { 173 final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR); 174 if (pair.length == 1) { 175 mExtraValueHashMapCache.put(pair[0], null); 176 } else if (pair.length > 1) { 177 if (pair.length > 2) { 178 Slog.w(TAG, "ExtraValue has two or more '='s"); 179 } 180 mExtraValueHashMapCache.put(pair[0], pair[1]); 181 } 182 } 183 } 184 return mExtraValueHashMapCache; 185 } 186 187 /** 188 * The string of ExtraValue in subtype should be defined as follows: 189 * example: key0,key1=value1,key2,key3,key4=value4 190 * @param key the key of extra value 191 * @return the subtype contains specified the extra value 192 */ 193 public boolean containsExtraValueKey(String key) { 194 return getExtraValueHashMap().containsKey(key); 195 } 196 197 /** 198 * The string of ExtraValue in subtype should be defined as follows: 199 * example: key0,key1=value1,key2,key3,key4=value4 200 * @param key the key of extra value 201 * @return the value of the specified key 202 */ 203 public String getExtraValueOf(String key) { 204 return getExtraValueHashMap().get(key); 205 } 206 207 @Override 208 public int hashCode() { 209 return mSubtypeHashCode; 210 } 211 212 @Override 213 public boolean equals(Object o) { 214 if (o instanceof InputMethodSubtype) { 215 InputMethodSubtype subtype = (InputMethodSubtype) o; 216 return (subtype.hashCode() == hashCode()) 217 && (subtype.getNameResId() == getNameResId()) 218 && (subtype.getMode().equals(getMode())) 219 && (subtype.getIconResId() == getIconResId()) 220 && (subtype.getLocale().equals(getLocale())) 221 && (subtype.getExtraValue().equals(getExtraValue())); 222 } 223 return false; 224 } 225 226 @Override 227 public int describeContents() { 228 return 0; 229 } 230 231 @Override 232 public void writeToParcel(Parcel dest, int parcelableFlags) { 233 dest.writeInt(mSubtypeNameResId); 234 dest.writeInt(mSubtypeIconResId); 235 dest.writeString(mSubtypeLocale); 236 dest.writeString(mSubtypeMode); 237 dest.writeString(mSubtypeExtraValue); 238 dest.writeInt(mIsAuxiliary ? 1 : 0); 239 } 240 241 public static final Parcelable.Creator<InputMethodSubtype> CREATOR 242 = new Parcelable.Creator<InputMethodSubtype>() { 243 @Override 244 public InputMethodSubtype createFromParcel(Parcel source) { 245 return new InputMethodSubtype(source); 246 } 247 248 @Override 249 public InputMethodSubtype[] newArray(int size) { 250 return new InputMethodSubtype[size]; 251 } 252 }; 253 254 private static int hashCodeInternal(String locale, String mode, String extraValue) { 255 return Arrays.hashCode(new Object[] {locale, mode, extraValue}); 256 } 257 258 /** 259 * Sort the list of InputMethodSubtype 260 * @param context Context will be used for getting localized strings from IME 261 * @param flags Flags for the sort order 262 * @param imi InputMethodInfo of which subtypes are subject to be sorted 263 * @param subtypeList List of InputMethodSubtype which will be sorted 264 * @return Sorted list of subtypes 265 * @hide 266 */ 267 public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi, 268 List<InputMethodSubtype> subtypeList) { 269 if (imi == null) return subtypeList; 270 final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>( 271 subtypeList); 272 final ArrayList<InputMethodSubtype> sortedList = new ArrayList<InputMethodSubtype>(); 273 int N = imi.getSubtypeCount(); 274 for (int i = 0; i < N; ++i) { 275 InputMethodSubtype subtype = imi.getSubtypeAt(i); 276 if (inputSubtypesSet.contains(subtype)) { 277 sortedList.add(subtype); 278 inputSubtypesSet.remove(subtype); 279 } 280 } 281 // If subtypes in inputSubtypesSet remain, that means these subtypes are not 282 // contained in imi, so the remaining subtypes will be appended. 283 for (InputMethodSubtype subtype: inputSubtypesSet) { 284 sortedList.add(subtype); 285 } 286 return sortedList; 287 } 288} 289