Merge "Hide the SelfAvailablePreference if it is not available."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bec62e9..9c69bd1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4131,7 +4131,6 @@
     <!-- Manage applications, text for move error messages -->
     <string name="insufficient_storage">Not enough storage space.</string>
     <string name="does_not_exist">App doesn\u2019t exist.</string>
-    <string name="app_forward_locked">App is copy-protected.</string>
     <string name="invalid_location">Install location isn\u2019t valid.</string>
     <string name="system_package">System updates can\u2019t be installed on external media.</string>
     <!-- Error message shown when trying to move device administrators to external disks, such as SD card [CHAR_LIMIT=none] -->
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 8ed368c..07f59e2 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -133,15 +133,10 @@
             ":settings:show_fragment_title_res_package_name";
     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
             ":settings:show_fragment_title_resid";
-    public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
-            ":settings:show_fragment_as_shortcut";
 
     public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
             ":settings:show_fragment_as_subsetting";
 
-    @Deprecated
-    public static final String EXTRA_HIDE_DRAWER = ":settings:hide_drawer";
-
     public static final String META_DATA_KEY_FRAGMENT_CLASS =
             "com.android.settings.FRAGMENT_CLASS";
 
diff --git a/src/com/android/settings/core/SubSettingLauncher.java b/src/com/android/settings/core/SubSettingLauncher.java
index fb730b8..39ecd78 100644
--- a/src/com/android/settings/core/SubSettingLauncher.java
+++ b/src/com/android/settings/core/SubSettingLauncher.java
@@ -84,11 +84,6 @@
         return this;
     }
 
-    public SubSettingLauncher setIsShortCut(boolean isShortCut) {
-        mLaunchRequest.isShortCut = isShortCut;
-        return this;
-    }
-
     public SubSettingLauncher setArguments(Bundle arguments) {
         mLaunchRequest.arguments = arguments;
         return this;
@@ -159,8 +154,6 @@
         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
                 mLaunchRequest.titleResId);
         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, mLaunchRequest.title);
-        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT,
-                mLaunchRequest.isShortCut);
         intent.addFlags(mLaunchRequest.flags);
         return intent;
     }
@@ -195,7 +188,6 @@
         int titleResId;
         String titleResPackageName;
         CharSequence title;
-        boolean isShortCut;
         int sourceMetricsCategory = -100;
         int flags;
         Fragment mResultListener;
diff --git a/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java b/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java
index 7a2f0c1..27fa9be 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java
@@ -88,8 +88,6 @@
                 return getString(R.string.move_error_device_admin);
             case PackageManager.MOVE_FAILED_DOESNT_EXIST:
                 return getString(R.string.does_not_exist);
-            case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
-                return getString(R.string.app_forward_locked);
             case PackageManager.MOVE_FAILED_INVALID_LOCATION:
                 return getString(R.string.invalid_location);
             case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
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;
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 14a918e..cbb3593 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -19,8 +19,7 @@
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
 
-import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment
-        .RESULT_FINISHED;
+import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Activity;
@@ -77,12 +76,6 @@
     public Intent getIntent() {
         Intent modIntent = new Intent(super.getIntent());
         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
-
-        String action = modIntent.getAction();
-        if (ACTION_SET_NEW_PASSWORD.equals(action)
-                || ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(action)) {
-            modIntent.putExtra(EXTRA_HIDE_DRAWER, true);
-        }
         return modIntent;
     }
 
@@ -154,7 +147,6 @@
         private FingerprintManager mFingerprintManager;
         private FaceManager mFaceManager;
         private int mUserId;
-        private boolean mHideDrawer = false;
         private ManagedLockPasswordProvider mManagedPasswordProvider;
         private boolean mIsSetNewPassword = false;
         private UserManager mUserManager;
@@ -194,7 +186,6 @@
                 mUserPassword = getActivity().getIntent().getStringExtra(
                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             }
-            mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
 
             mHasChallenge = getActivity().getIntent().getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
@@ -345,7 +336,6 @@
                         mForFingerprint);
                 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE,
                         mForFace);
-                intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
                 startActivityForResult(
                         intent,
                         mIsSetNewPassword && mHasChallenge
@@ -748,9 +738,6 @@
             } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
                 intent = getLockPatternIntent();
             }
-            if (intent != null) {
-                intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
-            }
             return intent;
         }
 
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index cd04193..1ef95da 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -190,7 +190,6 @@
         private int mPasswordMinNonLetter = 0;
         private int mPasswordMinLengthToFulfillAllPolicies = 0;
         protected int mUserId;
-        private boolean mHideDrawer = false;
         private byte[] mPasswordHistoryHashFactor;
         /**
          * Password requirements that we need to verify.
@@ -375,7 +374,6 @@
             mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
             processPasswordRequirements(intent);
             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
-            mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
 
             if (intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) {
@@ -975,7 +973,6 @@
             if (!wasSecureBefore) {
                 Intent intent = getRedactionInterstitialIntent(getActivity());
                 if (intent != null) {
-                    intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
                     startActivity(intent);
                 }
             }
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index 4e4730f..0d06ed8 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -196,7 +196,6 @@
         private TextView mFooterLeftButton;
         private TextView mFooterRightButton;
         protected List<LockPatternView.Cell> mChosenPattern = null;
-        private boolean mHideDrawer = false;
         private ColorStateList mDefaultHeaderColorList;
 
         // ScrollView that contains title and header, only exist in land mode
@@ -464,7 +463,6 @@
                 w.start(mChooseLockSettingsHelper.utils(), required,
                         false, 0, LockPatternUtils.stringToPattern(current), current, mUserId);
             }
-            mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
             mForFingerprint = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
             mForFace = intent.getBooleanExtra(
@@ -808,7 +806,6 @@
             if (!wasSecureBefore) {
                 Intent intent = getRedactionInterstitialIntent(getActivity());
                 if (intent != null) {
-                    intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
                     startActivity(intent);
                 }
             }
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index d5182b3..938dec4 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -391,8 +391,6 @@
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, hasChallenge);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
-        // we should never have a drawer when confirming device credentials.
-        intent.putExtra(SettingsActivity.EXTRA_HIDE_DRAWER, true);
         intent.putExtra(Intent.EXTRA_USER_ID, userId);
         intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton);
         if (extras != null) {
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java
index ab82a54..688e94c 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.homepage.contextualcards.conditional;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -25,6 +27,7 @@
 import android.content.Context;
 
 import com.android.settings.homepage.contextualcards.ContextualCard;
+import com.android.settings.homepage.contextualcards.ContextualCard.CardType;
 import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
@@ -38,6 +41,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class ConditionContextualCardControllerTest {
@@ -95,4 +99,81 @@
 
         verify(mListener, never()).onContextualCardUpdated(any());
     }
+
+    @Test
+    public void getConditionalCards_hasEmptyConditionCards_shouldReturnThreeEmptyList() {
+        final Map<Integer, List<ContextualCard>> conditionalCards =
+                mController.buildConditionalCardsWithFooterOrHeader(generateConditionCards(0));
+
+        assertThat(conditionalCards).hasSize(3);
+        for (@CardType int cardType : conditionalCards.keySet()) {
+            assertThat(conditionalCards.get(cardType)).isEmpty();
+        }
+    }
+
+    @Test
+    public void getConditionalCards_hasOneConditionCard_shouldGetOneFullWidthCard() {
+        final Map<Integer, List<ContextualCard>> conditionalCards =
+                mController.buildConditionalCardsWithFooterOrHeader(generateConditionCards(1));
+
+        assertThat(conditionalCards).hasSize(3);
+        assertThat(conditionalCards.get(CardType.CONDITIONAL)).hasSize(1);
+        assertThat(conditionalCards.get(CardType.CONDITIONAL).get(0).isHalfWidth()).isFalse();
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_HEADER)).isEmpty();
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_FOOTER)).isEmpty();
+    }
+
+    @Test
+    public void getConditionalCards_hasTwoConditionCards_shouldGetTwoHalfWidthCards() {
+        final Map<Integer, List<ContextualCard>> conditionalCards =
+                mController.buildConditionalCardsWithFooterOrHeader(generateConditionCards(2));
+
+        assertThat(conditionalCards).hasSize(3);
+        assertThat(conditionalCards.get(CardType.CONDITIONAL)).hasSize(2);
+        for (ContextualCard card : conditionalCards.get(CardType.CONDITIONAL)) {
+            assertThat(card.isHalfWidth()).isTrue();
+        }
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_HEADER)).isEmpty();
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_FOOTER)).isEmpty();
+    }
+
+    @Test
+    public void getConditionalCards_hasThreeCardsAndExpanded_shouldGetThreeCardsWithFooter() {
+        mController.setIsExpanded(true);
+        final Map<Integer, List<ContextualCard>> conditionalCards =
+                mController.buildConditionalCardsWithFooterOrHeader(generateConditionCards(3));
+
+        assertThat(conditionalCards).hasSize(3);
+        assertThat(conditionalCards.get(CardType.CONDITIONAL)).hasSize(3);
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_HEADER)).isEmpty();
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_FOOTER)).isNotEmpty();
+    }
+
+    @Test
+    public void getConditionalCards_hasThreeCardsAndCollapsed_shouldGetOneConditionalHeader() {
+        mController.setIsExpanded(false);
+        final Map<Integer, List<ContextualCard>> conditionalCards =
+                mController.buildConditionalCardsWithFooterOrHeader(generateConditionCards(3));
+
+        assertThat(conditionalCards).hasSize(3);
+        assertThat(conditionalCards.get(CardType.CONDITIONAL)).isEmpty();
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_HEADER)).isNotEmpty();
+        assertThat(conditionalCards.get(CardType.CONDITIONAL_FOOTER)).isEmpty();
+    }
+
+    private List<ContextualCard> generateConditionCards(int numberOfCondition) {
+        final List<ContextualCard> conditionCards = new ArrayList<>();
+        for (int i = 0; i < numberOfCondition; i++) {
+            conditionCards.add(new ConditionalContextualCard.Builder()
+                    .setConditionId(123 + i)
+                    .setMetricsConstant(1)
+                    .setActionText("test_action" + i)
+                    .setName("test_name" + i)
+                    .setTitleText("test_title" + i)
+                    .setSummaryText("test_summary" + i)
+                    .setIsHalfWidth(true)
+                    .build());
+        }
+        return conditionCards;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionFooterContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionFooterContextualCardRendererTest.java
new file mode 100644
index 0000000..524a690
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionFooterContextualCardRendererTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage.contextualcards.conditional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.homepage.contextualcards.ContextualCard;
+import com.android.settings.homepage.contextualcards.ControllerRendererPool;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ConditionFooterContextualCardRendererTest {
+
+    @Mock
+    private ControllerRendererPool mControllerRendererPool;
+    @Mock
+    private ConditionContextualCardController mController;
+    private Context mContext;
+    private ConditionFooterContextualCardRenderer mRenderer;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mRenderer = new ConditionFooterContextualCardRenderer(mContext, mControllerRendererPool);
+    }
+
+    @Test
+    public void bindView_shouldSetClickListener() {
+        final int viewType = mRenderer.getViewType(false /* isHalfWidth */);
+        final RecyclerView recyclerView = new RecyclerView(mContext);
+        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+        final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
+        final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(view);
+        when(mControllerRendererPool.getController(mContext,
+                ContextualCard.CardType.CONDITIONAL_FOOTER)).thenReturn(mController);
+
+        mRenderer.bindView(viewHolder, generateConditionFooterContextualCard());
+
+        assertThat(viewHolder.itemView).isNotNull();
+        assertThat(viewHolder.itemView.hasOnClickListeners()).isTrue();
+    }
+
+    @Test
+    public void bindView_clickView_shouldSetTrueToIsConditionExpanded() {
+        final int viewType = mRenderer.getViewType(false /* isHalfWidth */);
+        final RecyclerView recyclerView = new RecyclerView(mContext);
+        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+        final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
+        final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(view);
+        when(mControllerRendererPool.getController(mContext,
+                ContextualCard.CardType.CONDITIONAL_FOOTER)).thenReturn(mController);
+
+        mRenderer.bindView(viewHolder, generateConditionFooterContextualCard());
+
+        assertThat(viewHolder.itemView).isNotNull();
+        viewHolder.itemView.performClick();
+
+        verify(mController).setIsExpanded(false);
+        verify(mController).onConditionsChanged();
+    }
+
+    private ContextualCard generateConditionFooterContextualCard() {
+        return new ConditionFooterContextualCard.Builder()
+                .setName("test_condition_footer")
+                .setRankingScore(-9999.0)
+                .build();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionFooterContextualCardTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionFooterContextualCardTest.java
new file mode 100644
index 0000000..783ae9f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionFooterContextualCardTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage.contextualcards.conditional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.homepage.contextualcards.ContextualCard;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ConditionFooterContextualCardTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void newInstance_changeCardType_shouldCrash() {
+        new ConditionFooterContextualCard.Builder()
+                .setCardType(ContextualCard.CardType.LEGACY_SUGGESTION)
+                .build();
+    }
+
+    @Test
+    public void getCardType_shouldAlwaysBeConditionalFooter() {
+        assertThat(new ConditionFooterContextualCard.Builder().build().getCardType())
+                .isEqualTo(ContextualCard.CardType.CONDITIONAL_FOOTER);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCardRendererTest.java
new file mode 100644
index 0000000..dbe5544
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCardRendererTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage.contextualcards.conditional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.homepage.contextualcards.ContextualCard;
+import com.android.settings.homepage.contextualcards.ControllerRendererPool;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ConditionHeaderContextualCardRendererTest {
+
+    @Mock
+    private ControllerRendererPool mControllerRendererPool;
+    @Mock
+    private ConditionContextualCardController mController;
+    private Context mContext;
+    private ConditionHeaderContextualCardRenderer mRenderer;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mRenderer = new ConditionHeaderContextualCardRenderer(mContext, mControllerRendererPool);
+    }
+
+    @Test
+    public void bindView_shouldSetClickListener() {
+        final int viewType = mRenderer.getViewType(false /* isHalfWidth */);
+        final RecyclerView recyclerView = new RecyclerView(mContext);
+        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+        final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
+        final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(view);
+        when(mControllerRendererPool.getController(mContext,
+                ContextualCard.CardType.CONDITIONAL_HEADER)).thenReturn(mController);
+
+        mRenderer.bindView(viewHolder, generateConditionHeaderContextualCard());
+
+        assertThat(viewHolder.itemView).isNotNull();
+        assertThat(viewHolder.itemView.hasOnClickListeners()).isTrue();
+    }
+
+    @Test
+    public void bindView_clickView_shouldSetTrueToIsConditionExpanded() {
+        final int viewType = mRenderer.getViewType(false /* isHalfWidth */);
+        final RecyclerView recyclerView = new RecyclerView(mContext);
+        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+        final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
+        final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(view);
+        when(mControllerRendererPool.getController(mContext,
+                ContextualCard.CardType.CONDITIONAL_HEADER)).thenReturn(mController);
+
+        mRenderer.bindView(viewHolder, generateConditionHeaderContextualCard());
+
+        assertThat(viewHolder.itemView).isNotNull();
+        viewHolder.itemView.performClick();
+
+        verify(mController).setIsExpanded(true);
+        verify(mController).onConditionsChanged();
+    }
+
+    private ContextualCard generateConditionHeaderContextualCard() {
+        return new ConditionHeaderContextualCard.Builder()
+                .setConditionalCards(generateConditionCards(3))
+                .setName("test_condition_header")
+                .setRankingScore(-9999.0)
+                .build();
+    }
+
+    private List<ContextualCard> generateConditionCards(int numberOfCondition) {
+        final List<ContextualCard> conditionCards = new ArrayList<>();
+        for (int i = 0; i < numberOfCondition; i++) {
+            conditionCards.add(new ConditionalContextualCard.Builder()
+                    .setConditionId(123 + i)
+                    .setMetricsConstant(1)
+                    .setActionText("test_action" + i)
+                    .setName("test_name" + i)
+                    .setTitleText("test_title" + i)
+                    .setSummaryText("test_summary" + i)
+                    .setIsHalfWidth(true)
+                    .build());
+        }
+        return conditionCards;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCardTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCardTest.java
new file mode 100644
index 0000000..767adad
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionHeaderContextualCardTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage.contextualcards.conditional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.homepage.contextualcards.ContextualCard;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ConditionHeaderContextualCardTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void newInstance_changeCardType_shouldCrash() {
+        new ConditionHeaderContextualCard.Builder()
+                .setCardType(ContextualCard.CardType.LEGACY_SUGGESTION)
+                .build();
+    }
+
+    @Test
+    public void getCardType_shouldAlwaysBeConditionalHeader() {
+        assertThat(new ConditionHeaderContextualCard.Builder().build().getCardType())
+                .isEqualTo(ContextualCard.CardType.CONDITIONAL_HEADER);
+    }
+}