From 228bc78bf9295b51bc1bb6fce46e56aaa1ca49c1 Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Tue, 11 Aug 2020 16:03:02 +0800 Subject: [PATCH 1/3] [DO NOT MERGE] Pre-allocate height for contextual cards. To prevent the UI jank causing by the async card loads, we pre-allocate some space for the card to fill in. After this change, only one card can be shown at a time. More details: - When the card number configuration is set to 0, don't trigger the card loader. - The height adjusting logic is as follows. When Settings is opened, pre-allocate a space first. After the RV finish laying out, reset the RV to wrap_content. So if the card has to be expanded(eg. wifi large mode or dismissal view), then it will adjust the height accordingly. While if a card previously shown becomes unavailable(dismissed or conditions not meet), we also reset the RV so the space can be gone. Bug: 163288869 Test: robotest Change-Id: I0dcb2dae8f0533e562ad06f664b7ae7a9afecd21 --- res/values/dimens.xml | 1 + .../contextualcards/ContextualCardLoader.java | 9 +++-- .../ContextualCardManager.java | 4 +++ .../ContextualCardsAdapter.java | 20 ++++++++++- .../ContextualCardsFragment.java | 36 ++++++++++++++++++- .../ContextualCardLoaderTest.java | 12 +++---- 6 files changed, 69 insertions(+), 13 deletions(-) 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/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java index e4a644cf780..de7eda2197f 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; @@ -167,11 +167,10 @@ public class ContextualCardLoader extends AsyncLoaderCompat 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 sliceLiveData = mSliceLiveDataMap.get(uri); diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java index ea9a7452202..4d3c60b7c4b 100644 --- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java +++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java @@ -25,6 +25,7 @@ import android.net.NetworkInfo.State; import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.provider.Settings; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; @@ -44,6 +45,9 @@ import com.android.settingslib.wifi.AccessPoint; */ public class ContextualWifiSlice extends WifiSlice { + @VisibleForTesting + static final String CONTEXTUAL_WIFI_EXPANDABLE = "contextual_wifi_expandable"; + @VisibleForTesting static final int COLLAPSED_ROW_COUNT = 0; @@ -63,13 +67,17 @@ public class ContextualWifiSlice extends WifiSlice { @Override public Slice getSlice() { - final long currentUiSession = FeatureFactory.getFactory(mContext) - .getSlicesFeatureProvider().getUiSessionToken(); - if (currentUiSession != sActiveUiSession) { - sActiveUiSession = currentUiSession; - sApRowCollapsed = hasWorkingNetwork(); - } else if (!mWifiManager.isWifiEnabled()) { - sApRowCollapsed = false; + if (isExpandable()) { + final long currentUiSession = FeatureFactory.getFactory(mContext) + .getSlicesFeatureProvider().getUiSessionToken(); + if (currentUiSession != sActiveUiSession) { + sActiveUiSession = currentUiSession; + sApRowCollapsed = hasWorkingNetwork(); + } else if (!mWifiManager.isWifiEnabled()) { + sApRowCollapsed = false; + } + } else { + sApRowCollapsed = true; } return super.getSlice(); } @@ -87,12 +95,18 @@ public class ContextualWifiSlice extends WifiSlice { protected ListBuilder.RowBuilder getHeaderRow(boolean isWifiEnabled, AccessPoint accessPoint) { final ListBuilder.RowBuilder builder = super.getHeaderRow(isWifiEnabled, accessPoint); builder.setTitleItem(getHeaderIcon(isWifiEnabled, accessPoint), ListBuilder.ICON_IMAGE); - if (sApRowCollapsed) { + if (sApRowCollapsed && isWifiEnabled) { builder.setSubtitle(getSubtitle(accessPoint)); } return builder; } + private boolean isExpandable() { + // Return whether this slice can be expandable. + return Settings.Global.getInt(mContext.getContentResolver(), CONTEXTUAL_WIFI_EXPANDABLE, 0) + != 0; + } + private IconCompat getHeaderIcon(boolean isWifiEnabled, AccessPoint accessPoint) { final Drawable drawable; final int tint; 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(); From bbcd54dd634b9ae577d3f6f2a5a478f4fe9591c0 Mon Sep 17 00:00:00 2001 From: Jiashen Wang Date: Sun, 9 Aug 2020 20:18:13 -0700 Subject: [PATCH 3/3] Adding dialogs to warn users about the potential eSIM erase failure eUICC card could potential in the card error state and all the eSIM operations will fail which could be fixed by a reboot. Thus, we need to pop up a dialog to inform users that their eSIM profiles cannot be erased successfully. Bug: 163078560 Test: Manually tested by mocking the eUICC card error state. Mock: https://docs.google.com/presentation/d/1AVNXxPbieTX1KmoewG4mo3xWPnEZyba_h9rPcwHHcr8/edit#slide=id.p Change-Id: I102fb2de889e08554f525f2cbe1528bb9a37afd7 --- res/values/strings.xml | 14 +++ .../android/settings/MasterClearConfirm.java | 91 +++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index 5829cc95cc5..4f0ea10152b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4319,6 +4319,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);