diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index dc23c3d8d06..28ab8f8323e 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -348,6 +348,7 @@
12dp
16dp
12dp
+ 0dp
12dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 629394aba41..2ad35e0b77d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4321,6 +4321,20 @@
result from their use.
+
+
+ There is an issue erasing the downloaded SIMs
+
+ Please reboot the device and try again. If you continue factory reset, the downloaded SIMs may remain on the device.
+
+ Reboot
+
+ Continue factory reset?
+
+ Downloaded SIMs will remain on device.
+
+ Factory reset
+
Your device and personal data are more vulnerable
diff --git a/src/com/android/settings/MasterClearConfirm.java b/src/com/android/settings/MasterClearConfirm.java
index 3ace43621b6..676789edc8d 100644
--- a/src/com/android/settings/MasterClearConfirm.java
+++ b/src/com/android/settings/MasterClearConfirm.java
@@ -21,20 +21,26 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.ActionBar;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.app.admin.DevicePolicyManager;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.PowerManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotInfo;
+import android.telephony.euicc.EuiccManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -54,6 +60,8 @@ import com.google.android.setupcompat.template.FooterButton.ButtonType;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifLayout;
+import java.util.Arrays;
+
/**
* Confirm and execute a reset of the device to a clean "just out of the box"
* state. Multiple confirmations are required: first, a general "are you sure
@@ -83,6 +91,89 @@ public class MasterClearConfirm extends InstrumentedFragment {
return;
}
+ // If the eSIM slot is in an error state, display a dialog to warn users that their eSIM
+ // profiles may not be fully deleted during FDR.
+ if (shouldShowEsimEraseFailureDialog()) {
+ Log.e(TAG, "eUICC card is in an error state. Display a dialog to warn the user.");
+ showEsimErrorDialog();
+ return;
+ }
+
+ performFactoryReset();
+ }
+
+ /**
+ * Returns true if the user choose to erase eSIM profile but the eUICC card is in an error
+ * state.
+ */
+ private boolean shouldShowEsimEraseFailureDialog() {
+ EuiccManager euiccManager = getActivity().getSystemService(EuiccManager.class);
+ TelephonyManager telephonyManager =
+ getActivity().getSystemService(TelephonyManager.class);
+
+ if (euiccManager == null || !euiccManager.isEnabled()) {
+ Log.i(
+ TAG,
+ "eSIM manager is disabled. No need to check eSIM slot before FDR.");
+ return false;
+ }
+ if (!mEraseEsims) {
+ Log.i(
+ TAG,
+ "eSIM does not need to be reset. No need to check eSIM slot before FDR.");
+ return false;
+ }
+ UiccSlotInfo[] slotInfos = telephonyManager.getUiccSlotsInfo();
+ if (slotInfos == null) {
+ Log.i(TAG, "Unable to get UICC slots.");
+ return false;
+ }
+ // If getIsEuicc() returns false for an eSIM slot, it means the eSIM is in the error
+ // state.
+ return Arrays.stream(slotInfos).anyMatch(
+ slot -> slot != null && !slot.isRemovable() && !slot.getIsEuicc());
+ }
+
+ private void showEsimErrorDialog() {
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.fdr_esim_failure_title)
+ .setMessage(R.string.fdr_esim_failure_text)
+ .setNeutralButton(R.string.dlg_cancel,
+ (DialogInterface.OnClickListener) (dialog, which) -> {
+ dialog.dismiss();
+ })
+ .setNegativeButton(R.string.fdr_esim_failure_reboot_btn,
+ (DialogInterface.OnClickListener) (dialog, which) -> {
+ dialog.dismiss();
+ PowerManager pm = (PowerManager) getActivity()
+ .getSystemService(Context.POWER_SERVICE);
+ pm.reboot(null);
+ })
+ .setPositiveButton(R.string.lockpassword_continue_label,
+ (DialogInterface.OnClickListener) (dialog, which) -> {
+ dialog.dismiss();
+ showContinueFdrDialog();
+ })
+ .show();
+ }
+
+ private void showContinueFdrDialog() {
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.fdr_continue_title)
+ .setMessage(R.string.fdr_continue_text)
+ .setNegativeButton(R.string.dlg_cancel,
+ (DialogInterface.OnClickListener) (dialog, which) -> {
+ dialog.dismiss();
+ })
+ .setPositiveButton(R.string.fdr_continue_btn,
+ (DialogInterface.OnClickListener) (dialog, which) -> {
+ dialog.dismiss();
+ performFactoryReset();
+ })
+ .show();
+ }
+
+ private void performFactoryReset() {
final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
index 7f29ecb836e..684e6585709 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
@@ -24,6 +24,9 @@ public interface ContextualCardFeatureProvider {
/** Get contextual cards from the card provider */
Cursor getContextualCards();
+ /** Get the default contextual card to display */
+ ContextualCard getDefaultContextualCard();
+
/**
* Mark a specific {@link ContextualCard} as dismissed with dismissal signal in the database
* to indicate that the card has been dismissed.
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
index 5059d90a15e..643625bf190 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
@@ -54,6 +54,11 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
return cursor;
}
+ @Override
+ public ContextualCard getDefaultContextualCard() {
+ return null;
+ }
+
@Override
public int markCardAsDismissed(Context context, String cardName) {
final SQLiteDatabase db = CardDatabaseHelper.getInstance(mContext).getWritableDatabase();
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
index e4a644cf780..81ea45a818c 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -50,7 +50,7 @@ import java.util.stream.Collectors;
public class ContextualCardLoader extends AsyncLoaderCompat> {
@VisibleForTesting
- static final int DEFAULT_CARD_COUNT = 3;
+ static final int DEFAULT_CARD_COUNT = 1;
@VisibleForTesting
static final String CONTEXTUAL_CARD_COUNT = "contextual_card_count";
static final int CARD_CONTENT_LOADER_ID = 1;
@@ -131,7 +131,7 @@ public class ContextualCardLoader extends AsyncLoaderCompat
final List visibleCards = new ArrayList<>();
final List hiddenCards = new ArrayList<>();
- final int maxCardCount = getCardCount();
+ final int maxCardCount = getCardCount(mContext);
eligibleCards.forEach(card -> {
if (card.getCategory() != STICKY_VALUE) {
return;
@@ -164,14 +164,23 @@ public class ContextualCardLoader extends AsyncLoaderCompat
SettingsEnums.ACTION_CONTEXTUAL_CARD_NOT_SHOW,
ContextualCardLogUtils.buildCardListLog(hiddenCards));
}
+
+ // Add a default card if no other visible cards
+ if (visibleCards.isEmpty() && maxCardCount == 1) {
+ final ContextualCard defaultCard = FeatureFactory.getFactory(mContext)
+ .getContextualCardFeatureProvider(mContext).getDefaultContextualCard();
+ if (defaultCard != null) {
+ Log.i(TAG, "Default card: " + defaultCard.getSliceUri());
+ visibleCards.add(defaultCard);
+ }
+ }
return visibleCards;
}
- @VisibleForTesting
- int getCardCount() {
+ static int getCardCount(Context context) {
// Return the card count if Settings.Global has KEY_CONTEXTUAL_CARD_COUNT key,
// otherwise return the default one.
- return Settings.Global.getInt(mContext.getContentResolver(),
+ return Settings.Global.getInt(context.getContentResolver(),
CONTEXTUAL_CARD_COUNT, DEFAULT_CARD_COUNT);
}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index ac35017baab..00755296d0a 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
@@ -122,6 +122,10 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
Log.w(TAG, "Legacy suggestion contextual card enabled, skipping contextual cards.");
return;
}
+ if (ContextualCardLoader.getCardCount(mContext) <= 0) {
+ Log.w(TAG, "Card count is zero, skipping contextual cards.");
+ return;
+ }
mStartTime = System.currentTimeMillis();
final CardContentLoaderCallbacks cardContentLoaderCallbacks =
new CardContentLoaderCallbacks(mContext);
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java
index b9bc43b32b5..cf6f53c7075 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java
@@ -16,7 +16,10 @@
package com.android.settings.homepage.contextualcards;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
import android.content.Context;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -131,7 +134,22 @@ public class ContextualCardsAdapter extends RecyclerView.Adapter cards = getContextualCardList().stream().limit(2)
.collect(Collectors.toList());
doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List result = mContextualCardLoader.getDisplayableCards(cards);
- assertThat(result).hasSize(cards.size());
+ assertThat(result).hasSize(Math.min(cards.size(), DEFAULT_CARD_COUNT));
}
@Test
- public void getDisplayableCards_fourEligibleCards_shouldShowDefaultCardCount() {
+ public void getDisplayableCards_fourEligibleCards_notExceedDefaultCardCount() {
final List cards = getContextualCardList().stream().limit(4)
.collect(Collectors.toList());
doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List result = mContextualCardLoader.getDisplayableCards(cards);
- assertThat(result).hasSize(DEFAULT_CARD_COUNT);
+ assertThat(result).hasSize(Math.min(cards.size(), DEFAULT_CARD_COUNT));
}
@Test
@@ -139,7 +139,7 @@ public class ContextualCardLoaderTest {
@Test
public void getCardCount_noConfiguredCardCount_returnDefaultCardCount() {
- assertThat(mContextualCardLoader.getCardCount()).isEqualTo(DEFAULT_CARD_COUNT);
+ assertThat(mContextualCardLoader.getCardCount(mContext)).isEqualTo(DEFAULT_CARD_COUNT);
}
@Test
@@ -148,7 +148,7 @@ public class ContextualCardLoaderTest {
Settings.Global.putLong(mContext.getContentResolver(),
ContextualCardLoader.CONTEXTUAL_CARD_COUNT, configCount);
- assertThat(mContextualCardLoader.getCardCount()).isEqualTo(configCount);
+ assertThat(mContextualCardLoader.getCardCount(mContext)).isEqualTo(configCount);
}
private List getContextualCardList() {
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
index 54b7c2db358..fe1bb11b1b2 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
@@ -30,6 +30,7 @@ import android.content.Context;
import android.net.NetworkCapabilities;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.provider.Settings;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -77,6 +78,9 @@ public class ContextualWifiSliceTest {
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
mWifiManager.setWifiEnabled(true);
+ // Set WifiSlice expandable
+ Settings.Global.putInt(mContext.getContentResolver(),
+ ContextualWifiSlice.CONTEXTUAL_WIFI_EXPANDABLE, 1);
mWifiSlice = new ContextualWifiSlice(mContext);
}
@@ -126,6 +130,18 @@ public class ContextualWifiSliceTest {
assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
}
+ @Test
+ public void getWifiSlice_notExpandable_shouldCollapseSlice() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ ContextualWifiSlice.CONTEXTUAL_WIFI_EXPANDABLE, 0);
+ mWifiSlice.sApRowCollapsed = false;
+
+ final Slice wifiSlice = mWifiSlice.getSlice();
+
+ assertWifiHeader(wifiSlice);
+ assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
+ }
+
@Test
public void getWifiSlice_contextualWifiSlice_shouldReturnContextualWifiSliceUri() {
mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();