Home | History | Annotate | Download | only in inputmethod
      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 
     17 package android.view.inputmethod;
     18 
     19 import org.xmlpull.v1.XmlPullParser;
     20 import org.xmlpull.v1.XmlPullParserException;
     21 
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.pm.ApplicationInfo;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.PackageManager.NameNotFoundException;
     27 import android.content.pm.ResolveInfo;
     28 import android.content.pm.ServiceInfo;
     29 import android.content.res.Resources;
     30 import android.content.res.TypedArray;
     31 import android.content.res.XmlResourceParser;
     32 import android.graphics.drawable.Drawable;
     33 import android.os.Parcel;
     34 import android.os.Parcelable;
     35 import android.util.AttributeSet;
     36 import android.util.Printer;
     37 import android.util.Xml;
     38 
     39 import java.io.IOException;
     40 import java.util.ArrayList;
     41 import java.util.List;
     42 import java.util.Map;
     43 
     44 /**
     45  * This class is used to specify meta information of an input method.
     46  */
     47 public 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