From 030aadad103480da3e2462c602ce48408b48e141 Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Wed, 12 Dec 2018 00:31:33 +0800 Subject: [PATCH] Build a way to decide card width Slice cards in contextual homepage are shown as half card or full card. We will use Category field of ContextualCard to decide card width. In this CL, also fixed the problem of not showing full card after a consecutive suggestion card dismissal. Bug: 119655434 Bug: 121315057 Test: visual, robotest Change-Id: I3243b9db21b8f288cab88238b20d7d50a2a20d46 --- .../contextualcards/ContextualCardLoader.java | 4 +- .../ContextualCardManager.java | 22 ++- .../ContextualCardLoaderTest.java | 24 +-- .../ContextualCardManagerTest.java | 171 ++++++++++++++++++ 4 files changed, 206 insertions(+), 15 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java index 88478e3d065..49e2a7697f6 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java @@ -107,12 +107,12 @@ public class ContextualCardLoader extends AsyncLoaderCompat } } } - return getFinalDisplayableCards(result); + return getDisplayableCards(result); } // Get final displayed cards and log what cards will be displayed/hidden @VisibleForTesting - List getFinalDisplayableCards(List candidates) { + List getDisplayableCards(List candidates) { final List eligibleCards = filterEligibleCards(candidates); final List visibleCards = new ArrayList<>(); final List hiddenCards = new ArrayList<>(); diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java index 067de7ccf3a..12088f8a106 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java @@ -17,6 +17,7 @@ package com.android.settings.homepage.contextualcards; import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID; +import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE; import static java.util.stream.Collectors.groupingBy; @@ -172,7 +173,8 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo //replace with the new data mContextualCards.clear(); - mContextualCards.addAll(sortCards(allCards)); + final List sortedCards = sortCards(allCards); + mContextualCards.addAll(assignCardWidth(sortedCards)); loadCardControllers(); @@ -224,6 +226,24 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo mListener = listener; } + @VisibleForTesting + List assignCardWidth(List cards) { + final List result = new ArrayList<>(cards); + // Shows as half cards if 2 suggestion type of cards are next to each other. + // Shows as full card if 1 suggestion type of card lives alone. + for (int index = 1; index < result.size(); index++) { + final ContextualCard previous = result.get(index - 1); + final ContextualCard current = result.get(index); + if (current.getCategory() == SUGGESTION_VALUE + && previous.getCategory() == SUGGESTION_VALUE) { + result.set(index - 1, previous.mutate().setIsHalfWidth(true).build()); + result.set(index, current.mutate().setIsHalfWidth(true).build()); + index++; + } + } + return result; + } + private List getCardsToKeep(List cards) { if (mSavedCards != null) { //screen rotate diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java index c62a6bbbfdb..c47fa38868d 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java @@ -29,16 +29,16 @@ import android.net.Uri; import com.android.settings.slices.CustomSliceRegistry; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + @RunWith(RobolectricTestRunner.class) public class ContextualCardLoaderTest { @@ -82,29 +82,29 @@ public class ContextualCardLoaderTest { } @Test - public void getFinalDisplayableCards_twoEligibleCards_shouldShowAll() { + public void getDisplayableCards_twoEligibleCards_shouldShowAll() { final List cards = getContextualCardList().stream().limit(2) .collect(Collectors.toList()); doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class)); - final List result = mContextualCardLoader.getFinalDisplayableCards(cards); + final List result = mContextualCardLoader.getDisplayableCards(cards); assertThat(result).hasSize(cards.size()); } @Test - public void getFinalDisplayableCards_fiveEligibleCardsNoLarge_shouldShowDefaultCardCount() { + public void getDisplayableCards_fiveEligibleCardsNoLarge_shouldShowDefaultCardCount() { final List fiveCards = getContextualCardListWithNoLargeCard(); doReturn(fiveCards).when(mContextualCardLoader).filterEligibleCards(any(List.class)); - final List result = mContextualCardLoader.getFinalDisplayableCards( + final List result = mContextualCardLoader.getDisplayableCards( fiveCards); assertThat(result).hasSize(DEFAULT_CARD_COUNT); } @Test - public void getFinalDisplayableCards_threeEligibleCardsOneLarge_shouldShowThreeCards() { + public void getDisplayableCards_threeEligibleCardsOneLarge_shouldShowThreeCards() { final List cards = getContextualCardList().stream().limit(2) .collect(Collectors.toList()); cards.add(new ContextualCard.Builder() @@ -115,18 +115,18 @@ public class ContextualCardLoaderTest { .build()); doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class)); - final List result = mContextualCardLoader.getFinalDisplayableCards(cards); + final List result = mContextualCardLoader.getDisplayableCards(cards); assertThat(result).hasSize(3); } @Test - public void getFinalDisplayableCards_threeEligibleCardsTwoLarge_shouldShowTwoCards() { + public void getDisplayableCards_threeEligibleCardsTwoLarge_shouldShowTwoCards() { final List threeCards = getContextualCardList().stream().limit(3) .collect(Collectors.toList()); doReturn(threeCards).when(mContextualCardLoader).filterEligibleCards(any(List.class)); - final List result = mContextualCardLoader.getFinalDisplayableCards( + final List result = mContextualCardLoader.getDisplayableCards( threeCards); assertThat(result).hasSize(2); diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java index c405ffc4f1b..3fc8e060456 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java @@ -32,6 +32,8 @@ import android.util.ArrayMap; import com.android.settings.homepage.contextualcards.conditional.ConditionFooterContextualCard; import com.android.settings.homepage.contextualcards.conditional.ConditionHeaderContextualCard; import com.android.settings.homepage.contextualcards.conditional.ConditionalContextualCard; +import com.android.settings.intelligence.ContextualCardProto; +import com.android.settings.slices.CustomSliceRegistry; import org.junit.Before; import org.junit.Test; @@ -203,6 +205,134 @@ public class ContextualCardManagerTest { assertThat(actualCards).containsExactlyElementsIn(expectedCards); } + + @Test + public void assignCardWidth_noSuggestionCards_shouldNotHaveHalfCards() { + final List categories = Arrays.asList( + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE + ); + final List noSuggestionCards = buildCategoriedCards(getContextualCardList(), + categories); + + final List result = mManager.assignCardWidth(noSuggestionCards); + + assertThat(result).hasSize(5); + for (ContextualCard card : result) { + assertThat(card.isHalfWidth()).isFalse(); + } + } + + @Test + public void assignCardWidth_oneSuggestionCards_shouldNotHaveHalfCards() { + final List categories = Arrays.asList( + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE + ); + final List oneSuggestionCards = buildCategoriedCards( + getContextualCardList(), categories); + + final List result = mManager.assignCardWidth(oneSuggestionCards); + + assertThat(result).hasSize(5); + for (ContextualCard card : result) { + assertThat(card.isHalfWidth()).isFalse(); + } + } + + @Test + public void assignCardWidth_twoConsecutiveSuggestionCards_shouldHaveTwoHalfCards() { + final List categories = Arrays.asList( + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE + ); + final List twoConsecutiveSuggestionCards = buildCategoriedCards( + getContextualCardList(), categories); + final List expectedValues = Arrays.asList(false, false, true, true, false); + + final List result = mManager.assignCardWidth( + twoConsecutiveSuggestionCards); + + assertThat(result).hasSize(5); + for (int i = 0; i < result.size(); i++) { + assertThat(result.get(i).isHalfWidth()).isEqualTo(expectedValues.get(i)); + } + } + + @Test + public void assignCardWidth_twoNonConsecutiveSuggestionCards_shouldNotHaveHalfCards() { + final List categories = Arrays.asList( + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE + ); + final List twoNonConsecutiveSuggestionCards = buildCategoriedCards( + getContextualCardList(), categories); + + final List result = mManager.assignCardWidth( + twoNonConsecutiveSuggestionCards); + + assertThat(result).hasSize(5); + for (ContextualCard card : result) { + assertThat(card.isHalfWidth()).isFalse(); + } + } + + @Test + public void assignCardWidth_threeConsecutiveSuggestionCards_shouldHaveTwoHalfCards() { + final List categories = Arrays.asList( + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE + ); + final List threeConsecutiveSuggestionCards = buildCategoriedCards( + getContextualCardList(), categories); + final List expectedValues = Arrays.asList(false, true, true, false, false); + + final List result = mManager.assignCardWidth( + threeConsecutiveSuggestionCards); + + assertThat(result).hasSize(5); + for (int i = 0; i < result.size(); i++) { + assertThat(result.get(i).isHalfWidth()).isEqualTo(expectedValues.get(i)); + } + } + + @Test + public void assignCardWidth_fourConsecutiveSuggestionCards_shouldHaveFourHalfCards() { + final List categories = Arrays.asList( + ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE + ); + final List fourConsecutiveSuggestionCards = buildCategoriedCards( + getContextualCardList(), categories); + final List expectedValues = Arrays.asList(false, true, true, true, true); + + final List result = mManager.assignCardWidth( + fourConsecutiveSuggestionCards); + + assertThat(result).hasSize(5); + for (int i = 0; i < result.size(); i++) { + assertThat(result.get(i).isHalfWidth()).isEqualTo(expectedValues.get(i)); + } + } + private ContextualCard buildContextualCard(String sliceUri) { return new ContextualCard.Builder() .setName(TEST_SLICE_NAME) @@ -210,4 +340,45 @@ public class ContextualCardManagerTest { .setSliceUri(Uri.parse(sliceUri)) .build(); } + + private List buildCategoriedCards(List cards, + List categories) { + final List result = new ArrayList<>(); + for (int i = 0; i < cards.size(); i++) { + result.add(cards.get(i).mutate().setCategory(categories.get(i)).build()); + } + return result; + } + + private List getContextualCardList() { + final List cards = new ArrayList<>(); + cards.add(new ContextualCard.Builder() + .setName("test_wifi") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI) + .build()); + cards.add(new ContextualCard.Builder() + .setName("test_flashlight") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri( + Uri.parse("content://com.android.settings.test.slices/action/flashlight")) + .build()); + cards.add(new ContextualCard.Builder() + .setName("test_connected") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI) + .build()); + cards.add(new ContextualCard.Builder() + .setName("test_gesture") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(Uri.parse( + "content://com.android.settings.test.slices/action/gesture_pick_up")) + .build()); + cards.add(new ContextualCard.Builder() + .setName("test_battery") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(CustomSliceRegistry.BATTERY_INFO_SLICE_URI) + .build()); + return cards; + } }