diff --git a/protos/contextual_card_list.proto b/protos/contextual_card_list.proto index 69c3741cada..54e5654fa5e 100644 --- a/protos/contextual_card_list.proto +++ b/protos/contextual_card_list.proto @@ -19,6 +19,7 @@ message ContextualCard { POSSIBLE = 2; IMPORTANT = 3; DEFERRED_SETUP = 5; + STICKY = 6; } /** Slice uri of the contextual card */ diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index 3a744a9f6d4..a2233fc9a26 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -258,7 +258,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { preference.setSummary(summary); } else if (tile.getMetaData() != null && tile.getMetaData().containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) { - // Set a placeholder summary before starting to fetch real summary, this is necessary + // Set a placeholder summary before starting to fetch real summary, this is necessary // to avoid preference height change. preference.setSummary(R.string.summary_placeholder); diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java index 17a226ddca7..3e179a674f7 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java @@ -16,6 +16,7 @@ package com.android.settings.homepage.contextualcards; +import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.STICKY_VALUE; import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI; import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI; import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI; @@ -129,18 +130,34 @@ public class ContextualCardLoader extends AsyncLoaderCompat @VisibleForTesting List getDisplayableCards(List candidates) { final List eligibleCards = filterEligibleCards(candidates); + final List stickyCards = new ArrayList<>(); final List visibleCards = new ArrayList<>(); final List hiddenCards = new ArrayList<>(); - final int size = eligibleCards.size(); - final int cardCount = getCardCount(); - for (int i = 0; i < size; i++) { - if (i < cardCount) { - visibleCards.add(eligibleCards.get(i)); - } else { - hiddenCards.add(eligibleCards.get(i)); + final int maxCardCount = getCardCount(); + eligibleCards.forEach(card -> { + if (card.getCategory() != STICKY_VALUE) { + return; } - } + if (stickyCards.size() < maxCardCount) { + stickyCards.add(card); + } else { + hiddenCards.add(card); + } + }); + + final int nonStickyCardCount = maxCardCount - stickyCards.size(); + eligibleCards.forEach(card -> { + if (card.getCategory() == STICKY_VALUE) { + return; + } + if (visibleCards.size() < nonStickyCardCount) { + visibleCards.add(card); + } else { + hiddenCards.add(card); + } + }); + visibleCards.addAll(stickyCards); if (!CardContentProvider.DELETE_CARD_URI.equals(mNotifyUri)) { final MetricsFeatureProvider metricsFeatureProvider = diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java index 9e9069fc5f7..9beb624cb86 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.STICKY_VALUE; import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE; import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI; import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI; @@ -346,16 +347,23 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo return result; } - // TODO(b/143055685):use category to determine whether they are sticky. private List getCardsWithStickyViewType(List cards) { final List result = new ArrayList<>(cards); int replaceCount = 0; for (int index = 0; index < result.size(); index++) { + final ContextualCard card = cards.get(index); + if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.CONTEXTUAL_HOME2)) { + if (card.getCategory() == STICKY_VALUE) { + result.set(index, card.mutate().setViewType( + SliceContextualCardRenderer.VIEW_TYPE_STICKY).build()); + } + continue; + } + if (replaceCount > STICKY_CARDS.size() - 1) { break; } - final ContextualCard card = cards.get(index); if (card.getCardType() != ContextualCard.CardType.SLICE) { continue; } 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 00993f66951..fceb79dd767 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java @@ -17,6 +17,7 @@ package com.android.settings.homepage.contextualcards; import static com.android.settings.homepage.contextualcards.ContextualCardLoader.DEFAULT_CARD_COUNT; +import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.STICKY_VALUE; import static com.google.common.truth.Truth.assertThat; @@ -82,15 +83,40 @@ public class ContextualCardLoaderTest { @Test public void getDisplayableCards_fourEligibleCards_shouldShowDefaultCardCount() { - final List fourCards = getContextualCardList(); - doReturn(fourCards).when(mContextualCardLoader).filterEligibleCards(anyList()); + final List cards = getContextualCardList().stream().limit(4) + .collect(Collectors.toList()); + doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList()); - final List result = mContextualCardLoader - .getDisplayableCards(fourCards); + final List result = mContextualCardLoader.getDisplayableCards(cards); assertThat(result).hasSize(DEFAULT_CARD_COUNT); } + @Test + public void getDisplayableCards_oneStickyCard_shouldShowOneStickyCardAtTheTail() { + final List cards = getContextualCardList().stream().limit(5) + .collect(Collectors.toList()); + doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList()); + + final List result = mContextualCardLoader.getDisplayableCards(cards); + + assertThat(result).hasSize(DEFAULT_CARD_COUNT); + assertThat(result.get(DEFAULT_CARD_COUNT - 1).getCategory()).isEqualTo(STICKY_VALUE); + } + + @Test + public void getDisplayableCards_threeStickyCards_shouldShowThreeStickyCardAtTheTail() { + final List cards = getContextualCardList(); + doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList()); + + final List result = mContextualCardLoader.getDisplayableCards(cards); + + assertThat(result).hasSize(DEFAULT_CARD_COUNT); + for (int i = 1; i <= Math.min(3, DEFAULT_CARD_COUNT); i++) { + assertThat(result.get(DEFAULT_CARD_COUNT - i).getCategory()).isEqualTo(STICKY_VALUE); + } + } + @Test public void getDisplayableCards_refreshCardUri_shouldLogContextualCard() { mContextualCardLoader.mNotifyUri = CardContentProvider.REFRESH_CARD_URI; @@ -128,20 +154,20 @@ public class ContextualCardLoaderTest { private List getContextualCardList() { final List cards = new ArrayList<>(); cards.add(new ContextualCard.Builder() - .setName("test_wifi") + .setName("test_low_storage") .setCardType(ContextualCard.CardType.SLICE) - .setSliceUri(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI) + .setSliceUri(CustomSliceRegistry.LOW_STORAGE_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")) + .setSliceUri(Uri.parse( + "content://com.android.settings.test.slices/action/flashlight")) .build()); cards.add(new ContextualCard.Builder() - .setName("test_connected") + .setName("test_dark_theme") .setCardType(ContextualCard.CardType.SLICE) - .setSliceUri(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI) + .setSliceUri(CustomSliceRegistry.DARK_THEME_SLICE_URI) .build()); cards.add(new ContextualCard.Builder() .setName("test_gesture") @@ -149,6 +175,24 @@ public class ContextualCardLoaderTest { .setSliceUri(Uri.parse( "content://com.android.settings.test.slices/action/gesture_pick_up")) .build()); + cards.add(new ContextualCard.Builder() + .setName("test_wifi") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI) + .setCategory(STICKY_VALUE) + .build()); + cards.add(new ContextualCard.Builder() + .setName("test_connected") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI) + .setCategory(STICKY_VALUE) + .build()); + cards.add(new ContextualCard.Builder() + .setName("test_sticky") + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(Uri.parse("content://com.android.settings.test.slices/action/sticky")) + .setCategory(STICKY_VALUE) + .build()); return cards; } } 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 d179cae0235..8958d6e1c86 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java @@ -564,8 +564,28 @@ public class ContextualCardManagerTest { } } + @Test + public void getCardsWithViewType_hasOneStickySlice_shouldHaveOneStickyCard() { + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTEXTUAL_HOME2, true); + final List cards = new ArrayList<>(); + cards.add(buildContextualCard(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI.toString())); + cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString())); + final List categories = Arrays.asList( + ContextualCardProto.ContextualCard.Category.STICKY_VALUE, + ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE + ); + final List cardListWithWifi = buildCategoriedCards(cards, categories); + + final List result = mManager.getCardsWithViewType(cardListWithWifi); + + assertThat(result).hasSize(cards.size()); + assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_STICKY); + assertThat(result.get(1).getViewType()).isEqualTo(VIEW_TYPE_FULL_WIDTH); + } + @Test public void getCardsWithViewType_hasWifiSlice_shouldHaveOneStickyCard() { + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTEXTUAL_HOME2, false); final List cards = new ArrayList<>(); cards.add(buildContextualCard(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI.toString())); cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString())); @@ -584,6 +604,7 @@ public class ContextualCardManagerTest { @Test public void getCardsWithViewType_hasBluetoothDeviceSlice_shouldHaveOneStickyCard() { + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTEXTUAL_HOME2, false); final List cards = new ArrayList<>(); cards.add(buildContextualCard(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString())); cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString())); @@ -602,6 +623,7 @@ public class ContextualCardManagerTest { @Test public void getCardsWithViewType_hasWifiAndBtDeviceSlice_shouldHaveTwoStickyCards() { + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTEXTUAL_HOME2, false); final List cards = new ArrayList<>(); cards.add(buildContextualCard(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI.toString())); cards.add(buildContextualCard(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString())); @@ -624,6 +646,7 @@ public class ContextualCardManagerTest { @Test public void getCardsWithViewType_noWifiOrBtDeviceSlice_shouldNotHaveStickyCard() { + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTEXTUAL_HOME2, false); final List categories = Arrays.asList( ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE, @@ -683,8 +706,8 @@ public class ContextualCardManagerTest { cards.add(new ContextualCard.Builder() .setName("test_flashlight") .setCardType(ContextualCard.CardType.SLICE) - .setSliceUri( - Uri.parse("content://com.android.settings.test.slices/action/flashlight")) + .setSliceUri(Uri.parse( + "content://com.android.settings.test.slices/action/flashlight")) .setViewType(VIEW_TYPE_FULL_WIDTH) .build()); cards.add(new ContextualCard.Builder()