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; + } }