Loading src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java +1 −3 Original line number Diff line number Diff line Loading @@ -66,9 +66,7 @@ abstract public class AbstractZenModeAutomaticRulePreferenceController extends } protected Map.Entry<String, AutomaticZenRule>[] getRules() { if (mRules == null) { mRules = mBackend.getAutomaticZenRules(); } return mRules; } Loading src/com/android/settings/notification/zen/ZenModeAutomaticRulesPreferenceController.java +46 −61 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.notification.zen; import android.app.AutomaticZenRule; import android.content.Context; import android.util.ArrayMap; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.Fragment; Loading @@ -28,23 +27,21 @@ import androidx.preference.PreferenceScreen; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.HashMap; import java.util.Map; import java.util.Objects; public class ZenModeAutomaticRulesPreferenceController extends AbstractZenModeAutomaticRulePreferenceController { protected static final String KEY = "zen_mode_automatic_rules"; @VisibleForTesting protected PreferenceCategory mPreferenceCategory; // Map of rule key -> preference so that we can update each preference as needed @VisibleForTesting protected Map<String, ZenRulePreference> mZenRulePreferences = new ArrayMap<>(); Map.Entry<String, AutomaticZenRule>[] mSortedRules; public ZenModeAutomaticRulesPreferenceController(Context context, Fragment parent, Lifecycle lifecycle) { lifecycle, ZenModeBackend backend) { super(context, KEY, parent, lifecycle); mBackend = backend; } @Override Loading @@ -60,81 +57,69 @@ public class ZenModeAutomaticRulesPreferenceController extends @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mPreferenceCategory = screen.findPreference(getPreferenceKey()); mPreferenceCategory.setPersistent(false); // if mPreferenceCategory was un-set, make sure to clear out mZenRulePreferences too, just // in case if (mPreferenceCategory.getPreferenceCount() == 0) { mZenRulePreferences.clear(); } PreferenceCategory preferenceCategory = screen.findPreference(getPreferenceKey()); preferenceCategory.setPersistent(false); mSortedRules = getRules(); updateRules(preferenceCategory); } @Override public void updateState(Preference preference) { super.updateState(preference); Map.Entry<String, AutomaticZenRule>[] sortedRules = getRules(); // refresh the whole preference category list if the total number of rules has changed, or // if any individual rules have changed, so we can rebuild the list & keep things in sync boolean refreshPrefs = false; if (mPreferenceCategory.getPreferenceCount() != sortedRules.length) { refreshPrefs = true; boolean rulesChanged = false; if (sortedRules.length != mSortedRules.length) { rulesChanged = true; } else { // check whether any rules in sortedRules are not in mZenRulePreferences; that should // be enough to see whether something has changed for (int i = 0; i < sortedRules.length; i++) { if (!mZenRulePreferences.containsKey(sortedRules[i].getKey())) { refreshPrefs = true; for (int i = 0; i < mSortedRules.length; i++) { if (!Objects.equals(mSortedRules[i].getKey(), sortedRules[i].getKey()) || !Objects.equals(mSortedRules[i].getValue(), sortedRules[i].getValue())) { rulesChanged = true; break; } } } // if we need to refresh the whole list, clear the preference category and also start a // new map of preferences according to the preference category contents // we need to not update the existing one yet, as we'll need to know what preferences // previously existed in order to update and re-attach them to the preference category Map<String, ZenRulePreference> newPrefs = new ArrayMap<>(); if (refreshPrefs) { mPreferenceCategory.removeAll(); if (rulesChanged) { mSortedRules = sortedRules; updateRules((PreferenceCategory) preference); } } private void updateRules(PreferenceCategory preferenceCategory) { Map<String, ZenRulePreference> originalPreferences = new HashMap<>(); for (int i = 0; i < preferenceCategory.getPreferenceCount(); i++) { ZenRulePreference pref = (ZenRulePreference) preferenceCategory.getPreference(i); originalPreferences.put(pref.getKey(), pref); } // Loop through each rule, either updating the existing rule or creating the rule's // preference if needed (and, in the case where we need to rebuild the preference category // list, do so as well) for (int i = 0; i < sortedRules.length; i++) { String key = sortedRules[i].getKey(); if (mZenRulePreferences.containsKey(key)) { // existing rule; update its info if it's changed since the last display AutomaticZenRule rule = sortedRules[i].getValue(); ZenRulePreference pref = mZenRulePreferences.get(key); pref.updatePreference(rule); // preference for (int i = 0; i < mSortedRules.length; i++) { String key = mSortedRules[i].getKey(); // only add to preference category if the overall set of rules has changed so this // needs to be rearranged if (refreshPrefs) { mPreferenceCategory.addPreference(pref); newPrefs.put(key, pref); } if (originalPreferences.containsKey(key)) { // existing rule; update its info if it's changed since the last display AutomaticZenRule rule = mSortedRules[i].getValue(); originalPreferences.get(key).updatePreference(rule); } else { // new rule; create a new ZenRulePreference & add it to the preference category // and the map so we'll know about it later ZenRulePreference pref = createZenRulePreference(sortedRules[i]); mPreferenceCategory.addPreference(pref); newPrefs.put(key, pref); } ZenRulePreference pref = createZenRulePreference( mSortedRules[i], preferenceCategory); preferenceCategory.addPreference(pref); } // If anything was new, then make sure we overwrite mZenRulePreferences with our new data if (refreshPrefs) { mZenRulePreferences = newPrefs; originalPreferences.remove(key); } // Remove preferences that no longer have a rule for (String key : originalPreferences.keySet()) { preferenceCategory.removePreferenceRecursively(key); } } @VisibleForTesting ZenRulePreference createZenRulePreference(Map.Entry<String, AutomaticZenRule> rule) { return new ZenRulePreference(mPreferenceCategory.getContext(), rule, mParent, mMetricsFeatureProvider); ZenRulePreference createZenRulePreference(Map.Entry<String, AutomaticZenRule> rule, PreferenceCategory preferenceCategory) { return new ZenRulePreference(preferenceCategory.getContext(), rule, mParent, mMetricsFeatureProvider, mBackend); } } src/com/android/settings/notification/zen/ZenModeAutomationSettings.java +3 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.app.NotificationManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.service.notification.ConditionProviderService; import android.view.Menu; import android.view.MenuInflater; Loading Loading @@ -59,10 +58,12 @@ public class ZenModeAutomationSettings extends ZenModeSettingsBase { private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, Fragment parent, ZenServiceListing serviceListing, Lifecycle lifecycle) { ZenModeBackend backend = new ZenModeBackend(context); List<AbstractPreferenceController> controllers = new ArrayList<>(); controllers.add(new ZenModeAddAutomaticRulePreferenceController(context, parent, serviceListing, lifecycle)); controllers.add(new ZenModeAutomaticRulesPreferenceController(context, parent, lifecycle)); controllers.add(new ZenModeAutomaticRulesPreferenceController( context, parent, lifecycle, backend)); return controllers; } Loading src/com/android/settings/notification/zen/ZenRulePreference.java +4 −2 Original line number Diff line number Diff line Loading @@ -60,13 +60,15 @@ public class ZenRulePreference extends PrimarySwitchPreference { public ZenRulePreference(Context context, final Map.Entry<String, AutomaticZenRule> ruleEntry, Fragment parent, MetricsFeatureProvider metricsProvider) { Fragment parent, MetricsFeatureProvider metricsProvider, ZenModeBackend backend) { super(context); mBackend = ZenModeBackend.getInstance(context); mBackend = backend; mContext = context; mRule = ruleEntry.getValue(); mName = mRule.getName(); mId = ruleEntry.getKey(); setKey(mId); mParent = parent; mPm = mContext.getPackageManager(); mServiceListing = new ZenServiceListing(mContext, CONFIG); Loading tests/robotests/src/com/android/settings/notification/zen/ZenModeAutomaticRulesPreferenceControllerTest.java +104 −89 Original line number Diff line number Diff line Loading @@ -17,13 +17,15 @@ package com.android.settings.notification.zen; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading @@ -31,13 +33,19 @@ import static org.mockito.Mockito.when; import android.app.AutomaticZenRule; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.provider.Settings; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -47,6 +55,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.util.ReflectionHelpers; import java.lang.reflect.Field; import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; Loading @@ -56,101 +65,122 @@ public class ZenModeAutomaticRulesPreferenceControllerTest { private ZenModeAutomaticRulesPreferenceController mController; @Mock private ZenModeBackend mBackend; @Mock private PreferenceCategory mockPref; @Mock private PreferenceCategory mPreferenceCategory; private PreferenceScreen mPreferenceScreen; @Mock private ZenRulePreference mZenRulePreference; private Context mContext; @Mock PackageManager mPm; @Before public void setup() { MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); mController = spy(new ZenModeAutomaticRulesPreferenceController(mContext, mock(Fragment.class), null)); ReflectionHelpers.setField(mController, "mBackend", mBackend); when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( mockPref); mController.displayPreference(mPreferenceScreen); doReturn(mZenRulePreference).when(mController).createZenRulePreference(any()); mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getPackageManager()).thenReturn(mPm); when(mPm.queryIntentActivities(any(), any())).thenReturn( ImmutableList.of(mock(ResolveInfo.class))); when(mBackend.getAutomaticZenRules()).thenReturn(new Map.Entry[0]); mController = spy(new ZenModeAutomaticRulesPreferenceController( mContext, mock(Fragment.class), null, mBackend)); final PreferenceManager preferenceManager = new PreferenceManager(mContext); mPreferenceScreen = preferenceManager.createPreferenceScreen(mContext); mPreferenceCategory = spy(new PreferenceCategory(mContext)); mPreferenceCategory.setKey(mController.getPreferenceKey()); mPreferenceScreen.addPreference(mPreferenceCategory); } @Test public void testDisplayPreference_resetsPreferencesWhenCategoryEmpty() { // when the PreferenceCategory is empty (no preferences), make sure we clear out any // stale state in the cached set of zen rule preferences mController.mZenRulePreferences.put("test1_id", mZenRulePreference); when(mockPref.getPreferenceCount()).thenReturn(0); public void testDisplayPreference_notPersistent() { mController.displayPreference(mPreferenceScreen); assertTrue(mController.mZenRulePreferences.isEmpty()); assertFalse(mPreferenceCategory.isPersistent()); } @Test public void testUpdateState_clearsPreferencesWhenAddingNewPreferences() { final int NUM_RULES = 3; public void testDisplayThenUpdateState_onlyAddsOnceRulesUnchanged() { final int NUM_RULES = 1; Map<String, AutomaticZenRule> rMap = new HashMap<>(); String ruleId1 = "test1_id"; String ruleId2 = "test2_id"; String ruleId3 = "test3_id"; AutomaticZenRule autoRule1 = new AutomaticZenRule("test_rule_1", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); AutomaticZenRule autoRule2 = new AutomaticZenRule("test_rule_2", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 20); AutomaticZenRule autoRule3 = new AutomaticZenRule("test_rule_3", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 30); rMap.put(ruleId1, autoRule1); rMap.put(ruleId2, autoRule2); rMap.put(ruleId3, autoRule3); // should add 3 new preferences to mockPref // should add 1 new preferences to mockPref mockGetAutomaticZenRules(NUM_RULES, rMap); mController.updateState(mockPref); verify(mockPref, times(1)).removeAll(); verify(mockPref, times(NUM_RULES)).addPreference(any()); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); mController.displayPreference(mPreferenceScreen); mController.updateState(mPreferenceCategory); assertEquals(NUM_RULES, mPreferenceCategory.getPreferenceCount()); verify(mPreferenceCategory, times(1)).addPreference(any()); } @Test public void testUpdateState_clearsPreferencesWhenRemovingPreferences(){ final int NUM_RULES = 2; public void testDisplayThenUpdateState_addsIfRulesChange() { Map<String, AutomaticZenRule> rMap = new HashMap<>(); String ruleId1 = "test1_id"; AutomaticZenRule autoRule1 = new AutomaticZenRule("test_rule_1", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); rMap.put(ruleId1, autoRule1); mockGetAutomaticZenRules(1, rMap); // adds one mController.displayPreference(mPreferenceScreen); String ruleId2 = "test2_id"; AutomaticZenRule autoRule2 = new AutomaticZenRule("test_rule_2", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 20); rMap.put(ruleId2, autoRule2); mockGetAutomaticZenRules(2, rMap); mController.updateState(mPreferenceCategory); assertEquals(2, mPreferenceCategory.getPreferenceCount()); verify(mPreferenceCategory, times(2)).addPreference(any()); } @Test public void testUpdateState_addingNewPreferences() { mController.displayPreference(mPreferenceScreen); final int NUM_RULES = 3; Map<String, AutomaticZenRule> rMap = new HashMap<>(); String ruleId1 = "test1_id"; String ruleId2 = "test2_id"; String ruleId3 = "test3_id"; AutomaticZenRule autoRule1 = new AutomaticZenRule("test_rule_1", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); AutomaticZenRule autoRule2 = new AutomaticZenRule("test_rule_2", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 20); AutomaticZenRule autoRule3 = new AutomaticZenRule("test_rule_3", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 30); rMap.put(ruleId1, autoRule1); rMap.put(ruleId2, autoRule2); rMap.put(ruleId3, autoRule3); // Add three preferences to the set of previously-known-about ZenRulePreferences; in this // case, test3_id is "deleted" mController.mZenRulePreferences.put("test1_id", mZenRulePreference); mController.mZenRulePreferences.put("test2_id", mZenRulePreference); mController.mZenRulePreferences.put("test3_id", mZenRulePreference); // update state should re-add all preferences since a preference was deleted when(mockPref.getPreferenceCount()).thenReturn(NUM_RULES + 1); // should add 3 new preferences to mockPref mockGetAutomaticZenRules(NUM_RULES, rMap); mController.updateState(mockPref); verify(mockPref, times(1)).removeAll(); verify(mockPref, times(NUM_RULES)).addPreference(any()); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); mController.updateState(mPreferenceCategory); assertEquals(NUM_RULES, mPreferenceCategory.getPreferenceCount()); } @Test public void testUpdateState_clearsPreferencesWhenSameNumberButDifferentPrefs() { public void testUpdateState_addsAndRemoves(){ mController.displayPreference(mPreferenceScreen); final int NUM_RULES = 2; Map<String, AutomaticZenRule> rMap = new HashMap<>(); String FAKE_1 = "fake key 1"; String FAKE_2 = "fake 2"; mPreferenceCategory.addPreference(new ZenRulePreference(mContext, new AbstractMap.SimpleEntry<>(FAKE_1, mock(AutomaticZenRule.class)), null, null, mBackend)); mPreferenceCategory.addPreference(new ZenRulePreference(mContext, new AbstractMap.SimpleEntry<>(FAKE_2, mock(AutomaticZenRule.class)), null, null, mBackend)); assertNotNull(mPreferenceCategory.findPreference(FAKE_1)); assertNotNull(mPreferenceCategory.findPreference(FAKE_2)); String ruleId1 = "test1_id"; String ruleId2 = "test2_id"; Loading @@ -162,52 +192,37 @@ public class ZenModeAutomaticRulesPreferenceControllerTest { rMap.put(ruleId1, autoRule1); rMap.put(ruleId2, autoRule2); // Add two preferences to the set of previously-known-about ZenRulePreferences; in this // case, test3_id is "deleted" but test2_id is "added" mController.mZenRulePreferences.put("test1_id", mZenRulePreference); mController.mZenRulePreferences.put("test3_id", mZenRulePreference); // update state should re-add all preferences since a preference was deleted when(mockPref.getPreferenceCount()).thenReturn(NUM_RULES); mockGetAutomaticZenRules(NUM_RULES, rMap); mController.updateState(mockPref); verify(mockPref, times(1)).removeAll(); verify(mockPref, times(NUM_RULES)).addPreference(any()); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); mController.updateState(mPreferenceCategory); assertNull(mPreferenceCategory.findPreference(FAKE_1)); assertNull(mPreferenceCategory.findPreference(FAKE_2)); assertNotNull(mPreferenceCategory.findPreference(ruleId1)); assertNotNull(mPreferenceCategory.findPreference(ruleId2)); } @Test public void testUpdateState_updateEnableState() throws NoSuchFieldException { final int NUM_RULES = 1; Map<String, AutomaticZenRule> rMap = new HashMap<>(); public void testUpdateState_updateEnableState() { mController.displayPreference(mPreferenceScreen); String testId = "test1_id"; AutomaticZenRule rule = new AutomaticZenRule("rule_name", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); rMap.put(testId, rule); when(mockPref.getPreferenceCount()).thenReturn(NUM_RULES); when(mockPref.getPreference(anyInt())).thenReturn(mZenRulePreference); mController.mZenRulePreferences.put("test1_id", mZenRulePreference); mPreferenceCategory.addPreference(new ZenRulePreference(mContext, new AbstractMap.SimpleEntry<>(testId, rule), null, null, mBackend)); // update state should NOT re-add all the preferences, should only update enable state rule.setEnabled(false); rMap.put(testId, rule); final int NUM_RULES = 1; Map<String, AutomaticZenRule> rMap = new HashMap<>(); AutomaticZenRule ruleUpdated = new AutomaticZenRule("rule_name", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); ruleUpdated.setEnabled(false); rMap.put(testId, ruleUpdated); mockGetAutomaticZenRules(NUM_RULES, rMap); setZenRulePreferenceField("mId", testId); mController.updateState(mockPref); verify(mZenRulePreference, times(1)).updatePreference(any()); verify(mockPref, never()).removeAll(); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); } private void setZenRulePreferenceField(String name, Object value) { try { Field field = ZenRulePreference.class.getDeclaredField("mId"); field.setAccessible(true); field.set(mZenRulePreference, value); } catch (ReflectiveOperationException e) { fail("Unable to set mZenRulePreference field: " + name); } mController.updateState(mPreferenceCategory); assertFalse(mPreferenceCategory.findPreference(testId).isEnabled()); assertEquals(NUM_RULES, mPreferenceCategory.getPreferenceCount()); } private void mockGetAutomaticZenRules(int numRules, Map<String, AutomaticZenRule> rules) { Loading Loading
src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java +1 −3 Original line number Diff line number Diff line Loading @@ -66,9 +66,7 @@ abstract public class AbstractZenModeAutomaticRulePreferenceController extends } protected Map.Entry<String, AutomaticZenRule>[] getRules() { if (mRules == null) { mRules = mBackend.getAutomaticZenRules(); } return mRules; } Loading
src/com/android/settings/notification/zen/ZenModeAutomaticRulesPreferenceController.java +46 −61 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.notification.zen; import android.app.AutomaticZenRule; import android.content.Context; import android.util.ArrayMap; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.Fragment; Loading @@ -28,23 +27,21 @@ import androidx.preference.PreferenceScreen; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.HashMap; import java.util.Map; import java.util.Objects; public class ZenModeAutomaticRulesPreferenceController extends AbstractZenModeAutomaticRulePreferenceController { protected static final String KEY = "zen_mode_automatic_rules"; @VisibleForTesting protected PreferenceCategory mPreferenceCategory; // Map of rule key -> preference so that we can update each preference as needed @VisibleForTesting protected Map<String, ZenRulePreference> mZenRulePreferences = new ArrayMap<>(); Map.Entry<String, AutomaticZenRule>[] mSortedRules; public ZenModeAutomaticRulesPreferenceController(Context context, Fragment parent, Lifecycle lifecycle) { lifecycle, ZenModeBackend backend) { super(context, KEY, parent, lifecycle); mBackend = backend; } @Override Loading @@ -60,81 +57,69 @@ public class ZenModeAutomaticRulesPreferenceController extends @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mPreferenceCategory = screen.findPreference(getPreferenceKey()); mPreferenceCategory.setPersistent(false); // if mPreferenceCategory was un-set, make sure to clear out mZenRulePreferences too, just // in case if (mPreferenceCategory.getPreferenceCount() == 0) { mZenRulePreferences.clear(); } PreferenceCategory preferenceCategory = screen.findPreference(getPreferenceKey()); preferenceCategory.setPersistent(false); mSortedRules = getRules(); updateRules(preferenceCategory); } @Override public void updateState(Preference preference) { super.updateState(preference); Map.Entry<String, AutomaticZenRule>[] sortedRules = getRules(); // refresh the whole preference category list if the total number of rules has changed, or // if any individual rules have changed, so we can rebuild the list & keep things in sync boolean refreshPrefs = false; if (mPreferenceCategory.getPreferenceCount() != sortedRules.length) { refreshPrefs = true; boolean rulesChanged = false; if (sortedRules.length != mSortedRules.length) { rulesChanged = true; } else { // check whether any rules in sortedRules are not in mZenRulePreferences; that should // be enough to see whether something has changed for (int i = 0; i < sortedRules.length; i++) { if (!mZenRulePreferences.containsKey(sortedRules[i].getKey())) { refreshPrefs = true; for (int i = 0; i < mSortedRules.length; i++) { if (!Objects.equals(mSortedRules[i].getKey(), sortedRules[i].getKey()) || !Objects.equals(mSortedRules[i].getValue(), sortedRules[i].getValue())) { rulesChanged = true; break; } } } // if we need to refresh the whole list, clear the preference category and also start a // new map of preferences according to the preference category contents // we need to not update the existing one yet, as we'll need to know what preferences // previously existed in order to update and re-attach them to the preference category Map<String, ZenRulePreference> newPrefs = new ArrayMap<>(); if (refreshPrefs) { mPreferenceCategory.removeAll(); if (rulesChanged) { mSortedRules = sortedRules; updateRules((PreferenceCategory) preference); } } private void updateRules(PreferenceCategory preferenceCategory) { Map<String, ZenRulePreference> originalPreferences = new HashMap<>(); for (int i = 0; i < preferenceCategory.getPreferenceCount(); i++) { ZenRulePreference pref = (ZenRulePreference) preferenceCategory.getPreference(i); originalPreferences.put(pref.getKey(), pref); } // Loop through each rule, either updating the existing rule or creating the rule's // preference if needed (and, in the case where we need to rebuild the preference category // list, do so as well) for (int i = 0; i < sortedRules.length; i++) { String key = sortedRules[i].getKey(); if (mZenRulePreferences.containsKey(key)) { // existing rule; update its info if it's changed since the last display AutomaticZenRule rule = sortedRules[i].getValue(); ZenRulePreference pref = mZenRulePreferences.get(key); pref.updatePreference(rule); // preference for (int i = 0; i < mSortedRules.length; i++) { String key = mSortedRules[i].getKey(); // only add to preference category if the overall set of rules has changed so this // needs to be rearranged if (refreshPrefs) { mPreferenceCategory.addPreference(pref); newPrefs.put(key, pref); } if (originalPreferences.containsKey(key)) { // existing rule; update its info if it's changed since the last display AutomaticZenRule rule = mSortedRules[i].getValue(); originalPreferences.get(key).updatePreference(rule); } else { // new rule; create a new ZenRulePreference & add it to the preference category // and the map so we'll know about it later ZenRulePreference pref = createZenRulePreference(sortedRules[i]); mPreferenceCategory.addPreference(pref); newPrefs.put(key, pref); } ZenRulePreference pref = createZenRulePreference( mSortedRules[i], preferenceCategory); preferenceCategory.addPreference(pref); } // If anything was new, then make sure we overwrite mZenRulePreferences with our new data if (refreshPrefs) { mZenRulePreferences = newPrefs; originalPreferences.remove(key); } // Remove preferences that no longer have a rule for (String key : originalPreferences.keySet()) { preferenceCategory.removePreferenceRecursively(key); } } @VisibleForTesting ZenRulePreference createZenRulePreference(Map.Entry<String, AutomaticZenRule> rule) { return new ZenRulePreference(mPreferenceCategory.getContext(), rule, mParent, mMetricsFeatureProvider); ZenRulePreference createZenRulePreference(Map.Entry<String, AutomaticZenRule> rule, PreferenceCategory preferenceCategory) { return new ZenRulePreference(preferenceCategory.getContext(), rule, mParent, mMetricsFeatureProvider, mBackend); } }
src/com/android/settings/notification/zen/ZenModeAutomationSettings.java +3 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.app.NotificationManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.service.notification.ConditionProviderService; import android.view.Menu; import android.view.MenuInflater; Loading Loading @@ -59,10 +58,12 @@ public class ZenModeAutomationSettings extends ZenModeSettingsBase { private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, Fragment parent, ZenServiceListing serviceListing, Lifecycle lifecycle) { ZenModeBackend backend = new ZenModeBackend(context); List<AbstractPreferenceController> controllers = new ArrayList<>(); controllers.add(new ZenModeAddAutomaticRulePreferenceController(context, parent, serviceListing, lifecycle)); controllers.add(new ZenModeAutomaticRulesPreferenceController(context, parent, lifecycle)); controllers.add(new ZenModeAutomaticRulesPreferenceController( context, parent, lifecycle, backend)); return controllers; } Loading
src/com/android/settings/notification/zen/ZenRulePreference.java +4 −2 Original line number Diff line number Diff line Loading @@ -60,13 +60,15 @@ public class ZenRulePreference extends PrimarySwitchPreference { public ZenRulePreference(Context context, final Map.Entry<String, AutomaticZenRule> ruleEntry, Fragment parent, MetricsFeatureProvider metricsProvider) { Fragment parent, MetricsFeatureProvider metricsProvider, ZenModeBackend backend) { super(context); mBackend = ZenModeBackend.getInstance(context); mBackend = backend; mContext = context; mRule = ruleEntry.getValue(); mName = mRule.getName(); mId = ruleEntry.getKey(); setKey(mId); mParent = parent; mPm = mContext.getPackageManager(); mServiceListing = new ZenServiceListing(mContext, CONFIG); Loading
tests/robotests/src/com/android/settings/notification/zen/ZenModeAutomaticRulesPreferenceControllerTest.java +104 −89 Original line number Diff line number Diff line Loading @@ -17,13 +17,15 @@ package com.android.settings.notification.zen; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading @@ -31,13 +33,19 @@ import static org.mockito.Mockito.when; import android.app.AutomaticZenRule; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.provider.Settings; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -47,6 +55,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.util.ReflectionHelpers; import java.lang.reflect.Field; import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; Loading @@ -56,101 +65,122 @@ public class ZenModeAutomaticRulesPreferenceControllerTest { private ZenModeAutomaticRulesPreferenceController mController; @Mock private ZenModeBackend mBackend; @Mock private PreferenceCategory mockPref; @Mock private PreferenceCategory mPreferenceCategory; private PreferenceScreen mPreferenceScreen; @Mock private ZenRulePreference mZenRulePreference; private Context mContext; @Mock PackageManager mPm; @Before public void setup() { MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); mController = spy(new ZenModeAutomaticRulesPreferenceController(mContext, mock(Fragment.class), null)); ReflectionHelpers.setField(mController, "mBackend", mBackend); when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( mockPref); mController.displayPreference(mPreferenceScreen); doReturn(mZenRulePreference).when(mController).createZenRulePreference(any()); mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getPackageManager()).thenReturn(mPm); when(mPm.queryIntentActivities(any(), any())).thenReturn( ImmutableList.of(mock(ResolveInfo.class))); when(mBackend.getAutomaticZenRules()).thenReturn(new Map.Entry[0]); mController = spy(new ZenModeAutomaticRulesPreferenceController( mContext, mock(Fragment.class), null, mBackend)); final PreferenceManager preferenceManager = new PreferenceManager(mContext); mPreferenceScreen = preferenceManager.createPreferenceScreen(mContext); mPreferenceCategory = spy(new PreferenceCategory(mContext)); mPreferenceCategory.setKey(mController.getPreferenceKey()); mPreferenceScreen.addPreference(mPreferenceCategory); } @Test public void testDisplayPreference_resetsPreferencesWhenCategoryEmpty() { // when the PreferenceCategory is empty (no preferences), make sure we clear out any // stale state in the cached set of zen rule preferences mController.mZenRulePreferences.put("test1_id", mZenRulePreference); when(mockPref.getPreferenceCount()).thenReturn(0); public void testDisplayPreference_notPersistent() { mController.displayPreference(mPreferenceScreen); assertTrue(mController.mZenRulePreferences.isEmpty()); assertFalse(mPreferenceCategory.isPersistent()); } @Test public void testUpdateState_clearsPreferencesWhenAddingNewPreferences() { final int NUM_RULES = 3; public void testDisplayThenUpdateState_onlyAddsOnceRulesUnchanged() { final int NUM_RULES = 1; Map<String, AutomaticZenRule> rMap = new HashMap<>(); String ruleId1 = "test1_id"; String ruleId2 = "test2_id"; String ruleId3 = "test3_id"; AutomaticZenRule autoRule1 = new AutomaticZenRule("test_rule_1", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); AutomaticZenRule autoRule2 = new AutomaticZenRule("test_rule_2", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 20); AutomaticZenRule autoRule3 = new AutomaticZenRule("test_rule_3", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 30); rMap.put(ruleId1, autoRule1); rMap.put(ruleId2, autoRule2); rMap.put(ruleId3, autoRule3); // should add 3 new preferences to mockPref // should add 1 new preferences to mockPref mockGetAutomaticZenRules(NUM_RULES, rMap); mController.updateState(mockPref); verify(mockPref, times(1)).removeAll(); verify(mockPref, times(NUM_RULES)).addPreference(any()); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); mController.displayPreference(mPreferenceScreen); mController.updateState(mPreferenceCategory); assertEquals(NUM_RULES, mPreferenceCategory.getPreferenceCount()); verify(mPreferenceCategory, times(1)).addPreference(any()); } @Test public void testUpdateState_clearsPreferencesWhenRemovingPreferences(){ final int NUM_RULES = 2; public void testDisplayThenUpdateState_addsIfRulesChange() { Map<String, AutomaticZenRule> rMap = new HashMap<>(); String ruleId1 = "test1_id"; AutomaticZenRule autoRule1 = new AutomaticZenRule("test_rule_1", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); rMap.put(ruleId1, autoRule1); mockGetAutomaticZenRules(1, rMap); // adds one mController.displayPreference(mPreferenceScreen); String ruleId2 = "test2_id"; AutomaticZenRule autoRule2 = new AutomaticZenRule("test_rule_2", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 20); rMap.put(ruleId2, autoRule2); mockGetAutomaticZenRules(2, rMap); mController.updateState(mPreferenceCategory); assertEquals(2, mPreferenceCategory.getPreferenceCount()); verify(mPreferenceCategory, times(2)).addPreference(any()); } @Test public void testUpdateState_addingNewPreferences() { mController.displayPreference(mPreferenceScreen); final int NUM_RULES = 3; Map<String, AutomaticZenRule> rMap = new HashMap<>(); String ruleId1 = "test1_id"; String ruleId2 = "test2_id"; String ruleId3 = "test3_id"; AutomaticZenRule autoRule1 = new AutomaticZenRule("test_rule_1", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); AutomaticZenRule autoRule2 = new AutomaticZenRule("test_rule_2", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 20); AutomaticZenRule autoRule3 = new AutomaticZenRule("test_rule_3", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 30); rMap.put(ruleId1, autoRule1); rMap.put(ruleId2, autoRule2); rMap.put(ruleId3, autoRule3); // Add three preferences to the set of previously-known-about ZenRulePreferences; in this // case, test3_id is "deleted" mController.mZenRulePreferences.put("test1_id", mZenRulePreference); mController.mZenRulePreferences.put("test2_id", mZenRulePreference); mController.mZenRulePreferences.put("test3_id", mZenRulePreference); // update state should re-add all preferences since a preference was deleted when(mockPref.getPreferenceCount()).thenReturn(NUM_RULES + 1); // should add 3 new preferences to mockPref mockGetAutomaticZenRules(NUM_RULES, rMap); mController.updateState(mockPref); verify(mockPref, times(1)).removeAll(); verify(mockPref, times(NUM_RULES)).addPreference(any()); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); mController.updateState(mPreferenceCategory); assertEquals(NUM_RULES, mPreferenceCategory.getPreferenceCount()); } @Test public void testUpdateState_clearsPreferencesWhenSameNumberButDifferentPrefs() { public void testUpdateState_addsAndRemoves(){ mController.displayPreference(mPreferenceScreen); final int NUM_RULES = 2; Map<String, AutomaticZenRule> rMap = new HashMap<>(); String FAKE_1 = "fake key 1"; String FAKE_2 = "fake 2"; mPreferenceCategory.addPreference(new ZenRulePreference(mContext, new AbstractMap.SimpleEntry<>(FAKE_1, mock(AutomaticZenRule.class)), null, null, mBackend)); mPreferenceCategory.addPreference(new ZenRulePreference(mContext, new AbstractMap.SimpleEntry<>(FAKE_2, mock(AutomaticZenRule.class)), null, null, mBackend)); assertNotNull(mPreferenceCategory.findPreference(FAKE_1)); assertNotNull(mPreferenceCategory.findPreference(FAKE_2)); String ruleId1 = "test1_id"; String ruleId2 = "test2_id"; Loading @@ -162,52 +192,37 @@ public class ZenModeAutomaticRulesPreferenceControllerTest { rMap.put(ruleId1, autoRule1); rMap.put(ruleId2, autoRule2); // Add two preferences to the set of previously-known-about ZenRulePreferences; in this // case, test3_id is "deleted" but test2_id is "added" mController.mZenRulePreferences.put("test1_id", mZenRulePreference); mController.mZenRulePreferences.put("test3_id", mZenRulePreference); // update state should re-add all preferences since a preference was deleted when(mockPref.getPreferenceCount()).thenReturn(NUM_RULES); mockGetAutomaticZenRules(NUM_RULES, rMap); mController.updateState(mockPref); verify(mockPref, times(1)).removeAll(); verify(mockPref, times(NUM_RULES)).addPreference(any()); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); mController.updateState(mPreferenceCategory); assertNull(mPreferenceCategory.findPreference(FAKE_1)); assertNull(mPreferenceCategory.findPreference(FAKE_2)); assertNotNull(mPreferenceCategory.findPreference(ruleId1)); assertNotNull(mPreferenceCategory.findPreference(ruleId2)); } @Test public void testUpdateState_updateEnableState() throws NoSuchFieldException { final int NUM_RULES = 1; Map<String, AutomaticZenRule> rMap = new HashMap<>(); public void testUpdateState_updateEnableState() { mController.displayPreference(mPreferenceScreen); String testId = "test1_id"; AutomaticZenRule rule = new AutomaticZenRule("rule_name", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); rMap.put(testId, rule); when(mockPref.getPreferenceCount()).thenReturn(NUM_RULES); when(mockPref.getPreference(anyInt())).thenReturn(mZenRulePreference); mController.mZenRulePreferences.put("test1_id", mZenRulePreference); mPreferenceCategory.addPreference(new ZenRulePreference(mContext, new AbstractMap.SimpleEntry<>(testId, rule), null, null, mBackend)); // update state should NOT re-add all the preferences, should only update enable state rule.setEnabled(false); rMap.put(testId, rule); final int NUM_RULES = 1; Map<String, AutomaticZenRule> rMap = new HashMap<>(); AutomaticZenRule ruleUpdated = new AutomaticZenRule("rule_name", null, null, null, null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 10); ruleUpdated.setEnabled(false); rMap.put(testId, ruleUpdated); mockGetAutomaticZenRules(NUM_RULES, rMap); setZenRulePreferenceField("mId", testId); mController.updateState(mockPref); verify(mZenRulePreference, times(1)).updatePreference(any()); verify(mockPref, never()).removeAll(); assertEquals(NUM_RULES, mController.mZenRulePreferences.size()); } private void setZenRulePreferenceField(String name, Object value) { try { Field field = ZenRulePreference.class.getDeclaredField("mId"); field.setAccessible(true); field.set(mZenRulePreference, value); } catch (ReflectiveOperationException e) { fail("Unable to set mZenRulePreference field: " + name); } mController.updateState(mPreferenceCategory); assertFalse(mPreferenceCategory.findPreference(testId).isEnabled()); assertEquals(NUM_RULES, mPreferenceCategory.getPreferenceCount()); } private void mockGetAutomaticZenRules(int numRules, Map<String, AutomaticZenRule> rules) { Loading