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