InputMethodInfo.java revision ab751aa085433e9f735d2e7603459c6c7e9d2fb0
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.getResourceId(com.android.internal.R.styleable
146                                    .InputMethod_Subtype_imeSubtypeMode, 0),
147                            a.getString(com.android.internal.R.styleable
148                                    .InputMethod_Subtype_imeSubtypeExtraValue));
149                    mSubtypes.add(subtype);
150                }
151            }
152        } catch (NameNotFoundException e) {
153            throw new XmlPullParserException(
154                    "Unable to create context for: " + si.packageName);
155        } finally {
156            if (parser != null) parser.close();
157        }
158        mSettingsActivityName = settingsActivityComponent;
159        mIsDefaultResId = isDefaultResId;
160    }
161
162    InputMethodInfo(Parcel source) {
163        mId = source.readString();
164        mSettingsActivityName = source.readString();
165        mIsDefaultResId = source.readInt();
166        mService = ResolveInfo.CREATOR.createFromParcel(source);
167        source.readTypedList(mSubtypes, InputMethodSubtype.CREATOR);
168    }
169
170    /**
171     * Temporary API for creating a built-in input method.
172     */
173    public InputMethodInfo(String packageName, String className,
174            CharSequence label, String settingsActivity) {
175        ResolveInfo ri = new ResolveInfo();
176        ServiceInfo si = new ServiceInfo();
177        ApplicationInfo ai = new ApplicationInfo();
178        ai.packageName = packageName;
179        ai.enabled = true;
180        si.applicationInfo = ai;
181        si.enabled = true;
182        si.packageName = packageName;
183        si.name = className;
184        si.exported = true;
185        si.nonLocalizedLabel = label;
186        ri.serviceInfo = si;
187        mService = ri;
188        mId = new ComponentName(si.packageName, si.name).flattenToShortString();
189        mSettingsActivityName = settingsActivity;
190        mIsDefaultResId = 0;
191    }
192
193    /**
194     * Return a unique ID for this input method.  The ID is generated from
195     * the package and class name implementing the method.
196     */
197    public String getId() {
198        return mId;
199    }
200
201    /**
202     * Return the .apk package that implements this input method.
203     */
204    public String getPackageName() {
205        return mService.serviceInfo.packageName;
206    }
207
208    /**
209     * Return the class name of the service component that implements
210     * this input method.
211     */
212    public String getServiceName() {
213        return mService.serviceInfo.name;
214    }
215
216    /**
217     * Return the raw information about the Service implementing this
218     * input method.  Do not modify the returned object.
219     */
220    public ServiceInfo getServiceInfo() {
221        return mService.serviceInfo;
222    }
223
224    /**
225     * Return the component of the service that implements this input
226     * method.
227     */
228    public ComponentName getComponent() {
229        return new ComponentName(mService.serviceInfo.packageName,
230                mService.serviceInfo.name);
231    }
232
233    /**
234     * Load the user-displayed label for this input method.
235     *
236     * @param pm Supply a PackageManager used to load the input method's
237     * resources.
238     */
239    public CharSequence loadLabel(PackageManager pm) {
240        return mService.loadLabel(pm);
241    }
242
243    /**
244     * Load the user-displayed icon for this input method.
245     *
246     * @param pm Supply a PackageManager used to load the input method's
247     * resources.
248     */
249    public Drawable loadIcon(PackageManager pm) {
250        return mService.loadIcon(pm);
251    }
252
253    /**
254     * Return the class name of an activity that provides a settings UI for
255     * the input method.  You can launch this activity be starting it with
256     * an {@link android.content.Intent} whose action is MAIN and with an
257     * explicit {@link android.content.ComponentName}
258     * composed of {@link #getPackageName} and the class name returned here.
259     *
260     * <p>A null will be returned if there is no settings activity associated
261     * with the input method.
262     */
263    public String getSettingsActivity() {
264        return mSettingsActivityName;
265    }
266
267    /**
268     * Return the subtypes of Input Method.
269     */
270    public ArrayList<InputMethodSubtype> getSubtypes() {
271        return mSubtypes;
272    }
273
274    /**
275     * Return the resource identifier of a resource inside of this input
276     * method's .apk that determines whether it should be considered a
277     * default input method for the system.
278     */
279    public int getIsDefaultResourceId() {
280        return mIsDefaultResId;
281    }
282
283    public void dump(Printer pw, String prefix) {
284        pw.println(prefix + "mId=" + mId
285                + " mSettingsActivityName=" + mSettingsActivityName);
286        pw.println(prefix + "mIsDefaultResId=0x"
287                + Integer.toHexString(mIsDefaultResId));
288        pw.println(prefix + "Service:");
289        mService.dump(pw, prefix + "  ");
290    }
291
292    @Override
293    public String toString() {
294        return "InputMethodInfo{" + mId
295                + ", settings: "
296                + mSettingsActivityName + "}";
297    }
298
299    /**
300     * Used to test whether the given parameter object is an
301     * {@link InputMethodInfo} and its Id is the same to this one.
302     *
303     * @return true if the given parameter object is an
304     *         {@link InputMethodInfo} and its Id is the same to this one.
305     */
306    @Override
307    public boolean equals(Object o) {
308        if (o == this) return true;
309        if (o == null) return false;
310
311        if (!(o instanceof InputMethodInfo)) return false;
312
313        InputMethodInfo obj = (InputMethodInfo) o;
314        return mId.equals(obj.mId);
315    }
316
317    /**
318     * Used to package this object into a {@link Parcel}.
319     *
320     * @param dest The {@link Parcel} to be written.
321     * @param flags The flags used for parceling.
322     */
323    public void writeToParcel(Parcel dest, int flags) {
324        dest.writeString(mId);
325        dest.writeString(mSettingsActivityName);
326        dest.writeInt(mIsDefaultResId);
327        mService.writeToParcel(dest, flags);
328        dest.writeTypedList(mSubtypes);
329    }
330
331    /**
332     * Used to make this class parcelable.
333     */
334    public static final Parcelable.Creator<InputMethodInfo> CREATOR
335            = new Parcelable.Creator<InputMethodInfo>() {
336        public InputMethodInfo createFromParcel(Parcel source) {
337            return new InputMethodInfo(source);
338        }
339
340        public InputMethodInfo[] newArray(int size) {
341            return new InputMethodInfo[size];
342        }
343    };
344
345    public int describeContents() {
346        return 0;
347    }
348}
349