From a1a84e6530ea3f17f0c0f80b1b984d062289aea1 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 19 Oct 2016 14:15:34 -0700 Subject: [PATCH] Make dashboard tile refresh more effcient. Instead of removing and re-adding all dashboard tiles, figure out a diff and rebind/add/remove as necessary. Bug: 32255863 Test: RunSettingsRoboTests Change-Id: I9d87ba30ab746257d0ea71282951348ebc4e8965 --- .../dashboard/DashboardFeatureProvider.java | 7 + .../DashboardFeatureProviderImpl.java | 6 + .../settings/dashboard/DashboardFragment.java | 141 +++++++++++------- .../dashboard/ProgressiveDisclosureMixin.java | 65 +++++--- .../dashboard/DashboardFragmentTest.java | 38 ++++- .../dashboard/ProgressiveDisclosureTest.java | 78 +++++++++- 6 files changed, 256 insertions(+), 79 deletions(-) diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java index 92154be5cd4..0a8dbbfb97e 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java @@ -15,6 +15,7 @@ */ package com.android.settings.dashboard; +import android.content.Context; import android.support.v7.preference.Preference; import com.android.settingslib.drawer.DashboardCategory; @@ -53,4 +54,10 @@ public interface DashboardFeatureProvider { * Returns an unique string key for the tile. */ String getDashboardKeyForTile(Tile tile); + + /** + * Returns a {@link ProgressiveDisclosureMixin} for specified fragment. + */ + ProgressiveDisclosureMixin getProgressiveDisclosureMixin(Context context, + DashboardFragment fragment); } diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index df3f6ef5ba0..ed0520af511 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -72,4 +72,10 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { sb.append(component.getClassName()); return sb.toString(); } + + @Override + public ProgressiveDisclosureMixin getProgressiveDisclosureMixin(Context context, + DashboardFragment fragment) { + return new ProgressiveDisclosureMixin(context, this, fragment); + } } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index ef49e3091cf..9ab8cf4b6e7 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -22,7 +22,9 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import android.util.ArrayMap; @@ -41,6 +43,7 @@ import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.SettingsDrawerActivity; import com.android.settingslib.drawer.Tile; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -69,7 +72,8 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment super.onAttach(context); mDashboardFeatureProvider = FeatureFactory.getFactory(context).getDashboardFeatureProvider(context); - mProgressiveDisclosureMixin = new ProgressiveDisclosureMixin(context, this); + mProgressiveDisclosureMixin = mDashboardFeatureProvider + .getProgressiveDisclosureMixin(context, this); getLifecycle().addObserver(mProgressiveDisclosureMixin); final List controllers = getPreferenceControllers(context); @@ -81,6 +85,24 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment } } + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + // Set ComparisonCallback so we get better animation when list changes. + getPreferenceManager().setPreferenceComparisonCallback( + new PreferenceManager.SimplePreferenceComparisonCallback()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = super.onCreateView(inflater, container, savedInstanceState); + if (mDashboardFeatureProvider.isEnabled()) { + getListView().addItemDecoration(mDividerDecoration); + } + return view; + } + @Override public void onCategoriesChanged() { final DashboardCategory category = @@ -98,6 +120,18 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment refreshAllPreferences(getLogTag()); } + @Override + public void setDivider(Drawable divider) { + if (mDashboardFeatureProvider.isEnabled()) { + // Intercept divider and set it transparent so system divider decoration is disabled. + // We will use our decoration to draw divider more intelligently. + mDividerDecoration.setDivider(divider); + super.setDivider(new ColorDrawable(Color.TRANSPARENT)); + } else { + super.setDivider(divider); + } + } + @Override public void onStart() { super.onStart(); @@ -210,16 +244,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment } } - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - final View view = super.onCreateView(inflater, container, savedInstanceState); - if (mDashboardFeatureProvider.isEnabled()) { - getListView().addItemDecoration(mDividerDecoration); - } - return view; - } - /** * Update state of each preference managed by PreferenceController. */ @@ -238,18 +262,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment } } - @Override - public void setDivider(Drawable divider) { - if (mDashboardFeatureProvider.isEnabled()) { - // Intercept divider and set it transparent so system divider decoration is disabled. - // We will use our decoration to draw divider more intelligently. - mDividerDecoration.setDivider(divider); - super.setDivider(new ColorDrawable(Color.TRANSPARENT)); - } else { - super.setDivider(divider); - } - } - /** * Refresh all preference items, including both static prefs from xml, and dynamic items from * DashboardCategory. @@ -275,18 +287,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment /** * Refresh preference items backed by DashboardCategory. */ - private void refreshDashboardTiles(final String TAG) { + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + void refreshDashboardTiles(final String TAG) { final PreferenceScreen screen = getPreferenceScreen(); - for (String key : mDashboardTilePrefKeys) { - // Remove tiles from screen - mProgressiveDisclosureMixin.removePreference(screen, key); - } - mDashboardTilePrefKeys.clear(); + final Context context = getContext(); final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(getCategoryKey()); if (category == null) { - Log.d(TAG, "NO dynamic tiles for " + TAG); + Log.d(TAG, "NO dashboard tiles for " + TAG); return; } List tiles = category.tiles; @@ -294,6 +303,9 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment Log.d(TAG, "tile list is empty, skipping category " + category.title); return; } + // Create a list to track which tiles are to be removed. + final List remove = new ArrayList<>(mDashboardTilePrefKeys); + // There are dashboard tiles, so we need to install SummaryLoader. if (mSummaryLoader != null) { mSummaryLoader.release(); @@ -307,32 +319,51 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment Log.d(TAG, "tile does not contain a key, skipping " + tile); continue; } - mDashboardTilePrefKeys.add(key); - final Preference pref = new DashboardTilePreference(context); - pref.setTitle(tile.title); - pref.setKey(key); - pref.setSummary(tile.summary); - if (tile.icon != null) { - pref.setIcon(tile.icon.loadDrawable(context)); + if (mDashboardTilePrefKeys.contains(key)) { + // Have the key already, will rebind. + final Preference preference = mProgressiveDisclosureMixin.findPreference( + screen, key); + bindPreferenceToTile(context, preference, tile, key); + } else { + // Don't have this key, add it. + final Preference pref = new DashboardTilePreference(context); + bindPreferenceToTile(context, pref, tile, key); + mProgressiveDisclosureMixin.addPreference(screen, pref); + mDashboardTilePrefKeys.add(key); } - final Bundle metadata = tile.metaData; - if (metadata != null) { - String clsName = metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS); - if (!TextUtils.isEmpty(clsName)) { - pref.setFragment(clsName); - } - } else if (tile.intent != null) { - final Intent intent = new Intent(tile.intent); - pref.setOnPreferenceClickListener(preference -> { - getActivity().startActivityForResult(intent, 0); - return true; - }); - } - // Use negated priority for order, because tile priority is based on intent-filter - // (larger value has higher priority). However pref order defines smaller value has - // higher priority. - pref.setOrder(-tile.priority); - mProgressiveDisclosureMixin.addPreference(screen, pref); + remove.remove(key); + } + // Finally remove tiles that are gone. + for (String key : remove) { + mDashboardTilePrefKeys.remove(key); + mProgressiveDisclosureMixin.removePreference(screen, key); } } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + void bindPreferenceToTile(Context context, Preference pref, Tile tile, String key) { + pref.setTitle(tile.title); + pref.setKey(key); + pref.setSummary(tile.summary); + if (tile.icon != null) { + pref.setIcon(tile.icon.loadDrawable(context)); + } + final Bundle metadata = tile.metaData; + if (metadata != null) { + String clsName = metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS); + if (!TextUtils.isEmpty(clsName)) { + pref.setFragment(clsName); + } + } else if (tile.intent != null) { + final Intent intent = new Intent(tile.intent); + pref.setOnPreferenceClickListener(preference -> { + getActivity().startActivityForResult(intent, 0); + return true; + }); + } + // Use negated priority for order, because tile priority is based on intent-filter + // (larger value has higher priority). However pref order defines smaller value has + // higher priority. + pref.setOrder(-tile.priority); + } } diff --git a/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java b/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java index 84b6b7f2b22..75251b3e1eb 100644 --- a/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java +++ b/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java @@ -28,9 +28,9 @@ import android.util.Log; import com.android.settings.core.lifecycle.LifecycleObserver; import com.android.settings.core.lifecycle.events.OnCreate; import com.android.settings.core.lifecycle.events.OnSaveInstanceState; -import com.android.settings.overlay.FeatureFactory; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickListener, @@ -43,18 +43,19 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL private int mTileLimit = DEFAULT_TILE_LIMIT; private final DashboardFeatureProvider mDashboardFeatureProvider; - private final List collapsedPrefs = new ArrayList<>(); + // Collapsed preference sorted by order. + private final List mCollapsedPrefs = new ArrayList<>(); private final ExpandPreference mExpandButton; private final PreferenceFragment mFragment; private boolean mUserExpanded; - public ProgressiveDisclosureMixin(Context context, PreferenceFragment fragment) { + public ProgressiveDisclosureMixin(Context context, + DashboardFeatureProvider dashboardFeatureProvider, PreferenceFragment fragment) { mFragment = fragment; mExpandButton = new ExpandPreference(context); mExpandButton.setOnPreferenceClickListener(this); - mDashboardFeatureProvider = FeatureFactory.getFactory(context) - .getDashboardFeatureProvider(context); + mDashboardFeatureProvider = dashboardFeatureProvider; } @Override @@ -75,10 +76,10 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL final PreferenceScreen screen = mFragment.getPreferenceScreen(); if (screen != null) { screen.removePreference(preference); - for (Preference pref : collapsedPrefs) { + for (Preference pref : mCollapsedPrefs) { screen.addPreference(pref); } - collapsedPrefs.clear(); + mCollapsedPrefs.clear(); mUserExpanded = true; } } @@ -96,7 +97,7 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL * Whether the controller is in collapsed state. */ public boolean isCollapsed() { - return !collapsedPrefs.isEmpty(); + return !mCollapsedPrefs.isEmpty(); } /** @@ -115,7 +116,7 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL if (!shouldCollapse(screen)) { return; } - if (!collapsedPrefs.isEmpty()) { + if (!mCollapsedPrefs.isEmpty()) { Log.w(TAG, "collapsed list should ALWAYS BE EMPTY before collapsing!"); } @@ -134,12 +135,27 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL public void addPreference(PreferenceScreen screen, Preference pref) { // Either add to screen, or to collapsed list. if (isCollapsed()) { - // Already collapsed, add to collapsed list. - addToCollapsedList(pref); + // insert the preference to right position. + final int lastPreferenceIndex = screen.getPreferenceCount() - 2; + if (lastPreferenceIndex >= 0) { + final Preference lastPreference = screen.getPreference(lastPreferenceIndex); + if (lastPreference.getOrder() > pref.getOrder()) { + // insert to screen and move the last pref to collapsed list. + screen.removePreference(lastPreference); + screen.addPreference(pref); + addToCollapsedList(lastPreference); + } else { + // Insert to collapsed list. + addToCollapsedList(pref); + } + } else { + // Couldn't find last preference on screen, just add to collapsed list. + addToCollapsedList(pref); + } } else if (shouldCollapse(screen)) { // About to have too many tiles on scree, collapse and add pref to collapsed list. + screen.addPreference(pref); collapse(screen); - addToCollapsedList(pref); } else { // No need to collapse, add to screen directly. screen.addPreference(pref); @@ -158,11 +174,11 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL return; } // Didn't find on screen, try removing from collapsed list. - for (int i = 0; i < collapsedPrefs.size(); i++) { - final Preference pref = collapsedPrefs.get(i); + for (int i = 0; i < mCollapsedPrefs.size(); i++) { + final Preference pref = mCollapsedPrefs.get(i); if (TextUtils.equals(key, pref.getKey())) { - collapsedPrefs.remove(pref); - if (collapsedPrefs.isEmpty()) { + mCollapsedPrefs.remove(pref); + if (mCollapsedPrefs.isEmpty()) { // Removed last element, remove expand button too. screen.removePreference(mExpandButton); } @@ -179,8 +195,8 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL if (preference != null) { return preference; } - for (int i = 0; i < collapsedPrefs.size(); i++) { - final Preference pref = collapsedPrefs.get(i); + for (int i = 0; i < mCollapsedPrefs.size(); i++) { + final Preference pref = mCollapsedPrefs.get(i); if (TextUtils.equals(key, pref.getKey())) { return pref; } @@ -192,9 +208,18 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL /** * Add preference to collapsed list. */ - @VisibleForTesting + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) void addToCollapsedList(Preference preference) { - collapsedPrefs.add(preference); + // Insert preference based on it's order. + int insertionIndex = Collections.binarySearch(mCollapsedPrefs, preference); + if (insertionIndex < 0) { + insertionIndex = insertionIndex * -1 - 1; + } + mCollapsedPrefs.add(insertionIndex, preference); } + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + List getCollapsedPrefs() { + return mCollapsedPrefs; + } } diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java index cc064df87e1..e4222ced169 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java @@ -16,10 +16,13 @@ package com.android.settings.dashboard; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; +import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; import com.android.settings.core.PreferenceController; import com.android.settings.overlay.FeatureFactory; @@ -44,6 +47,7 @@ import java.util.List; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -59,6 +63,8 @@ public class DashboardFragmentTest { private DashboardCategory mDashboardCategory; @Mock private FakeFeatureFactory mFakeFeatureFactory; + @Mock + private ProgressiveDisclosureMixin mDisclosureMixin; private TestFragment mTestFragment; @Before @@ -69,9 +75,12 @@ public class DashboardFragmentTest { mDashboardCategory.tiles = new ArrayList<>(); mDashboardCategory.tiles.add(new Tile()); mTestFragment = new TestFragment(ShadowApplication.getInstance().getApplicationContext()); - mTestFragment.onAttach(ShadowApplication.getInstance().getApplicationContext()); + when(mFakeFeatureFactory.dashboardFeatureProvider + .getProgressiveDisclosureMixin(any(Context.class), eq(mTestFragment))) + .thenReturn(mDisclosureMixin); when(mFakeFeatureFactory.dashboardFeatureProvider.getTilesForCategory(anyString())) .thenReturn(mDashboardCategory); + mTestFragment.onAttach(ShadowApplication.getInstance().getApplicationContext()); } @Test @@ -87,11 +96,14 @@ public class DashboardFragmentTest { @Test public void displayTilesAsPreference_shouldAddTilesWithIntent() { + when(mFakeFeatureFactory.dashboardFeatureProvider.getTilesForCategory(anyString())) + .thenReturn(mDashboardCategory); when(mFakeFeatureFactory.dashboardFeatureProvider.getDashboardKeyForTile(any(Tile.class))) .thenReturn("test_key"); mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); - verify(mTestFragment.mScreen).addPreference(any(DashboardTilePreference.class)); + verify(mDisclosureMixin).addPreference(any(PreferenceScreen.class), + any(DashboardTilePreference.class)); } @Test @@ -109,6 +121,27 @@ public class DashboardFragmentTest { verify(mTestFragment.mScreen, never()).addPreference(any(DashboardTilePreference.class)); } + @Test + public void bindPreference_shouldBindAllData() { + final Preference preference = new Preference( + ShadowApplication.getInstance().getApplicationContext()); + final Tile tile = new Tile(); + tile.title = "title"; + tile.summary = "summary"; + tile.icon = Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)); + tile.metaData = new Bundle(); + tile.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); + tile.priority = 10; + mTestFragment.bindPreferenceToTile(mContext, preference, tile, "123"); + + assertThat(preference.getTitle()).isEqualTo(tile.title); + assertThat(preference.getSummary()).isEqualTo(tile.summary); + assertThat(preference.getIcon()).isNotNull(); + assertThat(preference.getFragment()) + .isEqualTo(tile.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS)); + assertThat(preference.getOrder()).isEqualTo(-tile.priority); + } + public static class TestPreferenceController extends PreferenceController { public TestPreferenceController(Context context) { @@ -139,7 +172,6 @@ public class DashboardFragmentTest { public static class TestFragment extends DashboardFragment { private final Context mContext; - @Mock public PreferenceScreen mScreen; public TestFragment(Context context) { diff --git a/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java b/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java index 986ccd926ea..be02596db03 100644 --- a/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java @@ -35,6 +35,8 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; +import java.util.List; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; @@ -67,7 +69,8 @@ public class ProgressiveDisclosureTest { mScreen = mPreferenceFragment.getPreferenceScreen(); mAppContext = ShadowApplication.getInstance().getApplicationContext(); mFakeFeatureFactory = (FakeFeatureFactory) FeatureFactory.getFactory(mContext); - mMixin = new ProgressiveDisclosureMixin(mAppContext, mPreferenceFragment); + mMixin = new ProgressiveDisclosureMixin(mAppContext, + mFakeFeatureFactory.dashboardFeatureProvider, mPreferenceFragment); mPreference = new Preference(mAppContext); mPreference.setKey("test"); when(mFakeFeatureFactory.dashboardFeatureProvider.isEnabled()).thenReturn(true); @@ -180,4 +183,77 @@ public class ProgressiveDisclosureTest { verify(screen, times(3)).removePreference(any(Preference.class)); } + @Test + public void addToCollapsedList_shouldAddInOrder() { + final Preference pref1 = new Preference(mAppContext); + final Preference pref2 = new Preference(mAppContext); + pref1.setOrder(10); + pref2.setOrder(20); + + // Pref1 has lower order than pref2, but add pref2 first. The collapsed list should maintain + // items in increasing order. + mMixin.addToCollapsedList(pref2); + mMixin.addToCollapsedList(pref1); + + List collapsedList = mMixin.getCollapsedPrefs(); + assertThat(collapsedList.get(0)).isSameAs(pref1); + assertThat(collapsedList.get(1)).isSameAs(pref2); + } + + @Test + public void addPreferenceWhenCollapsed_noPrefOnScreen_shouldAddToList() { + // Add something to collapsed list so we are in collapsed state. + mMixin.addToCollapsedList(new Preference(mAppContext)); + assertThat(mMixin.getCollapsedPrefs().size()).isEqualTo(1); + + // Just 1 preference on screen: the more button + when(mScreen.getPreferenceCount()).thenReturn(1); + final Preference toBeAdded = new Preference(mAppContext); + toBeAdded.setOrder(100); + mMixin.addPreference(mScreen, toBeAdded); + + // Should have 2 prefs in collapsed list now + assertThat(mMixin.getCollapsedPrefs().size()).isEqualTo(2); + assertThat(mMixin.getCollapsedPrefs().get(0)).isSameAs(toBeAdded); + } + + @Test + public void addPreferenceWhenCollapsed_prefOrderLessThanLastOnScreen_shouldAddToScreen() { + final Preference lastPref = new Preference(mAppContext); + lastPref.setOrder(100); + // Add something to collapsed list so we are in collapsed state. + mMixin.addToCollapsedList(new Preference(mAppContext)); + assertThat(mMixin.getCollapsedPrefs().size()).isEqualTo(1); + // 3 prefs on screen, 2 are real and the last one is more button. + when(mScreen.getPreferenceCount()).thenReturn(3); + when(mScreen.getPreference(1)).thenReturn(lastPref); + + final Preference toBeAdded = new Preference(mAppContext); + toBeAdded.setOrder(50); + mMixin.addPreference(mScreen, toBeAdded); + + verify(mScreen).removePreference(lastPref); + verify(mScreen).addPreference(toBeAdded); + assertThat(mMixin.getCollapsedPrefs().get(0)).isSameAs(lastPref); + } + + @Test + public void addPreferenceWhenCollapsed_prefOrderMoreThanLastOnScreen_shouldAddToList() { + final Preference lastPref = new Preference(mAppContext); + lastPref.setOrder(100); + // Add something to collapsed list so we are in collapsed state. + mMixin.addToCollapsedList(new Preference(mAppContext)); + assertThat(mMixin.getCollapsedPrefs().size()).isEqualTo(1); + // 3 prefs on screen, 2 are real and the last one is more button. + when(mScreen.getPreferenceCount()).thenReturn(3); + when(mScreen.getPreference(1)).thenReturn(lastPref); + + final Preference toBeAdded = new Preference(mAppContext); + toBeAdded.setOrder(200); + mMixin.addPreference(mScreen, toBeAdded); + + verify(mScreen, never()).removePreference(any(Preference.class)); + verify(mScreen, never()).addPreference(any(Preference.class)); + assertThat(mMixin.getCollapsedPrefs().get(0)).isSameAs(toBeAdded); + } }