From 42e9d266359fc438cbef992b0f4f0eb7351ad513 Mon Sep 17 00:00:00 2001 From: Salvador Martinez Date: Tue, 4 Dec 2018 14:13:10 -0800 Subject: [PATCH] Allow appending static preferences to RadioButtonPickerFragment This makes it so that static preferences can also be added after the candidates so they can show up on the bottom of the screen. This is done via a new xml tag for the preference screen that is usually mostly empty that RaiodButtonPickerFragments use Test: robotests Bug: 111450127 Change-Id: I0fe2f480f0ff59b9bf9269e94b33ab4b008b47b8 --- res/values/attrs.xml | 8 +++++ res/values/strings.xml | 6 ++++ res/xml/battery_saver_schedule_settings.xml | 23 ++++++++++++++ .../core/PreferenceXmlParserUtils.java | 28 ++++++++++++++--- .../widget/RadioButtonPickerFragment.java | 31 +++++++++++++++++-- .../battery_saver_schedule_settings.xml | 23 ++++++++++++++ .../core/PreferenceXmlParserUtilsTest.java | 27 ++++++++++++++++ .../ColorModePreferenceFragmentTest.java | 1 + .../widget/RadioButtonPickerFragmentTest.java | 23 ++++++++++++++ 9 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 res/xml/battery_saver_schedule_settings.xml create mode 100644 tests/robotests/res/xml-mcc999/battery_saver_schedule_settings.xml diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 752fd3dada5..86763b77169 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -72,6 +72,14 @@ + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 4aa09515ca0..4aab4e0fceb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5406,6 +5406,12 @@ Turn on automatically + + No schedule + + + Set a schedule + At %1$s diff --git a/res/xml/battery_saver_schedule_settings.xml b/res/xml/battery_saver_schedule_settings.xml new file mode 100644 index 00000000000..f91e4ca25d1 --- /dev/null +++ b/res/xml/battery_saver_schedule_settings.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/src/com/android/settings/core/PreferenceXmlParserUtils.java b/src/com/android/settings/core/PreferenceXmlParserUtils.java index 9fdeeefbe5a..ce5c5051f5a 100644 --- a/src/com/android/settings/core/PreferenceXmlParserUtils.java +++ b/src/com/android/settings/core/PreferenceXmlParserUtils.java @@ -55,6 +55,8 @@ public class PreferenceXmlParserUtils { private static final List SUPPORTED_PREF_TYPES = Arrays.asList( "Preference", "PreferenceCategory", "PreferenceScreen", "com.android.settings.widget.WorkOnlyCategory"); + public static final int PREPEND_VALUE = 0; + public static final int APPEND_VALUE = 1; /** * Flag definition to indicate which metadata should be extracted when @@ -84,6 +86,7 @@ public class PreferenceXmlParserUtils { int FLAG_NEED_KEYWORDS = 1 << 8; int FLAG_NEED_SEARCHABLE = 1 << 9; int FLAG_ALLOW_DYNAMIC_SUMMARY_IN_SLICE = 1 << 10; + int FLAG_NEED_PREF_APPEND = 1 << 11; } public static final String METADATA_PREF_TYPE = "type"; @@ -97,6 +100,7 @@ public class PreferenceXmlParserUtils { public static final String METADATA_SEARCHABLE = "searchable"; public static final String METADATA_ALLOW_DYNAMIC_SUMMARY_IN_SLICE = "allow_dynamic_summary_in_slice"; + public static final String METADATA_APPEND = "staticPreferenceLocation"; private static final String ENTRIES_SEPARATOR = "|"; @@ -184,14 +188,13 @@ public class PreferenceXmlParserUtils { // Parse next until start tag is found } final int outerDepth = parser.getDepth(); - + final boolean hasPrefScreenFlag = hasFlag(flags, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN); do { if (type != XmlPullParser.START_TAG) { continue; } final String nodeName = parser.getName(); - if (!hasFlag(flags, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN) - && TextUtils.equals(PREF_SCREEN_TAG, nodeName)) { + if (!hasPrefScreenFlag && TextUtils.equals(PREF_SCREEN_TAG, nodeName)) { continue; } if (!SUPPORTED_PREF_TYPES.contains(nodeName) && !nodeName.endsWith("Preference")) { @@ -199,8 +202,14 @@ public class PreferenceXmlParserUtils { } final Bundle preferenceMetadata = new Bundle(); final AttributeSet attrs = Xml.asAttributeSet(parser); + final TypedArray preferenceAttributes = context.obtainStyledAttributes(attrs, R.styleable.Preference); + TypedArray preferenceScreenAttributes = null; + if (hasPrefScreenFlag) { + preferenceScreenAttributes = context.obtainStyledAttributes( + attrs, R.styleable.PreferenceScreen); + } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TYPE)) { preferenceMetadata.putString(METADATA_PREF_TYPE, nodeName); @@ -236,6 +245,10 @@ public class PreferenceXmlParserUtils { preferenceMetadata.putBoolean(METADATA_ALLOW_DYNAMIC_SUMMARY_IN_SLICE, isDynamicSummaryAllowed(preferenceAttributes)); } + if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_APPEND) && hasPrefScreenFlag) { + preferenceMetadata.putBoolean(METADATA_APPEND, + isAppended(preferenceScreenAttributes)); + } metadata.add(preferenceMetadata); preferenceAttributes.recycle(); @@ -325,7 +338,12 @@ public class PreferenceXmlParserUtils { false /* default */); } - private static String getKeywords(TypedArray styleAttributes) { - return styleAttributes.getString(R.styleable.Preference_keywords); + private static String getKeywords(TypedArray styledAttributes) { + return styledAttributes.getString(R.styleable.Preference_keywords); + } + + private static boolean isAppended(TypedArray styledAttributes) { + return styledAttributes.getInt(R.styleable.PreferenceScreen_staticPreferenceLocation, + PREPEND_VALUE) == APPEND_VALUE; } } diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java index 89df487200e..50c1b58442f 100644 --- a/src/com/android/settings/widget/RadioButtonPickerFragment.java +++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java @@ -22,10 +22,12 @@ import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import androidx.annotation.LayoutRes; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -34,16 +36,23 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.InstrumentedPreferenceFragment; +import com.android.settings.core.PreferenceXmlParserUtils; +import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; import com.android.settingslib.widget.CandidateInfo; +import java.io.IOException; import java.util.List; import java.util.Map; +import org.xmlpull.v1.XmlPullParserException; public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFragment implements RadioButtonPreference.OnClickListener { - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + @VisibleForTesting static final String EXTRA_FOR_WORK = "for_work"; + private static final String TAG = "RadioButtonPckrFrgmt"; + @VisibleForTesting + boolean mAppendStaticPreferences = false; private final Map mCandidates = new ArrayMap<>(); @@ -69,6 +78,19 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { super.onCreatePreferences(savedInstanceState, rootKey); + try { + // Check if the xml specifies if static preferences should go on the top or bottom + final List metadata = PreferenceXmlParserUtils.extractMetadata(getContext(), + getPreferenceScreenResId(), + MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | + MetadataFlag.FLAG_NEED_PREF_APPEND); + mAppendStaticPreferences = metadata.get(0) + .getBoolean(PreferenceXmlParserUtils.METADATA_APPEND); + } catch (IOException e) { + Log.e(TAG, "Error trying to open xml file", e); + } catch (XmlPullParserException e) { + Log.e(TAG, "Error parsing xml", e); + } updateCandidates(); } @@ -142,7 +164,9 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr final String systemDefaultKey = getSystemDefaultKey(); final PreferenceScreen screen = getPreferenceScreen(); screen.removeAll(); - addStaticPreferences(screen); + if (!mAppendStaticPreferences) { + addStaticPreferences(screen); + } final int customLayoutResId = getRadioButtonPreferenceCustomLayoutResId(); if (shouldShowItemNone()) { @@ -168,6 +192,9 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr } } mayCheckOnlyRadioButton(); + if (mAppendStaticPreferences) { + addStaticPreferences(screen); + } } @VisibleForTesting diff --git a/tests/robotests/res/xml-mcc999/battery_saver_schedule_settings.xml b/tests/robotests/res/xml-mcc999/battery_saver_schedule_settings.xml new file mode 100644 index 00000000000..f91e4ca25d1 --- /dev/null +++ b/tests/robotests/res/xml-mcc999/battery_saver_schedule_settings.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java b/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java index 3b1b5afc3ab..ebf325273a0 100644 --- a/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java +++ b/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java @@ -18,6 +18,7 @@ package com.android.settings.core; import static com.android.settings.core.PreferenceXmlParserUtils .METADATA_ALLOW_DYNAMIC_SUMMARY_IN_SLICE; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_APPEND; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEYWORDS; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SEARCHABLE; @@ -315,6 +316,32 @@ public class PreferenceXmlParserUtilsTest { } } + @Test + @Config(qualifiers = "mcc999") + public void extractMetadata_requestAppendProperty_shouldDefaultToFalse() + throws Exception { + final List metadata = PreferenceXmlParserUtils.extractMetadata(mContext, + R.xml.display_settings, + MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_NEED_PREF_APPEND); + + for (Bundle bundle : metadata) { + assertThat(bundle.getBoolean(METADATA_APPEND)).isFalse(); + } + } + + @Test + @Config(qualifiers = "mcc999") + public void extractMetadata_requestAppendProperty_shouldReturnCorrectValue() + throws Exception { + final List metadata = PreferenceXmlParserUtils.extractMetadata(mContext, + R.xml.battery_saver_schedule_settings, + MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_NEED_PREF_APPEND); + + for (Bundle bundle : metadata) { + assertThat(bundle.getBoolean(METADATA_APPEND)).isTrue(); + } + } + /** * @param resId the ID for the XML preference * @return an XML resource parser that points to the start tag diff --git a/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java index 859912aa71c..57f0ebbc6a0 100644 --- a/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java @@ -209,6 +209,7 @@ public class ColorModePreferenceFragmentTest { @Test public void onCreatePreferences_useNewTitle_shouldAddColorModePreferences() { + when(mFragment.getContext()).thenReturn(RuntimeEnvironment.application); doNothing().when(mFragment).addPreferencesFromResource(anyInt()); doNothing().when(mFragment).updateCandidates(); diff --git a/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java b/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java index 64352d96638..55d212ffef1 100644 --- a/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java +++ b/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java @@ -18,6 +18,7 @@ package com.android.settings.widget; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -37,7 +38,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @@ -98,6 +101,26 @@ public class RadioButtonPickerFragmentTest { assertThat(mFragment.setDefaultKeyCalled).isTrue(); } + @Test + public void staticPreferencesPrepended_addedFirst() { + mFragment.mAppendStaticPreferences = false; + mFragment.updateCandidates(); + + InOrder inOrder = Mockito.inOrder(mFragment); + inOrder.verify(mFragment).addStaticPreferences(any()); + inOrder.verify(mFragment).getRadioButtonPreferenceCustomLayoutResId(); + } + + @Test + public void staticPreferencesAppended_addedLast() { + mFragment.mAppendStaticPreferences = true; + mFragment.updateCandidates(); + + InOrder inOrder = Mockito.inOrder(mFragment); + inOrder.verify(mFragment).mayCheckOnlyRadioButton(); + inOrder.verify(mFragment).addStaticPreferences(any()); + } + @Test public void shouldHaveNoCustomPreferenceLayout() { assertThat(mFragment.getRadioButtonPreferenceCustomLayoutResId()).isEqualTo(0);