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