diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b488dbbe516..310ed3840ee 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -110,6 +110,7 @@
+
@@ -311,6 +313,7 @@
@@ -673,12 +676,12 @@
+ android:theme="@style/Theme.AlertDialog"/>
+ android:theme="@style/Theme.AlertDialog"/>
+ android:theme="@style/Theme.AlertDialog"/>
+ android:theme="@style/Theme.AlertDialog"/>
diff --git a/res/drawable/ic_settings_safety_center.xml b/res/drawable/ic_settings_safety_center.xml
new file mode 100644
index 00000000000..f43359fefd4
--- /dev/null
+++ b/res/drawable/ic_settings_safety_center.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/font_size_preview.xml b/res/layout/font_size_preview.xml
index 2b1773bcee3..f916ac41014 100644
--- a/res/layout/font_size_preview.xml
+++ b/res/layout/font_size_preview.xml
@@ -26,6 +26,7 @@
android:layout_height="wrap_content">
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/suw_font_size_fragment.xml b/res/layout/suw_font_size_fragment.xml
index 0e03a6989f5..898b9eb6328 100644
--- a/res/layout/suw_font_size_fragment.xml
+++ b/res/layout/suw_font_size_fragment.xml
@@ -75,12 +75,6 @@
android:contentDescription="@string/font_size_make_larger_desc"
style="@style/screen_size_imageview_style"/>
-
-
diff --git a/res/layout/suw_screen_zoom_fragment.xml b/res/layout/suw_screen_zoom_fragment.xml
index 0747381085f..369ff145405 100644
--- a/res/layout/suw_screen_zoom_fragment.xml
+++ b/res/layout/suw_screen_zoom_fragment.xml
@@ -74,12 +74,6 @@
android:contentDescription="@string/screen_zoom_make_larger_desc"
style="@style/screen_size_imageview_style"/>
-
-
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 543dfeeabd3..f22d7bafc85 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -19,4 +19,6 @@
2
+
+ true
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 45c28dda461..b3577581884 100755
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -63,6 +63,9 @@
0dp
64dp
+
+ 32dp
+
40dp
40dp
diff --git a/res/values/config.xml b/res/values/config.xml
index d83e10dd970..277176f5471 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -532,6 +532,9 @@
false
+
+ false
+
diff --git a/res/values/menu_keys.xml b/res/values/menu_keys.xml
index e69664a3408..2841b699c97 100755
--- a/res/values/menu_keys.xml
+++ b/res/values/menu_keys.xml
@@ -26,6 +26,7 @@
top_level_display
top_level_wallpaper
top_level_accessibility
+ top_level_safety_center
top_level_security
top_level_privacy
top_level_location
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f84b03027ae..ed867d2c4cf 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -722,6 +722,12 @@
Security status
Screen lock, Find My Device, app security
+
+
+ Security & privacy
+
+
+ Permissions, screen lock, app security
@@ -871,11 +877,9 @@
Delete face model?
- Your face model will be permanently and securely deleted. After deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps.
-
- Delete face model?
-
- Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone or for authentication in apps.
+ Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps.
+
+ Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone.
Use Face Unlock to unlock your phone
@@ -4386,25 +4390,6 @@
Set a work pattern
-
- For added security, set a password to unlock the phone
-
- For added security, set a PIN to unlock the phone
-
- For added security, set a pattern to unlock the phone
-
- For added security, set a password to unlock the tablet
-
- For added security, set a PIN to unlock the tablet
-
- For added security, set a pattern to unlock the tablet
-
- For added security, set a password to unlock the device
-
- For added security, set a PIN to unlock the device
-
- For added security, set a pattern to unlock the device
-
To use fingerprint, set password
@@ -8122,11 +8107,17 @@
Always use this for calls
- Select a SIM for data
+ Choose SIM for mobile data
Select a SIM for SMS
Switching data SIM, this may take up to a minute\u2026
+
+ Use %1$s for mobile data?
+
+ If you switch to %1$s, %2$s will no longer be used for mobile data.
+
+ Use %1$s
Call with
@@ -12936,14 +12927,22 @@
Switch to %1$s?
Switch to using SIM card?
+
+ Use %1$s?
Only one SIM can be active at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service.
Only one downloaded SIM can be active at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service.
Only one SIM can be active at a time.\n\nSwitching won\u2019t cancel your %1$s service.
+
+ You can use 2 SIMs at a time. To use %1$s, turn off another SIM.
Switch to %1$s
+
+ Turn off %1$s
+
+ Turning off a SIM won\u2019t cancel your service
Connecting to network…
diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml
index 042ce43999a..280c3f31bc3 100644
--- a/res/xml/top_level_settings.xml
+++ b/res/xml/top_level_settings.xml
@@ -116,6 +116,15 @@
settings:highlightableMenuKey="@string/menu_key_accessibility"
settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>
+
+
+ settings:highlightableMenuKey="@string/menu_key_privacy"
+ settings:controller="com.android.settings.privacy.TopLevelPrivacyEntryPreferenceController"/>
uiccSlotMappings = mTelephonyManager.getSimSlotMapping();
+ for (UiccSlotMapping uiccSlotMapping :
+ uiccSlotMappings.stream()
+ .filter(
+ uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex()
+ == getTargetSlot())
+ .collect(Collectors.toList())) {
+ if (uiccSlotMapping.getPortIndex() == port) {
+ port++;
+ }
+ }
+ return port;
+ }
+
+ private int getTargetSlot() {
+ return ESIM_SLOT_ID;
+ }
+
+ private void onSwitchSlotSidecarStateChange() {
+ switch (mSwitchSlotSidecar.getState()) {
+ case State.SUCCESS:
+ mSwitchSlotSidecar.reset();
+ Log.i(TAG,
+ "Successfully SimSlotMapping. Start to enable/disable esim");
+ switchToSubscription();
+ break;
+ case State.ERROR:
+ mSwitchSlotSidecar.reset();
+ Log.i(TAG, "Failed to set SimSlotMapping");
+ setState(State.ERROR, Substate.UNUSED);
+ break;
+ }
+ }
+
+ private boolean isMultipleEnabledProfilesSupported() {
+ List cardInfos = mTelephonyManager.getUiccCardsInfo();
+ if (cardInfos == null) {
+ Log.w(TAG, "UICC cards info list is empty.");
+ return false;
+ }
+ return cardInfos.stream().anyMatch(
+ cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
+ }
+
+ private void switchToSubscription() {
+ // The SimSlotMapping is ready, then to execute activate/inactivate esim.
+ EuiccManager.ResultListener callback = new EuiccManager.ResultListener() {
+ @Override
+ public void onComplete(int resultCode, Intent resultIntent) {
+ Log.i(TAG, String.format("Result code : %d;", resultCode));
+ if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+ setState(State.SUCCESS, Substate.UNUSED);
+ } else {
+ setState(State.ERROR, resultCode);
+ }
+ }
+ };
+ mEuiccManager.switchToSubscription(mSubId, mPort, getContext().getMainExecutor(),
+ callback);
+ }
}
diff --git a/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java b/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java
index 132a2fd2367..2c184c6057a 100644
--- a/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java
+++ b/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java
@@ -38,8 +38,8 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
// Stateless members.
private SwitchToEuiccSubscriptionSidecar mSwitchToSubscriptionSidecar;
- private SwitchSlotSidecar mSwitchSlotSidecar;
private int mPhysicalSlotId;
+ private SubscriptionInfo mRemovedSubInfo;
/** Returns a SwitchToRemovableSlotSidecar sidecar instance. */
public static SwitchToRemovableSlotSidecar get(FragmentManager fm) {
@@ -51,20 +51,17 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
super.onCreate(savedInstanceState);
mSwitchToSubscriptionSidecar =
SwitchToEuiccSubscriptionSidecar.get(getChildFragmentManager());
- mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
}
@Override
public void onResume() {
super.onResume();
mSwitchToSubscriptionSidecar.addListener(this);
- mSwitchSlotSidecar.addListener(this);
}
@Override
public void onPause() {
mSwitchToSubscriptionSidecar.removeListener(this);
- mSwitchSlotSidecar.removeListener(this);
super.onPause();
}
@@ -90,29 +87,46 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
*
* @param physicalSlotId removable physical SIM slot ID.
*/
+ // ToDo: delete this api and refactor the related code.
public void run(int physicalSlotId) {
mPhysicalSlotId = physicalSlotId;
SubscriptionManager subscriptionManager =
getContext().getSystemService(SubscriptionManager.class);
if (SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
.anyMatch(SubscriptionInfo::isEmbedded)) {
+ // In SS mode, the esim is active, then inactivate the esim.
Log.i(TAG, "There is an active eSIM profile. Disable the profile first.");
// Use INVALID_SUBSCRIPTION_ID to disable the only active profile.
- mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0, null);
} else {
Log.i(TAG, "There is no active eSIM profiles. Start to switch to removable slot.");
- mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
+ mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, null);
}
}
+ /**
+ * Starts switching to the removable slot.
+ *
+ * @param physicalSlotId removable physical SIM slot ID.
+ * @param removedSubInfo if the all of slots have sims, it should remove the one of active sim.
+ * If the removedSubInfo is null, then use the default value.
+ * The default value is the removable physical SIM slot and portId 0.
+ */
+ public void run(int physicalSlotId, SubscriptionInfo removedSubInfo) {
+ mPhysicalSlotId = physicalSlotId;
+ mRemovedSubInfo = removedSubInfo;
+
+ Log.i(TAG, "Start to switch to removable slot.");
+ mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo);
+ }
+
private void onSwitchToSubscriptionSidecarStateChange() {
switch (mSwitchToSubscriptionSidecar.getState()) {
case State.SUCCESS:
mSwitchToSubscriptionSidecar.reset();
- Log.i(
- TAG,
+ Log.i(TAG,
"Successfully disabled eSIM profile. Start to switch to Removable slot.");
- mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
+ mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo);
break;
case State.ERROR:
mSwitchToSubscriptionSidecar.reset();
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index ccf3f91e868..8938cdbb2e8 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -19,8 +19,10 @@ package com.android.settings.network;
import android.annotation.IntDef;
import android.content.Context;
import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotInfo;
+import android.telephony.UiccSlotMapping;
import android.util.Log;
import com.android.settingslib.utils.ThreadUtils;
@@ -29,17 +31,21 @@ import com.google.common.collect.ImmutableList;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+// ToDo: to do the refactor for renaming
public class UiccSlotUtil {
private static final String TAG = "UiccSlotUtil";
private static final long DEFAULT_WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25 * 1000L;
- ;
public static final int INVALID_PHYSICAL_SLOT_ID = -1;
+ public static final int INVALID_PORT_ID = -1;
/**
* Mode for switching to eSIM slot which decides whether there is cleanup process, e.g.
@@ -47,9 +53,9 @@ public class UiccSlotUtil {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- SwitchingEsimMode.NO_CLEANUP,
- SwitchingEsimMode.ASYNC_CLEANUP,
- SwitchingEsimMode.SYNC_CLEANUP
+ SwitchingEsimMode.NO_CLEANUP,
+ SwitchingEsimMode.ASYNC_CLEANUP,
+ SwitchingEsimMode.SYNC_CLEANUP
})
public @interface SwitchingEsimMode {
/** No cleanup process after switching to eSIM slot */
@@ -76,44 +82,108 @@ public class UiccSlotUtil {
* Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
* INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
*
- * @param slotId the physical removable slot id.
+ * @param slotId the physical removable slot id.
* @param context the application context.
* @throws UiccSlotsException if there is an error.
*/
+ //ToDo: delete this api and refactor the related code.
public static synchronized void switchToRemovableSlot(int slotId, Context context)
throws UiccSlotsException {
+ switchToRemovableSlot(context, slotId, null);
+ }
+
+ /**
+ * Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
+ * INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
+ *
+ * @param slotId the physical removable slot id.
+ * @param context the application context.
+ * @param removedSubInfo In the DSDS+MEP mode, if the all of slots have sims, it should
+ * remove the one of active sim.
+ * If the removedSubInfo is null, then use the default value.
+ * The default value is the esim slot and portId 0.
+ * @throws UiccSlotsException if there is an error.
+ */
+ public static synchronized void switchToRemovableSlot(Context context, int slotId,
+ SubscriptionInfo removedSubInfo) throws UiccSlotsException {
if (ThreadUtils.isMainThread()) {
throw new IllegalThreadStateException(
"Do not call switchToRemovableSlot on the main thread.");
}
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
- if (telMgr.isMultiSimEnabled()) {
- // If this device supports multiple active slots, don't mess with TelephonyManager.
- Log.i(TAG, "Multiple active slots supported. Not calling switchSlots.");
- return;
- }
- UiccSlotInfo[] slots = telMgr.getUiccSlotsInfo();
- if (slotId == INVALID_PHYSICAL_SLOT_ID) {
- for (int i = 0; i < slots.length; i++) {
- if (slots[i].isRemovable()
- && !slots[i].getPorts().stream().findFirst().get().isActive()
- && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR
- && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
- performSwitchToRemovableSlot(i, context);
- return;
- }
- }
- } else {
- if (slotId >= slots.length || !slots[slotId].isRemovable()) {
- throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId);
- }
- if (!slots[slotId].getPorts().stream().findFirst().get().isActive()) {
- performSwitchToRemovableSlot(slotId, context);
- }
- }
+ int inactiveRemovableSlot = getInactiveRemovableSlot(telMgr.getUiccSlotsInfo(), slotId);
+ performSwitchToSlot(telMgr,
+ prepareUiccSlotMappingsForRemovableSlot(telMgr.getSimSlotMapping(),
+ inactiveRemovableSlot, removedSubInfo, telMgr.isMultiSimEnabled()),
+ context);
}
- private static void performSwitchToRemovableSlot(int slotId, Context context)
+ /**
+ * Switches to the Euicc slot. It waits for SIM_STATE_LOADED after switch.
+ *
+ * @param context the application context.
+ * @param slotId the Euicc slot id.
+ * @param port the Euicc slot port id.
+ * @param removedSubInfo In the DSDS+MEP mode, if the all of slots have sims, it should
+ * remove the one of active sim.
+ * If the removedSubInfo is null, then it uses the default value.
+ * The default value is the esim slot and portId 0.
+ * @throws UiccSlotsException if there is an error.
+ */
+ public static synchronized void switchToEuiccSlot(Context context, int slotId, int port,
+ SubscriptionInfo removedSubInfo) throws UiccSlotsException {
+ if (ThreadUtils.isMainThread()) {
+ throw new IllegalThreadStateException(
+ "Do not call switchToRemovableSlot on the main thread.");
+ }
+ TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
+ Collection uiccSlotMappings = telMgr.getSimSlotMapping();
+ Log.i(TAG, "The SimSlotMapping: " + uiccSlotMappings);
+
+ if (isTargetSlotActive(uiccSlotMappings, slotId, port)) {
+ Log.i(TAG, "The slot is active, then the sim can enable directly.");
+ return;
+ }
+
+ Collection newUiccSlotMappings = new ArrayList<>();
+ if (!telMgr.isMultiSimEnabled()) {
+ // In the 'SS mode', the port is 0.
+ newUiccSlotMappings.add(new UiccSlotMapping(port, slotId, 0));
+ } else {
+ // DSDS+MEP
+ // The target slot+port is not active, but the all of logical slots are full. It
+ // needs to replace one of logical slots.
+ int removedSlot =
+ (removedSubInfo != null) ? removedSubInfo.getSimSlotIndex() : slotId;
+ int removedPort = (removedSubInfo != null) ? removedSubInfo.getPortIndex() : 0;
+ Log.i(TAG,
+ String.format("Start to set SimSlotMapping from slot%d-port%d to slot%d-port%d",
+ slotId, port, removedSlot, removedPort));
+ newUiccSlotMappings =
+ uiccSlotMappings.stream().map(uiccSlotMapping -> {
+ if (uiccSlotMapping.getPhysicalSlotIndex() == removedSlot
+ && uiccSlotMapping.getPortIndex() == removedPort) {
+ return new UiccSlotMapping(port, slotId,
+ uiccSlotMapping.getLogicalSlotIndex());
+ }
+ return uiccSlotMapping;
+ }).collect(Collectors.toList());
+ }
+
+ Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings);
+ performSwitchToSlot(telMgr, newUiccSlotMappings, context);
+ }
+
+ private static boolean isTargetSlotActive(Collection uiccSlotMappings,
+ int slotId, int port) {
+ return uiccSlotMappings.stream()
+ .anyMatch(
+ uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() == slotId
+ && uiccSlotMapping.getPortIndex() == port);
+ }
+
+ private static void performSwitchToSlot(TelephonyManager telMgr,
+ Collection uiccSlotMappings, Context context)
throws UiccSlotsException {
CarrierConfigChangedReceiver receiver = null;
long waitingTimeMillis =
@@ -125,7 +195,7 @@ public class UiccSlotUtil {
CountDownLatch latch = new CountDownLatch(1);
receiver = new CarrierConfigChangedReceiver(latch);
receiver.registerOn(context);
- switchSlots(context, slotId);
+ telMgr.setSimSlotMapping(uiccSlotMappings);
latch.await(waitingTimeMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -138,22 +208,80 @@ public class UiccSlotUtil {
}
/**
- * Changes the logical slot to physical slot mapping. OEM should override this to provide
- * device-specific implementation if the device supports switching slots.
- *
- * @param context the application context.
- * @param physicalSlots List of physical slot ids in the order of logical slots.
+ * @param slots The UiccSlotInfo list.
+ * @param slotId The physical removable slot id.
+ * @return The inactive physical removable slot id. If the physical removable slot id is
+ * active, then return -1.
+ * @throws UiccSlotsException if there is an error.
*/
- private static void switchSlots(Context context, int... physicalSlots)
+ private static int getInactiveRemovableSlot(UiccSlotInfo[] slots, int slotId)
throws UiccSlotsException {
- TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
- if (telMgr.isMultiSimEnabled()) {
- // If this device supports multiple active slots, don't mess with TelephonyManager.
- Log.i(TAG, "Multiple active slots supported. Not calling switchSlots.");
- return;
+ if (slots == null) {
+ throw new UiccSlotsException("UiccSlotInfo is null");
}
- if (!telMgr.switchSlots(physicalSlots)) {
- throw new UiccSlotsException("Failed to switch slots");
+ if (slotId == INVALID_PHYSICAL_SLOT_ID) {
+ for (int i = 0; i < slots.length; i++) {
+ if (slots[i].isRemovable()
+ && !slots[i].getPorts().stream().findFirst().get().isActive()
+ && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR
+ && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
+ return i;
+ }
+ }
+ } else {
+ if (slotId >= slots.length || !slots[slotId].isRemovable()) {
+ throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId);
+ }
+ if (!slots[slotId].getPorts().stream().findFirst().get().isActive()) {
+ return slotId;
+ }
}
+ return INVALID_PHYSICAL_SLOT_ID;
+ }
+
+ private static Collection prepareUiccSlotMappingsForRemovableSlot(
+ Collection uiccSlotMappings, int slotId,
+ SubscriptionInfo removedSubInfo, boolean isMultiSimEnabled) {
+ if (slotId == INVALID_PHYSICAL_SLOT_ID
+ || uiccSlotMappings.stream().anyMatch(uiccSlotMapping ->
+ uiccSlotMapping.getPhysicalSlotIndex() == slotId
+ && uiccSlotMapping.getPortIndex() == 0)) {
+ // The slot is invalid slot id, then to skip this.
+ // The slot is active, then the sim can enable directly.
+ return uiccSlotMappings;
+ }
+
+ Collection newUiccSlotMappings = new ArrayList<>();
+ if (!isMultiSimEnabled) {
+ // In the 'SS mode', the port is 0.
+ newUiccSlotMappings.add(new UiccSlotMapping(0, slotId, 0));
+ } else if (removedSubInfo != null) {
+ // DSDS+MEP
+ // The target slot+port is not active, but the all of logical slots are full. It
+ // needs to replace one of logical slots.
+ Log.i(TAG,
+ String.format("Start to set SimSlotMapping from slot%d-port%d to slot%d-port%d",
+ slotId, 0, removedSubInfo.getSimSlotIndex(),
+ removedSubInfo.getPortIndex()));
+ newUiccSlotMappings =
+ uiccSlotMappings.stream().map(uiccSlotMapping -> {
+ if (uiccSlotMapping.getPhysicalSlotIndex()
+ == removedSubInfo.getSimSlotIndex()
+ && uiccSlotMapping.getPortIndex()
+ == removedSubInfo.getPortIndex()) {
+ return new UiccSlotMapping(0, slotId,
+ uiccSlotMapping.getLogicalSlotIndex());
+ }
+ return uiccSlotMapping;
+ }).collect(Collectors.toList());
+ } else {
+ // DSDS+no MEP
+ // The removable slot should be in UiccSlotMapping.
+ newUiccSlotMappings = uiccSlotMappings;
+ Log.i(TAG, "The removedSubInfo is null");
+ }
+
+ Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings);
+ return newUiccSlotMappings;
}
}
diff --git a/src/com/android/settings/network/telephony/AlertDialogFragment.java b/src/com/android/settings/network/telephony/AlertDialogFragment.java
index 5940789673e..aaccc2db837 100644
--- a/src/com/android/settings/network/telephony/AlertDialogFragment.java
+++ b/src/com/android/settings/network/telephony/AlertDialogFragment.java
@@ -16,13 +16,14 @@
package com.android.settings.network.telephony;
-import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.TextUtils;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentActivity;
+
/** Fragment to show an alert dialog which only has the positive button. */
public class AlertDialogFragment extends BaseDialogFragment
implements DialogInterface.OnClickListener {
@@ -37,13 +38,13 @@ public class AlertDialogFragment extends BaseDialogFragment
* @param title
* @param msg
*/
- public static void show(Activity activity, String title, String msg) {
+ public static void show(FragmentActivity activity, String title, String msg) {
AlertDialogFragment fragment = new AlertDialogFragment();
Bundle arguments = new Bundle();
arguments.putString(ARG_TITLE, title);
arguments.putString(ARG_MSG, msg);
fragment.setArguments(arguments);
- fragment.show(activity.getFragmentManager(), TAG);
+ fragment.show(activity.getSupportFragmentManager(), TAG);
}
@Override
@@ -55,7 +56,7 @@ public class AlertDialogFragment extends BaseDialogFragment
if (!TextUtils.isEmpty(getArguments().getString(ARG_MSG))) {
builder.setMessage(getArguments().getString(ARG_MSG));
}
- return builder.show();
+ return builder.create();
}
@Override
diff --git a/src/com/android/settings/network/telephony/BaseDialogFragment.java b/src/com/android/settings/network/telephony/BaseDialogFragment.java
index 7da325910e9..0465cef152b 100644
--- a/src/com/android/settings/network/telephony/BaseDialogFragment.java
+++ b/src/com/android/settings/network/telephony/BaseDialogFragment.java
@@ -17,11 +17,11 @@
package com.android.settings.network.telephony;
import android.app.Activity;
-import android.app.DialogFragment;
-import android.app.Fragment;
import android.os.Bundle;
import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
/**
* Base dialog fragment class with the functionality to make a fragment or an activity as a listener
diff --git a/src/com/android/settings/network/telephony/ConfirmDialogFragment.java b/src/com/android/settings/network/telephony/ConfirmDialogFragment.java
index 1ba99c567bf..bad981a5c4c 100644
--- a/src/com/android/settings/network/telephony/ConfirmDialogFragment.java
+++ b/src/com/android/settings/network/telephony/ConfirmDialogFragment.java
@@ -16,13 +16,24 @@
package com.android.settings.network.telephony;
-import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.R;
+
+import java.util.ArrayList;
/** Fragment to show a confirm dialog. The caller should implement onConfirmListener. */
public class ConfirmDialogFragment extends BaseDialogFragment
@@ -32,22 +43,27 @@ public class ConfirmDialogFragment extends BaseDialogFragment
private static final String ARG_MSG = "msg";
private static final String ARG_POS_BUTTON_STRING = "pos_button_string";
private static final String ARG_NEG_BUTTON_STRING = "neg_button_string";
+ private static final String ARG_LIST = "list";
/**
* Interface defining the method that will be invoked when the user has done with the dialog.
*/
public interface OnConfirmListener {
/**
- * @param tag The tag in the caller.
- * @param confirmed True if the user has clicked the positive button. False if the user has
- * clicked the negative button or cancel the dialog.
+ * @param tag The tag in the caller.
+ * @param confirmed True if the user has clicked the positive button. False if the
+ * user has
+ * clicked the negative button or cancel the dialog.
+ * @param itemPosition It is the position of item, if user selects one of the list item.
+ * If the user select "cancel" or the dialog does not have list, then
+ * the value is -1.
*/
- void onConfirm(int tag, boolean confirmed);
+ void onConfirm(int tag, boolean confirmed, int itemPosition);
}
/** Displays a confirmation dialog which has confirm and cancel buttons. */
public static void show(
- Activity activity,
+ FragmentActivity activity,
Class callbackInterfaceClass,
int tagInCaller,
String title,
@@ -62,7 +78,29 @@ public class ConfirmDialogFragment extends BaseDialogFragment
arguments.putString(ARG_NEG_BUTTON_STRING, negButtonString);
setListener(activity, null, callbackInterfaceClass, tagInCaller, arguments);
fragment.setArguments(arguments);
- fragment.show(activity.getFragmentManager(), TAG);
+ fragment.show(activity.getSupportFragmentManager(), TAG);
+ }
+
+ /** Displays a confirmation dialog which has confirm and cancel buttons and carrier list.*/
+ public static void show(
+ FragmentActivity activity,
+ Class callbackInterfaceClass,
+ int tagInCaller,
+ String title,
+ String msg,
+ String posButtonString,
+ String negButtonString,
+ ArrayList list) {
+ ConfirmDialogFragment fragment = new ConfirmDialogFragment();
+ Bundle arguments = new Bundle();
+ arguments.putString(ARG_TITLE, title);
+ arguments.putCharSequence(ARG_MSG, msg);
+ arguments.putString(ARG_POS_BUTTON_STRING, posButtonString);
+ arguments.putString(ARG_NEG_BUTTON_STRING, negButtonString);
+ arguments.putStringArrayList(ARG_LIST, list);
+ setListener(activity, null, callbackInterfaceClass, tagInCaller, arguments);
+ fragment.setArguments(arguments);
+ fragment.show(activity.getSupportFragmentManager(), TAG);
}
@Override
@@ -71,37 +109,77 @@ public class ConfirmDialogFragment extends BaseDialogFragment
String message = getArguments().getString(ARG_MSG);
String posBtnString = getArguments().getString(ARG_POS_BUTTON_STRING);
String negBtnString = getArguments().getString(ARG_NEG_BUTTON_STRING);
+ ArrayList list = getArguments().getStringArrayList(ARG_LIST);
- Log.i("Showing dialog with title = %s", title);
+ Log.i(TAG, "Showing dialog with title =" + title);
AlertDialog.Builder builder =
new AlertDialog.Builder(getContext())
.setTitle(title)
.setPositiveButton(posBtnString, this)
.setNegativeButton(negBtnString, this);
- if (!TextUtils.isEmpty(message)) {
- builder.setMessage(message);
+ if (list != null && !list.isEmpty()) {
+ Log.i(TAG, "list =" + list.toString());
+
+ View content = LayoutInflater.from(getContext()).inflate(
+ R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
+
+ TextView dialogMessage = content.findViewById(R.id.msg);
+ if (!TextUtils.isEmpty(message) && dialogMessage != null) {
+ dialogMessage.setText(message);
+ }
+
+ final ArrayAdapter arrayAdapterItems = new ArrayAdapter(
+ getContext(), android.R.layout.select_dialog_item, list);
+ final ListView lvItems = content.findViewById(R.id.carrier_list);
+ if (lvItems != null) {
+ lvItems.setAdapter(arrayAdapterItems);
+ lvItems.setChoiceMode(ListView.CHOICE_MODE_NONE);
+ lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position,
+ long id) {
+ Log.i(TAG, "list onClick =" + position);
+ Log.i(TAG, "list item =" + list.get(position));
+
+ if (position == list.size() - 1) {
+ // user select the "cancel" item;
+ informCaller(false, -1);
+ } else {
+ informCaller(true, position);
+ }
+ }
+ });
+ }
+ builder.setView(content);
+ } else {
+ if (!TextUtils.isEmpty(message)) {
+ builder.setMessage(message);
+ }
}
- AlertDialog dialog = builder.show();
+
+ AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
@Override
public void onClick(DialogInterface dialog, int which) {
- informCaller(which == DialogInterface.BUTTON_POSITIVE);
+ Log.i(TAG, "dialog onClick =" + which);
+
+ informCaller(which == DialogInterface.BUTTON_POSITIVE, -1);
}
@Override
public void onCancel(DialogInterface dialog) {
- informCaller(false);
+ informCaller(false, -1);
}
- private void informCaller(boolean confirmed) {
+ private void informCaller(boolean confirmed, int itemPosition) {
OnConfirmListener listener = getListener(OnConfirmListener.class);
if (listener == null) {
return;
}
- listener.onConfirm(getTagInCaller(), confirmed);
+ listener.onConfirm(getTagInCaller(), confirmed, itemPosition);
}
}
diff --git a/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java
index f429f8b59c0..8247f63c9f9 100644
--- a/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java
@@ -96,7 +96,7 @@ public class DeleteEuiccSubscriptionDialogActivity extends SubscriptionActionDia
}
@Override
- public void onConfirm(int tag, boolean confirmed) {
+ public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed) {
finish();
return;
diff --git a/src/com/android/settings/network/telephony/EuiccOperationSidecar.java b/src/com/android/settings/network/telephony/EuiccOperationSidecar.java
index 05c866e7e9a..d1d362b8e76 100644
--- a/src/com/android/settings/network/telephony/EuiccOperationSidecar.java
+++ b/src/com/android/settings/network/telephony/EuiccOperationSidecar.java
@@ -24,10 +24,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.SystemClock;
+import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
import com.android.settings.SidecarFragment;
+import com.android.settings.network.SwitchSlotSidecar;
import java.util.concurrent.atomic.AtomicInteger;
@@ -37,7 +39,8 @@ import java.util.concurrent.atomic.AtomicInteger;
* should implement its own get() function to return an instance of that class, and implement the
* functional class like run() to actually trigger the function in EuiccManager.
*/
-public abstract class EuiccOperationSidecar extends SidecarFragment {
+public abstract class EuiccOperationSidecar extends SidecarFragment
+ implements SidecarFragment.Listener{
private static final String TAG = "EuiccOperationSidecar";
private static final int REQUEST_CODE = 0;
private static final String EXTRA_OP_ID = "op_id";
@@ -45,6 +48,9 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
new AtomicInteger((int) SystemClock.elapsedRealtime());
protected EuiccManager mEuiccManager;
+ protected TelephonyManager mTelephonyManager;
+ protected SwitchSlotSidecar mSwitchSlotSidecar;
+
private int mResultCode;
private int mDetailedCode;
@@ -107,6 +113,8 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEuiccManager = getContext().getSystemService(EuiccManager.class);
+ mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
+ mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
getContext()
.getApplicationContext()
@@ -118,12 +126,42 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
Context.RECEIVER_EXPORTED);
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ mSwitchSlotSidecar.addListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ mSwitchSlotSidecar.removeListener(this);
+ super.onPause();
+ }
+
@Override
public void onDestroy() {
getContext().getApplicationContext().unregisterReceiver(mReceiver);
super.onDestroy();
}
+ @Override
+ public void onStateChange(SidecarFragment fragment) {
+ if (fragment == mSwitchSlotSidecar) {
+ switch (mSwitchSlotSidecar.getState()) {
+ case State.SUCCESS:
+ mSwitchSlotSidecar.reset();
+ Log.i(TAG, "mSwitchSlotSidecar SUCCESS");
+ break;
+ case State.ERROR:
+ mSwitchSlotSidecar.reset();
+ Log.i(TAG, "mSwitchSlotSidecar ERROR");
+ break;
+ }
+ } else {
+ Log.wtf(TAG, "Received state change from a sidecar not expected.");
+ }
+ }
+
public int getResultCode() {
return mResultCode;
}
diff --git a/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java b/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java
index 7ff0d9a874c..288f1ac1e53 100644
--- a/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java
@@ -16,12 +16,13 @@
package com.android.settings.network.telephony;
-import android.app.Activity;
import android.os.Bundle;
import android.telephony.SubscriptionManager;
+import androidx.fragment.app.FragmentActivity;
+
/** The base class for subscription action dialogs */
-public class SubscriptionActionDialogActivity extends Activity {
+public class SubscriptionActionDialogActivity extends FragmentActivity {
private static final String TAG = "SubscriptionActionDialogActivity";
// Arguments
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index 2f6e8a12805..cc2986d5344 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -23,6 +23,7 @@ import android.os.UserManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.text.TextUtils;
import android.util.Log;
@@ -40,7 +41,9 @@ import com.android.settings.sim.SimActivationNotifier;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
/** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */
public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity
@@ -55,6 +58,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION = 2;
private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 3;
private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 4;
+ private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP = 5;
+
// Number of SIMs for DSDS
private static final int NUM_OF_SIMS_FOR_DSDS = 2;
// Support RTL mode
@@ -85,11 +90,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private boolean mIsEsimOperation;
private TelephonyManager mTelMgr;
private boolean isRtlMode;
+ private List mActiveSubInfos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
Intent intent = getIntent();
int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mTelMgr = getSystemService(TelephonyManager.class);
@@ -107,6 +112,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return;
}
+ mActiveSubInfos = SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded();
mSwitchToEuiccSubscriptionSidecar =
@@ -116,6 +122,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
mEnable = intent.getBooleanExtra(ARG_enable, true);
isRtlMode = getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_RTL;
+ Log.i(TAG, "isMultipleEnabledProfilesSupported():" + isMultipleEnabledProfilesSupported());
if (savedInstanceState == null) {
if (mEnable) {
@@ -154,7 +161,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
@Override
- public void onConfirm(int tag, boolean confirmed) {
+ public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed
&& tag != DIALOG_TAG_ENABLE_DSDS_CONFIRMATION
&& tag != DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION) {
@@ -162,14 +169,16 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return;
}
+ SubscriptionInfo removedSubInfo = null;
switch (tag) {
case DIALOG_TAG_DISABLE_SIM_CONFIRMATION:
if (mIsEsimOperation) {
Log.i(TAG, "Disabling the eSIM profile.");
showProgressDialog(
getString(R.string.privileged_action_disable_sub_dialog_progress));
+ int port = mSubInfo != null ? mSubInfo.getPortIndex() : 0;
mSwitchToEuiccSubscriptionSidecar.run(
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, port, null);
return;
}
Log.i(TAG, "Disabling the pSIM profile.");
@@ -201,6 +210,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
SimActivationNotifier.setShowSimSettingsNotification(this, true);
mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
break;
+ case DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP:
+ if (itemPosition != -1) {
+ removedSubInfo = (mActiveSubInfos != null) ? mActiveSubInfos.get(itemPosition)
+ : null;
+ }
case DIALOG_TAG_ENABLE_SIM_CONFIRMATION:
Log.i(TAG, "User confirmed to enable the subscription.");
if (mIsEsimOperation) {
@@ -209,12 +223,15 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
R.string.sim_action_switch_sub_dialog_progress,
SubscriptionUtil.getUniqueSubscriptionDisplayName(
mSubInfo, this)));
- mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
+ mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
+ UiccSlotUtil.INVALID_PORT_ID,
+ removedSubInfo);
return;
}
showProgressDialog(
getString(R.string.sim_action_enabling_sim_without_carrier_name));
- mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID);
+ mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
+ removedSubInfo);
break;
default:
Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
@@ -225,8 +242,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private void handleSwitchToEuiccSubscriptionSidecarStateChange() {
switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
case SidecarFragment.State.SUCCESS:
- Log.i(
- TAG,
+ Log.i(TAG,
String.format(
"Successfully %s the eSIM profile.",
mEnable ? "enable" : "disable"));
@@ -235,8 +251,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
finish();
break;
case SidecarFragment.State.ERROR:
- Log.i(
- TAG,
+ Log.i(TAG,
String.format(
"Failed to %s the eSIM profile.", mEnable ? "enable" : "disable"));
mSwitchToEuiccSubscriptionSidecar.reset();
@@ -290,7 +305,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
if (mIsEsimOperation) {
Log.i(TAG, "DSDS enabled, start to enable profile: " + mSubInfo.getSubscriptionId());
// For eSIM operations, we simply switch to the selected eSIM profile.
- mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
+ mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
+ UiccSlotUtil.INVALID_PORT_ID, null);
return;
}
@@ -305,10 +321,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable);
finish();
} else {
- Log.i(
- TAG,
- "The device does not support toggling pSIM. It is enough to just "
- + "enable the removable slot.");
+ Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
+ + "enable the removable slot.");
}
}
@@ -319,7 +333,10 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
showEnableDsdsConfirmDialog();
return;
}
- if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()) {
+ if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()
+ && isRemovableSimEnabled()) {
+ // This case is for switching on psim when device is not multiple enable profile
+ // supported.
Log.i(TAG, "Toggle on pSIM, no dialog displayed.");
handleTogglePsimAction();
finish();
@@ -372,27 +389,55 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
private void showEnableSimConfirmDialog() {
- List activeSubs =
- SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
- SubscriptionInfo activeSub = activeSubs.isEmpty() ? null : activeSubs.get(0);
- if (activeSub == null) {
+ if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
Log.i(TAG, "No active subscriptions available.");
showNonSwitchSimConfirmDialog();
return;
}
- Log.i(TAG, "Found active subscription.");
- boolean isBetweenEsim = mIsEsimOperation && activeSub.isEmbedded();
- if (mTelMgr.isMultiSimEnabled() && !isBetweenEsim) {
+ Log.i(TAG, "mActiveSubInfos:" + mActiveSubInfos);
+
+ boolean isSwitchingBetweenEsims = mIsEsimOperation
+ && mActiveSubInfos.stream().anyMatch(activeSubInfo -> activeSubInfo.isEmbedded());
+ boolean isMultiSimEnabled = mTelMgr.isMultiSimEnabled();
+ if (isMultiSimEnabled
+ && !isMultipleEnabledProfilesSupported()
+ && !isSwitchingBetweenEsims) {
+ // Showing the "no switch dialog" for below cases.
+ // DSDS mode + no MEP +
+ // (there is the active psim -> esim switch on => active (psim + esim))
showNonSwitchSimConfirmDialog();
return;
}
+ if (isMultiSimEnabled && isMultipleEnabledProfilesSupported()) {
+ if (mActiveSubInfos.size() < NUM_OF_SIMS_FOR_DSDS) {
+ // The sim can add into device directly, so showing the "no switch dialog".
+ // DSDS + MEP + (active sim < NUM_OF_SIMS_FOR_DSDS)
+ showNonSwitchSimConfirmDialog();
+ } else {
+ // The all of slots have sim, it needs to show the "MEP switch dialog".
+ // DSDS + MEP + two active sims
+ showMepSwitchSimConfirmDialog();
+ }
+ return;
+ }
+
+ // Showing the "switch dialog" for below cases.
+ // case1: SS mode + psim switch on from esim.
+ // case2: SS mode + esim switch from psim.
+ // case3: DSDS mode + No MEP + esim switch on from another esim.
+ SubscriptionInfo activeSub =
+ (isMultiSimEnabled && isSwitchingBetweenEsims)
+ ? mActiveSubInfos.stream()
+ .filter(activeSubInfo -> activeSubInfo.isEmbedded())
+ .findFirst().get()
+ : mActiveSubInfos.get(0);
ConfirmDialogFragment.show(
this,
ConfirmDialogFragment.OnConfirmListener.class,
DIALOG_TAG_ENABLE_SIM_CONFIRMATION,
getSwitchSubscriptionTitle(),
- getSwitchDialogBodyMsg(activeSub, isBetweenEsim),
+ getSwitchDialogBodyMsg(activeSub, isSwitchingBetweenEsims),
getSwitchDialogPosBtnText(),
getString(R.string.sim_action_cancel));
}
@@ -408,6 +453,35 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
getString(R.string.sim_action_cancel));
}
+ private void showMepSwitchSimConfirmDialog() {
+ Log.i(TAG, "showMepSwitchSimConfirmDialog");
+ final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
+ mSubInfo, this);
+ String title = getString(R.string.sim_action_switch_sub_dialog_mep_title, displayName);
+ final StringBuilder switchDialogMsg = new StringBuilder();
+ switchDialogMsg.append(
+ getString(R.string.sim_action_switch_sub_dialog_mep_text, displayName));
+ if (isRtlMode) {
+ /* There are two lines of message in the dialog, and the RTL symbols must be added
+ * before and after each sentence, so use the line break symbol to find the position.
+ * (Each message are all with two line break symbols)
+ */
+ switchDialogMsg.insert(0, RTL_MARK)
+ .insert(switchDialogMsg.indexOf(LINE_BREAK) - LINE_BREAK_OFFSET_ONE, RTL_MARK)
+ .insert(switchDialogMsg.indexOf(LINE_BREAK) + LINE_BREAK_OFFSET_TWO, RTL_MARK)
+ .insert(switchDialogMsg.length(), RTL_MARK);
+ }
+ ConfirmDialogFragment.show(
+ this,
+ ConfirmDialogFragment.OnConfirmListener.class,
+ DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP,
+ title,
+ switchDialogMsg.toString(),
+ null,
+ null,
+ getSwitchDialogBodyList());
+ }
+
private String getSwitchDialogPosBtnText() {
return mIsEsimOperation
? getString(
@@ -468,6 +542,20 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return switchDialogMsg.toString();
}
+ private ArrayList getSwitchDialogBodyList() {
+ ArrayList list = new ArrayList(mActiveSubInfos.stream()
+ .map(subInfo -> {
+ CharSequence subInfoName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
+ subInfo, this);
+ return getString(
+ R.string.sim_action_switch_sub_dialog_carrier_list_item_for_turning_off,
+ subInfoName);
+ })
+ .collect(Collectors.toList()));
+ list.add(getString(R.string.sim_action_cancel));
+ return list;
+ }
+
private boolean isDsdsConditionSatisfied() {
if (mTelMgr.isMultiSimEnabled()) {
Log.i(TAG, "DSDS is already enabled. Condition not satisfied.");
@@ -477,17 +565,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
Log.i(TAG, "Hardware does not support DSDS.");
return false;
}
- ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr);
- boolean isRemovableSimEnabled =
- slotInfos.stream()
- .anyMatch(
- slot ->
- slot != null
- && slot.isRemovable()
- && slot.getPorts().stream().anyMatch(
- port -> port.isActive())
- && slot.getCardStateInfo()
- == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
+ boolean isRemovableSimEnabled = isRemovableSimEnabled();
if (mIsEsimOperation && isRemovableSimEnabled) {
Log.i(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied.");
return true;
@@ -496,13 +574,36 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream()
.anyMatch(SubscriptionInfo::isEmbedded);
if (!mIsEsimOperation && isEsimProfileEnabled) {
- Log.i(
- TAG,
- "Removable SIM operation and eSIM profile is enabled. DSDS condition"
- + " satisfied.");
+ Log.i(TAG, "Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ + " satisfied.");
return true;
}
Log.i(TAG, "DSDS condition not satisfied.");
return false;
}
+
+ private boolean isRemovableSimEnabled() {
+ ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr);
+ boolean isRemovableSimEnabled =
+ slotInfos.stream()
+ .anyMatch(
+ slot -> slot != null
+ && slot.isRemovable()
+ && slot.getPorts().stream().anyMatch(
+ port -> port.isActive())
+ && slot.getCardStateInfo()
+ == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
+ Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
+ return isRemovableSimEnabled;
+ }
+
+ private boolean isMultipleEnabledProfilesSupported() {
+ List cardInfos = mTelMgr.getUiccCardsInfo();
+ if (cardInfos == null) {
+ Log.w(TAG, "UICC cards info list is empty.");
+ return false;
+ }
+ return cardInfos.stream().anyMatch(
+ cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
+ }
}
diff --git a/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java
index 170c699fa8c..5ce8b48b27c 100644
--- a/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java
+++ b/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java
@@ -59,6 +59,7 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase {
@Override
public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
mContext = getActivity();
final Intent intent = getActivity().getIntent();
@@ -81,7 +82,6 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase {
return;
}
- super.onCreate(icicle);
mCustomBehaviorPreference = getPreferenceScreen().findPreference(CUSTOM_BEHAVIOR_KEY);
mCustomBehaviorPreference.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index c03362510d0..a2f9922531b 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -268,9 +268,7 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_choose_your_pin_header_for_fingerprint,
R.string.lockpassword_choose_your_pin_header_for_face,
R.string.lockpassword_choose_your_pin_header_for_biometrics,
- R.string.lockpassword_choose_password_description,
R.string.lock_settings_picker_biometrics_added_security_message,
- R.string.lockpassword_choose_pin_description,
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.next_label),
@@ -287,8 +285,6 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_confirm_your_pin_header,
0,
0,
- 0,
- 0,
R.string.lockpassword_confirm_label),
ConfirmWrong(
@@ -304,8 +300,6 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_confirm_pins_dont_match,
0,
0,
- 0,
- 0,
R.string.lockpassword_confirm_label);
Stage(int hintInAlpha,
@@ -318,9 +312,7 @@ public class ChooseLockPassword extends SettingsActivity {
int hintInNumericForFingerprint,
int hintInNumericForFace,
int hintInNumericForBiometrics,
- int messageInAlpha,
int messageInAlphaForBiometrics,
- int messageInNumeric,
int messageInNumericForBiometrics,
int nextButtonText) {
@@ -336,10 +328,7 @@ public class ChooseLockPassword extends SettingsActivity {
this.numericHintForFace = hintInNumericForFace;
this.numericHintForBiometrics = hintInNumericForBiometrics;
- this.alphaMessage = messageInAlpha;
this.alphaMessageForBiometrics = messageInAlphaForBiometrics;
-
- this.numericMessage = messageInNumeric;
this.numericMessageForBiometrics = messageInNumericForBiometrics;
this.buttonText = nextButtonText;
@@ -365,11 +354,9 @@ public class ChooseLockPassword extends SettingsActivity {
public final int numericHintForBiometrics;
// Password description
- public final int alphaMessage;
public final int alphaMessageForBiometrics;
// PIN description
- public final int numericMessage;
public final int numericMessageForBiometrics;
public final int buttonText;
@@ -407,7 +394,7 @@ public class ChooseLockPassword extends SettingsActivity {
case TYPE_NONE:
default:
- return isAlpha ? alphaMessage : numericMessage;
+ return 0;
}
}
}
@@ -869,12 +856,17 @@ public class ChooseLockPassword extends SettingsActivity {
setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
}
- int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
- if (message != 0) {
- mMessage.setVisibility(View.VISIBLE);
- mMessage.setText(message);
+ final int stage = getStageType();
+ if (getStageType() != Stage.TYPE_NONE) {
+ int message = mUiStage.getMessage(mIsAlphaMode, stage);
+ if (message != 0) {
+ mMessage.setVisibility(View.VISIBLE);
+ mMessage.setText(message);
+ } else {
+ mMessage.setVisibility(View.INVISIBLE);
+ }
} else {
- mMessage.setVisibility(View.INVISIBLE);
+ mMessage.setVisibility(View.GONE);
}
setNextText(mUiStage.buttonText);
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index 016906aea10..3e7622c6b66 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -363,54 +363,49 @@ public class ChooseLockPattern extends SettingsActivity {
Introduction(
R.string.lock_settings_picker_biometrics_added_security_message,
- R.string.lockpattern_choose_pattern_description,
R.string.lockpattern_recording_intro_header,
LeftButtonMode.Gone, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
HelpScreen(
- ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record,
+ ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record,
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
ChoiceTooShort(
R.string.lock_settings_picker_biometrics_added_security_message,
- R.string.lockpattern_choose_pattern_description,
R.string.lockpattern_recording_incorrect_too_short,
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
FirstChoiceValid(
R.string.lock_settings_picker_biometrics_added_security_message,
- R.string.lockpattern_choose_pattern_description,
R.string.lockpattern_pattern_entered_header,
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
NeedToConfirm(
- ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_confirm,
+ ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_confirm,
LeftButtonMode.Gone, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ConfirmWrong(
- ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_unlock_wrong,
+ ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_unlock_wrong,
LeftButtonMode.Gone, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ChoiceConfirmed(
- ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_pattern_confirmed_header,
+ ID_EMPTY_MESSAGE, R.string.lockpattern_pattern_confirmed_header,
LeftButtonMode.Gone, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false);
/**
* @param messageForBiometrics The message displayed at the top, above header for
* fingerprint flow.
- * @param message The message displayed at the top.
* @param headerMessage The message displayed at the top.
* @param leftMode The mode of the left button.
* @param rightMode The mode of the right button.
* @param footerMessage The footer message.
* @param patternEnabled Whether the pattern widget is enabled.
*/
- Stage(int messageForBiometrics, int message, int headerMessage,
+ Stage(int messageForBiometrics, int headerMessage,
LeftButtonMode leftMode,
RightButtonMode rightMode,
int footerMessage, boolean patternEnabled) {
this.headerMessage = headerMessage;
this.messageForBiometrics = messageForBiometrics;
- this.message = message;
this.leftMode = leftMode;
this.rightMode = rightMode;
this.footerMessage = footerMessage;
@@ -419,7 +414,6 @@ public class ChooseLockPattern extends SettingsActivity {
final int headerMessage;
final int messageForBiometrics;
- final int message;
final LeftButtonMode leftMode;
final RightButtonMode rightMode;
final int footerMessage;
@@ -735,11 +729,14 @@ public class ChooseLockPattern extends SettingsActivity {
}
final GlifLayout layout = getActivity().findViewById(R.id.setup_wizard_layout);
final boolean forAnyBiometric = mForFingerprint || mForFace || mForBiometrics;
- int message = forAnyBiometric ? stage.messageForBiometrics : stage.message;
- if (message == ID_EMPTY_MESSAGE) {
- layout.setDescriptionText("");
+ if (forAnyBiometric) {
+ if (stage.messageForBiometrics == ID_EMPTY_MESSAGE) {
+ layout.setDescriptionText("");
+ } else {
+ layout.setDescriptionText(stage.messageForBiometrics);
+ }
} else {
- layout.setDescriptionText(message);
+ layout.getDescriptionTextView().setVisibility(View.GONE);
}
if (stage.footerMessage == ID_EMPTY_MESSAGE) {
mFooterText.setText("");
diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java
index 70cd6f2aebb..7151c6d89cc 100644
--- a/src/com/android/settings/password/SetupChooseLockPattern.java
+++ b/src/com/android/settings/password/SetupChooseLockPattern.java
@@ -142,14 +142,8 @@ public class SetupChooseLockPattern extends ChooseLockPattern {
mLeftButtonIsSkip = false;
}
- // Show generic pattern message when pattern lock screen launch in Setup wizard flow
- // before fingerprint and face setup.
final GlifLayout layout = getActivity().findViewById(R.id.setup_wizard_layout);
- if (stage.message == ID_EMPTY_MESSAGE) {
- layout.setDescriptionText("");
- } else {
- layout.setDescriptionText(stage.message);
- }
+ layout.setDescriptionText("");
}
@Override
diff --git a/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java b/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java
new file mode 100644
index 00000000000..dcc68ff4956
--- /dev/null
+++ b/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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.privacy;
+
+import android.annotation.NonNull;
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.safetycenter.SafetyCenterStatus;
+
+/** The preference controller for the top level privacy tile. */
+public class TopLevelPrivacyEntryPreferenceController extends BasePreferenceController {
+
+ public TopLevelPrivacyEntryPreferenceController(@NonNull Context context, @NonNull String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!SafetyCenterStatus.isEnabled()) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+}
diff --git a/src/com/android/settings/safetycenter/SafetyCenterStatus.java b/src/com/android/settings/safetycenter/SafetyCenterStatus.java
new file mode 100644
index 00000000000..d96bb32cf27
--- /dev/null
+++ b/src/com/android/settings/safetycenter/SafetyCenterStatus.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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.safetycenter;
+
+import android.provider.DeviceConfig;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Knows whether safety center is enabled or disabled. */
+public class SafetyCenterStatus {
+
+ /** Whether SafetyCenter page is enabled. */
+ @VisibleForTesting
+ public static final String SAFETY_CENTER_IS_ENABLED = "safety_center_is_enabled";
+
+ /** Returns true is SafetyCenter page is enabled, false otherwise. */
+ public static boolean isEnabled() {
+ // TODO(b/208625216): use SafetyManager API instead
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY, SAFETY_CENTER_IS_ENABLED, false);
+ }
+}
diff --git a/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java b/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java
new file mode 100644
index 00000000000..b3b49b07f1d
--- /dev/null
+++ b/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 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.safetycenter;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.core.BasePreferenceController;
+
+/** Controller for the SafetyCenter entry in top level Settings. */
+public class TopLevelSafetyCenterEntryPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "TopLevelSafetyCenterEntryPreferenceController";
+
+ public TopLevelSafetyCenterEntryPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (SafetyCenterStatus.isEnabled()) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ return super.handlePreferenceTreeClick(preference);
+ }
+
+ try {
+ mContext.startActivity(new Intent(Intent.ACTION_SAFETY_CENTER));
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Unable to open safety center", e);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
index 964482e08d5..2d9860636fa 100644
--- a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
+++ b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
@@ -24,6 +24,7 @@ import androidx.preference.Preference;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.safetycenter.SafetyCenterStatus;
public class TopLevelSecurityEntryPreferenceController extends BasePreferenceController {
@@ -37,7 +38,10 @@ public class TopLevelSecurityEntryPreferenceController extends BasePreferenceCon
@Override
public int getAvailabilityStatus() {
- return AVAILABLE;
+ if (!SafetyCenterStatus.isEnabled()) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
}
@Override
diff --git a/src/com/android/settings/sim/DsdsDialogActivity.java b/src/com/android/settings/sim/DsdsDialogActivity.java
index 62a6995a54c..7d9a43bc86d 100644
--- a/src/com/android/settings/sim/DsdsDialogActivity.java
+++ b/src/com/android/settings/sim/DsdsDialogActivity.java
@@ -85,7 +85,7 @@ public class DsdsDialogActivity extends SubscriptionActionDialogActivity
}
@Override
- public void onConfirm(int tag, boolean confirmed) {
+ public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed) {
Log.i(TAG, "User cancel the dialog to enable DSDS.");
startChooseSimActivity();
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index e5457ae3459..f9aca77d20b 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -160,8 +160,10 @@ public class SimDialogActivity extends FragmentActivity {
final TelephonyManager telephonyManager = getSystemService(
TelephonyManager.class).createForSubscriptionId(subId);
subscriptionManager.setDefaultDataSubId(subId);
- telephonyManager.setDataEnabled(true);
- Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ telephonyManager.setDataEnabled(true);
+ Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
+ }
}
private void setDefaultCallsSubId(final int subId) {
diff --git a/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java b/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java
index 385deffd2ba..be2fa2d1c6d 100644
--- a/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java
+++ b/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java
@@ -101,7 +101,7 @@ public class SwitchToEsimConfirmDialogActivity extends SubscriptionActionDialogA
}
@Override
- public void onConfirm(int tag, boolean confirmed) {
+ public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed) {
AlertDialogFragment.show(
this,
diff --git a/tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java
new file mode 100644
index 00000000000..570df739488
--- /dev/null
+++ b/tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 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.privacy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.safetycenter.SafetyCenterStatus;
+import com.android.settings.security.TopLevelSecurityEntryPreferenceController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class TopLevelPrivacyEntryPreferenceControllerTest {
+
+ private static final String PREFERENCE_KEY = "top_level_privacy";
+
+ private TopLevelPrivacyEntryPreferenceController mTopLevelPrivacyEntryPreferenceController;
+
+ @Mock
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+
+ mTopLevelPrivacyEntryPreferenceController =
+ new TopLevelPrivacyEntryPreferenceController(mContext, PREFERENCE_KEY);
+ }
+
+ @After
+ public void tearDown() {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+ }
+
+ @Test
+ public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsUnavailable() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(true),
+ /* makeDefault = */ false);
+
+ assertThat(mTopLevelPrivacyEntryPreferenceController.getAvailabilityStatus())
+ .isEqualTo(TopLevelSecurityEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsAvailable() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(false),
+ /* makeDefault = */ false);
+
+ assertThat(mTopLevelPrivacyEntryPreferenceController.getAvailabilityStatus())
+ .isEqualTo(TopLevelSecurityEntryPreferenceController.AVAILABLE);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetyCenterStatusTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetyCenterStatusTest.java
new file mode 100644
index 00000000000..f36fbe1f552
--- /dev/null
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetyCenterStatusTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 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.safetycenter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SafetyCenterStatusTest {
+
+ @Before
+ public void setUp() {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+ }
+
+ @After
+ public void tearDown() {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+ }
+
+ @Test
+ public void isEnabled_whenFlagTrue_returnsTrue() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(true),
+ /* makeDefault = */ false);
+
+ assertThat(SafetyCenterStatus.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void isEnabled_whenFlagFalse_returnsFalse() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(false),
+ /* makeDefault = */ false);
+
+ assertThat(SafetyCenterStatus.isEnabled()).isFalse();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java
new file mode 100644
index 00000000000..907fb99d00c
--- /dev/null
+++ b/tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2021 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.safetycenter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TopLevelSafetyCenterEntryPreferenceControllerTest {
+
+ private static final String PREFERENCE_KEY = "top_level_safety_center";
+
+ private TopLevelSafetyCenterEntryPreferenceController
+ mTopLevelSafetyCenterEntryPreferenceController;
+ private Preference mPreference;
+
+ @Mock
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mPreference = new Preference(ApplicationProvider.getApplicationContext());
+ mPreference.setKey(PREFERENCE_KEY);
+
+ doNothing().when(mContext).startActivity(any(Intent.class));
+ mTopLevelSafetyCenterEntryPreferenceController =
+ new TopLevelSafetyCenterEntryPreferenceController(mContext, PREFERENCE_KEY);
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+ }
+
+ @After
+ public void tearDown() {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_forDifferentPreferenceKey_isNotHandled() {
+ Preference preference = new Preference(ApplicationProvider.getApplicationContext());
+ preference.setKey("some_other_preference");
+
+ boolean preferenceHandled =
+ mTopLevelSafetyCenterEntryPreferenceController.handlePreferenceTreeClick(
+ preference);
+
+ assertThat(preferenceHandled).isFalse();
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_launchesIntendedIntent() {
+ final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ boolean preferenceHandled = mTopLevelSafetyCenterEntryPreferenceController
+ .handlePreferenceTreeClick(mPreference);
+
+ assertThat(preferenceHandled).isTrue();
+ verify(mContext).startActivity(intentCaptor.capture());
+ assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_SAFETY_CENTER);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_onStartActivityThrows_returnsFalse() {
+ doThrow(ActivityNotFoundException.class)
+ .when(mContext).startActivity(any(Intent.class));
+
+ boolean preferenceHandled = mTopLevelSafetyCenterEntryPreferenceController
+ .handlePreferenceTreeClick(mPreference);
+
+ assertThat(preferenceHandled).isFalse();
+ }
+
+ @Test
+ public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsUnavailable() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(false),
+ /* makeDefault = */ false);
+
+ assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus())
+ .isEqualTo(TopLevelSafetyCenterEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsAvailable() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(true),
+ /* makeDefault = */ false);
+
+ assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus())
+ .isEqualTo(TopLevelSafetyCenterEntryPreferenceController.AVAILABLE);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java
index a9acd2a6037..7e83ca7dcf4 100644
--- a/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java
@@ -25,14 +25,18 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.SettingsActivity;
+import com.android.settings.safetycenter.SafetyCenterStatus;
import com.android.settings.testutils.FakeFeatureFactory;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,6 +64,9 @@ public class TopLevelSecurityEntryPreferenceControllerTest {
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSecuritySettingsFeatureProvider = mFeatureFactory.getSecuritySettingsFeatureProvider();
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+
mPreference = new Preference(ApplicationProvider.getApplicationContext());
mPreference.setKey(PREFERENCE_KEY);
@@ -68,6 +75,12 @@ public class TopLevelSecurityEntryPreferenceControllerTest {
new TopLevelSecurityEntryPreferenceController(mContext, PREFERENCE_KEY);
}
+ @After
+ public void tearDown() {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_PRIVACY);
+ }
+
@Test
public void handlePreferenceTreeClick_forDifferentPreferenceKey_isNotHandled() {
Preference preference = new Preference(ApplicationProvider.getApplicationContext());
@@ -121,4 +134,28 @@ public class TopLevelSecurityEntryPreferenceControllerTest {
assertThat(preferenceHandled).isFalse();
}
+
+ @Test
+ public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsUnavailable() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(true),
+ /* makeDefault = */ false);
+
+ assertThat(mTopLevelSecurityEntryPreferenceController.getAvailabilityStatus())
+ .isEqualTo(TopLevelSecurityEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsAvailable() {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
+ /* value = */ Boolean.toString(false),
+ /* makeDefault = */ false);
+
+ assertThat(mTopLevelSecurityEntryPreferenceController.getAvailabilityStatus())
+ .isEqualTo(TopLevelSecurityEntryPreferenceController.AVAILABLE);
+ }
}