Merge "Implement the collapsible feature of conditional card"
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
index dabc88c..a4a8419 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
@@ -24,10 +24,14 @@
import com.android.settings.homepage.contextualcards.ContextualCard.CardType;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
+import com.android.settings.homepage.contextualcards.conditional
+ .ConditionHeaderContextualCardRenderer;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardController;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardRenderer;
+import com.android.settings.homepage.contextualcards.conditional
+ .ConditionFooterContextualCardRenderer;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardController;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -83,6 +87,14 @@
SliceContextualCardRenderer.VIEW_TYPE,
SliceContextualCardController.class,
SliceContextualCardRenderer.class));
+ add(new ControllerRendererMapping(CardType.CONDITIONAL_FOOTER,
+ ConditionFooterContextualCardRenderer.VIEW_TYPE,
+ ConditionContextualCardController.class,
+ ConditionFooterContextualCardRenderer.class));
+ add(new ControllerRendererMapping(CardType.CONDITIONAL_HEADER,
+ ConditionHeaderContextualCardRenderer.VIEW_TYPE,
+ ConditionContextualCardController.class,
+ ConditionHeaderContextualCardRenderer.class));
}};
public static Class<? extends ContextualCardController> getCardControllerClass(
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java
index 0a8749d..10bed90 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java
@@ -129,5 +129,7 @@
// Adding items to empty list, should animate.
mRecyclerView.scheduleLayoutAnimation();
}
+
+ //TODO(b/119465242): flickering conditional cards after collapsing/expanding
}
}
diff --git a/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java b/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java
index 931bfb7..992d4c9 100644
--- a/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java
+++ b/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java
@@ -26,10 +26,14 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
+import com.android.settings.homepage.contextualcards.conditional
+ .ConditionHeaderContextualCardRenderer;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardController;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardRenderer;
+import com.android.settings.homepage.contextualcards.conditional
+ .ConditionFooterContextualCardRenderer;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardController;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -131,6 +135,12 @@
} else if (LegacySuggestionContextualCardRenderer.class == clz) {
return new LegacySuggestionContextualCardRenderer(context,
this /* controllerRendererPool */);
+ } else if (ConditionFooterContextualCardRenderer.class == clz) {
+ return new ConditionFooterContextualCardRenderer(context,
+ this /*controllerRendererPool*/);
+ } else if (ConditionHeaderContextualCardRenderer.class == clz) {
+ return new ConditionHeaderContextualCardRenderer(context,
+ this /*controllerRendererPool*/);
}
return null;
}
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java
index 4eb4c6b..02854c2 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.util.ArrayMap;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardController;
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
@@ -26,8 +28,11 @@
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
/**
* This controller triggers the loading of conditional cards and monitors state changes to
@@ -35,8 +40,12 @@
*/
public class ConditionContextualCardController implements ContextualCardController,
ConditionListener, LifecycleObserver, OnStart, OnStop {
+ public static final int EXPANDING_THRESHOLD = 2;
+ private static final double UNSUPPORTED_RANKING = -99999.0;
private static final String TAG = "ConditionCtxCardCtrl";
+ private static final String CONDITION_FOOTER = "condition_footer";
+ private static final String CONDITION_HEADER = "condition_header";
private final Context mContext;
private final ConditionManager mConditionManager;
@@ -93,20 +102,102 @@
@Override
public void onConditionsChanged() {
+ if (mListener == null) {
+ return;
+ }
final List<ContextualCard> conditionCards = mConditionManager.getDisplayableCards();
+ final Map<Integer, List<ContextualCard>> conditionalCards =
+ buildConditionalCardsWithFooterOrHeader(conditionCards);
+ mListener.onContextualCardUpdated(conditionalCards);
- final boolean isOddNumber = conditionCards.size() % 2 == 1;
+ }
+
+ /**
+ * According to conditional cards, build a map that includes conditional cards, header card and
+ * footer card.
+ *
+ * Rules:
+ * - The last one of conditional cards will be displayed as a full-width card if the size of
+ * conditional cards is odd number. The rest will be displayed as a half-width card.
+ * - By default conditional cards will be collapsed if there are more than TWO cards.
+ *
+ * For examples:
+ * - Only one conditional card: Returns a map that contains a full-width conditional card,
+ * no header card and no footer card.
+ * <p>Map{(CONDITIONAL, conditionCards), (CONDITIONAL_FOOTER, EMPTY_LIST), (CONDITIONAL_HEADER,
+ * EMPTY_LIST)}</p>
+ * - Two conditional cards: Returns a map that contains two half-width conditional cards,
+ * no header card and no footer card.
+ * <p>Map{(CONDITIONAL, conditionCards), (CONDITIONAL_FOOTER, EMPTY_LIST), (CONDITIONAL_HEADER,
+ * EMPTY_LIST)}</p>
+ * - Three conditional cards or above: By default, returns a map that contains no conditional
+ * card, one header card and no footer card. If conditional cards are expanded, will returns a
+ * map that contains three conditional cards, no header card and one footer card.
+ * If expanding conditional cards:
+ * <p>Map{(CONDITIONAL, conditionCards), (CONDITIONAL_FOOTER, footerCards), (CONDITIONAL_HEADER,
+ * EMPTY_LIST)}</p>
+ * If collapsing conditional cards:
+ * <p>Map{(CONDITIONAL, EMPTY_LIST), (CONDITIONAL_FOOTER, EMPTY_LIST), (CONDITIONAL_HEADER,
+ * headerCards)}</p>
+ *
+ * @param conditionCards A list of conditional cards that are from {@link
+ * ConditionManager#getDisplayableCards}
+ * @return A map contained three types of lists
+ */
+ @VisibleForTesting
+ Map<Integer, List<ContextualCard>> buildConditionalCardsWithFooterOrHeader(
+ List<ContextualCard> conditionCards) {
+ final Map<Integer, List<ContextualCard>> conditionalCards = new ArrayMap<>();
+ conditionalCards.put(ContextualCard.CardType.CONDITIONAL,
+ getExpandedConditionalCards(conditionCards));
+ conditionalCards.put(ContextualCard.CardType.CONDITIONAL_FOOTER,
+ getConditionalFooterCard(conditionCards));
+ conditionalCards.put(ContextualCard.CardType.CONDITIONAL_HEADER,
+ getConditionalHeaderCard(conditionCards));
+ return conditionalCards;
+ }
+
+ private List<ContextualCard> getExpandedConditionalCards(List<ContextualCard> conditionCards) {
+ if (conditionCards.isEmpty() || (conditionCards.size() > EXPANDING_THRESHOLD
+ && !mIsExpanded)) {
+ return Collections.EMPTY_LIST;
+ }
+ final List<ContextualCard> expandedCards = conditionCards.stream().collect(
+ Collectors.toList());
+ final boolean isOddNumber = expandedCards.size() % 2 == 1;
if (isOddNumber) {
- final int lastIndex = conditionCards.size() - 1;
- final ConditionalContextualCard card = (ConditionalContextualCard) conditionCards
- .get(lastIndex);
- conditionCards.set(lastIndex, card.mutate().setIsHalfWidth(false).build());
+ final int lastIndex = expandedCards.size() - 1;
+ final ConditionalContextualCard card =
+ (ConditionalContextualCard) expandedCards.get(lastIndex);
+ expandedCards.set(lastIndex, card.mutate().setIsHalfWidth(false).build());
}
+ return expandedCards;
+ }
- if (mListener != null) {
- final Map<Integer, List<ContextualCard>> conditionalCards = new ArrayMap<>();
- conditionalCards.put(ContextualCard.CardType.CONDITIONAL, conditionCards);
- mListener.onContextualCardUpdated(conditionalCards);
+ private List<ContextualCard> getConditionalFooterCard(List<ContextualCard> conditionCards) {
+ if (!conditionCards.isEmpty() && mIsExpanded
+ && conditionCards.size() > EXPANDING_THRESHOLD) {
+ final List<ContextualCard> footerCards = new ArrayList<>();
+ footerCards.add(new ConditionFooterContextualCard.Builder()
+ .setName(CONDITION_FOOTER)
+ .setRankingScore(UNSUPPORTED_RANKING)
+ .build());
+ return footerCards;
}
+ return Collections.EMPTY_LIST;
+ }
+
+ private List<ContextualCard> getConditionalHeaderCard(List<ContextualCard> conditionCards) {
+ if (!conditionCards.isEmpty() && !mIsExpanded
+ && conditionCards.size() > EXPANDING_THRESHOLD) {
+ final List<ContextualCard> headerCards = new ArrayList<>();
+ headerCards.add(new ConditionHeaderContextualCard.Builder()
+ .setConditionalCards(conditionCards)
+ .setName(CONDITION_HEADER)
+ .setRankingScore(UNSUPPORTED_RANKING)
+ .build());
+ return headerCards;
+ }
+ return Collections.EMPTY_LIST;
}
}
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCard.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCard.java
index f1a0fed..b72f9f7 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCard.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCard.java
@@ -16,9 +16,12 @@
package com.android.settings.homepage.contextualcards.conditional;
+import android.text.TextUtils;
+
import com.android.settings.homepage.contextualcards.ContextualCard;
import java.util.List;
+import java.util.Objects;
/**
* Data class representing a condition header {@link ContextualCard}.
@@ -44,6 +47,25 @@
return mConditionalCards;
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(getName(), mConditionalCards);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ConditionHeaderContextualCard)) {
+ return false;
+ }
+ final ConditionHeaderContextualCard that = (ConditionHeaderContextualCard) obj;
+
+ return TextUtils.equals(getName(), that.getName()) && mConditionalCards.equals(
+ that.mConditionalCards);
+ }
+
public static class Builder extends ContextualCard.Builder {
private List<ContextualCard> mConditionalCards;