Merge changes Iee650897,Icb00b3fc,I5c6ad4b3,Ief4299e7,I85800227, ...
* changes: [MEP] disable enabled esim profile before set simSlotMapping [MEP] renew the sim confirm dialog UI [MEP] renew the default data selection UI switch SIM refactor to support MEP switch SIM refactor to support MEP Support RTL mode in Sim switch dialog
This commit is contained in:
@@ -690,12 +690,12 @@
|
||||
<activity android:name=".network.telephony.ToggleSubscriptionDialogActivity"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
|
||||
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight" />
|
||||
android:theme="@style/Theme.AlertDialog.SimConfirmDialog"/>
|
||||
|
||||
<activity android:name=".network.telephony.DeleteEuiccSubscriptionDialogActivity"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
|
||||
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight" />
|
||||
android:theme="@style/Theme.AlertDialog.SimConfirmDialog"/>
|
||||
|
||||
<activity
|
||||
android:name="Settings$TetherSettingsActivity"
|
||||
@@ -3370,7 +3370,7 @@
|
||||
|
||||
<activity
|
||||
android:name=".sim.SimDialogActivity"
|
||||
android:theme="@style/Theme.AlertDialog"
|
||||
android:theme="@style/Theme.AlertDialog.SimConfirmDialog"
|
||||
android:label="@string/sim_settings_title"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true"
|
||||
@@ -4202,14 +4202,14 @@
|
||||
android:exported="false"
|
||||
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight" />
|
||||
android:theme="@style/Theme.AlertDialog.SimConfirmDialog"/>
|
||||
|
||||
<activity
|
||||
android:name=".sim.DsdsDialogActivity"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight" />
|
||||
android:theme="@style/Theme.AlertDialog.SimConfirmDialog"/>
|
||||
|
||||
<service android:name=".sim.SimNotificationService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
41
res/drawable/sim_confirm_dialog_btn_outline.xml
Normal file
41
res/drawable/sim_confirm_dialog_btn_outline.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
android:insetTop="16dp"
|
||||
android:insetBottom="24dp">
|
||||
<ripple android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white"/>
|
||||
<corners android:radius="24dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="24dp"/>
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
|
||||
android:width="1dp" />
|
||||
<padding android:left="16dp"
|
||||
android:top="8dp"
|
||||
android:right="16dp"
|
||||
android:bottom="8dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
</inset>
|
30
res/drawable/sim_confirm_dialog_rounded_bg.xml
Normal file
30
res/drawable/sim_confirm_dialog_rounded_bg.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="24dp"
|
||||
android:insetRight="24dp">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/settingslib_state_on_color"/>
|
||||
<corners
|
||||
android:bottomLeftRadius="8dp"
|
||||
android:topLeftRadius="8dp"
|
||||
android:bottomRightRadius="8dp"
|
||||
android:topRightRadius="8dp"
|
||||
/>
|
||||
</shape>
|
||||
</inset>
|
@@ -15,43 +15,27 @@
|
||||
-->
|
||||
|
||||
<!-- Layout of a single item for displaying sim cards. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<ImageView android:id="@+id/icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="center" />
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="15dip"
|
||||
android:layout_marginEnd="6dip"
|
||||
android:layout_marginTop="6dip"
|
||||
android:layout_marginBottom="6dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1" >
|
||||
<TextView android:id="@+id/title"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:gravity="start|center_vertical"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:background="@drawable/sim_confirm_dialog_rounded_bg"
|
||||
android:gravity="center">
|
||||
<TextView android:id="@+id/title"
|
||||
android:textAppearance="@style/TextAppearance.SimConfirmDialogList"
|
||||
android:gravity="start|center_vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal" />
|
||||
<TextView android:id="@+id/summary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textAppearance="@style/TextAppearance.SimConfirmDialogList.Summary"
|
||||
android:gravity="start|center_vertical"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_alignStart="@id/title" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
style="?attr/materialAlertDialogBodyTextStyle"
|
||||
android:gravity="center"
|
||||
android:paddingTop="?attr/listPreferredItemPaddingStart"
|
||||
android:paddingBottom="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:background="@drawable/sim_confirm_dialog_rounded_bg"
|
||||
android:textAppearance="@style/TextAppearance.SimConfirmDialogList"
|
||||
/>
|
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/msg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingStart="24dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.DialogMessage"/>
|
||||
<ListView
|
||||
android:id="@+id/carrier_list"
|
||||
android:layout_gravity="center"
|
||||
android:paddingTop="16dp"
|
||||
android:dividerHeight="1dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/info_outline_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start|top"
|
||||
android:orientation="horizontal"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingStart="24dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:baselineAligned="true">
|
||||
<ImageView
|
||||
android:src="@drawable/ic_info_outline_24dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:tint="?android:attr/textColorTertiary"/>
|
||||
<TextView
|
||||
android:id="@+id/info_outline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:text="@string/sim_action_switch_sub_dialog_info_outline_for_turning_off"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="@style/TextAppearance.DialogMessage"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingLeft="24dp"
|
||||
android:gravity="center"
|
||||
style="?android:attr/textAppearanceLarge"/>
|
@@ -16,9 +16,18 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="Widget.ActionBar.Base" parent="@android:style/Widget.DeviceDefault.ActionBar.Solid">
|
||||
<item name="android:background">?android:attr/colorPrimaryDark</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.SimConfirmDialogList" parent="@style/TextAppearance.DialogMessage">
|
||||
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.SimConfirmDialogList.Summary">
|
||||
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
|
||||
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
|
||||
</style>
|
||||
</resources>
|
@@ -34,6 +34,12 @@
|
||||
<item name="android:colorBackground">@*android:color/surface_dark</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.AlertDialog.Base.Material3" parent="Theme.MaterialComponents.DayNight.Dialog.Alert">
|
||||
<item name="colorPrimary">@*android:color/primary_device_default_settings</item>
|
||||
<item name="colorAccent">@*android:color/accent_device_default_dark</item>
|
||||
<item name="android:colorBackground">@*android:color/surface_dark</item>
|
||||
</style>
|
||||
|
||||
<!-- Material theme for the pages containing TabLayout and ViewPager -->
|
||||
<style name="Theme.TabTheme" parent="@style/Theme.MaterialComponents.DayNight">
|
||||
<item name="colorPrimary">@*android:color/edge_effect_device_default_dark</item>
|
||||
|
@@ -7954,11 +7954,17 @@
|
||||
<!-- Checkbox to always use for calls. [CHAR LIMIT=40] -->
|
||||
<string name="sim_calls_always_use">Always use this for calls</string>
|
||||
<!-- Message for selecting sim for data in settings. [CHAR LIMIT=40] -->
|
||||
<string name="select_sim_for_data">Select a SIM for data</string>
|
||||
<string name="select_sim_for_data">Choose SIM for mobile data</string>
|
||||
<!-- Message for selecting sim for SMS in settings. [CHAR LIMIT=40] -->
|
||||
<string name="select_sim_for_sms">Select a SIM for SMS</string>
|
||||
<!-- Message for switching data SIM; switching takes a while -->
|
||||
<string name="data_switch_started">Switching data SIM, this may take up to a minute\u2026</string>
|
||||
<!-- Title for selecting specific sim for data in settings. [CHAR LIMIT=40] -->
|
||||
<string name="select_specific_sim_for_data_title">Use <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g> for mobile data?</string>
|
||||
<!-- Message for selecting specific sim for data in settings. [CHAR LIMIT=NONE] -->
|
||||
<string name="select_specific_sim_for_data_msg">If you switch to <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g>, <xliff:g id="old_sim" example="carrierB">%2$s</xliff:g> will no longer be used for mobile data.</string>
|
||||
<!-- Button on a selecting specific sim dialog to confirm data in settings. [CHAR LIMIT=40] -->
|
||||
<string name="select_specific_sim_for_data_button">Use <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g></string>
|
||||
<!-- Message for selecting sim for call in settings. [CHAR LIMIT=40] -->
|
||||
<string name="select_sim_for_calls">Call with</string>
|
||||
<!-- Title for selecting a SIM card. [CHAR LIMIT=40] -->
|
||||
@@ -12737,18 +12743,26 @@
|
||||
<string name="sim_action_switch_sub_dialog_title">Switch to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
|
||||
<!-- Title of confirmation dialog asking the user if they want to switch to the SIM card. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_psim_dialog_title">Switch to using SIM card?</string>
|
||||
<!-- Title of confirmation dialog asking the user if they want to switch subscription. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_mep_title">Use <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
|
||||
<!-- Body text of confirmation dialog for switching subscription that involves switching SIM slots. Indicates that only one SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_text">Only one SIM can be active at a time.\n\nSwitching to <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> won\u2019t cancel your <xliff:g id="from_carrier_name" example="Sprint">%2$s</xliff:g> service.</string>
|
||||
<!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one downloaded SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_text_downloaded">Only one downloaded SIM can be active at a time.\n\nSwitching to <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> won\u2019t cancel your <xliff:g id="from_carrier_name" example="Sprint">%2$s</xliff:g> service.</string>
|
||||
<!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_text_single_sim">Only one SIM can be active at a time.\n\nSwitching won\u2019t cancel your <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> service.</string>
|
||||
<!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one downloaded SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_mep_text">You can use 2 SIMs at a time. To use <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>, turn off another SIM.</string>
|
||||
<!-- Text of confirm button in the confirmation dialog asking the user if they want to switch subscription. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_confirm">Switch to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
|
||||
<!-- Text of carrier list item in the mep confirmation dialog asking the user if they want to turn off the carrier. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_carrier_list_item_for_turning_off">Turn off <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
|
||||
<!-- Text of carrier list item in the mep confirmation dialog asking the user if they want to turn off the carrier. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_info_outline_for_turning_off">Turning off a SIM won\u2019t cancel your service</string>
|
||||
<!-- Status message indicating the device is in the process of disconnecting from one mobile network and immediately connecting to another. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_enabling_sim_without_carrier_name">Connecting to network…</string>
|
||||
<!-- Text of progress dialog indicating the subscription switch is in progress. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_switch_sub_dialog_progress">Switching to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
|
||||
<string name="sim_action_switch_sub_dialog_progress">Switching to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> for calls and messages…</string>
|
||||
<!-- Title of error message indicating that the device could not disconnect from one mobile network and immediately connect to another. [CHAR_LIMIT=NONE] -->
|
||||
<string name="sim_action_enable_sim_fail_title">Can\u2019t switch carrier</string>
|
||||
<!-- Body text of error message indicating the device could not disconnect from one mobile network and immediately connect to another, due to an unspecified issue. [CHAR_LIMIT=NONE] -->
|
||||
|
@@ -914,4 +914,25 @@
|
||||
parent="@*android:style/TextAppearance.DeviceDefault">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.SimConfirmDialogList" parent="@style/TextAppearance.DialogMessage"/>
|
||||
|
||||
<style name="TextAppearance.SimConfirmDialogList.Summary">
|
||||
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
|
||||
</style>
|
||||
|
||||
<style name="SimConfirmDialog.OutlineButton" parent="@android:style/Widget.Material.Button">
|
||||
<item name="android:layout_marginStart">8dp</item>
|
||||
<item name="android:layout_height">36dp</item>
|
||||
<item name="android:minWidth">0dp</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:lineHeight">20sp</item>
|
||||
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:background">@drawable/sim_confirm_dialog_btn_outline</item>
|
||||
</style>
|
||||
|
||||
<style name="SimConfirmDialog.ButtonBarStyle" parent="@android:style/Widget.Material.ButtonBar">
|
||||
<item name="android:paddingEnd">8dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
@@ -142,6 +142,13 @@
|
||||
<item name="buttonBarButtonStyle">@*android:style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Theme.AlertDialog.SimConfirmDialog">
|
||||
<item name="buttonBarStyle">@style/SimConfirmDialog.ButtonBarStyle</item>
|
||||
<item name="buttonBarButtonStyle">@style/SimConfirmDialog.OutlineButton</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.ConfirmDeviceCredentials" parent="Theme.SubSettings">
|
||||
<item name="confirmDeviceCredentialsSideMargin">@dimen/confirm_credentials_side_margin</item>
|
||||
<item name="confirmDeviceCredentialsTopMargin">@dimen/confirm_credentials_top_margin</item>
|
||||
|
@@ -19,6 +19,7 @@ package com.android.settings.network;
|
||||
import android.annotation.IntDef;
|
||||
import android.app.FragmentManager;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.AsyncTaskSidecar;
|
||||
@@ -41,11 +42,14 @@ public class SwitchSlotSidecar
|
||||
})
|
||||
private @interface Command {
|
||||
int SWITCH_TO_REMOVABLE_SIM = 0;
|
||||
int SWITCH_TO_EUICC_SIM = 1;
|
||||
}
|
||||
|
||||
static class Param {
|
||||
@Command int command;
|
||||
int slotId;
|
||||
int port;
|
||||
SubscriptionInfo removedSubInfo;
|
||||
}
|
||||
|
||||
static class Result {
|
||||
@@ -65,13 +69,24 @@ public class SwitchSlotSidecar
|
||||
}
|
||||
|
||||
/** Starts switching to the removable slot. */
|
||||
public void runSwitchToRemovableSlot(int id) {
|
||||
public void runSwitchToRemovableSlot(int id, SubscriptionInfo removedSubInfo) {
|
||||
Param param = new Param();
|
||||
param.command = Command.SWITCH_TO_REMOVABLE_SIM;
|
||||
param.slotId = id;
|
||||
param.removedSubInfo = removedSubInfo;
|
||||
param.port = 0;
|
||||
super.run(param);
|
||||
}
|
||||
|
||||
/** Starts switching to the removable slot. */
|
||||
public void runSwitchToEuiccSlot(int id, int port, SubscriptionInfo removedSubInfo) {
|
||||
Param param = new Param();
|
||||
param.command = Command.SWITCH_TO_EUICC_SIM;
|
||||
param.slotId = id;
|
||||
param.removedSubInfo = removedSubInfo;
|
||||
param.port = port;
|
||||
super.run(param);
|
||||
}
|
||||
/**
|
||||
* Returns the exception thrown during the execution of a command. Will be null in any state
|
||||
* other than {@link State#SUCCESS}, and may be null in that state if there was not an error.
|
||||
@@ -91,7 +106,14 @@ public class SwitchSlotSidecar
|
||||
try {
|
||||
switch (param.command) {
|
||||
case Command.SWITCH_TO_REMOVABLE_SIM:
|
||||
UiccSlotUtil.switchToRemovableSlot(param.slotId, getContext());
|
||||
Log.i(TAG, "Start to switch to removable slot.");
|
||||
UiccSlotUtil.switchToRemovableSlot(getContext(), param.slotId,
|
||||
param.removedSubInfo);
|
||||
break;
|
||||
case Command.SWITCH_TO_EUICC_SIM:
|
||||
Log.i(TAG, "Start to switch to euicc slot.");
|
||||
UiccSlotUtil.switchToEuiccSlot(getContext(), param.slotId, param.port,
|
||||
param.removedSubInfo);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Wrong command.");
|
||||
|
@@ -18,17 +18,33 @@ package com.android.settings.network;
|
||||
|
||||
import android.app.FragmentManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.UiccCardInfo;
|
||||
import android.telephony.UiccSlotMapping;
|
||||
import android.telephony.euicc.EuiccManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.SidecarFragment;
|
||||
import com.android.settings.network.telephony.EuiccOperationSidecar;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** A headless fragment encapsulating long-running eSIM enabling/disabling operations. */
|
||||
public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
|
||||
private static final String TAG = "SwitchToEuiccSubscriptionSidecar";
|
||||
private static final String TAG = "SwitchToEuiccSidecar";
|
||||
private static final String ACTION_SWITCH_TO_SUBSCRIPTION =
|
||||
"com.android.settings.network.SWITCH_TO_SUBSCRIPTION";
|
||||
private static final int ESIM_SLOT_ID = 1;
|
||||
|
||||
private PendingIntent mCallbackIntent;
|
||||
private int mSubId;
|
||||
private int mPort;
|
||||
private SubscriptionInfo mRemovedSubInfo;
|
||||
private boolean mIsDuringSimSlotMapping;
|
||||
|
||||
/** Returns a SwitchToEuiccSubscriptionSidecar sidecar instance. */
|
||||
public static SwitchToEuiccSubscriptionSidecar get(FragmentManager fm) {
|
||||
@@ -46,10 +62,155 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
|
||||
return mCallbackIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChange(SidecarFragment fragment) {
|
||||
if (fragment == mSwitchSlotSidecar) {
|
||||
onSwitchSlotSidecarStateChange();
|
||||
} else {
|
||||
Log.wtf(TAG, "Received state change from a sidecar not expected.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts calling EuiccManager#switchToSubscription to enable/disable the eSIM profile. */
|
||||
// ToDo: delete this api and refactor the related code.
|
||||
public void run(int subscriptionId) {
|
||||
setState(State.RUNNING, Substate.UNUSED);
|
||||
mCallbackIntent = createCallbackIntent();
|
||||
mEuiccManager.switchToSubscription(subscriptionId, mCallbackIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts calling EuiccManager#switchToSubscription to enable/disable the eSIM profile.
|
||||
*
|
||||
* @param subscriptionId the esim's subscriptionId.
|
||||
* @param port the esim's portId. If user wants to inactivate esim, then user must to assign the
|
||||
* the port. If user wants to activate esim, then the port can be -1.
|
||||
* @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 esim slot and portId 0.
|
||||
*/
|
||||
public void run(int subscriptionId, int port, SubscriptionInfo removedSubInfo) {
|
||||
setState(State.RUNNING, Substate.UNUSED);
|
||||
mCallbackIntent = createCallbackIntent();
|
||||
mSubId = subscriptionId;
|
||||
// To check whether the esim slot's port is active. If yes, skip setSlotMapping. If no,
|
||||
// set this slot+port into setSimSlotMapping.
|
||||
mPort = (port < 0) ? getTargetPortId(removedSubInfo) : port;
|
||||
mRemovedSubInfo = removedSubInfo;
|
||||
Log.i(TAG, "The SubId is " + mSubId + ". The port is " + mPort);
|
||||
|
||||
if (mTelephonyManager.isMultiSimEnabled() && removedSubInfo != null
|
||||
&& removedSubInfo.isEmbedded()) {
|
||||
// In DSDS mode+MEP, if the replaced esim is active, then it should be disabled esim
|
||||
// profile before changing SimSlotMapping process.
|
||||
// Use INVALID_SUBSCRIPTION_ID to disable the esim profile.
|
||||
// The SimSlotMapping is ready, then to execute activate/inactivate esim.
|
||||
mIsDuringSimSlotMapping = true;
|
||||
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) {
|
||||
mSwitchSlotSidecar.runSwitchToEuiccSlot(getTargetSlot(), mPort,
|
||||
removedSubInfo);
|
||||
} else {
|
||||
setState(State.ERROR, resultCode);
|
||||
}
|
||||
}
|
||||
};
|
||||
mEuiccManager.switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, mPort,
|
||||
getContext().getMainExecutor(),
|
||||
callback);
|
||||
} else {
|
||||
mSwitchSlotSidecar.runSwitchToEuiccSlot(getTargetSlot(), mPort, removedSubInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private int getTargetPortId(SubscriptionInfo removedSubInfo) {
|
||||
if (!mTelephonyManager.isMultiSimEnabled() || !isMultipleEnabledProfilesSupported()) {
|
||||
// In the 'SS mode' or 'DSDS+no MEP', the port is 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// In the 'DSDS+MEP', if the removedSubInfo is esim, then the port is
|
||||
// removedSubInfo's port.
|
||||
if (removedSubInfo != null && removedSubInfo.isEmbedded()) {
|
||||
return removedSubInfo.getPortIndex();
|
||||
}
|
||||
|
||||
// In DSDS+MEP mode, the removedSubInfo is psim or is null, it means the this esim need
|
||||
// another port in the esim slot.
|
||||
// To find another esim's port and value is from 0;
|
||||
int port = 0;
|
||||
Collection<UiccSlotMapping> 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<UiccCardInfo> 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActionReceived() {
|
||||
if (getResultCode() == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK
|
||||
&& mIsDuringSimSlotMapping) {
|
||||
// Continue to switch the SimSlotMapping, after the esim is disabled.
|
||||
mIsDuringSimSlotMapping = false;
|
||||
mSwitchSlotSidecar.runSwitchToEuiccSlot(getTargetSlot(), mPort, mRemovedSubInfo);
|
||||
} else {
|
||||
super.onActionReceived();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -31,15 +31,14 @@ import com.android.settings.network.telephony.EuiccOperationSidecar;
|
||||
*/
|
||||
public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
|
||||
implements SidecarFragment.Listener {
|
||||
|
||||
private static final String TAG = "DisableSubscriptionAndSwitchSlotSidecar";
|
||||
private static final String TAG = "SwitchRemovableSidecar";
|
||||
private static final String ACTION_DISABLE_SUBSCRIPTION_AND_SWITCH_SLOT =
|
||||
"disable_subscription_and_switch_slot_sidecar";
|
||||
|
||||
// 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 +50,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,18 +86,52 @@ 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;
|
||||
SubscriptionManager subscriptionManager =
|
||||
getContext().getSystemService(SubscriptionManager.class);
|
||||
if (!mTelephonyManager.isMultiSimEnabled()
|
||||
&& 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, 0, null);
|
||||
} else if (mTelephonyManager.isMultiSimEnabled() && mRemovedSubInfo != null) {
|
||||
// In DSDS mode+MEP, if the replaced esim is active, then it should be disabled esim
|
||||
// profile before changing SimSlotMapping process.
|
||||
// Use INVALID_SUBSCRIPTION_ID to disable the esim profile.
|
||||
mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
|
||||
mRemovedSubInfo.getPortIndex(), null);
|
||||
} else {
|
||||
Log.i(TAG, "Start to switch to removable slot.");
|
||||
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,10 +139,9 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
|
||||
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();
|
||||
|
@@ -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.
|
||||
@@ -80,40 +86,104 @@ public class UiccSlotUtil {
|
||||
* @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<UiccSlotMapping> 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<UiccSlotMapping> 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<UiccSlotMapping> uiccSlotMappings,
|
||||
int slotId, int port) {
|
||||
return uiccSlotMappings.stream()
|
||||
.anyMatch(
|
||||
uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() == slotId
|
||||
&& uiccSlotMapping.getPortIndex() == port);
|
||||
}
|
||||
|
||||
private static void performSwitchToSlot(TelephonyManager telMgr,
|
||||
Collection<UiccSlotMapping> 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<UiccSlotMapping> prepareUiccSlotMappingsForRemovableSlot(
|
||||
Collection<UiccSlotMapping> 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<UiccSlotMapping> 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;
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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,6 +43,7 @@ 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.
|
||||
@@ -39,15 +51,19 @@ public class ConfirmDialogFragment extends BaseDialogFragment
|
||||
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
|
||||
* @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 <T> void show(
|
||||
Activity activity,
|
||||
FragmentActivity activity,
|
||||
Class<T> 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 <T> void show(
|
||||
FragmentActivity activity,
|
||||
Class<T> callbackInterfaceClass,
|
||||
int tagInCaller,
|
||||
String title,
|
||||
String msg,
|
||||
String posButtonString,
|
||||
String negButtonString,
|
||||
ArrayList<String> 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,87 @@ 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<String> list = getArguments().getStringArrayList(ARG_LIST);
|
||||
|
||||
Log.i("Showing dialog with title = %s", title);
|
||||
AlertDialog.Builder builder =
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(title)
|
||||
Log.i(TAG, "Showing dialog with title =" + title);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
|
||||
.setPositiveButton(posBtnString, this)
|
||||
.setNegativeButton(negBtnString, this);
|
||||
|
||||
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);
|
||||
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
View titleView = LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.sim_confirm_dialog_title_multiple_enabled_profiles_supported,
|
||||
null);
|
||||
TextView titleTextView = titleView.findViewById(R.id.title);
|
||||
titleTextView.setText(title);
|
||||
builder.setCustomTitle(titleTextView);
|
||||
}
|
||||
TextView dialogMessage = content.findViewById(R.id.msg);
|
||||
if (!TextUtils.isEmpty(message) && dialogMessage != null) {
|
||||
dialogMessage.setText(message);
|
||||
}
|
||||
|
||||
final ArrayAdapter<String> arrayAdapterItems = new ArrayAdapter<String>(
|
||||
getContext(),
|
||||
R.layout.sim_confirm_dialog_item_multiple_enabled_profiles_supported, 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(title)) {
|
||||
builder.setTitle(title);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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()
|
||||
@@ -117,12 +125,42 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
|
||||
null);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -23,9 +23,11 @@ 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;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
@@ -39,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
|
||||
@@ -54,8 +58,15 @@ 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
|
||||
private static final String LINE_BREAK = "\n";
|
||||
private static final int LINE_BREAK_OFFSET_ONE = 1;
|
||||
private static final int LINE_BREAK_OFFSET_TWO = 2;
|
||||
private static final String RTL_MARK = "\u200F";
|
||||
|
||||
/**
|
||||
* Returns an intent of ToggleSubscriptionDialogActivity.
|
||||
@@ -78,11 +89,12 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
private boolean mEnable;
|
||||
private boolean mIsEsimOperation;
|
||||
private TelephonyManager mTelMgr;
|
||||
private boolean isRtlMode;
|
||||
private List<SubscriptionInfo> 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);
|
||||
@@ -100,6 +112,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
return;
|
||||
}
|
||||
|
||||
mActiveSubInfos = SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
|
||||
mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
|
||||
mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded();
|
||||
mSwitchToEuiccSubscriptionSidecar =
|
||||
@@ -107,6 +120,9 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
mSwitchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(getFragmentManager());
|
||||
mEnableMultiSimSidecar = EnableMultiSimSidecar.get(getFragmentManager());
|
||||
mEnable = intent.getBooleanExtra(ARG_enable, true);
|
||||
isRtlMode = getResources().getConfiguration().getLayoutDirection()
|
||||
== View.LAYOUT_DIRECTION_RTL;
|
||||
Log.i(TAG, "isMultipleEnabledProfilesSupported():" + isMultipleEnabledProfilesSupported());
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
if (mEnable) {
|
||||
@@ -145,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) {
|
||||
@@ -153,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.");
|
||||
@@ -192,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) {
|
||||
@@ -200,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);
|
||||
@@ -216,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"));
|
||||
@@ -226,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();
|
||||
@@ -281,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;
|
||||
}
|
||||
|
||||
@@ -296,9 +321,7 @@ 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 "
|
||||
Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
|
||||
+ "enable the removable slot.");
|
||||
}
|
||||
}
|
||||
@@ -310,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();
|
||||
@@ -363,26 +389,55 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
}
|
||||
|
||||
private void showEnableSimConfirmDialog() {
|
||||
List<SubscriptionInfo> 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));
|
||||
}
|
||||
@@ -398,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(
|
||||
@@ -429,21 +513,47 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
mSubInfo, this);
|
||||
final CharSequence activeSubName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
|
||||
activeSub, this);
|
||||
final StringBuilder switchDialogMsg = new StringBuilder();
|
||||
if (betweenEsim && mIsEsimOperation) {
|
||||
return getString(
|
||||
switchDialogMsg.append(getString(
|
||||
R.string.sim_action_switch_sub_dialog_text_downloaded,
|
||||
subInfoName,
|
||||
activeSubName);
|
||||
activeSubName));
|
||||
} else if (mIsEsimOperation) {
|
||||
return getString(
|
||||
switchDialogMsg.append(getString(
|
||||
R.string.sim_action_switch_sub_dialog_text,
|
||||
subInfoName,
|
||||
activeSubName);
|
||||
activeSubName));
|
||||
} else {
|
||||
return getString(
|
||||
switchDialogMsg.append(getString(
|
||||
R.string.sim_action_switch_sub_dialog_text_single_sim,
|
||||
activeSubName);
|
||||
activeSubName));
|
||||
}
|
||||
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);
|
||||
}
|
||||
return switchDialogMsg.toString();
|
||||
}
|
||||
|
||||
private ArrayList<String> getSwitchDialogBodyList() {
|
||||
ArrayList<String> list = new ArrayList<String>(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() {
|
||||
@@ -455,17 +565,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
Log.i(TAG, "Hardware does not support DSDS.");
|
||||
return false;
|
||||
}
|
||||
ImmutableList<UiccSlotInfo> 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;
|
||||
@@ -474,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"
|
||||
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<UiccSlotInfo> 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<UiccCardInfo> cardInfos = mTelMgr.getUiccCardsInfo();
|
||||
if (cardInfos == null) {
|
||||
Log.w(TAG, "UICC cards info list is empty.");
|
||||
return false;
|
||||
}
|
||||
return cardInfos.stream().anyMatch(
|
||||
cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.sim;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Presents a dialog asking the user if they want to switch the data to another sim
|
||||
*/
|
||||
public class SelectSpecificDataSimDialogFragment extends SimDialogFragment implements
|
||||
DialogInterface.OnClickListener {
|
||||
private static final String TAG = "PreferredSimDialogFrag";
|
||||
|
||||
private SubscriptionInfo mSubscriptionInfo;
|
||||
|
||||
/**
|
||||
* @return the dialog fragment.
|
||||
*/
|
||||
public static SelectSpecificDataSimDialogFragment newInstance() {
|
||||
final SelectSpecificDataSimDialogFragment
|
||||
fragment = new SelectSpecificDataSimDialogFragment();
|
||||
final Bundle args = initArguments(SimDialogActivity.DATA_PICK,
|
||||
R.string.select_specific_sim_for_data_title);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
final AlertDialog dialog = new AlertDialog.Builder(getContext())
|
||||
.setNegativeButton(R.string.sim_action_no_thanks, null)
|
||||
.create();
|
||||
updateDialog(dialog);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int buttonClicked) {
|
||||
if (buttonClicked != DialogInterface.BUTTON_POSITIVE) {
|
||||
return;
|
||||
}
|
||||
final SimDialogActivity activity = (SimDialogActivity) getActivity();
|
||||
final SubscriptionInfo info = getTargetSubscriptionInfo();
|
||||
if (info != null) {
|
||||
activity.onSubscriptionSelected(getDialogType(), info.getSubscriptionId());
|
||||
}
|
||||
}
|
||||
|
||||
private SubscriptionInfo getNonDefaultDataSubscriptionInfo(SubscriptionInfo dds) {
|
||||
List<SubscriptionInfo> subInfos = getSubscriptionManager().getActiveSubscriptionInfoList();
|
||||
if (subInfos == null || dds == null) {
|
||||
return null;
|
||||
}
|
||||
return subInfos.stream().filter(subinfo -> subinfo != dds).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private SubscriptionInfo getDefaultDataSubId() {
|
||||
return getSubscriptionManager().getDefaultDataSubscriptionInfo();
|
||||
}
|
||||
|
||||
private void updateDialog(AlertDialog dialog) {
|
||||
Log.d(TAG, "Dialog updated, dismiss status: " + mWasDismissed);
|
||||
if (mWasDismissed) {
|
||||
return;
|
||||
}
|
||||
|
||||
SubscriptionInfo activeSubInfo = getDefaultDataSubId();
|
||||
SubscriptionInfo newSubInfo = getNonDefaultDataSubscriptionInfo(activeSubInfo);
|
||||
|
||||
if (newSubInfo == null || activeSubInfo == null) {
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
setTargetSubscriptionInfo(newSubInfo);
|
||||
|
||||
CharSequence newDataCarrierName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
|
||||
newSubInfo, getContext());
|
||||
CharSequence currentDataCarrierName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
|
||||
activeSubInfo, getContext());
|
||||
|
||||
String positive = getContext().getString(
|
||||
R.string.select_specific_sim_for_data_button, newDataCarrierName);
|
||||
String message = getContext().getString(R.string.select_specific_sim_for_data_msg,
|
||||
newDataCarrierName, currentDataCarrierName);
|
||||
|
||||
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 ListView lvItems = content.findViewById(R.id.carrier_list);
|
||||
if (lvItems != null) {
|
||||
lvItems.setVisibility(View.GONE);
|
||||
}
|
||||
final LinearLayout infoOutline = content.findViewById(R.id.info_outline_layout);
|
||||
if (infoOutline != null) {
|
||||
infoOutline.setVisibility(View.GONE);
|
||||
}
|
||||
dialog.setView(content);
|
||||
|
||||
View titleView = LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.sim_confirm_dialog_title_multiple_enabled_profiles_supported, null);
|
||||
TextView titleTextView = titleView.findViewById(R.id.title);
|
||||
titleTextView.setText(getContext().getString(getTitleResId(), newDataCarrierName));
|
||||
|
||||
dialog.setCustomTitle(titleTextView);
|
||||
dialog.setButton(AlertDialog.BUTTON_POSITIVE, positive, this);
|
||||
}
|
||||
|
||||
private void setTargetSubscriptionInfo(SubscriptionInfo subInfo) {
|
||||
mSubscriptionInfo = subInfo;
|
||||
}
|
||||
|
||||
private SubscriptionInfo getTargetSubscriptionInfo() {
|
||||
return mSubscriptionInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDialog() {
|
||||
updateDialog((AlertDialog) getDialog());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected SubscriptionManager getSubscriptionManager() {
|
||||
return getContext().getSystemService(SubscriptionManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.DIALOG_PREFERRED_SIM_PICKER;
|
||||
}
|
||||
}
|
@@ -95,15 +95,16 @@ public class SimDialogActivity extends FragmentActivity {
|
||||
private SimDialogFragment createFragment(int dialogType) {
|
||||
switch (dialogType) {
|
||||
case DATA_PICK:
|
||||
return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
|
||||
false /* includeAskEveryTime */);
|
||||
return getDataPickDialogFramgent();
|
||||
case CALLS_PICK:
|
||||
return CallsSimListDialogFragment.newInstance(dialogType,
|
||||
R.string.select_sim_for_calls,
|
||||
true /* includeAskEveryTime */);
|
||||
true /* includeAskEveryTime */,
|
||||
false /* isCancelItemShowed */);
|
||||
case SMS_PICK:
|
||||
return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
|
||||
true /* includeAskEveryTime */);
|
||||
true /* includeAskEveryTime */,
|
||||
false /* isCancelItemShowed */);
|
||||
case PREFERRED_PICK:
|
||||
if (!getIntent().hasExtra(PREFERRED_SIM)) {
|
||||
throw new IllegalArgumentException("Missing required extra " + PREFERRED_SIM);
|
||||
@@ -111,12 +112,23 @@ public class SimDialogActivity extends FragmentActivity {
|
||||
return PreferredSimDialogFragment.newInstance();
|
||||
case SMS_PICK_FOR_MESSAGE:
|
||||
return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
|
||||
false /* includeAskEveryTime */);
|
||||
false /* includeAskEveryTime */,
|
||||
false /* isCancelItemShowed */);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent.");
|
||||
}
|
||||
}
|
||||
|
||||
private SimDialogFragment getDataPickDialogFramgent() {
|
||||
if (SubscriptionManager.getDefaultDataSubscriptionId()
|
||||
== SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
return SimListDialogFragment.newInstance(DATA_PICK, R.string.select_sim_for_data,
|
||||
false /* includeAskEveryTime */,
|
||||
true /* isCancelItemShowed */);
|
||||
}
|
||||
return SelectSpecificDataSimDialogFragment.newInstance();
|
||||
}
|
||||
|
||||
public void onSubscriptionSelected(int dialogType, int subId) {
|
||||
if (getSupportFragmentManager().findFragmentByTag(Integer.toString(dialogType)) == null) {
|
||||
Log.w(TAG, "onSubscriptionSelected ignored because stored fragment was null");
|
||||
@@ -160,9 +172,11 @@ public class SimDialogActivity extends FragmentActivity {
|
||||
final TelephonyManager telephonyManager = getSystemService(
|
||||
TelephonyManager.class).createForSubscriptionId(subId);
|
||||
subscriptionManager.setDefaultDataSubId(subId);
|
||||
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) {
|
||||
final PhoneAccountHandle phoneAccount = subscriptionIdToPhoneAccountHandle(subId);
|
||||
|
@@ -29,7 +29,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -38,7 +38,6 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -52,16 +51,19 @@ public class SimListDialogFragment extends SimDialogFragment implements
|
||||
DialogInterface.OnClickListener {
|
||||
private static final String TAG = "SimListDialogFragment";
|
||||
protected static final String KEY_INCLUDE_ASK_EVERY_TIME = "include_ask_every_time";
|
||||
protected static final String KEY_SHOW_CANCEL_ITEM = "show_cancel_item";
|
||||
private static final int LIST_VIEW_DIVIDER_LINE_WEIGHT = 2;
|
||||
|
||||
protected SelectSubscriptionAdapter mAdapter;
|
||||
@VisibleForTesting
|
||||
List<SubscriptionInfo> mSubscriptions;
|
||||
|
||||
public static SimListDialogFragment newInstance(int dialogType, int titleResId,
|
||||
boolean includeAskEveryTime) {
|
||||
boolean includeAskEveryTime, boolean isCancelItemShowed) {
|
||||
final SimListDialogFragment fragment = new SimListDialogFragment();
|
||||
final Bundle args = initArguments(dialogType, titleResId);
|
||||
args.putBoolean(KEY_INCLUDE_ASK_EVERY_TIME, includeAskEveryTime);
|
||||
args.putBoolean(KEY_SHOW_CANCEL_ITEM, isCancelItemShowed);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
@@ -72,12 +74,20 @@ public class SimListDialogFragment extends SimDialogFragment implements
|
||||
mSubscriptions = new ArrayList<>();
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
builder.setTitle(getTitleResId());
|
||||
View titleView = LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.sim_confirm_dialog_title_multiple_enabled_profiles_supported, null);
|
||||
TextView titleTextView = titleView.findViewById(R.id.title);
|
||||
titleTextView.setText(getContext().getString(getTitleResId()));
|
||||
builder.setCustomTitle(titleTextView);
|
||||
|
||||
mAdapter = new SelectSubscriptionAdapter(builder.getContext(), mSubscriptions);
|
||||
|
||||
setAdapter(builder);
|
||||
final Dialog dialog = builder.create();
|
||||
|
||||
final AlertDialog dialog = builder.create();
|
||||
ListView listView = dialog.getListView();
|
||||
if (listView != null) {
|
||||
listView.setDividerHeight(LIST_VIEW_DIVIDER_LINE_WEIGHT);
|
||||
}
|
||||
updateDialog();
|
||||
return dialog;
|
||||
}
|
||||
@@ -112,10 +122,22 @@ public class SimListDialogFragment extends SimDialogFragment implements
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (getArguments().getBoolean(KEY_INCLUDE_ASK_EVERY_TIME)) {
|
||||
final List<SubscriptionInfo> tmp = new ArrayList<>(currentSubscriptions.size() + 1);
|
||||
boolean includeAskEveryTime = getArguments().getBoolean(KEY_INCLUDE_ASK_EVERY_TIME);
|
||||
boolean isCancelItemShowed = getArguments().getBoolean(KEY_SHOW_CANCEL_ITEM);
|
||||
if (includeAskEveryTime || isCancelItemShowed) {
|
||||
int arraySize = currentSubscriptions.size()
|
||||
+ (includeAskEveryTime ? 1 : 0)
|
||||
+ (isCancelItemShowed ? 1 : 0);
|
||||
final List<SubscriptionInfo> tmp = new ArrayList<>(arraySize);
|
||||
if (includeAskEveryTime) {
|
||||
// add the value of 'AskEveryTime' item
|
||||
tmp.add(null);
|
||||
}
|
||||
tmp.addAll(currentSubscriptions);
|
||||
if (isCancelItemShowed) {
|
||||
// add the value of 'Cancel' item
|
||||
tmp.add(null);
|
||||
}
|
||||
currentSubscriptions = tmp;
|
||||
}
|
||||
if (currentSubscriptions.equals(mSubscriptions)) {
|
||||
@@ -177,19 +199,23 @@ public class SimListDialogFragment extends SimDialogFragment implements
|
||||
|
||||
final TextView title = convertView.findViewById(R.id.title);
|
||||
final TextView summary = convertView.findViewById(R.id.summary);
|
||||
final ImageView icon = convertView.findViewById(R.id.icon);
|
||||
|
||||
if (sub == null) {
|
||||
if (position == 0) {
|
||||
title.setText(R.string.sim_calls_ask_first_prefs_title);
|
||||
summary.setText("");
|
||||
icon.setImageDrawable(mContext.getDrawable(R.drawable.ic_feedback_24dp));
|
||||
icon.setImageTintList(
|
||||
Utils.getColorAttr(mContext, android.R.attr.textColorSecondary));
|
||||
} else {
|
||||
title.setText(R.string.sim_action_cancel);
|
||||
}
|
||||
summary.setVisibility(View.GONE);
|
||||
} else {
|
||||
title.setText(SubscriptionUtil.getUniqueSubscriptionDisplayName(sub, mContext));
|
||||
summary.setText(isMdnProvisioned(sub.getNumber()) ? sub.getNumber() : "");
|
||||
icon.setImageBitmap(sub.createIconBitmap(mContext));
|
||||
|
||||
String phoneNumber = isMdnProvisioned(sub.getNumber()) ? sub.getNumber() : "";
|
||||
if (!TextUtils.isEmpty(phoneNumber)) {
|
||||
summary.setVisibility(View.VISIBLE);
|
||||
summary.setText(phoneNumber);
|
||||
} else {
|
||||
summary.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -53,7 +53,8 @@ public class SimListDialogFragmentTest extends SimDialogFragmentTestBase<SimList
|
||||
final int dialogType = DATA_PICK;
|
||||
setDialogType(dialogType);
|
||||
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
|
||||
false /* includeAskEveryTime */));
|
||||
false /* includeAskEveryTime */,
|
||||
false /* isCancelItemShowed */));
|
||||
doReturn(null).when(mFragment).getCurrentSubscriptions();
|
||||
startDialog();
|
||||
verify(mFragment).dismiss();
|
||||
@@ -64,7 +65,8 @@ public class SimListDialogFragmentTest extends SimDialogFragmentTestBase<SimList
|
||||
final int dialogType = DATA_PICK;
|
||||
setDialogType(dialogType);
|
||||
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
|
||||
false /* includeAskEveryTime */));
|
||||
false /* includeAskEveryTime */,
|
||||
false /* isCancelItemShowed */));
|
||||
doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
|
||||
// Avoid problems robolectric has with our real adapter.
|
||||
doNothing().when(mFragment).setAdapter(any());
|
||||
@@ -84,7 +86,8 @@ public class SimListDialogFragmentTest extends SimDialogFragmentTestBase<SimList
|
||||
final int dialogType = DATA_PICK;
|
||||
setDialogType(dialogType);
|
||||
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
|
||||
false /* includeAskEveryTime */));
|
||||
false /* includeAskEveryTime */,
|
||||
false /* isCancelItemShowed */));
|
||||
doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
|
||||
// Avoid problems robolectric has with our real adapter.
|
||||
doNothing().when(mFragment).setAdapter(any());
|
||||
@@ -101,7 +104,8 @@ public class SimListDialogFragmentTest extends SimDialogFragmentTestBase<SimList
|
||||
final int dialogType = SMS_PICK;
|
||||
setDialogType(dialogType);
|
||||
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
|
||||
true /* includeAskEveryTime */));
|
||||
true /* includeAskEveryTime */,
|
||||
false /* isCancelItemShowed */));
|
||||
doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
|
||||
// Avoid problems robolectric has with our real adapter.
|
||||
doNothing().when(mFragment).setAdapter(any());
|
||||
|
Reference in New Issue
Block a user