Home | History | Annotate | Download | only in preference
      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