Loading src/com/android/settings/core/PreferenceControllerListHelper.java +3 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settingslib.core.AbstractPreferenceController; import org.xmlpull.v1.XmlPullParserException; Loading @@ -52,7 +53,8 @@ public class PreferenceControllerListHelper { final List<BasePreferenceController> controllers = new ArrayList<>(); List<Bundle> preferenceMetadata; try { preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId); preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Failed to parse preference xml for getting controllers"); return controllers; Loading src/com/android/settings/core/PreferenceXmlParserUtils.java +66 −6 Original line number Diff line number Diff line Loading @@ -18,10 +18,14 @@ package com.android.settings.core; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.XmlRes; import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.support.annotation.IntDef; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; Loading @@ -33,6 +37,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; Loading @@ -43,12 +49,41 @@ import java.util.List; public class PreferenceXmlParserUtils { private static final String TAG = "PreferenceXmlParserUtil"; @VisibleForTesting static final String PREF_SCREEN_TAG = "PreferenceScreen"; private static final List<String> SUPPORTED_PREF_TYPES = Arrays.asList( "Preference", "PreferenceCategory", "PreferenceScreen"); /** * Flag definition to indicate which metadata should be extracted when * {@link #extractMetadata(Context, int, int)} is called. The flags can be combined by using | * (binary or). */ @IntDef(flag = true, value = { MetadataFlag.FLAG_INCLUDE_PREF_SCREEN, MetadataFlag.FLAG_NEED_KEY, MetadataFlag.FLAG_NEED_PREF_TYPE, MetadataFlag.FLAG_NEED_PREF_CONTROLLER, MetadataFlag.FLAG_NEED_PREF_TITLE, MetadataFlag.FLAG_NEED_PREF_SUMMARY, MetadataFlag.FLAG_NEED_PREF_ICON}) @Retention(RetentionPolicy.SOURCE) public @interface MetadataFlag { int FLAG_INCLUDE_PREF_SCREEN = 1; int FLAG_NEED_KEY = 1 << 1; int FLAG_NEED_PREF_TYPE = 1 << 2; int FLAG_NEED_PREF_CONTROLLER = 1 << 3; int FLAG_NEED_PREF_TITLE = 1 << 4; int FLAG_NEED_PREF_SUMMARY = 1 << 5; int FLAG_NEED_PREF_ICON = 1 << 6; } public static final String METADATA_PREF_TYPE = "type"; public static final String METADATA_KEY = "key"; public static final String METADATA_CONTROLLER = "controller"; public static final String METADATA_TITLE = "title"; public static final String METADATA_SUMMARY = "summary"; public static final String METADATA_ICON = "icon"; private static final String ENTRIES_SEPARATOR = "|"; Loading Loading @@ -107,11 +142,11 @@ public class PreferenceXmlParserUtils { /** * Extracts metadata from preference xml and put them into a {@link Bundle}. * * TODO(zhfan): Similar logic exists in {@link SliceBuilderUtils} and * {@link UniquePreferenceTest}. Need refactoring to consolidate them all. * @param xmlResId xml res id of a preference screen * @param flags Should be one or more of {@link MetadataFlag}. */ @NonNull public static List<Bundle> extractMetadata(Context context, int xmlResId) public static List<Bundle> extractMetadata(Context context, @XmlRes int xmlResId, int flags) throws IOException, XmlPullParserException { final List<Bundle> metadata = new ArrayList<>(); if (xmlResId <= 0) { Loading @@ -132,16 +167,37 @@ public class PreferenceXmlParserUtils { continue; } final String nodeName = parser.getName(); if (!hasFlag(flags, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN) && TextUtils.equals(PREF_SCREEN_TAG, nodeName)) { continue; } if (!SUPPORTED_PREF_TYPES.contains(nodeName) && !nodeName.endsWith("Preference")) { continue; } final Bundle preferenceMetadata = new Bundle(); final AttributeSet attrs = Xml.asAttributeSet(parser); if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TYPE)) { preferenceMetadata.putString(METADATA_PREF_TYPE, nodeName); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_KEY)) { preferenceMetadata.putString(METADATA_KEY, getDataKey(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_CONTROLLER)) { preferenceMetadata.putString(METADATA_CONTROLLER, getController(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TITLE)) { preferenceMetadata.putString(METADATA_TITLE, getDataTitle(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_SUMMARY)) { preferenceMetadata.putString(METADATA_SUMMARY, getDataSummary(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_ICON)) { preferenceMetadata.putInt(METADATA_ICON, getDataIcon(context, attrs)); } metadata.add(preferenceMetadata); } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)); parser.close(); return metadata; } Loading @@ -161,6 +217,10 @@ public class PreferenceXmlParserUtils { return data; } private static boolean hasFlag(int flags, @MetadataFlag int flag) { return (flags & flag) != 0; } private static String getDataEntries(Context context, AttributeSet set, int[] attrs, int resId) { final TypedArray sa = context.obtainStyledAttributes(set, attrs); Loading src/com/android/settings/slices/SliceDataConverter.java +24 −20 Original line number Diff line number Diff line Loading @@ -16,21 +16,28 @@ package com.android.settings.slices; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_CONTROLLER; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_ICON; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SUMMARY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_TITLE; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.provider.SearchIndexableResource; import android.support.annotation.DrawableRes; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; import com.android.settings.core.PreferenceXmlParserUtils; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.DatabaseIndexingUtils; import com.android.settings.search.Indexable.SearchIndexProvider; import com.android.settings.core.PreferenceXmlParserUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -144,35 +151,32 @@ class SliceDataConverter { + nodeName + " at " + parser.getPositionDescription()); } final int outerDepth = parser.getDepth(); final AttributeSet attrs = Xml.asAttributeSet(parser); final String screenTitle = PreferenceXmlParserUtils.getDataTitle(mContext, attrs); // TODO (b/67996923) Investigate if we need headers for Slices, since they never // correspond to an actual setting. while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, xmlResId, MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER | MetadataFlag.FLAG_NEED_PREF_TYPE | MetadataFlag.FLAG_NEED_PREF_TITLE | MetadataFlag.FLAG_NEED_PREF_ICON | MetadataFlag.FLAG_NEED_PREF_SUMMARY); for (Bundle bundle : metadata) { // TODO (b/67996923) Non-controller Slices should become intent-only slices. // Note that without a controller, dynamic summaries are impossible. // TODO (b/67996923) This will not work if preferences have nested intents: // <pref ....> // <intent action="blab"/> </pref> final String controllerClassName = PreferenceXmlParserUtils.getController(mContext, attrs); final String controllerClassName = bundle.getString(METADATA_CONTROLLER); if (TextUtils.isEmpty(controllerClassName)) { continue; } final String title = PreferenceXmlParserUtils.getDataTitle(mContext, attrs); final String key = PreferenceXmlParserUtils.getDataKey(mContext, attrs); @DrawableRes final int iconResId = PreferenceXmlParserUtils.getDataIcon(mContext, attrs); final String summary = PreferenceXmlParserUtils.getDataSummary(mContext, attrs); final String key = bundle.getString(METADATA_KEY); final String title = bundle.getString(METADATA_TITLE); final String summary = bundle.getString(METADATA_SUMMARY); final int iconResId = bundle.getInt(METADATA_ICON); final int sliceType = SliceBuilderUtils.getSliceType(mContext, controllerClassName, key); Loading @@ -194,7 +198,7 @@ class SliceDataConverter { } catch (IOException e) { Log.w(TAG, "IO Error parsing PreferenceScreen: ", e); } catch (Resources.NotFoundException e) { Log.w(TAG, "Resoucre not found error parsing PreferenceScreen: ", e); Log.w(TAG, "Resource not found error parsing PreferenceScreen: ", e); } finally { if (parser != null) parser.close(); } Loading tests/robotests/src/com/android/settings/search/PreferenceXmlParserUtilTest.java→tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilTest.java +71 −6 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading @@ -12,21 +12,21 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.android.settings.search; package com.android.settings.core; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Xml; import com.android.settings.R; import com.android.settings.core.PreferenceXmlParserUtils; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; Loading Loading @@ -169,8 +169,9 @@ public class PreferenceXmlParserUtilTest { @Config(qualifiers = "mcc999") public void extractMetadata_shouldContainKeyAndControllerName() throws IOException, XmlPullParserException { final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings); List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER); assertThat(metadata).isNotEmpty(); for (Bundle bundle : metadata) { Loading @@ -179,6 +180,70 @@ public class PreferenceXmlParserUtilTest { } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestTitle_shouldContainTitle() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_TITLE); for (Bundle bundle : metadata) { assertThat(bundle.getString(PreferenceXmlParserUtils.METADATA_TITLE)).isNotNull(); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestSummary_shouldContainSummary() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_SUMMARY); for (Bundle bundle : metadata) { assertThat(bundle.getString(PreferenceXmlParserUtils.METADATA_SUMMARY)).isNotNull(); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestIcon_shouldContainIcon() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_ICON); for (Bundle bundle : metadata) { assertThat(bundle.getInt(PreferenceXmlParserUtils.METADATA_ICON)).isNotEqualTo(0); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestPrefType_shouldContainPrefType() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_TYPE); for (Bundle bundle : metadata) { assertThat(bundle.getString(PreferenceXmlParserUtils.METADATA_PREF_TYPE)).isNotNull(); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestIncludeScreen_shouldContainScreen() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_TYPE | MetadataFlag.FLAG_INCLUDE_PREF_SCREEN); boolean hasPreferenceScreen = false; for (Bundle bundle : metadata) { if (TextUtils.equals(bundle.getString(PreferenceXmlParserUtils.METADATA_PREF_TYPE), PreferenceXmlParserUtils.PREF_SCREEN_TAG)) { hasPreferenceScreen = true; break; } } assertThat(hasPreferenceScreen).isTrue(); } /** * @param resId the ID for the XML preference * @return an XML resource parser that points to the start tag Loading tests/unit/src/com/android/settings/core/UniquePreferenceTest.java +17 −36 Original line number Diff line number Diff line Loading @@ -20,17 +20,16 @@ import static junit.framework.Assert.fail; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.platform.test.annotations.Presubmit; import android.provider.SearchIndexableResource; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.DatabaseIndexingUtils; import com.android.settings.search.Indexable; Loading @@ -40,7 +39,6 @@ import com.android.settings.search.SearchIndexableResources; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; Loading @@ -56,8 +54,7 @@ public class UniquePreferenceTest { private static final String TAG = "UniquePreferenceTest"; private static final List<String> IGNORE_PREF_TYPES = Arrays.asList( "com.android.settingslib.widget.FooterPreference"); private static final List<String> SUPPORTED_PREF_TYPES = Arrays.asList( "Preference", "PreferenceCategory", "PreferenceScreen"); private static final List<String> WHITELISTED_DUPLICATE_KEYS = Arrays.asList( "owner_info_settings", // Lock screen message in security - multiple xml files // contain this because security page is constructed by Loading Loading @@ -177,48 +174,32 @@ public class UniquePreferenceTest { } for (SearchIndexableResource sir : resourcesToIndex) { if (sir.xmlResId <= 0) { Log.d(TAG, className + " doesn't have a valid xml to index."); final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, sir.xmlResId, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_TYPE); for (Bundle bundle : metadata) { final String type = bundle.getString(PreferenceXmlParserUtils.METADATA_PREF_TYPE); if (IGNORE_PREF_TYPES.contains(type)) { continue; } final XmlResourceParser parser = mContext.getResources().getXml(sir.xmlResId); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { // Parse next until start tag is found } final int outerDepth = parser.getDepth(); do { if (type != XmlPullParser.START_TAG) { continue; } final String nodeName = parser.getName(); if (IGNORE_PREF_TYPES.contains(nodeName)) { continue; } if (!SUPPORTED_PREF_TYPES.contains(nodeName) && !nodeName.endsWith("Preference")) { continue; } final AttributeSet attrs = Xml.asAttributeSet(parser); final String key = PreferenceXmlParserUtils.getDataKey(mContext, attrs); final String key = bundle.getString(PreferenceXmlParserUtils.METADATA_KEY); if (TextUtils.isEmpty(key)) { Log.e(TAG, "Every preference must have an key; found null key" + " in " + className + " at " + parser.getPositionDescription()); + " in " + className); nullKeyClasses.add(className); continue; } if (uniqueKeys.contains(key) && !WHITELISTED_DUPLICATE_KEYS.contains(key)) { Log.e(TAG, "Every preference key must unique; found " + nodeName Log.e(TAG, "Every preference key must unique; found " + " in " + className + " at " + parser.getPositionDescription()); + " / " + key); duplicatedKeys.add(key); } uniqueKeys.add(key); } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)); } } } Loading Loading
src/com/android/settings/core/PreferenceControllerListHelper.java +3 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settingslib.core.AbstractPreferenceController; import org.xmlpull.v1.XmlPullParserException; Loading @@ -52,7 +53,8 @@ public class PreferenceControllerListHelper { final List<BasePreferenceController> controllers = new ArrayList<>(); List<Bundle> preferenceMetadata; try { preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId); preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Failed to parse preference xml for getting controllers"); return controllers; Loading
src/com/android/settings/core/PreferenceXmlParserUtils.java +66 −6 Original line number Diff line number Diff line Loading @@ -18,10 +18,14 @@ package com.android.settings.core; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.XmlRes; import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.support.annotation.IntDef; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; Loading @@ -33,6 +37,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; Loading @@ -43,12 +49,41 @@ import java.util.List; public class PreferenceXmlParserUtils { private static final String TAG = "PreferenceXmlParserUtil"; @VisibleForTesting static final String PREF_SCREEN_TAG = "PreferenceScreen"; private static final List<String> SUPPORTED_PREF_TYPES = Arrays.asList( "Preference", "PreferenceCategory", "PreferenceScreen"); /** * Flag definition to indicate which metadata should be extracted when * {@link #extractMetadata(Context, int, int)} is called. The flags can be combined by using | * (binary or). */ @IntDef(flag = true, value = { MetadataFlag.FLAG_INCLUDE_PREF_SCREEN, MetadataFlag.FLAG_NEED_KEY, MetadataFlag.FLAG_NEED_PREF_TYPE, MetadataFlag.FLAG_NEED_PREF_CONTROLLER, MetadataFlag.FLAG_NEED_PREF_TITLE, MetadataFlag.FLAG_NEED_PREF_SUMMARY, MetadataFlag.FLAG_NEED_PREF_ICON}) @Retention(RetentionPolicy.SOURCE) public @interface MetadataFlag { int FLAG_INCLUDE_PREF_SCREEN = 1; int FLAG_NEED_KEY = 1 << 1; int FLAG_NEED_PREF_TYPE = 1 << 2; int FLAG_NEED_PREF_CONTROLLER = 1 << 3; int FLAG_NEED_PREF_TITLE = 1 << 4; int FLAG_NEED_PREF_SUMMARY = 1 << 5; int FLAG_NEED_PREF_ICON = 1 << 6; } public static final String METADATA_PREF_TYPE = "type"; public static final String METADATA_KEY = "key"; public static final String METADATA_CONTROLLER = "controller"; public static final String METADATA_TITLE = "title"; public static final String METADATA_SUMMARY = "summary"; public static final String METADATA_ICON = "icon"; private static final String ENTRIES_SEPARATOR = "|"; Loading Loading @@ -107,11 +142,11 @@ public class PreferenceXmlParserUtils { /** * Extracts metadata from preference xml and put them into a {@link Bundle}. * * TODO(zhfan): Similar logic exists in {@link SliceBuilderUtils} and * {@link UniquePreferenceTest}. Need refactoring to consolidate them all. * @param xmlResId xml res id of a preference screen * @param flags Should be one or more of {@link MetadataFlag}. */ @NonNull public static List<Bundle> extractMetadata(Context context, int xmlResId) public static List<Bundle> extractMetadata(Context context, @XmlRes int xmlResId, int flags) throws IOException, XmlPullParserException { final List<Bundle> metadata = new ArrayList<>(); if (xmlResId <= 0) { Loading @@ -132,16 +167,37 @@ public class PreferenceXmlParserUtils { continue; } final String nodeName = parser.getName(); if (!hasFlag(flags, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN) && TextUtils.equals(PREF_SCREEN_TAG, nodeName)) { continue; } if (!SUPPORTED_PREF_TYPES.contains(nodeName) && !nodeName.endsWith("Preference")) { continue; } final Bundle preferenceMetadata = new Bundle(); final AttributeSet attrs = Xml.asAttributeSet(parser); if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TYPE)) { preferenceMetadata.putString(METADATA_PREF_TYPE, nodeName); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_KEY)) { preferenceMetadata.putString(METADATA_KEY, getDataKey(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_CONTROLLER)) { preferenceMetadata.putString(METADATA_CONTROLLER, getController(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TITLE)) { preferenceMetadata.putString(METADATA_TITLE, getDataTitle(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_SUMMARY)) { preferenceMetadata.putString(METADATA_SUMMARY, getDataSummary(context, attrs)); } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_ICON)) { preferenceMetadata.putInt(METADATA_ICON, getDataIcon(context, attrs)); } metadata.add(preferenceMetadata); } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)); parser.close(); return metadata; } Loading @@ -161,6 +217,10 @@ public class PreferenceXmlParserUtils { return data; } private static boolean hasFlag(int flags, @MetadataFlag int flag) { return (flags & flag) != 0; } private static String getDataEntries(Context context, AttributeSet set, int[] attrs, int resId) { final TypedArray sa = context.obtainStyledAttributes(set, attrs); Loading
src/com/android/settings/slices/SliceDataConverter.java +24 −20 Original line number Diff line number Diff line Loading @@ -16,21 +16,28 @@ package com.android.settings.slices; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_CONTROLLER; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_ICON; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SUMMARY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_TITLE; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.provider.SearchIndexableResource; import android.support.annotation.DrawableRes; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; import com.android.settings.core.PreferenceXmlParserUtils; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.DatabaseIndexingUtils; import com.android.settings.search.Indexable.SearchIndexProvider; import com.android.settings.core.PreferenceXmlParserUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -144,35 +151,32 @@ class SliceDataConverter { + nodeName + " at " + parser.getPositionDescription()); } final int outerDepth = parser.getDepth(); final AttributeSet attrs = Xml.asAttributeSet(parser); final String screenTitle = PreferenceXmlParserUtils.getDataTitle(mContext, attrs); // TODO (b/67996923) Investigate if we need headers for Slices, since they never // correspond to an actual setting. while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, xmlResId, MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER | MetadataFlag.FLAG_NEED_PREF_TYPE | MetadataFlag.FLAG_NEED_PREF_TITLE | MetadataFlag.FLAG_NEED_PREF_ICON | MetadataFlag.FLAG_NEED_PREF_SUMMARY); for (Bundle bundle : metadata) { // TODO (b/67996923) Non-controller Slices should become intent-only slices. // Note that without a controller, dynamic summaries are impossible. // TODO (b/67996923) This will not work if preferences have nested intents: // <pref ....> // <intent action="blab"/> </pref> final String controllerClassName = PreferenceXmlParserUtils.getController(mContext, attrs); final String controllerClassName = bundle.getString(METADATA_CONTROLLER); if (TextUtils.isEmpty(controllerClassName)) { continue; } final String title = PreferenceXmlParserUtils.getDataTitle(mContext, attrs); final String key = PreferenceXmlParserUtils.getDataKey(mContext, attrs); @DrawableRes final int iconResId = PreferenceXmlParserUtils.getDataIcon(mContext, attrs); final String summary = PreferenceXmlParserUtils.getDataSummary(mContext, attrs); final String key = bundle.getString(METADATA_KEY); final String title = bundle.getString(METADATA_TITLE); final String summary = bundle.getString(METADATA_SUMMARY); final int iconResId = bundle.getInt(METADATA_ICON); final int sliceType = SliceBuilderUtils.getSliceType(mContext, controllerClassName, key); Loading @@ -194,7 +198,7 @@ class SliceDataConverter { } catch (IOException e) { Log.w(TAG, "IO Error parsing PreferenceScreen: ", e); } catch (Resources.NotFoundException e) { Log.w(TAG, "Resoucre not found error parsing PreferenceScreen: ", e); Log.w(TAG, "Resource not found error parsing PreferenceScreen: ", e); } finally { if (parser != null) parser.close(); } Loading
tests/robotests/src/com/android/settings/search/PreferenceXmlParserUtilTest.java→tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilTest.java +71 −6 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading @@ -12,21 +12,21 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.android.settings.search; package com.android.settings.core; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Xml; import com.android.settings.R; import com.android.settings.core.PreferenceXmlParserUtils; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; Loading Loading @@ -169,8 +169,9 @@ public class PreferenceXmlParserUtilTest { @Config(qualifiers = "mcc999") public void extractMetadata_shouldContainKeyAndControllerName() throws IOException, XmlPullParserException { final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings); List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER); assertThat(metadata).isNotEmpty(); for (Bundle bundle : metadata) { Loading @@ -179,6 +180,70 @@ public class PreferenceXmlParserUtilTest { } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestTitle_shouldContainTitle() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_TITLE); for (Bundle bundle : metadata) { assertThat(bundle.getString(PreferenceXmlParserUtils.METADATA_TITLE)).isNotNull(); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestSummary_shouldContainSummary() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_SUMMARY); for (Bundle bundle : metadata) { assertThat(bundle.getString(PreferenceXmlParserUtils.METADATA_SUMMARY)).isNotNull(); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestIcon_shouldContainIcon() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_ICON); for (Bundle bundle : metadata) { assertThat(bundle.getInt(PreferenceXmlParserUtils.METADATA_ICON)).isNotEqualTo(0); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestPrefType_shouldContainPrefType() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_TYPE); for (Bundle bundle : metadata) { assertThat(bundle.getString(PreferenceXmlParserUtils.METADATA_PREF_TYPE)).isNotNull(); } } @Test @Config(qualifiers = "mcc999") public void extractMetadata_requestIncludeScreen_shouldContainScreen() throws IOException, XmlPullParserException { List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, MetadataFlag.FLAG_NEED_PREF_TYPE | MetadataFlag.FLAG_INCLUDE_PREF_SCREEN); boolean hasPreferenceScreen = false; for (Bundle bundle : metadata) { if (TextUtils.equals(bundle.getString(PreferenceXmlParserUtils.METADATA_PREF_TYPE), PreferenceXmlParserUtils.PREF_SCREEN_TAG)) { hasPreferenceScreen = true; break; } } assertThat(hasPreferenceScreen).isTrue(); } /** * @param resId the ID for the XML preference * @return an XML resource parser that points to the start tag Loading
tests/unit/src/com/android/settings/core/UniquePreferenceTest.java +17 −36 Original line number Diff line number Diff line Loading @@ -20,17 +20,16 @@ import static junit.framework.Assert.fail; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.platform.test.annotations.Presubmit; import android.provider.SearchIndexableResource; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.DatabaseIndexingUtils; import com.android.settings.search.Indexable; Loading @@ -40,7 +39,6 @@ import com.android.settings.search.SearchIndexableResources; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; Loading @@ -56,8 +54,7 @@ public class UniquePreferenceTest { private static final String TAG = "UniquePreferenceTest"; private static final List<String> IGNORE_PREF_TYPES = Arrays.asList( "com.android.settingslib.widget.FooterPreference"); private static final List<String> SUPPORTED_PREF_TYPES = Arrays.asList( "Preference", "PreferenceCategory", "PreferenceScreen"); private static final List<String> WHITELISTED_DUPLICATE_KEYS = Arrays.asList( "owner_info_settings", // Lock screen message in security - multiple xml files // contain this because security page is constructed by Loading Loading @@ -177,48 +174,32 @@ public class UniquePreferenceTest { } for (SearchIndexableResource sir : resourcesToIndex) { if (sir.xmlResId <= 0) { Log.d(TAG, className + " doesn't have a valid xml to index."); final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext, sir.xmlResId, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_TYPE); for (Bundle bundle : metadata) { final String type = bundle.getString(PreferenceXmlParserUtils.METADATA_PREF_TYPE); if (IGNORE_PREF_TYPES.contains(type)) { continue; } final XmlResourceParser parser = mContext.getResources().getXml(sir.xmlResId); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { // Parse next until start tag is found } final int outerDepth = parser.getDepth(); do { if (type != XmlPullParser.START_TAG) { continue; } final String nodeName = parser.getName(); if (IGNORE_PREF_TYPES.contains(nodeName)) { continue; } if (!SUPPORTED_PREF_TYPES.contains(nodeName) && !nodeName.endsWith("Preference")) { continue; } final AttributeSet attrs = Xml.asAttributeSet(parser); final String key = PreferenceXmlParserUtils.getDataKey(mContext, attrs); final String key = bundle.getString(PreferenceXmlParserUtils.METADATA_KEY); if (TextUtils.isEmpty(key)) { Log.e(TAG, "Every preference must have an key; found null key" + " in " + className + " at " + parser.getPositionDescription()); + " in " + className); nullKeyClasses.add(className); continue; } if (uniqueKeys.contains(key) && !WHITELISTED_DUPLICATE_KEYS.contains(key)) { Log.e(TAG, "Every preference key must unique; found " + nodeName Log.e(TAG, "Every preference key must unique; found " + " in " + className + " at " + parser.getPositionDescription()); + " / " + key); duplicatedKeys.add(key); } uniqueKeys.add(key); } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)); } } } Loading