recognitionServiceAttributes =
+ parseRecognitionServiceXmlMetadata(context, serviceInfo);
+
+ if (recognitionServiceAttributes != null) {
+ validRecognitionServices.add(new RecognizerInfo(
+ context.getPackageManager(),
+ serviceInfo,
+ recognitionServiceAttributes.first /* settingsActivity */,
+ recognitionServiceAttributes.second /* selectableAsDefault */));
+ }
+ }
+
+ return validRecognitionServices;
+ }
+
+ /**
+ * Load recognition service's xml meta-data and parse it. Return the meta-data attributes,
+ * namely, `settingsActivity` {@link String} and `selectableAsDefault` {@link Boolean}.
+ *
+ * Parsing fails if the meta-data for the given service is not found
+ * or the found meta-data does not start with a `recognition-service`.
+ *
+ * @param context {@link Context} inside which the settings app is run.
+ * @param serviceInfo {@link ServiceInfo} containing info
+ * about the speech recognition service in question.
+ *
+ * @return {@link Pair}<{@link String}, {@link Boolean}> containing `settingsActivity`
+ * and `selectableAsDefault` attributes if the parsing was successful, {@code null} otherwise.
+ */
+ private static Pair parseRecognitionServiceXmlMetadata(
+ Context context, ServiceInfo serviceInfo) {
+ // Default recognition service attribute values.
+ // Every recognizer can be selected unless specified otherwise.
+ String settingsActivity;
+ boolean selectableAsDefault = true;
+
+ // Parse xml meta-data.
+ try (XmlResourceParser parser = serviceInfo.loadXmlMetaData(
+ context.getPackageManager(), RecognitionService.SERVICE_META_DATA)) {
+ if (parser == null) {
+ throw new XmlPullParserException(String.format("No %s meta-data for %s package",
+ RecognitionService.SERVICE_META_DATA, serviceInfo.packageName));
+ }
+
+ final Resources res = context.getPackageManager().getResourcesForApplication(
+ serviceInfo.applicationInfo);
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ // Xml meta-data must start with a `recognition-service tag`.
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // Intentionally do nothing.
+ }
+
+ final String nodeName = parser.getName();
+ if (!"recognition-service".equals(nodeName)) {
+ throw new XmlPullParserException(String.format(
+ "%s package meta-data does not start with a `recognition-service` tag",
+ serviceInfo.packageName));
+ }
+
+ final TypedArray array = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.RecognitionService);
+ settingsActivity = array.getString(
+ com.android.internal.R.styleable.RecognitionService_settingsActivity);
+ selectableAsDefault = array.getBoolean(
+ com.android.internal.R.styleable.RecognitionService_selectableAsDefault,
+ selectableAsDefault);
+ array.recycle();
+ } catch (XmlPullParserException | IOException
+ | PackageManager.NameNotFoundException e) {
+ Log.e(TAG, String.format("Error parsing %s package recognition service meta-data",
+ serviceInfo.packageName), e);
+ return null;
+ }
+
+ return Pair.create(settingsActivity, selectableAsDefault);
+ }
}