Merge "Build a way to decide card width"
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
index 88478e3..49e2a76 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -107,12 +107,12 @@
                 }
             }
         }
-        return getFinalDisplayableCards(result);
+        return getDisplayableCards(result);
     }
 
     // Get final displayed cards and log what cards will be displayed/hidden
     @VisibleForTesting
-    List<ContextualCard> getFinalDisplayableCards(List<ContextualCard> candidates) {
+    List<ContextualCard> getDisplayableCards(List<ContextualCard> candidates) {
         final List<ContextualCard> eligibleCards = filterEligibleCards(candidates);
         final List<ContextualCard> visibleCards = new ArrayList<>();
         final List<ContextualCard> hiddenCards = new ArrayList<>();
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index 067de7c..12088f8 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 @@
 
         //replace with the new data
         mContextualCards.clear();
-        mContextualCards.addAll(sortCards(allCards));
+        final List<ContextualCard> sortedCards = sortCards(allCards);
+        mContextualCards.addAll(assignCardWidth(sortedCards));
 
         loadCardControllers();
 
@@ -224,6 +226,24 @@
         mListener = listener;
     }
 
+    @VisibleForTesting
+    List<ContextualCard> assignCardWidth(List<ContextualCard> cards) {
+        final List<ContextualCard> 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<ContextualCard> getCardsToKeep(List<ContextualCard> 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 c62a6bb..c47fa38 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 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 @@
     }
 
     @Test
-    public void getFinalDisplayableCards_twoEligibleCards_shouldShowAll() {
+    public void getDisplayableCards_twoEligibleCards_shouldShowAll() {
         final List<ContextualCard> cards = getContextualCardList().stream().limit(2)
                 .collect(Collectors.toList());
         doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
 
-        final List<ContextualCard> result = mContextualCardLoader.getFinalDisplayableCards(cards);
+        final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(cards);
 
         assertThat(result).hasSize(cards.size());
     }
 
     @Test
-    public void getFinalDisplayableCards_fiveEligibleCardsNoLarge_shouldShowDefaultCardCount() {
+    public void getDisplayableCards_fiveEligibleCardsNoLarge_shouldShowDefaultCardCount() {
         final List<ContextualCard> fiveCards = getContextualCardListWithNoLargeCard();
         doReturn(fiveCards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
 
-        final List<ContextualCard> result = mContextualCardLoader.getFinalDisplayableCards(
+        final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(
                 fiveCards);
 
         assertThat(result).hasSize(DEFAULT_CARD_COUNT);
     }
 
     @Test
-    public void getFinalDisplayableCards_threeEligibleCardsOneLarge_shouldShowThreeCards() {
+    public void getDisplayableCards_threeEligibleCardsOneLarge_shouldShowThreeCards() {
         final List<ContextualCard> cards = getContextualCardList().stream().limit(2)
                 .collect(Collectors.toList());
         cards.add(new ContextualCard.Builder()
@@ -115,18 +115,18 @@
                 .build());
         doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
 
-        final List<ContextualCard> result = mContextualCardLoader.getFinalDisplayableCards(cards);
+        final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(cards);
 
         assertThat(result).hasSize(3);
     }
 
     @Test
-    public void getFinalDisplayableCards_threeEligibleCardsTwoLarge_shouldShowTwoCards() {
+    public void getDisplayableCards_threeEligibleCardsTwoLarge_shouldShowTwoCards() {
         final List<ContextualCard> threeCards = getContextualCardList().stream().limit(3)
                 .collect(Collectors.toList());
         doReturn(threeCards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
 
-        final List<ContextualCard> result = mContextualCardLoader.getFinalDisplayableCards(
+        final List<ContextualCard> 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 c405ffc..3fc8e06 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 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 @@
         assertThat(actualCards).containsExactlyElementsIn(expectedCards);
     }
 
+
+    @Test
+    public void assignCardWidth_noSuggestionCards_shouldNotHaveHalfCards() {
+        final List<Integer> 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<ContextualCard> noSuggestionCards = buildCategoriedCards(getContextualCardList(),
+                categories);
+
+        final List<ContextualCard> result = mManager.assignCardWidth(noSuggestionCards);
+
+        assertThat(result).hasSize(5);
+        for (ContextualCard card : result) {
+            assertThat(card.isHalfWidth()).isFalse();
+        }
+    }
+
+    @Test
+    public void assignCardWidth_oneSuggestionCards_shouldNotHaveHalfCards() {
+        final List<Integer> 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<ContextualCard> oneSuggestionCards = buildCategoriedCards(
+                getContextualCardList(), categories);
+
+        final List<ContextualCard> result = mManager.assignCardWidth(oneSuggestionCards);
+
+        assertThat(result).hasSize(5);
+        for (ContextualCard card : result) {
+            assertThat(card.isHalfWidth()).isFalse();
+        }
+    }
+
+    @Test
+    public void assignCardWidth_twoConsecutiveSuggestionCards_shouldHaveTwoHalfCards() {
+        final List<Integer> 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<ContextualCard> twoConsecutiveSuggestionCards = buildCategoriedCards(
+                getContextualCardList(), categories);
+        final List<Boolean> expectedValues = Arrays.asList(false, false, true, true, false);
+
+        final List<ContextualCard> 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<Integer> 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<ContextualCard> twoNonConsecutiveSuggestionCards = buildCategoriedCards(
+                getContextualCardList(), categories);
+
+        final List<ContextualCard> result = mManager.assignCardWidth(
+                twoNonConsecutiveSuggestionCards);
+
+        assertThat(result).hasSize(5);
+        for (ContextualCard card : result) {
+            assertThat(card.isHalfWidth()).isFalse();
+        }
+    }
+
+    @Test
+    public void assignCardWidth_threeConsecutiveSuggestionCards_shouldHaveTwoHalfCards() {
+        final List<Integer> 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<ContextualCard> threeConsecutiveSuggestionCards = buildCategoriedCards(
+                getContextualCardList(), categories);
+        final List<Boolean> expectedValues = Arrays.asList(false, true, true, false, false);
+
+        final List<ContextualCard> 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<Integer> 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<ContextualCard> fourConsecutiveSuggestionCards = buildCategoriedCards(
+                getContextualCardList(), categories);
+        final List<Boolean> expectedValues = Arrays.asList(false, true, true, true, true);
+
+        final List<ContextualCard> 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 @@
                 .setSliceUri(Uri.parse(sliceUri))
                 .build();
     }
+
+    private List<ContextualCard> buildCategoriedCards(List<ContextualCard> cards,
+            List<Integer> categories) {
+        final List<ContextualCard> 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<ContextualCard> getContextualCardList() {
+        final List<ContextualCard> 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;
+    }
 }