InputMethodInfo.java revision e62e6d8731ab1e02c1632ebc011792d07b902af8
1/* 2 * Copyright (C) 2007-2008 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 android.view.inputmethod; 18 19import org.xmlpull.v1.XmlPullParser; 20import org.xmlpull.v1.XmlPullParserException; 21 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.pm.ApplicationInfo; 25import android.content.pm.PackageManager; 26import android.content.pm.PackageManager.NameNotFoundException; 27import android.content.pm.ResolveInfo; 28import android.content.pm.ServiceInfo; 29import android.content.res.Resources; 30import android.content.res.TypedArray; 31import android.content.res.XmlResourceParser; 32import android.graphics.drawable.Drawable; 33import android.os.Parcel; 34import android.os.Parcelable; 35import android.util.AttributeSet; 36import android.util.Printer; 37import android.util.Slog; 38import android.util.Xml; 39 40import java.io.IOException; 41import java.util.ArrayList; 42import java.util.List; 43import java.util.Map; 44 45/** 46 * This class is used to specify meta information of an input method. 47 */ 48public final class InputMethodInfo implements Parcelable { 49 static final String TAG = "InputMethodInfo"; 50 51 /** 52 * The Service that implements this input method component. 53 */ 54 final ResolveInfo mService; 55 56 /** 57 * The unique string Id to identify the input method. This is generated 58 * from the input method component. 59 */ 60 final String mId; 61 62 /** 63 * The input method setting activity's name, used by the system settings to 64 * launch the setting activity of this input method. 65 */ 66 final String mSettingsActivityName; 67 68 /** 69 * The resource in the input method's .apk that holds a boolean indicating 70 * whether it should be considered the default input method for this 71 * system. This is a resource ID instead of the final value so that it 72 * can change based on the configuration (in particular locale). 73 */ 74 final int mIsDefaultResId; 75 76 /** 77 * The array of the subtypes. 78 */ 79 private final ArrayList<InputMethodSubtype> mSubtypes = new ArrayList<InputMethodSubtype>(); 80 81 private boolean mIsAuxIme; 82 83 /** 84 * Constructor. 85 * 86 * @param context The Context in which we are parsing the input method. 87 * @param service The ResolveInfo returned from the package manager about 88 * this input method's component. 89 */ 90 public InputMethodInfo(Context context, ResolveInfo service) 91 throws XmlPullParserException, IOException { 92 this(context, service, null); 93 } 94 95 /** 96 * Constructor. 97 * 98 * @param context The Context in which we are parsing the input method. 99 * @param service The ResolveInfo returned from the package manager about 100 * this input method's component. 101 * @param additionalSubtypes additional subtypes being added to this InputMethodInfo 102 * @hide 103 */ 104 public InputMethodInfo(Context context, ResolveInfo service, 105 Map<String, List<InputMethodSubtype>> additionalSubtypesMap) 106 throws XmlPullParserException, IOException { 107 mService = service; 108 ServiceInfo si = service.serviceInfo; 109 mId = new ComponentName(si.packageName, si.name).flattenToShortString(); 110 mIsAuxIme = true; 111 112 PackageManager pm = context.getPackageManager(); 113 String settingsActivityComponent = null; 114 int isDefaultResId = 0; 115 116 XmlResourceParser parser = null; 117 try { 118 parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA); 119 if (parser == null) { 120 throw new XmlPullParserException("No " 121 + InputMethod.SERVICE_META_DATA + " meta-data"); 122 } 123 124 Resources res = pm.getResourcesForApplication(si.applicationInfo); 125 126 AttributeSet attrs = Xml.asAttributeSet(parser); 127 128 int type; 129 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 130 && type != XmlPullParser.START_TAG) { 131 } 132 133 String nodeName = parser.getName(); 134 if (!"input-method".equals(nodeName)) { 135 throw new XmlPullParserException( 136 "Meta-data does not start with input-method tag"); 137 } 138 139 TypedArray sa = res.obtainAttributes(attrs, 140 com.android.internal.R.styleable.InputMethod); 141 settingsActivityComponent = sa.getString( 142 com.android.internal.R.styleable.InputMethod_settingsActivity); 143 isDefaultResId = sa.getResourceId( 144 com.android.internal.R.styleable.InputMethod_isDefault, 0); 145 sa.recycle(); 146 147 final int depth = parser.getDepth(); 148 // Parse all subtypes 149 while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 150 && type != XmlPullParser.END_DOCUMENT) { 151 if (type == XmlPullParser.START_TAG) { 152 nodeName = parser.getName(); 153 if (!"subtype".equals(nodeName)) { 154 throw new XmlPullParserException( 155 "Meta-data in input-method does not start with subtype tag"); 156 } 157 final TypedArray a = res.obtainAttributes( 158 attrs, com.android.internal.R.styleable.InputMethod_Subtype); 159 InputMethodSubtype subtype = new InputMethodSubtype( 160 a.getResourceId(com.android.internal.R.styleable 161 .InputMethod_Subtype_label, 0), 162 a.getResourceId(com.android.internal.R.styleable 163 .InputMethod_Subtype_icon, 0), 164 a.getString(com.android.internal.R.styleable 165 .InputMethod_Subtype_imeSubtypeLocale), 166 a.getString(com.android.internal.R.styleable 167 .InputMethod_Subtype_imeSubtypeMode), 168 a.getString(com.android.internal.R.styleable 169 .InputMethod_Subtype_imeSubtypeExtraValue), 170 a.getBoolean(com.android.internal.R.styleable 171 .InputMethod_Subtype_isAuxiliary, false), 172 a.getBoolean(com.android.internal.R.styleable 173 .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false), 174 a.getInt(com.android.internal.R.styleable 175 .InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */) 176 ); 177 if (!subtype.isAuxiliary()) { 178 mIsAuxIme = false; 179 } 180 mSubtypes.add(subtype); 181 } 182 } 183 } catch (NameNotFoundException e) { 184 throw new XmlPullParserException( 185 "Unable to create context for: " + si.packageName); 186 } finally { 187 if (parser != null) parser.close(); 188 } 189 190 if (mSubtypes.size() == 0) { 191 mIsAuxIme = false; 192 } 193 194 if (additionalSubtypesMap != null && additionalSubtypesMap.containsKey(mId)) { 195 final List<InputMethodSubtype> additionalSubtypes = additionalSubtypesMap.get(mId); 196 final int N = additionalSubtypes.size(); 197 for (int i = 0; i < N; ++i) { 198 final InputMethodSubtype subtype = additionalSubtypes.get(i); 199 if (!mSubtypes.contains(subtype)) { 200 mSubtypes.add(subtype); 201 } else { 202 Slog.w(TAG, "Duplicated subtype definition found: " 203 + subtype.getLocale() + ", " + subtype.getMode()); 204 } 205 } 206 } 207 mSettingsActivityName = settingsActivityComponent; 208 mIsDefaultResId = isDefaultResId; 209 } 210 211 InputMethodInfo(Parcel source) { 212 mId = source.readString(); 213 mSettingsActivityName = source.readString(); 214 mIsDefaultResId = source.readInt(); 215 mIsAuxIme = source.readInt() == 1; 216 mService = ResolveInfo.CREATOR.createFromParcel(source); 217 source.readTypedList(mSubtypes, InputMethodSubtype.CREATOR); 218 } 219 220 /** 221 * Temporary API for creating a built-in input method. 222 */ 223 public InputMethodInfo(String packageName, String className, 224 CharSequence label, String settingsActivity) { 225 ResolveInfo ri = new ResolveInfo(); 226 ServiceInfo si = new ServiceInfo(); 227 ApplicationInfo ai = new ApplicationInfo(); 228 ai.packageName = packageName; 229 ai.enabled = true; 230 si.applicationInfo = ai; 231 si.enabled = true; 232 si.packageName = packageName; 233 si.name = className; 234 si.exported = true; 235 si.nonLocalizedLabel = label; 236 ri.serviceInfo = si; 237 mService = ri; 238 mId = new ComponentName(si.packageName, si.name).flattenToShortString(); 239 mSettingsActivityName = settingsActivity; 240 mIsDefaultResId = 0; 241 mIsAuxIme = false; 242 } 243 244 /** 245 * Return a unique ID for this input method. The ID is generated from 246 * the package and class name implementing the method. 247 */ 248 public String getId() { 249 return mId; 250 } 251 252 /** 253 * Return the .apk package that implements this input method. 254 */ 255 public String getPackageName() { 256 return mService.serviceInfo.packageName; 257 } 258 259 /** 260 * Return the class name of the service component that implements 261 * this input method. 262 */ 263 public String getServiceName() { 264 return mService.serviceInfo.name; 265 } 266 267 /** 268 * Return the raw information about the Service implementing this 269 * input method. Do not modify the returned object. 270 */ 271 public ServiceInfo getServiceInfo() { 272 return mService.serviceInfo; 273 } 274 275 /** 276 * Return the component of the service that implements this input 277 * method. 278 */ 279 public ComponentName getComponent() { 280 return new ComponentName(mService.serviceInfo.packageName, 281 mService.serviceInfo.name); 282 } 283 284 /** 285 * Load the user-displayed label for this input method. 286 * 287 * @param pm Supply a PackageManager used to load the input method's 288 * resources. 289 */ 290 public CharSequence loadLabel(PackageManager pm) { 291 return mService.loadLabel(pm); 292 } 293 294 /** 295 * Load the user-displayed icon for this input method. 296 * 297 * @param pm Supply a PackageManager used to load the input method's 298 * resources. 299 */ 300 public Drawable loadIcon(PackageManager pm) { 301 return mService.loadIcon(pm); 302 } 303 304 /** 305 * Return the class name of an activity that provides a settings UI for 306 * the input method. You can launch this activity be starting it with 307 * an {@link android.content.Intent} whose action is MAIN and with an 308 * explicit {@link android.content.ComponentName} 309 * composed of {@link #getPackageName} and the class name returned here. 310 * 311 * <p>A null will be returned if there is no settings activity associated 312 * with the input method. 313 */ 314 public String getSettingsActivity() { 315 return mSettingsActivityName; 316 } 317 318 /** 319 * Return the count of the subtypes of Input Method. 320 */ 321 public int getSubtypeCount() { 322 return mSubtypes.size(); 323 } 324 325 /** 326 * Return the Input Method's subtype at the specified index. 327 * 328 * @param index the index of the subtype to return. 329 */ 330 public InputMethodSubtype getSubtypeAt(int index) { 331 return mSubtypes.get(index); 332 } 333 334 /** 335 * Return the resource identifier of a resource inside of this input 336 * method's .apk that determines whether it should be considered a 337 * default input method for the system. 338 */ 339 public int getIsDefaultResourceId() { 340 return mIsDefaultResId; 341 } 342 343 public void dump(Printer pw, String prefix) { 344 pw.println(prefix + "mId=" + mId 345 + " mSettingsActivityName=" + mSettingsActivityName); 346 pw.println(prefix + "mIsDefaultResId=0x" 347 + Integer.toHexString(mIsDefaultResId)); 348 pw.println(prefix + "Service:"); 349 mService.dump(pw, prefix + " "); 350 } 351 352 @Override 353 public String toString() { 354 return "InputMethodInfo{" + mId 355 + ", settings: " 356 + mSettingsActivityName + "}"; 357 } 358 359 /** 360 * Used to test whether the given parameter object is an 361 * {@link InputMethodInfo} and its Id is the same to this one. 362 * 363 * @return true if the given parameter object is an 364 * {@link InputMethodInfo} and its Id is the same to this one. 365 */ 366 @Override 367 public boolean equals(Object o) { 368 if (o == this) return true; 369 if (o == null) return false; 370 371 if (!(o instanceof InputMethodInfo)) return false; 372 373 InputMethodInfo obj = (InputMethodInfo) o; 374 return mId.equals(obj.mId); 375 } 376 377 @Override 378 public int hashCode() { 379 return mId.hashCode(); 380 } 381 382 /** 383 * @hide 384 */ 385 public boolean isAuxiliaryIme() { 386 return mIsAuxIme; 387 } 388 389 /** 390 * Used to package this object into a {@link Parcel}. 391 * 392 * @param dest The {@link Parcel} to be written. 393 * @param flags The flags used for parceling. 394 */ 395 @Override 396 public void writeToParcel(Parcel dest, int flags) { 397 dest.writeString(mId); 398 dest.writeString(mSettingsActivityName); 399 dest.writeInt(mIsDefaultResId); 400 dest.writeInt(mIsAuxIme ? 1 : 0); 401 mService.writeToParcel(dest, flags); 402 dest.writeTypedList(mSubtypes); 403 } 404 405 /** 406 * Used to make this class parcelable. 407 */ 408 public static final Parcelable.Creator<InputMethodInfo> CREATOR 409 = new Parcelable.Creator<InputMethodInfo>() { 410 @Override 411 public InputMethodInfo createFromParcel(Parcel source) { 412 return new InputMethodInfo(source); 413 } 414 415 @Override 416 public InputMethodInfo[] newArray(int size) { 417 return new InputMethodInfo[size]; 418 } 419 }; 420 421 @Override 422 public int describeContents() { 423 return 0; 424 } 425} 426