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.PrintWriterPrinter; 35import android.util.Slog; 36import android.util.Xml; 37 38import java.io.IOException; 39import java.io.PrintWriter; 40import java.util.ArrayList; 41 42/** 43 * This class is used to specify meta information of a spell checker. 44 */ 45public 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