packages/apps/Settings
Revisión | 5bfe1d3f9c8900e9ab196767fefe183a19389db1 (tree) |
---|---|
Tiempo | 2020-10-13 09:06:48 |
Autor | Jeff DeCew <jeffdq@goog...> |
Commiter | Jeff DeCew |
Update NotificationChannelSettings Preferences in place to prevent re-layout.
(cherry picked from commit b409b640451cfc58fb529659b80cd872ed717095)
Fixes: 110093185
Test: ChannelListPreferenceControllerTest
Change-Id: If6acf305c44085e502a3304ea57e409ce049b40f
Merged-In: If6acf305c44085e502a3304ea57e409ce049b40f
@@ -23,16 +23,14 @@ import android.app.NotificationChannel; | ||
23 | 23 | import android.app.NotificationChannelGroup; |
24 | 24 | import android.app.settings.SettingsEnums; |
25 | 25 | import android.content.Context; |
26 | -import android.graphics.BlendMode; | |
27 | -import android.graphics.BlendModeColorFilter; | |
28 | 26 | import android.graphics.drawable.Drawable; |
29 | -import android.graphics.drawable.GradientDrawable; | |
30 | -import android.graphics.drawable.LayerDrawable; | |
31 | 27 | import android.os.AsyncTask; |
32 | 28 | import android.os.Bundle; |
33 | 29 | import android.provider.Settings; |
34 | 30 | import android.text.TextUtils; |
35 | 31 | |
32 | +import androidx.annotation.NonNull; | |
33 | +import androidx.annotation.Nullable; | |
36 | 34 | import androidx.preference.Preference; |
37 | 35 | import androidx.preference.PreferenceCategory; |
38 | 36 | import androidx.preference.PreferenceGroup; |
@@ -53,7 +51,8 @@ import java.util.List; | ||
53 | 51 | public class ChannelListPreferenceController extends NotificationPreferenceController { |
54 | 52 | |
55 | 53 | private static final String KEY = "channels"; |
56 | - private static String KEY_GENERAL_CATEGORY = "categories"; | |
54 | + private static final String KEY_GENERAL_CATEGORY = "categories"; | |
55 | + private static final String KEY_ZERO_CATEGORIES = "zeroCategories"; | |
57 | 56 | public static final String ARG_FROM_SETTINGS = "fromSettings"; |
58 | 57 | |
59 | 58 | private List<NotificationChannelGroup> mChannelGroupList; |
@@ -102,62 +101,192 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr | ||
102 | 101 | if (mContext == null) { |
103 | 102 | return; |
104 | 103 | } |
105 | - populateList(); | |
104 | + updateFullList(mPreference, mChannelGroupList); | |
106 | 105 | } |
107 | 106 | }.execute(); |
108 | 107 | } |
109 | 108 | |
110 | - private void populateList() { | |
111 | - // TODO: if preference has children, compare with newly loaded list | |
112 | - mPreference.removeAll(); | |
109 | + /** | |
110 | + * Update the preferences group to match the | |
111 | + * @param groupPrefsList | |
112 | + * @param channelGroups | |
113 | + */ | |
114 | + void updateFullList(@NonNull PreferenceCategory groupPrefsList, | |
115 | + @NonNull List<NotificationChannelGroup> channelGroups) { | |
116 | + if (channelGroups.isEmpty()) { | |
117 | + if (groupPrefsList.getPreferenceCount() == 1 | |
118 | + && KEY_ZERO_CATEGORIES.equals(groupPrefsList.getPreference(0).getKey())) { | |
119 | + // Ensure the titles are correct for the current language, but otherwise leave alone | |
120 | + PreferenceGroup groupCategory = (PreferenceGroup) groupPrefsList.getPreference(0); | |
121 | + groupCategory.setTitle(R.string.notification_channels); | |
122 | + groupCategory.getPreference(0).setTitle(R.string.no_channels); | |
123 | + } else { | |
124 | + // Clear any contents and create the 'zero-categories' group. | |
125 | + groupPrefsList.removeAll(); | |
113 | 126 | |
114 | - if (mChannelGroupList.isEmpty()) { | |
115 | - PreferenceCategory groupCategory = new PreferenceCategory(mContext); | |
116 | - groupCategory.setTitle(R.string.notification_channels); | |
117 | - groupCategory.setKey(KEY_GENERAL_CATEGORY); | |
118 | - mPreference.addPreference(groupCategory); | |
127 | + PreferenceCategory groupCategory = new PreferenceCategory(mContext); | |
128 | + groupCategory.setTitle(R.string.notification_channels); | |
129 | + groupCategory.setKey(KEY_ZERO_CATEGORIES); | |
130 | + groupPrefsList.addPreference(groupCategory); | |
119 | 131 | |
120 | - Preference empty = new Preference(mContext); | |
121 | - empty.setTitle(R.string.no_channels); | |
122 | - empty.setEnabled(false); | |
123 | - groupCategory.addPreference(empty); | |
132 | + Preference empty = new Preference(mContext); | |
133 | + empty.setTitle(R.string.no_channels); | |
134 | + empty.setEnabled(false); | |
135 | + groupCategory.addPreference(empty); | |
136 | + } | |
124 | 137 | } else { |
125 | - populateGroupList(); | |
138 | + updateGroupList(groupPrefsList, channelGroups); | |
126 | 139 | } |
127 | 140 | } |
128 | 141 | |
129 | - private void populateGroupList() { | |
130 | - for (NotificationChannelGroup group : mChannelGroupList) { | |
131 | - PreferenceCategory groupCategory = new PreferenceCategory(mContext); | |
132 | - groupCategory.setOrderingAsAdded(true); | |
133 | - mPreference.addPreference(groupCategory); | |
134 | - if (group.getId() == null) { | |
135 | - groupCategory.setTitle(R.string.notification_channels_other); | |
136 | - groupCategory.setKey(KEY_GENERAL_CATEGORY); | |
137 | - } else { | |
138 | - groupCategory.setTitle(group.getName()); | |
139 | - groupCategory.setKey(group.getId()); | |
140 | - populateGroupToggle(groupCategory, group); | |
142 | + /** | |
143 | + * Looks for the category for the given group's key at the expected index, if that doesn't | |
144 | + * match, it checks all groups, and if it can't find that group anywhere, it creates it. | |
145 | + */ | |
146 | + @NonNull | |
147 | + private PreferenceCategory findOrCreateGroupCategoryForKey( | |
148 | + @NonNull PreferenceCategory groupPrefsList, @Nullable String key, int expectedIndex) { | |
149 | + if (key == null) { | |
150 | + key = KEY_GENERAL_CATEGORY; | |
151 | + } | |
152 | + int preferenceCount = groupPrefsList.getPreferenceCount(); | |
153 | + if (expectedIndex < preferenceCount) { | |
154 | + Preference preference = groupPrefsList.getPreference(expectedIndex); | |
155 | + if (key.equals(preference.getKey())) { | |
156 | + return (PreferenceCategory) preference; | |
141 | 157 | } |
142 | - if (!group.isBlocked()) { | |
143 | - final List<NotificationChannel> channels = group.getChannels(); | |
144 | - Collections.sort(channels, CHANNEL_COMPARATOR); | |
145 | - int N = channels.size(); | |
146 | - for (int i = 0; i < N; i++) { | |
147 | - final NotificationChannel channel = channels.get(i); | |
148 | - // conversations get their own section | |
149 | - if (TextUtils.isEmpty(channel.getConversationId()) || channel.isDemoted()) { | |
150 | - populateSingleChannelPrefs(groupCategory, channel, group.isBlocked()); | |
151 | - } | |
152 | - } | |
158 | + } | |
159 | + for (int i = 0; i < preferenceCount; i++) { | |
160 | + Preference preference = groupPrefsList.getPreference(i); | |
161 | + if (key.equals(preference.getKey())) { | |
162 | + preference.setOrder(expectedIndex); | |
163 | + return (PreferenceCategory) preference; | |
164 | + } | |
165 | + } | |
166 | + PreferenceCategory groupCategory = new PreferenceCategory(mContext); | |
167 | + groupCategory.setOrder(expectedIndex); | |
168 | + groupCategory.setKey(key); | |
169 | + groupPrefsList.addPreference(groupCategory); | |
170 | + return groupCategory; | |
171 | + } | |
172 | + | |
173 | + private void updateGroupList(@NonNull PreferenceCategory groupPrefsList, | |
174 | + @NonNull List<NotificationChannelGroup> channelGroups) { | |
175 | + // Update the list, but optimize for the most common case where the list hasn't changed. | |
176 | + int numFinalGroups = channelGroups.size(); | |
177 | + int initialPrefCount = groupPrefsList.getPreferenceCount(); | |
178 | + List<PreferenceCategory> finalOrderedGroups = new ArrayList<>(numFinalGroups); | |
179 | + for (int i = 0; i < numFinalGroups; i++) { | |
180 | + NotificationChannelGroup group = channelGroups.get(i); | |
181 | + PreferenceCategory groupCategory = | |
182 | + findOrCreateGroupCategoryForKey(groupPrefsList, group.getId(), i); | |
183 | + finalOrderedGroups.add(groupCategory); | |
184 | + updateGroupPreferences(group, groupCategory); | |
185 | + } | |
186 | + int postAddPrefCount = groupPrefsList.getPreferenceCount(); | |
187 | + // If any groups were inserted (into a non-empty list) or need to be removed, we need to | |
188 | + // remove all groups and re-add them all. | |
189 | + // This is required to ensure proper ordering of inserted groups, and it simplifies logic | |
190 | + // at the cost of computation in the rare case that the list is changing. | |
191 | + boolean hasInsertions = initialPrefCount != 0 && initialPrefCount != numFinalGroups; | |
192 | + boolean requiresRemoval = postAddPrefCount != numFinalGroups; | |
193 | + if (hasInsertions || requiresRemoval) { | |
194 | + groupPrefsList.removeAll(); | |
195 | + for (PreferenceCategory group : finalOrderedGroups) { | |
196 | + groupPrefsList.addPreference(group); | |
153 | 197 | } |
154 | 198 | } |
155 | 199 | } |
156 | 200 | |
157 | - protected void populateGroupToggle(final PreferenceGroup parent, | |
158 | - NotificationChannelGroup group) { | |
159 | - RestrictedSwitchPreference preference = | |
160 | - new RestrictedSwitchPreference(mContext); | |
201 | + /** | |
202 | + * Looks for the channel preference for the given channel's key at the expected index, if that | |
203 | + * doesn't match, it checks all rows, and if it can't find that channel anywhere, it creates | |
204 | + * the preference. | |
205 | + */ | |
206 | + @NonNull | |
207 | + private MasterSwitchPreference findOrCreateChannelPrefForKey( | |
208 | + @NonNull PreferenceGroup groupPrefGroup, @NonNull String key, int expectedIndex) { | |
209 | + int preferenceCount = groupPrefGroup.getPreferenceCount(); | |
210 | + if (expectedIndex < preferenceCount) { | |
211 | + Preference preference = groupPrefGroup.getPreference(expectedIndex); | |
212 | + if (key.equals(preference.getKey())) { | |
213 | + return (MasterSwitchPreference) preference; | |
214 | + } | |
215 | + } | |
216 | + for (int i = 0; i < preferenceCount; i++) { | |
217 | + Preference preference = groupPrefGroup.getPreference(i); | |
218 | + if (key.equals(preference.getKey())) { | |
219 | + preference.setOrder(expectedIndex); | |
220 | + return (MasterSwitchPreference) preference; | |
221 | + } | |
222 | + } | |
223 | + MasterSwitchPreference channelPref = new MasterSwitchPreference(mContext); | |
224 | + channelPref.setOrder(expectedIndex); | |
225 | + channelPref.setKey(key); | |
226 | + groupPrefGroup.addPreference(channelPref); | |
227 | + return channelPref; | |
228 | + } | |
229 | + | |
230 | + private void updateGroupPreferences(@NonNull NotificationChannelGroup group, | |
231 | + @NonNull PreferenceGroup groupPrefGroup) { | |
232 | + int initialPrefCount = groupPrefGroup.getPreferenceCount(); | |
233 | + List<Preference> finalOrderedPrefs = new ArrayList<>(); | |
234 | + if (group.getId() == null) { | |
235 | + // For the 'null' group, set the "Other" title. | |
236 | + groupPrefGroup.setTitle(R.string.notification_channels_other); | |
237 | + } else { | |
238 | + // For an app-defined group, set their name and create a row to toggle 'isBlocked'. | |
239 | + groupPrefGroup.setTitle(group.getName()); | |
240 | + finalOrderedPrefs.add(addOrUpdateGroupToggle(groupPrefGroup, group)); | |
241 | + } | |
242 | + // Here "empty" means having no channel rows; the group toggle is ignored for this purpose. | |
243 | + boolean initiallyEmpty = groupPrefGroup.getPreferenceCount() == finalOrderedPrefs.size(); | |
244 | + | |
245 | + // For each channel, add or update the preference object. | |
246 | + final List<NotificationChannel> channels = | |
247 | + group.isBlocked() ? Collections.emptyList() : group.getChannels(); | |
248 | + Collections.sort(channels, CHANNEL_COMPARATOR); | |
249 | + for (NotificationChannel channel : channels) { | |
250 | + if (!TextUtils.isEmpty(channel.getConversationId()) && !channel.isDemoted()) { | |
251 | + // conversations get their own section | |
252 | + continue; | |
253 | + } | |
254 | + // Get or create the row, and populate its current state. | |
255 | + MasterSwitchPreference channelPref = findOrCreateChannelPrefForKey(groupPrefGroup, | |
256 | + channel.getId(), /* expectedIndex */ finalOrderedPrefs.size()); | |
257 | + updateSingleChannelPrefs(channelPref, channel, group.isBlocked()); | |
258 | + finalOrderedPrefs.add(channelPref); | |
259 | + } | |
260 | + int postAddPrefCount = groupPrefGroup.getPreferenceCount(); | |
261 | + | |
262 | + // If any channels were inserted (into a non-empty list) or need to be removed, we need to | |
263 | + // remove all preferences and re-add them all. | |
264 | + // This is required to ensure proper ordering of inserted channels, and it simplifies logic | |
265 | + // at the cost of computation in the rare case that the list is changing. | |
266 | + int numFinalGroups = finalOrderedPrefs.size(); | |
267 | + boolean hasInsertions = !initiallyEmpty && initialPrefCount != numFinalGroups; | |
268 | + boolean requiresRemoval = postAddPrefCount != numFinalGroups; | |
269 | + if (hasInsertions || requiresRemoval) { | |
270 | + groupPrefGroup.removeAll(); | |
271 | + for (Preference preference : finalOrderedPrefs) { | |
272 | + groupPrefGroup.addPreference(preference); | |
273 | + } | |
274 | + } | |
275 | + } | |
276 | + | |
277 | + /** Add or find and update the toggle for disabling the entire notification channel group. */ | |
278 | + private Preference addOrUpdateGroupToggle(@NonNull final PreferenceGroup parent, | |
279 | + @NonNull final NotificationChannelGroup group) { | |
280 | + boolean shouldAdd = false; | |
281 | + final RestrictedSwitchPreference preference; | |
282 | + if (parent.getPreferenceCount() > 0 | |
283 | + && parent.getPreference(0) instanceof RestrictedSwitchPreference) { | |
284 | + preference = (RestrictedSwitchPreference) parent.getPreference(0); | |
285 | + } else { | |
286 | + shouldAdd = true; | |
287 | + preference = new RestrictedSwitchPreference(mContext); | |
288 | + } | |
289 | + preference.setOrder(-1); | |
161 | 290 | preference.setTitle(mContext.getString( |
162 | 291 | R.string.notification_switch_label, group.getName())); |
163 | 292 | preference.setEnabled(mAdmin == null |
@@ -171,23 +300,26 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr | ||
171 | 300 | onGroupBlockStateChanged(group); |
172 | 301 | return true; |
173 | 302 | }); |
174 | - | |
175 | - parent.addPreference(preference); | |
303 | + if (shouldAdd) { | |
304 | + parent.addPreference(preference); | |
305 | + } | |
306 | + return preference; | |
176 | 307 | } |
177 | 308 | |
178 | - protected Preference populateSingleChannelPrefs(PreferenceGroup parent, | |
179 | - final NotificationChannel channel, final boolean groupBlocked) { | |
180 | - MasterSwitchPreference channelPref = new MasterSwitchPreference(mContext); | |
309 | + /** Update the properties of the channel preference with the values from the channel object. */ | |
310 | + private void updateSingleChannelPrefs(@NonNull final MasterSwitchPreference channelPref, | |
311 | + @NonNull final NotificationChannel channel, | |
312 | + final boolean groupBlocked) { | |
181 | 313 | channelPref.setSwitchEnabled(mAdmin == null |
182 | 314 | && isChannelBlockable(channel) |
183 | 315 | && isChannelConfigurable(channel) |
184 | 316 | && !groupBlocked); |
185 | - channelPref.setIcon(null); | |
186 | 317 | if (channel.getImportance() > IMPORTANCE_LOW) { |
187 | 318 | channelPref.setIcon(getAlertingIcon()); |
319 | + } else { | |
320 | + channelPref.setIcon(null); | |
188 | 321 | } |
189 | 322 | channelPref.setIconSize(MasterSwitchPreference.ICON_SIZE_SMALL); |
190 | - channelPref.setKey(channel.getId()); | |
191 | 323 | channelPref.setTitle(channel.getName()); |
192 | 324 | channelPref.setSummary(NotificationBackend.getSentSummary( |
193 | 325 | mContext, mAppRow.sentByChannel.get(channel.getId()), false)); |
@@ -219,10 +351,6 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr | ||
219 | 351 | |
220 | 352 | return true; |
221 | 353 | }); |
222 | - if (parent.findPreference(channelPref.getKey()) == null) { | |
223 | - parent.addPreference(channelPref); | |
224 | - } | |
225 | - return channelPref; | |
226 | 354 | } |
227 | 355 | |
228 | 356 | private Drawable getAlertingIcon() { |
@@ -235,30 +363,9 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr | ||
235 | 363 | if (group == null) { |
236 | 364 | return; |
237 | 365 | } |
238 | - PreferenceGroup groupGroup = mPreference.findPreference(group.getId()); | |
239 | - | |
240 | - if (groupGroup != null) { | |
241 | - if (group.isBlocked()) { | |
242 | - List<Preference> toRemove = new ArrayList<>(); | |
243 | - int childCount = groupGroup.getPreferenceCount(); | |
244 | - for (int i = 0; i < childCount; i++) { | |
245 | - Preference pref = groupGroup.getPreference(i); | |
246 | - if (pref instanceof MasterSwitchPreference) { | |
247 | - toRemove.add(pref); | |
248 | - } | |
249 | - } | |
250 | - for (Preference pref : toRemove) { | |
251 | - groupGroup.removePreference(pref); | |
252 | - } | |
253 | - } else { | |
254 | - final List<NotificationChannel> channels = group.getChannels(); | |
255 | - Collections.sort(channels, CHANNEL_COMPARATOR); | |
256 | - int N = channels.size(); | |
257 | - for (int i = 0; i < N; i++) { | |
258 | - final NotificationChannel channel = channels.get(i); | |
259 | - populateSingleChannelPrefs(groupGroup, channel, group.isBlocked()); | |
260 | - } | |
261 | - } | |
366 | + PreferenceGroup groupPrefGroup = mPreference.findPreference(group.getId()); | |
367 | + if (groupPrefGroup != null) { | |
368 | + updateGroupPreferences(group, groupPrefGroup); | |
262 | 369 | } |
263 | 370 | } |
264 | 371 | } |
@@ -23,6 +23,8 @@ import android.view.View; | ||
23 | 23 | import android.view.View.OnClickListener; |
24 | 24 | import android.widget.Switch; |
25 | 25 | |
26 | +import androidx.annotation.Keep; | |
27 | +import androidx.annotation.Nullable; | |
26 | 28 | import androidx.preference.PreferenceViewHolder; |
27 | 29 | |
28 | 30 | import com.android.settings.R; |
@@ -101,6 +103,16 @@ public class MasterSwitchPreference extends RestrictedPreference { | ||
101 | 103 | return mSwitch != null && mChecked; |
102 | 104 | } |
103 | 105 | |
106 | + /** | |
107 | + * Used to validate the state of mChecked and mCheckedSet when testing, without requiring | |
108 | + * that a ViewHolder be bound to the object. | |
109 | + */ | |
110 | + @Keep | |
111 | + @Nullable | |
112 | + public Boolean getCheckedState() { | |
113 | + return mCheckedSet ? mChecked : null; | |
114 | + } | |
115 | + | |
104 | 116 | public void setChecked(boolean checked) { |
105 | 117 | // Always set checked the first time; don't assume the field's default of false. |
106 | 118 | final boolean changed = mChecked != checked; |
@@ -0,0 +1,395 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2020 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +package com.android.settings.notification.app; | |
18 | + | |
19 | +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; | |
20 | +import static android.app.NotificationManager.IMPORTANCE_HIGH; | |
21 | +import static android.app.NotificationManager.IMPORTANCE_NONE; | |
22 | + | |
23 | +import static junit.framework.TestCase.assertEquals; | |
24 | +import static junit.framework.TestCase.assertFalse; | |
25 | +import static junit.framework.TestCase.assertNotNull; | |
26 | +import static junit.framework.TestCase.assertNull; | |
27 | +import static junit.framework.TestCase.assertTrue; | |
28 | + | |
29 | +import android.app.Instrumentation; | |
30 | +import android.app.NotificationChannel; | |
31 | +import android.app.NotificationChannelGroup; | |
32 | +import android.content.Context; | |
33 | + | |
34 | +import androidx.preference.PreferenceCategory; | |
35 | +import androidx.preference.PreferenceGroup; | |
36 | +import androidx.preference.PreferenceManager; | |
37 | +import androidx.preference.PreferenceScreen; | |
38 | +import androidx.preference.SwitchPreference; | |
39 | +import androidx.test.annotation.UiThreadTest; | |
40 | +import androidx.test.filters.SmallTest; | |
41 | +import androidx.test.platform.app.InstrumentationRegistry; | |
42 | +import androidx.test.runner.AndroidJUnit4; | |
43 | + | |
44 | +import com.android.settings.notification.NotificationBackend; | |
45 | +import com.android.settings.notification.NotificationBackend.NotificationsSentState; | |
46 | +import com.android.settings.widget.MasterSwitchPreference; | |
47 | + | |
48 | +import org.junit.Before; | |
49 | +import org.junit.Test; | |
50 | +import org.junit.runner.RunWith; | |
51 | + | |
52 | +import java.util.ArrayList; | |
53 | +import java.util.List; | |
54 | + | |
55 | +@RunWith(AndroidJUnit4.class) | |
56 | +@SmallTest | |
57 | +public class ChannelListPreferenceControllerTest { | |
58 | + private Context mContext; | |
59 | + private NotificationBackend mBackend; | |
60 | + private NotificationBackend.AppRow mAppRow; | |
61 | + private ChannelListPreferenceController mController; | |
62 | + private PreferenceManager mPreferenceManager; | |
63 | + private PreferenceScreen mPreferenceScreen; | |
64 | + private PreferenceCategory mGroupList; | |
65 | + | |
66 | + @Before | |
67 | + public void setUp() throws Exception { | |
68 | + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); | |
69 | + mContext = instrumentation.getTargetContext(); | |
70 | + | |
71 | + instrumentation.runOnMainSync(() -> { | |
72 | + mBackend = new NotificationBackend(); | |
73 | + mAppRow = mBackend.loadAppRow(mContext, | |
74 | + mContext.getPackageManager(), mContext.getApplicationInfo()); | |
75 | + mController = new ChannelListPreferenceController(mContext, mBackend); | |
76 | + mController.onResume(mAppRow, null, null, null, null, null); | |
77 | + mPreferenceManager = new PreferenceManager(mContext); | |
78 | + mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext); | |
79 | + mGroupList = new PreferenceCategory(mContext); | |
80 | + mPreferenceScreen.addPreference(mGroupList); | |
81 | + }); | |
82 | + } | |
83 | + | |
84 | + @Test | |
85 | + @UiThreadTest | |
86 | + public void testUpdateFullList_incrementalUpdates() { | |
87 | + // Start by testing the case with no groups or channels | |
88 | + List<NotificationChannelGroup> inGroups = new ArrayList<>(); | |
89 | + mController.updateFullList(mGroupList, inGroups); | |
90 | + { | |
91 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
92 | + assertEquals("zeroCategories", mGroupList.getPreference(0).getKey()); | |
93 | + } | |
94 | + | |
95 | + // Test that adding a group clears the zero category and adds everything | |
96 | + NotificationChannelGroup inGroup1 = new NotificationChannelGroup("group1", "Group 1"); | |
97 | + inGroup1.addChannel(new NotificationChannel("ch1a", "Channel 1A", IMPORTANCE_DEFAULT)); | |
98 | + inGroups.add(inGroup1); | |
99 | + mController.updateFullList(mGroupList, inGroups); | |
100 | + { | |
101 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
102 | + PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0); | |
103 | + assertEquals("group1", group1.getKey()); | |
104 | + assertEquals(2, group1.getPreferenceCount()); | |
105 | + assertNull(group1.getPreference(0).getKey()); | |
106 | + assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle()); | |
107 | + assertEquals("ch1a", group1.getPreference(1).getKey()); | |
108 | + assertEquals("Channel 1A", group1.getPreference(1).getTitle()); | |
109 | + } | |
110 | + | |
111 | + // Test that adding a channel works -- no dupes or omissions | |
112 | + inGroup1.addChannel(new NotificationChannel("ch1b", "Channel 1B", IMPORTANCE_DEFAULT)); | |
113 | + mController.updateFullList(mGroupList, inGroups); | |
114 | + { | |
115 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
116 | + PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0); | |
117 | + assertEquals("group1", group1.getKey()); | |
118 | + assertEquals(3, group1.getPreferenceCount()); | |
119 | + assertNull(group1.getPreference(0).getKey()); | |
120 | + assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle()); | |
121 | + assertEquals("ch1a", group1.getPreference(1).getKey()); | |
122 | + assertEquals("Channel 1A", group1.getPreference(1).getTitle()); | |
123 | + assertEquals("ch1b", group1.getPreference(2).getKey()); | |
124 | + assertEquals("Channel 1B", group1.getPreference(2).getTitle()); | |
125 | + } | |
126 | + | |
127 | + // Test that renaming a channel does in fact rename the preferences | |
128 | + inGroup1.getChannels().get(1).setName("Channel 1B - Renamed"); | |
129 | + mController.updateFullList(mGroupList, inGroups); | |
130 | + { | |
131 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
132 | + PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0); | |
133 | + assertEquals("group1", group1.getKey()); | |
134 | + assertEquals(3, group1.getPreferenceCount()); | |
135 | + assertNull(group1.getPreference(0).getKey()); | |
136 | + assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle()); | |
137 | + assertEquals("ch1a", group1.getPreference(1).getKey()); | |
138 | + assertEquals("Channel 1A", group1.getPreference(1).getTitle()); | |
139 | + assertEquals("ch1b", group1.getPreference(2).getKey()); | |
140 | + assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle()); | |
141 | + } | |
142 | + | |
143 | + // Test that adding a group works and results in the correct sorting. | |
144 | + NotificationChannelGroup inGroup0 = new NotificationChannelGroup("group0", "Group 0"); | |
145 | + inGroup0.addChannel(new NotificationChannel("ch0b", "Channel 0B", IMPORTANCE_DEFAULT)); | |
146 | + // NOTE: updateFullList takes a List which has been sorted, so we insert at 0 for this check | |
147 | + inGroups.add(0, inGroup0); | |
148 | + mController.updateFullList(mGroupList, inGroups); | |
149 | + { | |
150 | + assertEquals(2, mGroupList.getPreferenceCount()); | |
151 | + PreferenceGroup group0 = (PreferenceGroup) mGroupList.getPreference(0); | |
152 | + assertEquals("group0", group0.getKey()); | |
153 | + assertEquals(2, group0.getPreferenceCount()); | |
154 | + assertNull(group0.getPreference(0).getKey()); | |
155 | + assertEquals("All \"Group 0\" notifications", group0.getPreference(0).getTitle()); | |
156 | + assertEquals("ch0b", group0.getPreference(1).getKey()); | |
157 | + assertEquals("Channel 0B", group0.getPreference(1).getTitle()); | |
158 | + PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(1); | |
159 | + assertEquals("group1", group1.getKey()); | |
160 | + assertEquals(3, group1.getPreferenceCount()); | |
161 | + assertNull(group1.getPreference(0).getKey()); | |
162 | + assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle()); | |
163 | + assertEquals("ch1a", group1.getPreference(1).getKey()); | |
164 | + assertEquals("Channel 1A", group1.getPreference(1).getTitle()); | |
165 | + assertEquals("ch1b", group1.getPreference(2).getKey()); | |
166 | + assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle()); | |
167 | + } | |
168 | + | |
169 | + // Test that adding a channel that comes before another works and has correct ordering. | |
170 | + // NOTE: the channels within a group are sorted inside updateFullList. | |
171 | + inGroup0.addChannel(new NotificationChannel("ch0a", "Channel 0A", IMPORTANCE_DEFAULT)); | |
172 | + mController.updateFullList(mGroupList, inGroups); | |
173 | + { | |
174 | + assertEquals(2, mGroupList.getPreferenceCount()); | |
175 | + PreferenceGroup group0 = (PreferenceGroup) mGroupList.getPreference(0); | |
176 | + assertEquals("group0", group0.getKey()); | |
177 | + assertEquals(3, group0.getPreferenceCount()); | |
178 | + assertNull(group0.getPreference(0).getKey()); | |
179 | + assertEquals("All \"Group 0\" notifications", group0.getPreference(0).getTitle()); | |
180 | + assertEquals("ch0a", group0.getPreference(1).getKey()); | |
181 | + assertEquals("Channel 0A", group0.getPreference(1).getTitle()); | |
182 | + assertEquals("ch0b", group0.getPreference(2).getKey()); | |
183 | + assertEquals("Channel 0B", group0.getPreference(2).getTitle()); | |
184 | + PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(1); | |
185 | + assertEquals("group1", group1.getKey()); | |
186 | + assertEquals(3, group1.getPreferenceCount()); | |
187 | + assertNull(group1.getPreference(0).getKey()); | |
188 | + assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle()); | |
189 | + assertEquals("ch1a", group1.getPreference(1).getKey()); | |
190 | + assertEquals("Channel 1A", group1.getPreference(1).getTitle()); | |
191 | + assertEquals("ch1b", group1.getPreference(2).getKey()); | |
192 | + assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle()); | |
193 | + } | |
194 | + | |
195 | + // Test that the "Other" group works. | |
196 | + // Also test a simultaneous addition and deletion. | |
197 | + inGroups.remove(inGroup0); | |
198 | + NotificationChannelGroup inGroupOther = new NotificationChannelGroup(null, null); | |
199 | + inGroupOther.addChannel(new NotificationChannel("chXa", "Other A", IMPORTANCE_DEFAULT)); | |
200 | + inGroupOther.addChannel(new NotificationChannel("chXb", "Other B", IMPORTANCE_DEFAULT)); | |
201 | + inGroups.add(inGroupOther); | |
202 | + mController.updateFullList(mGroupList, inGroups); | |
203 | + { | |
204 | + assertEquals(2, mGroupList.getPreferenceCount()); | |
205 | + PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0); | |
206 | + assertEquals("group1", group1.getKey()); | |
207 | + assertEquals(3, group1.getPreferenceCount()); | |
208 | + assertNull(group1.getPreference(0).getKey()); | |
209 | + assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle()); | |
210 | + assertEquals("ch1a", group1.getPreference(1).getKey()); | |
211 | + assertEquals("Channel 1A", group1.getPreference(1).getTitle()); | |
212 | + assertEquals("ch1b", group1.getPreference(2).getKey()); | |
213 | + assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle()); | |
214 | + PreferenceGroup groupOther = (PreferenceGroup) mGroupList.getPreference(1); | |
215 | + assertEquals("categories", groupOther.getKey()); | |
216 | + assertEquals(2, groupOther.getPreferenceCount()); | |
217 | + assertEquals("chXa", groupOther.getPreference(0).getKey()); | |
218 | + assertEquals("Other A", groupOther.getPreference(0).getTitle()); | |
219 | + assertEquals("chXb", groupOther.getPreference(1).getKey()); | |
220 | + assertEquals("Other B", groupOther.getPreference(1).getTitle()); | |
221 | + } | |
222 | + | |
223 | + // Test that the removal of a channel works. | |
224 | + inGroupOther.getChannels().remove(0); | |
225 | + mController.updateFullList(mGroupList, inGroups); | |
226 | + { | |
227 | + assertEquals(2, mGroupList.getPreferenceCount()); | |
228 | + PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0); | |
229 | + assertEquals("group1", group1.getKey()); | |
230 | + assertEquals(3, group1.getPreferenceCount()); | |
231 | + assertNull(group1.getPreference(0).getKey()); | |
232 | + assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle()); | |
233 | + assertEquals("ch1a", group1.getPreference(1).getKey()); | |
234 | + assertEquals("Channel 1A", group1.getPreference(1).getTitle()); | |
235 | + assertEquals("ch1b", group1.getPreference(2).getKey()); | |
236 | + assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle()); | |
237 | + PreferenceGroup groupOther = (PreferenceGroup) mGroupList.getPreference(1); | |
238 | + assertEquals("categories", groupOther.getKey()); | |
239 | + assertEquals(1, groupOther.getPreferenceCount()); | |
240 | + assertEquals("chXb", groupOther.getPreference(0).getKey()); | |
241 | + assertEquals("Other B", groupOther.getPreference(0).getTitle()); | |
242 | + } | |
243 | + | |
244 | + // Test that we go back to the empty state when clearing all groups and channels. | |
245 | + inGroups.clear(); | |
246 | + mController.updateFullList(mGroupList, inGroups); | |
247 | + { | |
248 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
249 | + assertEquals("zeroCategories", mGroupList.getPreference(0).getKey()); | |
250 | + } | |
251 | + } | |
252 | + | |
253 | + | |
254 | + @Test | |
255 | + @UiThreadTest | |
256 | + public void testUpdateFullList_groupBlockedChange() { | |
257 | + List<NotificationChannelGroup> inGroups = new ArrayList<>(); | |
258 | + NotificationChannelGroup inGroup = new NotificationChannelGroup("group", "My Group"); | |
259 | + inGroup.addChannel(new NotificationChannel("channelA", "Channel A", IMPORTANCE_DEFAULT)); | |
260 | + inGroup.addChannel(new NotificationChannel("channelB", "Channel B", IMPORTANCE_NONE)); | |
261 | + inGroups.add(inGroup); | |
262 | + | |
263 | + // Test that the group is initially showing all preferences | |
264 | + mController.updateFullList(mGroupList, inGroups); | |
265 | + { | |
266 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
267 | + PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0); | |
268 | + assertEquals("group", group.getKey()); | |
269 | + assertEquals(3, group.getPreferenceCount()); | |
270 | + SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0); | |
271 | + assertNull(groupBlockPref.getKey()); | |
272 | + assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle()); | |
273 | + assertTrue(groupBlockPref.isChecked()); | |
274 | + MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1); | |
275 | + assertEquals("channelA", channelAPref.getKey()); | |
276 | + assertEquals("Channel A", channelAPref.getTitle()); | |
277 | + assertEquals(Boolean.TRUE, channelAPref.getCheckedState()); | |
278 | + MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2); | |
279 | + assertEquals("channelB", channelBPref.getKey()); | |
280 | + assertEquals("Channel B", channelBPref.getTitle()); | |
281 | + assertEquals(Boolean.FALSE, channelBPref.getCheckedState()); | |
282 | + } | |
283 | + | |
284 | + // Test that when a group is blocked, the list removes its individual channel preferences | |
285 | + inGroup.setBlocked(true); | |
286 | + mController.updateFullList(mGroupList, inGroups); | |
287 | + { | |
288 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
289 | + PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0); | |
290 | + assertEquals("group", group.getKey()); | |
291 | + assertEquals(1, group.getPreferenceCount()); | |
292 | + SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0); | |
293 | + assertNull(groupBlockPref.getKey()); | |
294 | + assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle()); | |
295 | + assertFalse(groupBlockPref.isChecked()); | |
296 | + } | |
297 | + | |
298 | + // Test that when a group is unblocked, the list adds its individual channel preferences | |
299 | + inGroup.setBlocked(false); | |
300 | + mController.updateFullList(mGroupList, inGroups); | |
301 | + { | |
302 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
303 | + PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0); | |
304 | + assertEquals("group", group.getKey()); | |
305 | + assertEquals(3, group.getPreferenceCount()); | |
306 | + SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0); | |
307 | + assertNull(groupBlockPref.getKey()); | |
308 | + assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle()); | |
309 | + assertTrue(groupBlockPref.isChecked()); | |
310 | + MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1); | |
311 | + assertEquals("channelA", channelAPref.getKey()); | |
312 | + assertEquals("Channel A", channelAPref.getTitle()); | |
313 | + assertEquals(Boolean.TRUE, channelAPref.getCheckedState()); | |
314 | + MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2); | |
315 | + assertEquals("channelB", channelBPref.getKey()); | |
316 | + assertEquals("Channel B", channelBPref.getTitle()); | |
317 | + assertEquals(Boolean.FALSE, channelBPref.getCheckedState()); | |
318 | + } | |
319 | + } | |
320 | + | |
321 | + @Test | |
322 | + @UiThreadTest | |
323 | + public void testUpdateFullList_channelUpdates() { | |
324 | + List<NotificationChannelGroup> inGroups = new ArrayList<>(); | |
325 | + NotificationChannelGroup inGroup = new NotificationChannelGroup("group", "Group"); | |
326 | + NotificationChannel channelA = | |
327 | + new NotificationChannel("channelA", "Channel A", IMPORTANCE_HIGH); | |
328 | + NotificationChannel channelB = | |
329 | + new NotificationChannel("channelB", "Channel B", IMPORTANCE_NONE); | |
330 | + inGroup.addChannel(channelA); | |
331 | + inGroup.addChannel(channelB); | |
332 | + inGroups.add(inGroup); | |
333 | + | |
334 | + NotificationsSentState sentA = new NotificationsSentState(); | |
335 | + sentA.avgSentDaily = 2; | |
336 | + sentA.avgSentWeekly = 10; | |
337 | + NotificationsSentState sentB = new NotificationsSentState(); | |
338 | + sentB.avgSentDaily = 0; | |
339 | + sentB.avgSentWeekly = 2; | |
340 | + mAppRow.sentByChannel.put("channelA", sentA); | |
341 | + | |
342 | + // Test that the channels' properties are reflected in the preference | |
343 | + mController.updateFullList(mGroupList, inGroups); | |
344 | + { | |
345 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
346 | + PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0); | |
347 | + assertEquals("group", group.getKey()); | |
348 | + assertEquals(3, group.getPreferenceCount()); | |
349 | + assertNull(group.getPreference(0).getKey()); | |
350 | + assertEquals("All \"Group\" notifications", group.getPreference(0).getTitle()); | |
351 | + MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1); | |
352 | + assertEquals("channelA", channelAPref.getKey()); | |
353 | + assertEquals("Channel A", channelAPref.getTitle()); | |
354 | + assertEquals(Boolean.TRUE, channelAPref.getCheckedState()); | |
355 | + assertEquals("~2 notifications per day", channelAPref.getSummary()); | |
356 | + assertNotNull(channelAPref.getIcon()); | |
357 | + MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2); | |
358 | + assertEquals("channelB", channelBPref.getKey()); | |
359 | + assertEquals("Channel B", channelBPref.getTitle()); | |
360 | + assertEquals(Boolean.FALSE, channelBPref.getCheckedState()); | |
361 | + assertNull(channelBPref.getSummary()); | |
362 | + assertNull(channelBPref.getIcon()); | |
363 | + } | |
364 | + | |
365 | + channelA.setImportance(IMPORTANCE_NONE); | |
366 | + channelB.setImportance(IMPORTANCE_DEFAULT); | |
367 | + | |
368 | + mAppRow.sentByChannel.remove("channelA"); | |
369 | + mAppRow.sentByChannel.put("channelB", sentB); | |
370 | + | |
371 | + // Test that changing the channels' properties correctly updates the preference | |
372 | + mController.updateFullList(mGroupList, inGroups); | |
373 | + { | |
374 | + assertEquals(1, mGroupList.getPreferenceCount()); | |
375 | + PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0); | |
376 | + assertEquals("group", group.getKey()); | |
377 | + assertEquals(3, group.getPreferenceCount()); | |
378 | + assertNull(group.getPreference(0).getKey()); | |
379 | + assertEquals("All \"Group\" notifications", group.getPreference(0).getTitle()); | |
380 | + MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1); | |
381 | + assertEquals("channelA", channelAPref.getKey()); | |
382 | + assertEquals("Channel A", channelAPref.getTitle()); | |
383 | + assertEquals(Boolean.FALSE, channelAPref.getCheckedState()); | |
384 | + assertNull(channelAPref.getSummary()); | |
385 | + assertNull(channelAPref.getIcon()); | |
386 | + MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2); | |
387 | + assertEquals("channelB", channelBPref.getKey()); | |
388 | + assertEquals("Channel B", channelBPref.getTitle()); | |
389 | + assertEquals(Boolean.TRUE, channelBPref.getCheckedState()); | |
390 | + assertEquals("~2 notifications per week", channelBPref.getSummary()); | |
391 | + assertNotNull(channelBPref.getIcon()); | |
392 | + } | |
393 | + } | |
394 | + | |
395 | +} |