1 /* 2 * Copyright (C) 2017 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 androidx.preference; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.mockito.Mockito.any; 21 import static org.mockito.Mockito.anyLong; 22 import static org.mockito.Mockito.doAnswer; 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.reset; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.verify; 27 28 import android.content.Context; 29 import android.os.Handler; 30 import android.os.Message; 31 import android.os.Parcelable; 32 import android.support.test.InstrumentationRegistry; 33 import android.support.test.annotation.UiThreadTest; 34 import android.support.test.filters.SmallTest; 35 import android.support.test.runner.AndroidJUnit4; 36 37 import org.junit.Before; 38 import org.junit.Test; 39 import org.junit.runner.RunWith; 40 import org.mockito.MockitoAnnotations; 41 import org.mockito.invocation.InvocationOnMock; 42 import org.mockito.stubbing.Answer; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * Test for InitialExpandedChildrenCount in {@link androidx.preference.PreferenceGroup}. 49 */ 50 @RunWith(AndroidJUnit4.class) 51 @SmallTest 52 public class PreferenceGroupInitialExpandedChildrenCountTest { 53 54 private static final int INITIAL_EXPANDED_COUNT = 5; 55 private static final int TOTAL_PREFERENCE = 10; 56 private static final String PREFERENCE_TITLE_PREFIX = "Preference_"; 57 private static final String PREFERENCE_KEY = "testing"; 58 59 private Context mContext; 60 private PreferenceManager mPreferenceManager; 61 private PreferenceScreen mScreen; 62 private Handler mHandler; 63 private List<Preference> mPreferenceList; 64 65 @Before 66 @UiThreadTest 67 public void setup() { 68 MockitoAnnotations.initMocks(this); 69 mContext = InstrumentationRegistry.getTargetContext(); 70 mPreferenceManager = new PreferenceManager(mContext); 71 mScreen = mPreferenceManager.createPreferenceScreen(mContext); 72 mScreen.setKey(PREFERENCE_KEY); 73 74 // Add 10 preferences to the screen and to the cache 75 mPreferenceList = new ArrayList<>(); 76 createTestPreferences(mScreen, mPreferenceList, TOTAL_PREFERENCE); 77 78 // Execute the handler task immediately 79 mHandler = spy(new Handler()); 80 doAnswer(new Answer<Void>() { 81 @Override 82 public Void answer(InvocationOnMock invocation) { 83 Object[] args = invocation.getArguments(); 84 Message message = (Message) args[0]; 85 mHandler.dispatchMessage(message); 86 return null; 87 } 88 }).when(mHandler).sendMessageDelayed(any(Message.class), anyLong()); 89 } 90 91 /** 92 * Verifies that PreferenceGroupAdapter is showing the preferences on the screen correctly with 93 * and without the collapsed child count set. 94 */ 95 @Test 96 @UiThreadTest 97 public void createPreferenceGroupAdapter_displayTopLevelPreferences() { 98 // No limit, should display all 10 preferences 99 PreferenceGroupAdapter preferenceGroupAdapter = new PreferenceGroupAdapter(mScreen); 100 assertPreferencesAreExpanded(preferenceGroupAdapter); 101 102 // Limit > child count, should display all 10 preferences 103 mScreen.setInitialExpandedChildrenCount(TOTAL_PREFERENCE + 4); 104 preferenceGroupAdapter = new PreferenceGroupAdapter(mScreen); 105 assertPreferencesAreExpanded(preferenceGroupAdapter); 106 107 // Limit = child count, should display all 10 preferences 108 mScreen.setInitialExpandedChildrenCount(TOTAL_PREFERENCE); 109 preferenceGroupAdapter = new PreferenceGroupAdapter(mScreen); 110 assertPreferencesAreExpanded(preferenceGroupAdapter); 111 112 // Limit < child count, should display up to the limit + expand button 113 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 114 preferenceGroupAdapter = new PreferenceGroupAdapter(mScreen); 115 assertPreferencesAreCollapsed(preferenceGroupAdapter); 116 for (int i = 0; i < INITIAL_EXPANDED_COUNT; i++) { 117 assertEquals(mPreferenceList.get(i), preferenceGroupAdapter.getItem(i)); 118 } 119 assertEquals(CollapsiblePreferenceGroupController.ExpandButton.class, 120 preferenceGroupAdapter.getItem(INITIAL_EXPANDED_COUNT).getClass()); 121 } 122 123 /** 124 * Verifies that PreferenceGroupAdapter is showing nested preferences on the screen correctly 125 * with and without the collapsed child count set. 126 */ 127 @Test 128 @UiThreadTest 129 public void createPreferenceGroupAdapter_displayNestedPreferences() { 130 final PreferenceScreen screen = mPreferenceManager.createPreferenceScreen(mContext); 131 screen.setKey(PREFERENCE_KEY); 132 final List<Preference> preferenceList = new ArrayList<>(); 133 134 // Add 2 preferences and 2 categories to screen 135 createTestPreferences(screen, preferenceList, 2); 136 createTestPreferencesCategory(screen, preferenceList, 4); 137 createTestPreferencesCategory(screen, preferenceList, 4); 138 139 // No limit, should display all 10 preferences + 2 categories 140 PreferenceGroupAdapter preferenceGroupAdapter = new PreferenceGroupAdapter(screen); 141 assertEquals(TOTAL_PREFERENCE + 2, preferenceGroupAdapter.getItemCount()); 142 143 // Limit > child count, should display all 10 preferences + 2 categories 144 screen.setInitialExpandedChildrenCount(TOTAL_PREFERENCE + 4); 145 preferenceGroupAdapter = new PreferenceGroupAdapter(screen); 146 assertEquals(TOTAL_PREFERENCE + 2, preferenceGroupAdapter.getItemCount()); 147 148 // Limit = child count, should display all 10 preferences + 2 categories 149 screen.setInitialExpandedChildrenCount(TOTAL_PREFERENCE); 150 preferenceGroupAdapter = new PreferenceGroupAdapter(screen); 151 assertEquals(TOTAL_PREFERENCE + 2, preferenceGroupAdapter.getItemCount()); 152 153 // Limit < child count, should display 2 preferences and the first 3 preference in the 154 // category + expand button 155 screen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 156 preferenceGroupAdapter = new PreferenceGroupAdapter(screen); 157 assertEquals(INITIAL_EXPANDED_COUNT + 2, preferenceGroupAdapter.getItemCount()); 158 for (int i = 0; i <= INITIAL_EXPANDED_COUNT; i++) { 159 assertEquals(preferenceList.get(i), preferenceGroupAdapter.getItem(i)); 160 } 161 assertEquals(CollapsiblePreferenceGroupController.ExpandButton.class, 162 preferenceGroupAdapter.getItem(INITIAL_EXPANDED_COUNT + 1).getClass()); 163 } 164 165 /** 166 * Verifies that correct summary is set for the expand button. 167 */ 168 @Test 169 @UiThreadTest 170 public void createPreferenceGroupAdapter_setExpandButtonSummary() { 171 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 172 PreferenceGroupAdapter preferenceGroupAdapter = new PreferenceGroupAdapter(mScreen); 173 // Preference 5 to Preference 9 are collapsed 174 CharSequence summary = mPreferenceList.get(INITIAL_EXPANDED_COUNT).getTitle(); 175 for (int i = INITIAL_EXPANDED_COUNT + 1; i < TOTAL_PREFERENCE; i++) { 176 summary = mContext.getString(R.string.summary_collapsed_preference_list, 177 summary, mPreferenceList.get(i).getTitle()); 178 } 179 final Preference expandButton = preferenceGroupAdapter.getItem(INITIAL_EXPANDED_COUNT); 180 assertEquals(summary, expandButton.getSummary()); 181 } 182 183 /** 184 * Verifies that summary for the expand button only lists visible preferences. 185 */ 186 @Test 187 @UiThreadTest 188 public void createPreferenceGroupAdapter_expandButtonSummaryShouldListVisiblePreferencesOnly() { 189 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 190 mPreferenceList.get(INITIAL_EXPANDED_COUNT + 1).setVisible(false); 191 mPreferenceList.get(INITIAL_EXPANDED_COUNT + 4).setVisible(false); 192 PreferenceGroupAdapter preferenceGroupAdapter = new PreferenceGroupAdapter(mScreen); 193 // Preference 5 to Preference 9 are collapsed, only preferences 5, 7, 8 are visible 194 CharSequence summary = mPreferenceList.get(INITIAL_EXPANDED_COUNT).getTitle(); 195 summary = mContext.getString(R.string.summary_collapsed_preference_list, 196 summary, mPreferenceList.get(INITIAL_EXPANDED_COUNT + 2).getTitle()); 197 summary = mContext.getString(R.string.summary_collapsed_preference_list, 198 summary, mPreferenceList.get(INITIAL_EXPANDED_COUNT + 3).getTitle()); 199 final Preference expandButton = preferenceGroupAdapter.getItem(INITIAL_EXPANDED_COUNT); 200 assertEquals(summary, expandButton.getSummary()); 201 } 202 203 /** 204 * Verifies that clicking the expand button will show all preferences. 205 */ 206 @Test 207 @UiThreadTest 208 public void clickExpandButton_shouldShowAllPreferences() { 209 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 210 211 // First showing 5 preference with expand button 212 PreferenceGroupAdapter preferenceGroupAdapter = 213 PreferenceGroupAdapter.createInstanceWithCustomHandler(mScreen, mHandler); 214 assertPreferencesAreCollapsed(preferenceGroupAdapter); 215 216 // Click the expand button, should review all preferences 217 final Preference expandButton = preferenceGroupAdapter.getItem(INITIAL_EXPANDED_COUNT); 218 expandButton.performClick(); 219 assertPreferencesAreExpanded(preferenceGroupAdapter); 220 } 221 222 /** 223 * Verifies that when preference visibility changes, it will sync the preferences only if some 224 * preferences are collapsed. 225 */ 226 @Test 227 @UiThreadTest 228 public void onPreferenceVisibilityChange_shouldSyncPreferencesIfCollapsed() { 229 // No limit set, should not sync preference 230 PreferenceGroupAdapter preferenceGroupAdapter = 231 PreferenceGroupAdapter.createInstanceWithCustomHandler(mScreen, mHandler); 232 preferenceGroupAdapter.onPreferenceVisibilityChange(mPreferenceList.get(3)); 233 verify(mHandler, never()).sendMessageDelayed(any(Message.class), anyLong()); 234 235 // Has limit set, should sync preference 236 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 237 preferenceGroupAdapter = 238 PreferenceGroupAdapter.createInstanceWithCustomHandler(mScreen, mHandler); 239 preferenceGroupAdapter.onPreferenceVisibilityChange(mPreferenceList.get(3)); 240 verify(mHandler).sendMessageDelayed(any(Message.class), anyLong()); 241 242 // Preferences expanded already, should not sync preference 243 final Preference expandButton = preferenceGroupAdapter.getItem(INITIAL_EXPANDED_COUNT); 244 expandButton.performClick(); 245 reset(mHandler); 246 preferenceGroupAdapter.onPreferenceVisibilityChange(mPreferenceList.get(3)); 247 verify(mHandler, never()).sendMessageDelayed(any(Message.class), anyLong()); 248 } 249 250 /** 251 * Verifies that the correct maximum number of preferences to show is being saved in the 252 * instance state. 253 */ 254 @Test 255 @UiThreadTest 256 public void saveInstanceState_shouldSaveMaxNumberOfChildrenToShow() { 257 // No limit set, should save max value 258 Parcelable state = mScreen.onSaveInstanceState(); 259 assertEquals(PreferenceGroup.SavedState.class, state.getClass()); 260 assertEquals(Integer.MAX_VALUE, 261 ((PreferenceGroup.SavedState) state).mInitialExpandedChildrenCount); 262 263 // Has limit set, should save limit 264 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 265 state = mScreen.onSaveInstanceState(); 266 assertEquals(PreferenceGroup.SavedState.class, state.getClass()); 267 assertEquals(INITIAL_EXPANDED_COUNT, 268 ((PreferenceGroup.SavedState) state).mInitialExpandedChildrenCount); 269 } 270 271 /** 272 * Verifies that if we restore to the same number of preferences to show, it will not update 273 * anything. 274 */ 275 @Test 276 @UiThreadTest 277 public void restoreInstanceState_noChange_shouldDoNothing() { 278 PreferenceGroup.SavedState state; 279 280 // Initialized as expanded, restore as expanded, should remain expanded 281 state = new PreferenceGroup.SavedState( 282 Preference.BaseSavedState.EMPTY_STATE, Integer.MAX_VALUE); 283 PreferenceGroupAdapter preferenceGroupAdapter = 284 PreferenceGroupAdapter.createInstanceWithCustomHandler(mScreen, mHandler); 285 mScreen.onRestoreInstanceState(state); 286 assertPreferencesAreExpanded(preferenceGroupAdapter); 287 verify(mHandler, never()).sendMessageDelayed(any(Message.class), anyLong()); 288 289 // Initialized as collapsed, restore as collapsed, should remain collapsed 290 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 291 state = new PreferenceGroup.SavedState( 292 Preference.BaseSavedState.EMPTY_STATE, INITIAL_EXPANDED_COUNT); 293 preferenceGroupAdapter = 294 PreferenceGroupAdapter.createInstanceWithCustomHandler(mScreen, mHandler); 295 mScreen.onRestoreInstanceState(state); 296 assertPreferencesAreCollapsed(preferenceGroupAdapter); 297 verify(mHandler, never()).sendMessageDelayed(any(Message.class), anyLong()); 298 } 299 300 /** 301 * Verifies that if the children is collapsed previously, they should be collapsed after the 302 * state is being restored. 303 */ 304 @Test 305 @UiThreadTest 306 public void restoreHierarchyState_previouslyCollapsed_shouldRestoreToCollapsedState() { 307 PreferenceGroup.SavedState state = 308 new PreferenceGroup.SavedState( 309 Preference.BaseSavedState.EMPTY_STATE, Integer.MAX_VALUE); 310 // Initialized as expanded, restore as collapsed, should collapse 311 state.mInitialExpandedChildrenCount = INITIAL_EXPANDED_COUNT; 312 mScreen.setInitialExpandedChildrenCount(Integer.MAX_VALUE); 313 PreferenceGroupAdapter preferenceGroupAdapter = 314 PreferenceGroupAdapter.createInstanceWithCustomHandler(mScreen, mHandler); 315 mScreen.onRestoreInstanceState(state); 316 verify(mHandler).sendMessageDelayed(any(Message.class), anyLong()); 317 assertPreferencesAreCollapsed(preferenceGroupAdapter); 318 } 319 320 /** 321 * Verifies that if the children is expanded previously, they should be expanded after the 322 * state is being restored. 323 */ 324 @Test 325 @UiThreadTest 326 public void restoreHierarchyState_previouslyExpanded_shouldRestoreToExpandedState() { 327 PreferenceGroup.SavedState state = 328 new PreferenceGroup.SavedState( 329 Preference.BaseSavedState.EMPTY_STATE, Integer.MAX_VALUE); 330 // Initialized as collapsed, restore as expanded, should expand 331 state.mInitialExpandedChildrenCount = Integer.MAX_VALUE; 332 mScreen.setInitialExpandedChildrenCount(INITIAL_EXPANDED_COUNT); 333 PreferenceGroupAdapter preferenceGroupAdapter = 334 PreferenceGroupAdapter.createInstanceWithCustomHandler(mScreen, mHandler); 335 mScreen.onRestoreInstanceState(state); 336 verify(mHandler).sendMessageDelayed(any(Message.class), anyLong()); 337 assertPreferencesAreExpanded(preferenceGroupAdapter); 338 } 339 340 // assert that the preferences are all expanded 341 private void assertPreferencesAreExpanded(PreferenceGroupAdapter adapter) { 342 assertEquals(TOTAL_PREFERENCE, adapter.getItemCount()); 343 } 344 345 // assert that the preferences exceeding the limit are collapsed 346 private void assertPreferencesAreCollapsed(PreferenceGroupAdapter adapter) { 347 // list shows preferences up to the limit and the expand button 348 assertEquals(INITIAL_EXPANDED_COUNT + 1, adapter.getItemCount()); 349 } 350 351 // create the number of preference in the corresponding preference group and add it to the cache 352 private void createTestPreferences(PreferenceGroup preferenceGroup, 353 List<Preference> preferenceList, int numPreference) { 354 for (int i = 0; i < numPreference; i++) { 355 final Preference preference = new Preference(mContext); 356 preference.setTitle(PREFERENCE_TITLE_PREFIX + i); 357 preferenceGroup.addPreference(preference); 358 preferenceList.add(preference); 359 } 360 } 361 362 // add a preference category and add the number of preference to it and the cache 363 private void createTestPreferencesCategory(PreferenceGroup preferenceGroup, 364 List<Preference> preferenceList, int numPreference) { 365 PreferenceCategory category = new PreferenceCategory(mContext); 366 preferenceGroup.addPreference(category); 367 preferenceList.add(category); 368 createTestPreferences(category, preferenceList, numPreference); 369 } 370 371 } 372