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