Home | History | Annotate | Download | only in textservice
      1 /*
      2  * Copyright (C) 2011 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.textservice;
     18 
     19 import android.content.ComponentName;
     20 import android.content.Context;
     21 import android.content.pm.PackageManager;
     22 import android.content.pm.ResolveInfo;
     23 import android.content.pm.ServiceInfo;
     24 import android.content.res.Resources;
     25 import android.content.res.TypedArray;
     26 import android.content.res.XmlResourceParser;
     27 import android.graphics.drawable.Drawable;
     28 import android.os.Parcel;
     29 import android.os.Parcelable;
     30 import android.util.AttributeSet;
     31 import android.util.PrintWriterPrinter;
     32 import android.util.Slog;
     33 import android.util.Xml;
     34 
     35 import org.xmlpull.v1.XmlPullParser;
     36 import org.xmlpull.v1.XmlPullParserException;
     37 
     38 import java.io.IOException;
     39 import java.io.PrintWriter;
     40 import java.util.ArrayList;
     41 
     42 /**
     43  * This class is used to specify meta information of a spell checker.
     44  */
     45 public final class SpellCheckerInfo implements Parcelable {
     46     private static final String TAG = SpellCheckerInfo.class.getSimpleName();
     47     private final ResolveInfo mService;
     48     private final String mId;
     49     private final int mLabel;
     50 
     51     /**
     52      * The spell checker setting activity's name, used by the system settings to
     53      * launch the setting activity.
     54      */
     55     private final String mSettingsActivityName;
     56 
     57     /**
     58      * The array of subtypes.
     59      */
     60     private final ArrayList<SpellCheckerSubtype> mSubtypes = new ArrayList<>();
     61 
     62     /**
     63      * Constructor.
     64      * @hide
     65      */
     66     public SpellCheckerInfo(Context context, ResolveInfo service)
     67             throws XmlPullParserException, IOException {
     68         mService = service;
     69         ServiceInfo si = service.serviceInfo;
     70         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
     71 
     72         final PackageManager pm = context.getPackageManager();
     73         int label = 0;
     74         String settingsActivityComponent = null;
     75 
     76         XmlResourceParser parser = null;
     77         try {
     78             parser = si.loadXmlMetaData(pm, SpellCheckerSession.SERVICE_META_DATA);
     79             if (parser == null) {
     80                 throw new XmlPullParserException("No "
     81                         + SpellCheckerSession.SERVICE_META_DATA + " meta-data");
     82             }
     83 
     84             final Resources res = pm.getResourcesForApplication(si.applicationInfo);
     85             final AttributeSet attrs = Xml.asAttributeSet(parser);
     86             int type;
     87             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
     88                     && type != XmlPullParser.START_TAG) {
     89             }
     90 
     91             final String nodeName = parser.getName();
     92             if (!"spell-checker".equals(nodeName)) {
     93                 throw new XmlPullParserException(
     94                         "Meta-data does not start with spell-checker tag");
     95             }
     96 
     97             TypedArray sa = res.obtainAttributes(attrs,
     98                     com.android.internal.R.styleable.SpellChecker);
     99             label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0);
    100             settingsActivityComponent = sa.getString(
    101                     com.android.internal.R.styleable.SpellChecker_settingsActivity);
    102             sa.recycle();
    103 
    104             final int depth = parser.getDepth();
    105             // Parse all subtypes
    106             while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
    107                     && type != XmlPullParser.END_DOCUMENT) {
    108                 if (type == XmlPullParser.START_TAG) {
    109                     final String subtypeNodeName = parser.getName();
    110                     if (!"subtype".equals(subtypeNodeName)) {
    111                         throw new XmlPullParserException(
    112                                 "Meta-data in spell-checker does not start with subtype tag");
    113                     }
    114                     final TypedArray a = res.obtainAttributes(
    115                             attrs, com.android.internal.R.styleable.SpellChecker_Subtype);
    116                     SpellCheckerSubtype subtype = new SpellCheckerSubtype(
    117                             a.getResourceId(com.android.internal.R.styleable
    118                                     .SpellChecker_Subtype_label, 0),
    119                             a.getString(com.android.internal.R.styleable
    120                                     .SpellChecker_Subtype_subtypeLocale),
    121                             a.getString(com.android.internal.R.styleable
    122                                     .SpellChecker_Subtype_languageTag),
    123                             a.getString(com.android.internal.R.styleable
    124                                     .SpellChecker_Subtype_subtypeExtraValue),
    125                             a.getInt(com.android.internal.R.styleable
    126                                     .SpellChecker_Subtype_subtypeId, 0));
    127                     mSubtypes.add(subtype);
    128                 }
    129             }
    130         } catch (Exception e) {
    131             Slog.e(TAG, "Caught exception: " + e);
    132             throw new XmlPullParserException(
    133                     "Unable to create context for: " + si.packageName);
    134         } finally {
    135             if (parser != null) parser.close();
    136         }
    137         mLabel = label;
    138         mSettingsActivityName = settingsActivityComponent;
    139     }
    140 
    141     /**
    142      * Constructor.
    143      * @hide
    144      */
    145     public SpellCheckerInfo(Parcel source) {
    146         mLabel = source.readInt();
    147         mId = source.readString();
    148         mSettingsActivityName = source.readString();
    149         mService = ResolveInfo.CREATOR.createFromParcel(source);
    150         source.readTypedList(mSubtypes, SpellCheckerSubtype.CREATOR);
    151     }
    152 
    153     /**
    154      * Return a unique ID for this spell checker.  The ID is generated from
    155      * the package and class name implementing the method.
    156      */
    157     public String getId() {
    158         return mId;
    159     }
    160 
    161     /**
    162      * Return the component of the service that implements.
    163      */
    164     public ComponentName getComponent() {
    165         return new ComponentName(
    166                 mService.serviceInfo.packageName, mService.serviceInfo.name);
    167     }
    168 
    169     /**
    170      * Return the .apk package that implements this.
    171      */
    172     public String getPackageName() {
    173         return mService.serviceInfo.packageName;
    174     }
    175 
    176     /**
    177      * Used to package this object into a {@link Parcel}.
    178      *
    179      * @param dest The {@link Parcel} to be written.
    180      * @param flags The flags used for parceling.
    181      */
    182     @Override
    183     public void writeToParcel(Parcel dest, int flags) {
    184         dest.writeInt(mLabel);
    185         dest.writeString(mId);
    186         dest.writeString(mSettingsActivityName);
    187         mService.writeToParcel(dest, flags);
    188         dest.writeTypedList(mSubtypes);
    189     }
    190 
    191 
    192     /**
    193      * Used to make this class parcelable.
    194      */
    195     public static final Parcelable.Creator<SpellCheckerInfo> CREATOR
    196             = new Parcelable.Creator<SpellCheckerInfo>() {
    197         @Override
    198         public SpellCheckerInfo createFromParcel(Parcel source) {
    199             return new SpellCheckerInfo(source);
    200         }
    201 
    202         @Override
    203         public SpellCheckerInfo[] newArray(int size) {
    204             return new SpellCheckerInfo[size];
    205         }
    206     };
    207 
    208     /**
    209      * Load the user-displayed label for this spell checker.
    210      *
    211      * @param pm Supply a PackageManager used to load the spell checker's resources.
    212      */
    213     public CharSequence loadLabel(PackageManager pm) {
    214         if (mLabel == 0 || pm == null) return "";
    215         return pm.getText(getPackageName(), mLabel, mService.serviceInfo.applicationInfo);
    216     }
    217 
    218     /**
    219      * Load the user-displayed icon for this spell checker.
    220      *
    221      * @param pm Supply a PackageManager used to load the spell checker's resources.
    222      */
    223     public Drawable loadIcon(PackageManager pm) {
    224         return mService.loadIcon(pm);
    225     }
    226 
    227 
    228     /**
    229      * Return the raw information about the Service implementing this
    230      * spell checker.  Do not modify the returned object.
    231      */
    232     public ServiceInfo getServiceInfo() {
    233         return mService.serviceInfo;
    234     }
    235 
    236     /**
    237      * Return the class name of an activity that provides a settings UI.
    238      * You can launch this activity be starting it with
    239      * an {@link android.content.Intent} whose action is MAIN and with an
    240      * explicit {@link android.content.ComponentName}
    241      * composed of {@link #getPackageName} and the class name returned here.
    242      *
    243      * <p>A null will be returned if there is no settings activity.
    244      */
    245     public String getSettingsActivity() {
    246         return mSettingsActivityName;
    247     }
    248 
    249     /**
    250      * Return the count of the subtypes.
    251      */
    252     public int getSubtypeCount() {
    253         return mSubtypes.size();
    254     }
    255 
    256     /**
    257      * Return the subtype at the specified index.
    258      *
    259      * @param index the index of the subtype to return.
    260      */
    261     public SpellCheckerSubtype getSubtypeAt(int index) {
    262         return mSubtypes.get(index);
    263     }
    264 
    265     /**
    266      * Used to make this class parcelable.
    267      */
    268     @Override
    269     public int describeContents() {
    270         return 0;
    271     }
    272 
    273     /**
    274      * @hide
    275      */
    276     public void dump(final PrintWriter pw, final String prefix) {
    277         pw.println(prefix + "mId=" + mId);
    278         pw.println(prefix + "mSettingsActivityName=" + mSettingsActivityName);
    279         pw.println(prefix + "Service:");
    280         mService.dump(new PrintWriterPrinter(pw), prefix + "  ");
    281         final int N = getSubtypeCount();
    282         for (int i = 0; i < N; i++) {
    283             final SpellCheckerSubtype st = getSubtypeAt(i);
    284             pw.println(prefix + "  " + "Subtype #" + i + ":");
    285             pw.println(prefix + "    " + "locale=" + st.getLocale()
    286                     + " languageTag=" + st.getLanguageTag());
    287             pw.println(prefix + "    " + "extraValue=" + st.getExtraValue());
    288         }
    289     }
    290 }
    291