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