From a29346bab86a7100ff48f4025abfa54c6ca6f2d1 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 29 Jun 2017 12:58:39 -0700 Subject: [PATCH] Change the way we generate stable id for DashboardSummary - The countItem() method generates id based on position of Tile. This is not truely stable. - Added stable id constants for static views, and use hash of title as stable id for tiles. Bug: 33861822 Test: robotests Change-Id: Ibbc88c82655503dc3964cb0c430c779dc9c93d41 --- .../settings/dashboard/DashboardData.java | 139 +++++++++--------- .../settings/dashboard/DashboardDataTest.java | 20 +++ 2 files changed, 89 insertions(+), 70 deletions(-) diff --git a/src/com/android/settings/dashboard/DashboardData.java b/src/com/android/settings/dashboard/DashboardData.java index 7593f826ae9..f96ba544ca8 100644 --- a/src/com/android/settings/dashboard/DashboardData.java +++ b/src/com/android/settings/dashboard/DashboardData.java @@ -17,7 +17,7 @@ package com.android.settings.dashboard; import android.annotation.IntDef; import android.graphics.drawable.Icon; -import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.support.v7.util.DiffUtil; import android.text.TextUtils; @@ -30,6 +30,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Description about data list used in the DashboardAdapter. In the data list each item can be @@ -42,25 +43,34 @@ public class DashboardData { public static final int HEADER_MODE_SUGGESTION_EXPANDED = 1; public static final int HEADER_MODE_FULLY_EXPANDED = 2; public static final int HEADER_MODE_COLLAPSED = 3; + @Retention(RetentionPolicy.SOURCE) @IntDef({HEADER_MODE_DEFAULT, HEADER_MODE_SUGGESTION_EXPANDED, HEADER_MODE_FULLY_EXPANDED, - HEADER_MODE_COLLAPSED}) - public @interface HeaderMode{} + HEADER_MODE_COLLAPSED}) + public @interface HeaderMode { + } public static final int POSITION_NOT_FOUND = -1; public static final int DEFAULT_SUGGESTION_COUNT = 2; - // id namespace for different type of items. - private static final int NS_SPACER = 0; - private static final int NS_ITEMS = 2000; - private static final int NS_SUGGESTION_CONDITION = 3000; + // stable id for different type of items. + @VisibleForTesting + static final int STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER = 0; + @VisibleForTesting + static final int STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER = 1; + @VisibleForTesting + static final int STABLE_ID_SUGGESTION_CONDITION_FOOTER = 2; + @VisibleForTesting + static final int STABLE_ID_SUGGESTION_CONTAINER = 3; + @VisibleForTesting + static final int STABLE_ID_CONDITION_CONTAINER = 4; private final List mItems; private final List mCategories; private final List mConditions; private final List mSuggestions; - private final @HeaderMode int mSuggestionConditionMode; - private int mId; + @HeaderMode + private final int mSuggestionConditionMode; private DashboardData(Builder builder) { mCategories = builder.mCategories; @@ -69,7 +79,6 @@ public class DashboardData { mSuggestionConditionMode = builder.mSuggestionConditionMode; mItems = new ArrayList<>(); - mId = 0; buildItemsData(); } @@ -186,33 +195,18 @@ public class DashboardData { return suggestionSize; } - public boolean hasMoreSuggestions() { - return mSuggestionConditionMode == HEADER_MODE_COLLAPSED && mSuggestions.size() > 0 - || mSuggestionConditionMode == HEADER_MODE_DEFAULT - && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT; - } - - private void resetCount() { - mId = 0; - } - /** - * Count the item and add it into list when {@paramref add} is true. + * Add item into list when {@paramref add} is true. * - * Note that {@link #mId} will increment automatically and the real - * id stored in {@link Item} is shifted by {@paramref nameSpace}. This is a - * simple way to keep the id stable. - * - * @param object maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null - * @param type type of the item, and value is the layout id - * @param add flag about whether to add item into list - * @param nameSpace namespace based on the type + * @param item maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null + * @param type type of the item, and value is the layout id + * @param stableId The stable id for this item + * @param add flag about whether to add item into list */ - private void countItem(Object object, int type, boolean add, int nameSpace) { + private void addToItemList(Object item, int type, int stableId, boolean add) { if (add) { - mItems.add(new Item(object, type, mId + nameSpace)); + mItems.add(new Item(item, type, stableId)); } - mId++; } /** @@ -228,51 +222,52 @@ public class DashboardData { final int hiddenSuggestion = hasSuggestions ? sizeOf(mSuggestions) - sizeOf(suggestions) : 0; - resetCount(); - /* Top suggestion/condition header. This will be present when there is any suggestion or - * condition to show, except in the case that there is only conditions to show and the - * mode is fully expanded. */ - countItem(new SuggestionConditionHeaderData(conditions, hiddenSuggestion), - R.layout.suggestion_condition_header, hasSuggestions - || hasConditions && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED, - NS_SUGGESTION_CONDITION); - - /* Suggestion container. This is the card view that contains the list of suggestions. - * This will be added whenever the suggestion list is not empty */ - countItem(suggestions, R.layout.suggestion_condition_container, sizeOf(suggestions) > 0, - NS_SUGGESTION_CONDITION); - - /* Second suggestion/condition header. This will be added when there is at least one - * suggestion or condition that is not currently displayed, and the user can expand the - * section to view more items. */ - countItem(new SuggestionConditionHeaderData(conditions, hiddenSuggestion), + /* Top suggestion/condition header. This will be present when there is any suggestion or + * condition to show, except in the case that there is only conditions to show and the + * mode is fully expanded. */ + addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion), R.layout.suggestion_condition_header, + STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER, + hasSuggestions + || hasConditions && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED); + + /* Suggestion container. This is the card view that contains the list of suggestions. + * This will be added whenever the suggestion list is not empty */ + addToItemList(suggestions, R.layout.suggestion_condition_container, + STABLE_ID_SUGGESTION_CONTAINER, sizeOf(suggestions) > 0); + + /* Second suggestion/condition header. This will be added when there is at least one + * suggestion or condition that is not currently displayed, and the user can expand the + * section to view more items. */ + addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion), + R.layout.suggestion_condition_header, + STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER, mSuggestionConditionMode != HEADER_MODE_COLLAPSED && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED - && (hiddenSuggestion > 0 - || hasConditions && hasSuggestions), - NS_SUGGESTION_CONDITION); + && (hiddenSuggestion > 0 || hasConditions && hasSuggestions)); /* Condition container. This is the card view that contains the list of conditions. * This will be added whenever the condition list is not empty */ - countItem(conditions, R.layout.suggestion_condition_container, - hasConditions && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED, - NS_SUGGESTION_CONDITION); + addToItemList(conditions, R.layout.suggestion_condition_container, + STABLE_ID_CONDITION_CONTAINER, + hasConditions && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED); /* Suggestion/condition footer. This will be present when the section is fully expanded * or when there is no conditions and no hidden suggestions */ - countItem(null, R.layout.suggestion_condition_footer, - (hasConditions || hasSuggestions) && - mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED - || hasSuggestions && !hasConditions && hiddenSuggestion == 0, - NS_SUGGESTION_CONDITION); + addToItemList(null /* item */, R.layout.suggestion_condition_footer, + STABLE_ID_SUGGESTION_CONDITION_FOOTER, + (hasConditions || hasSuggestions) + && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED + || hasSuggestions + && !hasConditions + && hiddenSuggestion == 0); - resetCount(); for (int i = 0; mCategories != null && i < mCategories.size(); i++) { DashboardCategory category = mCategories.get(i); for (int j = 0; j < category.tiles.size(); j++) { - Tile tile = category.tiles.get(j); - countItem(tile, R.layout.dashboard_tile, true, NS_ITEMS); + final Tile tile = category.tiles.get(j); + addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title), + true /* add */); } } } @@ -313,7 +308,8 @@ public class DashboardData { * {@link #mSuggestionConditionMode} have default value while others are not. */ public static class Builder { - private @HeaderMode int mSuggestionConditionMode = HEADER_MODE_DEFAULT; + @HeaderMode + private int mSuggestionConditionMode = HEADER_MODE_DEFAULT; private List mCategories; private List mConditions; @@ -392,13 +388,13 @@ public class DashboardData { /** * An item contains the data needed in the DashboardData. */ - private static class Item { + static class Item { // valid types in field type private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile; private static final int TYPE_SUGGESTION_CONDITION_CONTAINER = - R.layout.suggestion_condition_container; + R.layout.suggestion_condition_container; private static final int TYPE_SUGGESTION_CONDITION_HEADER = - R.layout.suggestion_condition_header; + R.layout.suggestion_condition_header; private static final int TYPE_SUGGESTION_CONDITION_FOOTER = R.layout.suggestion_condition_footer; private static final int TYPE_DASHBOARD_SPACER = R.layout.dashboard_spacer; @@ -407,7 +403,8 @@ public class DashboardData { TYPE_SUGGESTION_CONDITION_HEADER, TYPE_SUGGESTION_CONDITION_FOOTER, TYPE_DASHBOARD_SPACER}) @Retention(RetentionPolicy.SOURCE) - public @interface ItemTypes{} + public @interface ItemTypes { + } /** * The main data object in item, usually is a {@link Tile}, {@link Condition} @@ -420,7 +417,8 @@ public class DashboardData { /** * The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile) */ - public final @ItemTypes int type; + @ItemTypes + public final int type; /** * Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item. @@ -435,6 +433,7 @@ public class DashboardData { /** * Override it to make comparision in the {@link ItemsDataDiffCallback} + * * @param obj object to compared with * @return true if the same object or has equal value. */ diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java index 46b37a82bf1..68725d9921f 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java @@ -37,7 +37,13 @@ import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; +import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_CONTAINER; +import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_FOOTER; +import static com.android.settings.dashboard.DashboardData + .STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER; +import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONTAINER; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -112,6 +118,20 @@ public class DashboardDataTest { .build(); } + @Test + public void testBuildItemsData_shouldSetstableId() { + final List items = mDashboardDataWithOneConditions.getItemList(); + + // Header, suggestion, condition, footer, 1 tile + assertThat(items).hasSize(5); + + assertThat(items.get(0).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER); + assertThat(items.get(1).id).isEqualTo(STABLE_ID_SUGGESTION_CONTAINER); + assertThat(items.get(2).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER); + assertThat(items.get(3).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_FOOTER); + assertThat(items.get(4).id).isEqualTo(Objects.hash(mTestCategoryTile.title)); + } + @Test public void testBuildItemsData_containsAllData() { final DashboardData.SuggestionConditionHeaderData data =