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