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 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.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.content.pm.ServiceInfo; 27 import android.content.res.Resources; 28 import android.content.res.TypedArray; 29 import android.content.res.XmlResourceParser; 30 import android.graphics.drawable.Drawable; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.util.AttributeSet; 34 import android.util.Slog; 35 import android.util.Xml; 36 37 import java.io.IOException; 38 import java.util.ArrayList; 39 40 /** 41 * This class is used to specify meta information of an spell checker. 42 */ 43 public 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 the 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 /** 157 * Return the component of the service that implements. 158 */ 159 public ComponentName getComponent() { 160 return new ComponentName( 161 mService.serviceInfo.packageName, mService.serviceInfo.name); 162 } 163 164 /** 165 * Return the .apk package that implements this. 166 */ 167 public String getPackageName() { 168 return mService.serviceInfo.packageName; 169 } 170 171 /** 172 * Used to package this object into a {@link Parcel}. 173 * 174 * @param dest The {@link Parcel} to be written. 175 * @param flags The flags used for parceling. 176 */ 177 @Override 178 public void writeToParcel(Parcel dest, int flags) { 179 dest.writeInt(mLabel); 180 dest.writeString(mId); 181 dest.writeString(mSettingsActivityName); 182 mService.writeToParcel(dest, flags); 183 dest.writeTypedList(mSubtypes); 184 } 185 186 187 /** 188 * Used to make this class parcelable. 189 */ 190 public static final Parcelable.Creator<SpellCheckerInfo> CREATOR 191 = new Parcelable.Creator<SpellCheckerInfo>() { 192 @Override 193 public SpellCheckerInfo createFromParcel(Parcel source) { 194 return new SpellCheckerInfo(source); 195 } 196 197 @Override 198 public SpellCheckerInfo[] newArray(int size) { 199 return new SpellCheckerInfo[size]; 200 } 201 }; 202 203 /** 204 * Load the user-displayed label for this spell checker. 205 * 206 * @param pm Supply a PackageManager used to load the spell checker's resources. 207 */ 208 public CharSequence loadLabel(PackageManager pm) { 209 if (mLabel == 0 || pm == null) return ""; 210 return pm.getText(getPackageName(), mLabel, mService.serviceInfo.applicationInfo); 211 } 212 213 /** 214 * Load the user-displayed icon for this spell checker. 215 * 216 * @param pm Supply a PackageManager used to load the spell checker's resources. 217 */ 218 public Drawable loadIcon(PackageManager pm) { 219 return mService.loadIcon(pm); 220 } 221 222 223 /** 224 * Return the raw information about the Service implementing this 225 * spell checker. Do not modify the returned object. 226 */ 227 public ServiceInfo getServiceInfo() { 228 return mService.serviceInfo; 229 } 230 231 /** 232 * Return the class name of an activity that provides a settings UI. 233 * You can launch this activity be starting it with 234 * an {@link android.content.Intent} whose action is MAIN and with an 235 * explicit {@link android.content.ComponentName} 236 * composed of {@link #getPackageName} and the class name returned here. 237 * 238 * <p>A null will be returned if there is no settings activity. 239 */ 240 public String getSettingsActivity() { 241 return mSettingsActivityName; 242 } 243 244 /** 245 * Return the count of the subtypes. 246 */ 247 public int getSubtypeCount() { 248 return mSubtypes.size(); 249 } 250 251 /** 252 * Return the subtype at the specified index. 253 * 254 * @param index the index of the subtype to return. 255 */ 256 public SpellCheckerSubtype getSubtypeAt(int index) { 257 return mSubtypes.get(index); 258 } 259 260 /** 261 * Used to make this class parcelable. 262 */ 263 @Override 264 public int describeContents() { 265 return 0; 266 } 267 } 268