Snap for 7117025 from 428273765b to sc-v2-release

Change-Id: I501eb08d54ebdac9f1ee30d072d763fdf1fde5ad
This commit is contained in:
android-build-team Robot
2021-02-02 02:11:40 +00:00
36 changed files with 1413 additions and 176 deletions

View File

@@ -517,12 +517,12 @@
<activity android:name=".network.telephony.ToggleSubscriptionDialogActivity"
android:exported="false"
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
android:theme="@style/Transparent" />
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight" />
<activity android:name=".network.telephony.DeleteEuiccSubscriptionDialogActivity"
android:exported="false"
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
android:theme="@style/Transparent" />
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight" />
<activity
android:name="Settings$TetherSettingsActivity"
@@ -3669,6 +3669,26 @@
</intent-filter>
</receiver>
<activity
android:name=".sim.ChooseSimActivity"
android:theme="@style/GlifV3Theme.DayNight.NoActionBar"
android:launchMode="singleInstance"
android:exported="false"/>
<activity
android:name=".sim.SwitchToEsimConfirmDialogActivity"
android:exported="false"
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
android:launchMode="singleInstance"
android:theme="@style/Transparent" />
<activity
android:name=".sim.DsdsDialogActivity"
android:exported="false"
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
android:launchMode="singleInstance"
android:theme="@style/Transparent" />
<service android:name=".sim.SimNotificationService"
android:permission="android.permission.BIND_JOB_SERVICE" />

View File

@@ -0,0 +1,37 @@
<?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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z" />
<path
android:fillColor="@color/homepage_generic_icon_background"
android:pathData="M24,5.33h1.33c0.74,0,1.33,0.6,1.33,1.33v18.67c0,0.74-0.6,1.33-1.33,1.33H24c-0.74,0-1.33-0.6-1.33-1.33
V6.67C22.67,5.93,23.26,5.33,24,5.33z" />
<path
android:fillColor="@color/homepage_generic_icon_background"
android:pathData="M8,18.67h1.33c0.74,0,1.33,0.6,1.33,1.33v5.33c0,0.74-0.6,1.33-1.33,1.33H8c-0.74,0-1.33-0.6-1.33-1.33V20
C6.67,19.26,7.26,18.67,8,18.67z" />
<path
android:fillColor="@color/homepage_generic_icon_background"
android:pathData="M16,12h1.33c0.74,0,1.33,0.6,1.33,1.33v12c0,0.74-0.6,1.33-1.33,1.33H16c-0.74,0-1.33-0.6-1.33-1.33v-12
C14.67,12.6,15.26,12,16,12z" />
</vector>

View File

@@ -0,0 +1,48 @@
<?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.
-->
<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/glif_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/ic_network_signal_blue">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/subtitle_bottom_padding">
<TextView
android:id="@+id/subtitle"
style="@style/SudDescription.Glif"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<com.google.android.setupdesign.GlifRecyclerLayout
android:id="@+id/recycler_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@xml/items_multiple_carrier" />
</LinearLayout>
</com.google.android.setupdesign.GlifLayout>

View File

@@ -21,6 +21,7 @@
<style name="GlifTheme.DayNight" parent="GlifTheme" />
<style name="GlifV2Theme.DayNight" parent="GlifV2Theme" />
<style name="GlifV3Theme.DayNight" parent="GlifV3Theme" />
<style name="GlifV3Theme.DayNight.NoActionBar" parent="GlifV3Theme.NoActionBar" />
<style name="GlifV2Theme.DayNight.Transparent" parent="GlifV2Theme.Transparent" />
<style name="GlifV3Theme.DayNight.Transparent" parent="GlifV3Theme.Transparent" />
<style name="SetupWizardTheme.DayNight.Transparent" parent="SetupWizardTheme.Transparent" />

View File

@@ -444,4 +444,7 @@
<!-- Text padding for EmptyTextSettings -->
<dimen name="empty_text_padding">24dp</dimen>
<!-- Choose SIM Activity dimens -->
<dimen name="subtitle_bottom_padding">24dp</dimen>
</resources>

View File

@@ -9958,6 +9958,10 @@
select what the USB connection for this device should be used for. This choice
is for transferring photos via PTP. -->
<string name="usb_use_photo_transfers">PTP</string>
<!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for transcoding the files that are transferred via MTP. -->
<string name="usb_transcode_files">Transcode exported media</string>
<!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for transferring photos via PTP. -->
@@ -9986,6 +9990,9 @@
<!-- The title used in USB Preferences which provides the user with the control over this
device's power role. -->
<string name="usb_power_title">Power options</string>
<!-- The title used in USB Preferences which lets the user control the options for the file
transfer mode. -->
<string name="usb_file_transfer_title">File transfer options</string>
<!-- Settings item title for USB preference [CHAR LIMIT=35] -->
<string name="usb_pref">USB</string>
@@ -12129,6 +12136,34 @@
<string name="post_dsds_reboot_notification_title_with_carrier"><xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> is active</string>
<!-- The body text of post DSDS reboot notification. [CHAR LIMIT=NONE] -->
<string name="post_dsds_reboot_notification_text">Tap to update SIM settings</string>
<!-- Title on a push notification indicating that the user's device switched to a new mobile network. [CHAR LIMIT=NONE] -->
<string name="switch_to_removable_notification">Switched to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
<!-- Title on a push notification indicating that the user's device switched to a new mobile network. [CHAR LIMIT=NONE] -->
<string name="switch_to_removable_notification_no_carrier_name">Switched to another carrier</string>
<!-- Message in a push notification indicating that the user's phone has connected to a different mobile network. [CHAR LIMIT=NONE] -->
<string name="network_changed_notification_text">Your mobile network has changed</string>
<!-- Strings for choose SIM activity -->
<!-- The title text of choose SIM activity. [CHAR LIMIT=NONE] -->
<string name="choose_sim_title">Choose a number to use</string>
<!-- The body text of choose SIM activity. [CHAR LIMIT=NONE] -->
<string name="choose_sim_text"><xliff:g id="number" example="2">%1$d</xliff:g> numbers are available on this device, but only one can be used at a time</string>
<!-- String indicating that we are activating the profile [CHAR LIMIT=NONE] -->
<string name="choose_sim_activating">Activating<xliff:g id="ellipsis" example="...">&#8230;</xliff:g></string>
<!-- String indicating that we failed to activate the selected profile [CHAR LIMIT=NONE] -->
<string name="choose_sim_could_not_activate">Couldn\u2019t be activated right now</string>
<!-- String indicating that the number for the specified profile is unknown [CHAR LIMIT=NONE] -->
<string name="choose_sim_item_summary_unknown">Unknown number</string>
<!-- Strings for switch SIM confirmation dialog. -->
<!-- The title text of switch SIM confirmation dialog. [CHAR LIMIT=NONE] -->
<string name="switch_sim_dialog_title">Use <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
<!-- The body text of switch SIM confirmation dialog. [CHAR LIMIT=NONE] -->
<string name="switch_sim_dialog_text"><xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> will be used for mobile data, calls, and SMS.</string>
<!-- The title text of skip sim switch dialog. [CHAR LIMIT=NONE] -->
<string name="switch_sim_dialog_no_switch_title">No active SIMs available</string>
<!-- The body text of skip sim switch dialog. [CHAR LIMIT=NONE] -->
<string name="switch_sim_dialog_no_switch_text">To use mobile data, call features, and SMS at a later time, go to your network settings</string>
<!-- Button label of the removable sim card. [CHAR LIMIT=NONE] -->
<string name="sim_card_label">SIM card</string>
@@ -12323,7 +12358,7 @@
<string name="backup_calling_settings_title">Backup calling</string>
<!-- Backup calling summary. [CHAR LIMIT=100] -->
<string name="backup_calling_setting_summary">If <xliff:g id="backup_calling_operator_text" example="Google Fi">%1$s</xliff:g> is unavailable, use your mobile data SIM to make and receive <xliff:g id="backup_calling_carrier_text" example="Google Fi">%1$s</xliff:g> calls.</string>
<string name="backup_calling_setting_summary">If <xliff:g id="backup_calling_operator_text" example="Google Fi">%1$s</xliff:g> is unavailable or roaming, use your mobile data SIM for <xliff:g id="backup_calling_carrier_text" example="Google Fi">%1$s</xliff:g> calls.</string>
<!-- List of synonyms for the cross SIM calling titles, used to match in settings search [CHAR LIMIT=NONE] -->
<string name="keywords_backup_calling">backup calling</string>
@@ -12519,19 +12554,20 @@
<!-- Provider Model: Summary for calling preference -->
<string name="calls_sms_wfc_summary">Make and receive calls over Wi\u2011Fi</string>
<!-- Provider Model: Label for footnote on calling preference -->
<string name="calls_sms_footnote">With Wi\u2011Fi calling, calls are made and received over non-carrier Wi\u2011Fi networks.</string>
<string name="calls_sms_footnote">With Wi\u2011Fi calling, calls are made and received over non\u2011carrier Wi\u2011Fi networks.
<annotation id="url">Learn more</annotation></string>
<!-- Provider Model: Calls preference title -->
<string name="calls_preference_title">Calls</string>
<!-- Provider Model: SMS preference title -->
<string name="sms_preference_title">SMS</string>
<!-- Provider Model: Preferred status in summary for Calls & SMS -->
<string name="calls_sms_preferred">preferred</string>
<string name="calls_sms_preferred">Preferred for calls &amp; SMS</string>
<!-- Provider Model: Calls Preferred status in summary for Calls & SMS -->
<string name="calls_sms_calls_preferred">calls preferred</string>
<string name="calls_sms_calls_preferred">Preferred for calls</string>
<!-- Provider Model: SMS Preferred status in summary for Calls & SMS -->
<string name="calls_sms_sms_preferred">SMS preferred</string>
<string name="calls_sms_sms_preferred">Preferred for SMS</string>
<!-- Provider Model: Unavailable status in summary for Calls & SMS -->
<string name="calls_sms_unavailable">unavailable</string>
<string name="calls_sms_unavailable">Temporarily unavailable</string>
<!-- Provider Model: No SIM status in summary for Calls & SMS -->
<string name="calls_sms_no_sim">No SIM</string>
<!-- Network & internet preferences title [CHAR LIMIT=NONE] -->

View File

@@ -133,6 +133,14 @@
<item name="*android:lockPatternStyle">@style/LockPatternStyle.Setup</item>
</style>
<style name="GlifV3Theme.Light.NoActionBar" parent="GlifV3Theme.Light">
<item name="android:windowActionBar">false</item>
</style>
<style name="GlifV3Theme.NoActionBar" parent="GlifV3Theme">
<item name="android:windowActionBar">false</item>
</style>
<style name="GlifV2Theme.Transparent">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
@@ -216,6 +224,7 @@
<style name="GlifTheme.DayNight" parent="GlifTheme.Light" />
<style name="GlifV2Theme.DayNight" parent="GlifV2Theme.Light" />
<style name="GlifV3Theme.DayNight" parent="GlifV3Theme.Light" />
<style name="GlifV3Theme.DayNight.NoActionBar" parent="GlifV3Theme.Light.NoActionBar" />
<style name="GlifV2Theme.DayNight.Transparent" parent="GlifV2Theme.Light.Transparent" />
<style name="GlifV3Theme.DayNight.Transparent" parent="GlifV3Theme.Light.Transparent" />
<style name="SetupWizardTheme.DayNight.Transparent" parent="SetupWizardTheme.Light.Transparent" />

View File

@@ -25,14 +25,18 @@
android:layout="@layout/settings_entity_header"
android:selectable="false"
android:order="-10000"
settings:allowDividerBelow="true"
settings:controller="com.android.settings.security.CredentialManagementAppHeaderController"/>
<com.android.settingslib.widget.TopIntroPreference
android:key="top_intro_request_manage_credentials"
android:order="-9999"
android:title="@string/request_manage_credentials_description"/>
<!-- Buttons -->
<com.android.settingslib.widget.ActionButtonsPreference
android:key="buttons"
android:selectable="true"
android:order="-9999"
android:order="-9998"
settings:allowDividerAbove="true"
settings:allowDividerBelow="true"
settings:controller="com.android.settings.security.CredentialManagementAppButtonsController"/>
@@ -42,6 +46,7 @@
android:key="authentication_policy"
android:layout="@layout/preference_category_no_label"
android:title="@string/summary_placeholder"
settings:allowDividerAbove="true"
settings:controller="com.android.settings.security.CredentialManagementAppPolicyController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,16 @@
<?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.
-->
<ItemGroup xmlns:android="http://schemas.android.com/apk/res/android" />

View File

@@ -56,6 +56,7 @@
android:key="provider_model_calls_sms_footer"
android:title="@string/calls_sms_footnote"
android:selectable="false"
settings:allowDividerAbove="true"
settings:searchable="false"
/>
settings:controller="com.android.settings.network.telephony.NetworkProviderWfcFooterPreferenceController"/>
</PreferenceScreen>

View File

@@ -33,8 +33,11 @@
android:key="usb_details_functions"
android:title="@string/usb_use"/>
<PreferenceCategory
android:key="usb_transcode_mtp"
android:title="@string/usb_file_transfer_title"/>
<PreferenceCategory
android:key="usb_details_power_role"
android:title="@string/usb_power_title"/>
</PreferenceScreen>

View File

@@ -92,6 +92,7 @@ public class UsbDetailsFragment extends DashboardFragment {
ret.add(new UsbDetailsDataRoleController(context, fragment, usbBackend));
ret.add(new UsbDetailsFunctionsController(context, fragment, usbBackend));
ret.add(new UsbDetailsPowerRoleController(context, fragment, usbBackend));
ret.add(new UsbDetailsTranscodeMtpController(context, fragment, usbBackend));
return ret;
}

View File

@@ -0,0 +1,95 @@
/*
* 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.connecteddevice.usb;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import android.content.Context;
import android.hardware.usb.UsbManager;
import android.os.SystemProperties;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.Utils;
/**
* This class controls the switch for setting if we should transcode files transferred via MTP over
* USB.
*/
public class UsbDetailsTranscodeMtpController extends UsbDetailsController
implements Preference.OnPreferenceClickListener {
private static final String TRANSCODE_MTP_SYS_PROP_KEY = "sys.fuse.transcode_mtp";
private static final String PREFERENCE_KEY = "usb_transcode_mtp";
private PreferenceCategory mPreferenceCategory;
private SwitchPreference mSwitchPreference;
public UsbDetailsTranscodeMtpController(Context context, UsbDetailsFragment fragment,
UsbBackend backend) {
super(context, fragment, backend);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceCategory = screen.findPreference(getPreferenceKey());
mSwitchPreference = new SwitchPreference(mPreferenceCategory.getContext());
mSwitchPreference.setTitle(R.string.usb_transcode_files);
mSwitchPreference.setOnPreferenceClickListener(this);
mPreferenceCategory.addPreference(mSwitchPreference);
}
@Override
protected void refresh(boolean connected, long functions, int powerRole, int dataRole) {
if (mUsbBackend.areFunctionsSupported(UsbManager.FUNCTION_MTP | UsbManager.FUNCTION_PTP)) {
mFragment.getPreferenceScreen().addPreference(mPreferenceCategory);
} else {
mFragment.getPreferenceScreen().removePreference(mPreferenceCategory);
}
mSwitchPreference.setChecked(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, true));
mPreferenceCategory.setEnabled(
connected && isDeviceInFileTransferMode(functions, dataRole));
}
@Override
public boolean onPreferenceClick(Preference preference) {
SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY,
Boolean.toString(mSwitchPreference.isChecked()));
return true;
}
@Override
public boolean isAvailable() {
return !Utils.isMonkeyRunning();
}
@Override
public String getPreferenceKey() {
return PREFERENCE_KEY;
}
private static boolean isDeviceInFileTransferMode(long functions, int dataRole) {
return dataRole == DATA_ROLE_DEVICE && ((functions & UsbManager.FUNCTION_MTP) != 0
|| (functions & UsbManager.FUNCTION_PTP) != 0);
}
}

View File

@@ -21,6 +21,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotInfo;
@@ -46,8 +47,7 @@ public class EnableMultiSimSidecar extends AsyncTaskSidecar<Void, Boolean> {
// Tags
private static final String TAG = "EnableMultiSimSidecar";
// TODO(b/171846124): Pass timeout value from LPA to Settings
private static final long ENABLE_MULTI_SIM_TIMEOUT_MILLS = 40 * 1000L;
private static final long DEFAULT_ENABLE_MULTI_SIM_TIMEOUT_MILLS = 40 * 1000L;
public static EnableMultiSimSidecar get(FragmentManager fm) {
return SidecarFragment.get(fm, TAG, EnableMultiSimSidecar.class, null /* args */);
@@ -77,7 +77,7 @@ public class EnableMultiSimSidecar extends AsyncTaskSidecar<Void, Boolean> {
TAG,
String.format(
"%d slots are active and %d SIMs are ready. Keep waiting until"
+ " timeout.",
+ " timeout.",
activeSlotsCount, readySimsCount));
}
};
@@ -123,8 +123,12 @@ public class EnableMultiSimSidecar extends AsyncTaskSidecar<Void, Boolean> {
mCarrierConfigChangeReceiver,
new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
mTelephonyManager.switchMultiSimConfig(mNumOfActiveSim);
if (mSimCardStateChangedLatch.await(
ENABLE_MULTI_SIM_TIMEOUT_MILLS, TimeUnit.MILLISECONDS)) {
long waitingTimeMillis =
Settings.Global.getLong(
getContext().getContentResolver(),
Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
DEFAULT_ENABLE_MULTI_SIM_TIMEOUT_MILLS);
if (mSimCardStateChangedLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
Log.i(TAG, "Multi SIM were successfully enabled.");
return true;
} else {

View File

@@ -31,6 +31,7 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import androidx.annotation.IdRes;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
@@ -42,6 +43,7 @@ import com.android.settings.widget.SummaryUpdater;
import com.android.settings.wifi.WifiSummaryUpdater;
import com.android.settingslib.Utils;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.utils.ThreadUtils;
import java.util.HashMap;
import java.util.Map;
@@ -60,7 +62,8 @@ public class InternetPreferenceController extends AbstractPreferenceController i
private InternetUpdater mInternetUpdater;
private @InternetUpdater.InternetType int mInternetType;
private static Map<Integer, Integer> sIconMap = new HashMap<>();
@VisibleForTesting
static Map<Integer, Integer> sIconMap = new HashMap<>();
static {
sIconMap.put(INTERNET_APM, R.drawable.ic_airplanemode_active);
sIconMap.put(INTERNET_APM_NETWORKS, R.drawable.ic_airplane_safe_networks_24dp);
@@ -147,8 +150,13 @@ public class InternetPreferenceController extends AbstractPreferenceController i
* @param internetType the internet type
*/
public void onInternetTypeChanged(@InternetUpdater.InternetType int internetType) {
final boolean needUpdate = (internetType != mInternetType);
mInternetType = internetType;
updateState(mPreference);
if (needUpdate) {
ThreadUtils.postOnMainThread(() -> {
updateState(mPreference);
});
}
}
@Override
@@ -158,13 +166,17 @@ public class InternetPreferenceController extends AbstractPreferenceController i
}
}
private void updateCellularSummary() {
@VisibleForTesting
void updateCellularSummary() {
final SubscriptionManager subscriptionManager =
mContext.getSystemService(SubscriptionManager.class);
if (subscriptionManager == null) {
return;
}
SubscriptionInfo subInfo = subscriptionManager.getDefaultDataSubscriptionInfo();
if (subInfo == null) {
return;
}
mPreference.setSummary(subInfo.getDisplayName());
}
}

View File

@@ -27,6 +27,7 @@ import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.network.telephony.CallsDefaultSubscriptionController;
import com.android.settings.network.telephony.NetworkProviderBackupCallingPreferenceController;
import com.android.settings.network.telephony.NetworkProviderWfcFooterPreferenceController;
import com.android.settings.network.telephony.NetworkProviderWifiCallingPreferenceController;
import com.android.settings.network.telephony.SmsDefaultSubscriptionController;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -45,6 +46,9 @@ public class NetworkProviderCallsSmsFragment extends DashboardFragment {
@VisibleForTesting
static final String KEY_PREFERENCE_CATEGORY_BACKUP_CALLING =
"provider_model_backup_calling_category";
static final String KEY_PREFERENCE_CATEGORY_WFC_FOOTER =
"provider_model_calls_sms_footer";
@VisibleForTesting
static final String KEY_PREFERENCE_CALLS= "provider_model_calls_preference";
@VisibleForTesting
@@ -70,6 +74,12 @@ public class NetworkProviderCallsSmsFragment extends DashboardFragment {
backupCallingPrefCtrl.init(getSettingsLifecycle());
controllers.add(backupCallingPrefCtrl);
NetworkProviderWfcFooterPreferenceController wfcFooterPreferenceController =
new NetworkProviderWfcFooterPreferenceController(context,
KEY_PREFERENCE_CATEGORY_WFC_FOOTER);
wfcFooterPreferenceController.init(getSettingsLifecycle());
controllers.add(wfcFooterPreferenceController);
return controllers;
}

View File

@@ -131,8 +131,9 @@ public class ProviderModelSlice extends WifiSlice {
}
// Third section: Add the Wi-Fi items which are not connected.
if (wifiList != null) {
log("get Wi-Fi items which are not connected");
if (wifiList != null && wifiList.size() > 0) {
log("get Wi-Fi items which are not connected. Wi-Fi items : " + wifiList.size());
final List<WifiSliceItem> disconnectedWifiList = wifiList.stream()
.filter(wifiSliceItem -> wifiSliceItem.getConnectedState()
!= WifiEntry.CONNECTED_STATE_CONNECTED)
@@ -149,8 +150,8 @@ public class ProviderModelSlice extends WifiSlice {
// 2) show all_network_unavailable:
// - while no wifi item + no carrier
// - while no wifi item + no data capability
if (worker == null || wifiList == null) {
log("wifiList is null");
if (worker == null || wifiList == null || wifiList.size() == 0) {
log("no wifi item");
int resId = R.string.non_carrier_network_unavailable;
if (!hasCarrier || !mHelper.isDataSimActive()) {
log("No carrier item or no carrier data.");

View File

@@ -175,7 +175,7 @@ public class ProviderModelSliceHelper {
}
protected boolean isDataSimActive() {
return MobileNetworkUtils.activeNetworkIsCellular(mContext);
return isNoCarrierData() ? false : MobileNetworkUtils.activeNetworkIsCellular(mContext);
}
protected boolean isNoCarrierData() {

View File

@@ -24,15 +24,18 @@ import static com.android.internal.util.CollectionUtils.emptyIfNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.ParcelUuid;
import android.telephony.PhoneNumberUtils;
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 androidx.annotation.VisibleForTesting;
import com.android.internal.telephony.MccTable;
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
import com.android.settingslib.DeviceInfoUtils;
@@ -338,6 +341,9 @@ public class SubscriptionUtil {
@VisibleForTesting
public static CharSequence getUniqueSubscriptionDisplayName(
SubscriptionInfo info, Context context) {
if (info == null) {
return "";
}
return getUniqueSubscriptionDisplayName(info.getSubscriptionId(), context);
}
@@ -514,4 +520,64 @@ public class SubscriptionUtil {
.filter(sub -> sub.isEmbedded() && groupUuid.equals(sub.getGroupUuid()))
.collect(Collectors.toList());
}
/** Returns the formatted phone number of a subscription. */
@Nullable
public static String getFormattedPhoneNumber(
Context context, SubscriptionInfo subscriptionInfo) {
if (subscriptionInfo == null) {
Log.e(TAG, "Invalid subscription.");
return null;
}
TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
String rawPhoneNumber =
telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId());
String countryIso = MccTable.countryCodeForMcc(subscriptionInfo.getMccString());
if (TextUtils.isEmpty(rawPhoneNumber)) {
return null;
}
return PhoneNumberUtils.formatNumber(rawPhoneNumber, countryIso);
}
/**
* Returns the subscription on a removable sim card. The device does not need to be on removable
* slot.
*/
@Nullable
public static SubscriptionInfo getFirstRemovableSubscription(Context context) {
TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
SubscriptionManager subscriptionManager =
context.getSystemService(SubscriptionManager.class);
List<UiccCardInfo> cardInfos = telephonyManager.getUiccCardsInfo();
if (cardInfos == null) {
Log.w(TAG, "UICC cards info list is empty.");
return null;
}
List<SubscriptionInfo> allSubscriptions = subscriptionManager.getAllSubscriptionInfoList();
if (allSubscriptions == null) {
Log.w(TAG, "All subscription info list is empty.");
return null;
}
for (UiccCardInfo cardInfo : cardInfos) {
if (cardInfo == null) {
Log.w(TAG, "Got null card.");
continue;
}
if (!cardInfo.isRemovable()
|| cardInfo.getCardId() == TelephonyManager.UNSUPPORTED_CARD_ID) {
Log.i(TAG, "Skip embedded card or invalid cardId on slot: "
+ cardInfo.getSlotIndex());
continue;
}
Log.i(TAG, "Target removable cardId :" + cardInfo.getCardId());
for (SubscriptionInfo subInfo : allSubscriptions) {
// Match the removable card id with subscription card id.
if (cardInfo.getCardId() == subInfo.getCardId()) {
return subInfo;
}
}
}
return null;
}
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.network;
import android.annotation.IntDef;
import android.content.Context;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotInfo;
import android.util.Log;
@@ -35,8 +36,8 @@ public class UiccSlotUtil {
private static final String TAG = "UiccSlotUtil";
// TODO(b/171846124): Pass timeout value from LPA to Settings
private static final long WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25000;
private static final long DEFAULT_WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25 * 1000L;
;
public static final int INVALID_PHYSICAL_SLOT_ID = -1;
@@ -115,12 +116,17 @@ public class UiccSlotUtil {
private static void performSwitchToRemovableSlot(int slotId, Context context)
throws UiccSlotsException {
CarrierConfigChangedReceiver receiver = null;
long waitingTimeMillis =
Settings.Global.getLong(
context.getContentResolver(),
Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
DEFAULT_WAIT_AFTER_SWITCH_TIMEOUT_MILLIS);
try {
CountDownLatch latch = new CountDownLatch(1);
receiver = new CarrierConfigChangedReceiver(latch);
receiver.registerOn(context);
switchSlots(context, slotId);
latch.await(WAIT_AFTER_SWITCH_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
latch.await(waitingTimeMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.e(TAG, "Failed switching to physical slot.", e);

View File

@@ -0,0 +1,77 @@
package com.android.settings.network.telephony;
import android.content.Context;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.utils.AnnotationSpan;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import java.util.List;
public class NetworkProviderWfcFooterPreferenceController extends BasePreferenceController
implements LifecycleObserver {
/**
* Constructor.
*/
public NetworkProviderWfcFooterPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
/**
* Initialize the binding with Lifecycle
*
* @param lifecycle Lifecycle of UI which owns this Preference
*/
public void init(Lifecycle lifecycle) {
lifecycle.addObserver(this);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
if (preference != null) {
// This is necessary to ensure that setting the title to the spannable string returned
// by getFooterText will be accepted. Internally, setTitle does an equality check on
// the spannable string being set to the text already set on the preference. That
// equality check apparently only takes into account the raw text and not and spannables
// that are part of the text. So we clear the title before applying the spannable
// footer to ensure it is accepted.
preference.setTitle("");
preference.setTitle(getFooterText());
}
}
private CharSequence getFooterText() {
final Intent helpIntent = HelpUtils.getHelpIntent(mContext,
mContext.getString(R.string.help_uri_wifi_calling),
mContext.getClass().getName());
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(mContext,
"url", helpIntent);
return AnnotationSpan.linkify(mContext.getText(R.string.calls_sms_footnote), linkInfo);
}
@Override
public int getAvailabilityStatus() {
final SubscriptionManager subscriptionManager =
mContext.getSystemService(SubscriptionManager.class);
final List<SubscriptionInfo> subscriptions = SubscriptionUtil.getActiveSubscriptions(
subscriptionManager);
if (subscriptions.size() >= 1) {
return AVAILABLE;
} else {
return CONDITIONALLY_UNAVAILABLE;
}
}
}

View File

@@ -17,7 +17,6 @@
package com.android.settings.network.telephony;
import android.content.Context;
import android.telephony.CarrierConfigManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;

View File

@@ -177,10 +177,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
showRebootConfirmDialog();
return;
}
Log.i(
TAG,
"Enabling DSDS without rebooting. "
+ getString(R.string.sim_action_enabling_sim_without_carrier_name));
Log.i(TAG, "Enabling DSDS without rebooting.");
showProgressDialog(
getString(R.string.sim_action_enabling_sim_without_carrier_name));
mEnableMultiSimSidecar.run(NUM_OF_SIMS_FOR_DSDS);
@@ -272,7 +269,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
case SidecarFragment.State.ERROR:
mEnableMultiSimSidecar.reset();
Log.i(TAG, "Failed to switch to DSDS without rebooting.");
ProgressDialogFragment.dismiss(getFragmentManager());
dismissProgressDialog();
showErrorDialog(
getString(R.string.dsds_activation_failure_title),
getString(R.string.dsds_activation_failure_body_msg2));
@@ -290,7 +287,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
Log.i(TAG, "DSDS enabled, start to enable pSIM profile.");
handleTogglePsimAction();
ProgressDialogFragment.dismiss(getFragmentManager());
dismissProgressDialog();
finish();
}

View File

@@ -25,6 +25,7 @@ import android.os.RemoteException;
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
@@ -54,7 +55,6 @@ public class CredentialManagementAppHeaderController extends BasePreferenceContr
}
private final PackageManager mPackageManager;
private boolean mHasCredentialManagerPackage;
private String mCredentialManagerPackageName;
@Override
@@ -69,7 +69,6 @@ public class CredentialManagementAppHeaderController extends BasePreferenceContr
mExecutor.execute(() -> {
try {
IKeyChainService service = KeyChain.bind(mContext).getService();
mHasCredentialManagerPackage = service.hasCredentialManagementApp();
mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
} catch (InterruptedException | RemoteException e) {
Log.e(TAG, "Unable to display credential management app header");
@@ -80,23 +79,21 @@ public class CredentialManagementAppHeaderController extends BasePreferenceContr
private void displayHeader(PreferenceScreen screen) {
LayoutPreference headerPref = screen.findPreference(getPreferenceKey());
ImageView mAppIconView = headerPref.findViewById(R.id.entity_header_icon);
TextView mTitleView = headerPref.findViewById(R.id.entity_header_title);
TextView mDescriptionView = headerPref.findViewById(R.id.entity_header_summary);
ImageView appIconView = headerPref.findViewById(R.id.entity_header_icon);
TextView titleView = headerPref.findViewById(R.id.entity_header_title);
TextView summary1 = headerPref.findViewById(R.id.entity_header_summary);
TextView summary2 = headerPref.findViewById(R.id.entity_header_second_summary);
summary1.setVisibility(View.GONE);
summary2.setVisibility(View.GONE);
try {
ApplicationInfo applicationInfo =
mPackageManager.getApplicationInfo(mCredentialManagerPackageName, 0);
mAppIconView.setImageDrawable(mPackageManager.getApplicationIcon(applicationInfo));
mTitleView.setText(applicationInfo.loadLabel(mPackageManager));
appIconView.setImageDrawable(mPackageManager.getApplicationIcon(applicationInfo));
titleView.setText(applicationInfo.loadLabel(mPackageManager));
} catch (PackageManager.NameNotFoundException e) {
mAppIconView.setImageDrawable(null);
mTitleView.setText(mCredentialManagerPackageName);
appIconView.setImageDrawable(null);
titleView.setText(mCredentialManagerPackageName);
}
// TODO (b/165641221): The description should be multi-lined, which is currently a
// limitation of using Settings entity header. However, the Settings entity header
// should be used to be consistent with the rest of Settings.
mDescriptionView.setText(
mContext.getString(R.string.request_manage_credentials_description));
}
}

View File

@@ -0,0 +1,321 @@
/*
* 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.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.SidecarFragment;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
import com.android.settings.network.SwitchToRemovableSlotSidecar;
import com.android.settings.network.UiccSlotUtil;
import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.GlifRecyclerLayout;
import com.google.android.setupdesign.items.Dividable;
import com.google.android.setupdesign.items.IItem;
import com.google.android.setupdesign.items.Item;
import com.google.android.setupdesign.items.ItemGroup;
import com.google.android.setupdesign.items.RecyclerItemAdapter;
import com.google.android.setupdesign.view.HeaderRecyclerView;
import java.util.ArrayList;
import java.util.List;
/** Activity to show a list of profiles for user to choose. */
public class ChooseSimActivity extends Activity
implements RecyclerItemAdapter.OnItemSelectedListener, SidecarFragment.Listener {
// Whether there is a pSIM profile in the selection list.
public static final String KEY_HAS_PSIM = "has_psim";
// After the user selects eSIM profile, whether continue to show Mobile Network Settings screen
// to select other preferences.
// Note: KEY_NO_PSIM_CONTINUE_TO_SETTINGS and mNoPsimContinueToSettings are not used for now
// for UI changes. We may use them in the future.
public static final String KEY_NO_PSIM_CONTINUE_TO_SETTINGS = "no_psim_continue_to_settings";
private static final String TAG = "ChooseSimActivity";
private static final int INDEX_PSIM = -1;
private static final String STATE_SELECTED_INDEX = "selected_index";
private static final String STATE_IS_SWITCHING = "is_switching";
private boolean mHasPsim;
private boolean mNoPsimContinueToSettings;
private ArrayList<SubscriptionInfo> mEmbeddedSubscriptions = new ArrayList<>();
private SubscriptionInfo mRemovableSubscription = null;
private ItemGroup mItemGroup;
private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
private SwitchToRemovableSlotSidecar mSwitchToRemovableSlotSidecar;
// Variables have states.
private int mSelectedItemIndex;
private boolean mIsSwitching;
/** Returns an intent of {@code ChooseSimActivity} */
public static Intent getIntent(Context context) {
return new Intent(context, ChooseSimActivity.class);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.choose_sim_activity);
Intent intent = getIntent();
mHasPsim = intent.getBooleanExtra(KEY_HAS_PSIM, false);
mNoPsimContinueToSettings = intent.getBooleanExtra(KEY_NO_PSIM_CONTINUE_TO_SETTINGS, false);
updateSubscriptions();
if (mEmbeddedSubscriptions.size() == 0) {
Log.e(TAG, "Unable to find available eSIM subscriptions.");
finish();
return;
}
if (savedInstanceState != null) {
mSelectedItemIndex = savedInstanceState.getInt(STATE_SELECTED_INDEX);
mIsSwitching = savedInstanceState.getBoolean(STATE_IS_SWITCHING);
}
GlifLayout layout = findViewById(R.id.glif_layout);
TextView textView = findViewById(R.id.subtitle);
int subscriptionCount = mEmbeddedSubscriptions.size();
if (mHasPsim) { // Choose a number to use
subscriptionCount++;
}
layout.setHeaderText(getString(R.string.choose_sim_title));
textView.setText(getString(R.string.choose_sim_text, subscriptionCount));
displaySubscriptions();
mSwitchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(getFragmentManager());
mSwitchToEuiccSubscriptionSidecar =
SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
}
@Override
public void onResume() {
super.onResume();
mSwitchToRemovableSlotSidecar.addListener(this);
mSwitchToEuiccSubscriptionSidecar.addListener(this);
}
@Override
public void onPause() {
mSwitchToEuiccSubscriptionSidecar.removeListener(this);
mSwitchToRemovableSlotSidecar.removeListener(this);
super.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_SELECTED_INDEX, mSelectedItemIndex);
outState.putBoolean(STATE_IS_SWITCHING, mIsSwitching);
super.onSaveInstanceState(outState);
}
@Override
public void onItemSelected(IItem item) {
if (mIsSwitching) {
// If we already selected an item, do not try to switch to another one.
return;
}
mIsSwitching = true;
Item subItem = (Item) item;
subItem.setSummary(getString(R.string.choose_sim_activating));
mSelectedItemIndex = subItem.getId();
if (mSelectedItemIndex == INDEX_PSIM) {
Log.i(TAG, "Ready to switch to pSIM slot.");
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID);
} else {
Log.i(TAG, "Ready to switch to eSIM subscription with index: " + mSelectedItemIndex);
mSwitchToEuiccSubscriptionSidecar.run(
mEmbeddedSubscriptions.get(mSelectedItemIndex).getSubscriptionId());
}
}
@Override
public void onStateChange(SidecarFragment fragment) {
if (fragment == mSwitchToRemovableSlotSidecar) {
switch (mSwitchToRemovableSlotSidecar.getState()) {
case SidecarFragment.State.SUCCESS:
mSwitchToRemovableSlotSidecar.reset();
Log.i(TAG, "Switch slot successfully.");
SubscriptionManager subMgr = getSystemService(SubscriptionManager.class);
if (subMgr.canDisablePhysicalSubscription()) {
SubscriptionInfo removableSub =
SubscriptionUtil.getFirstRemovableSubscription(this);
if (removableSub != null) {
subMgr.setUiccApplicationsEnabled(
removableSub.getSubscriptionId(), true);
}
}
finish();
break;
case SidecarFragment.State.ERROR:
mSwitchToRemovableSlotSidecar.reset();
Log.e(TAG, "Failed to switch slot in ChooseSubscriptionsActivity.");
handleEnableRemovableSimError();
// We don't call finish() and just stay on this page.
break;
}
} else if (fragment == mSwitchToEuiccSubscriptionSidecar) {
switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
case SidecarFragment.State.SUCCESS:
mSwitchToEuiccSubscriptionSidecar.reset();
if (mNoPsimContinueToSettings) {
// Currently, there shouldn't be a case that mNoPsimContinueToSettings is
// true. If this can be true in the future, we should finish() this page
// and direct to Settings page here.
Log.e(
TAG,
"mNoPsimContinueToSettings is true which is not supported for"
+ " now.");
} else {
Log.i(TAG, "User finished selecting eSIM profile.");
finish();
}
break;
case SidecarFragment.State.ERROR:
mSwitchToEuiccSubscriptionSidecar.reset();
Log.e(TAG, "Failed to switch subscription in ChooseSubscriptionsActivity.");
Item item = (Item) mItemGroup.getItemAt(mSelectedItemIndex);
item.setEnabled(false);
item.setSummary(getString(R.string.choose_sim_could_not_activate));
mIsSwitching = false;
// We don't call finish() and just stay on this page.
break;
}
}
}
private void displaySubscriptions() {
View rootView = findViewById(android.R.id.content);
GlifRecyclerLayout layout = rootView.findViewById(R.id.recycler_list);
RecyclerItemAdapter adapter = (RecyclerItemAdapter) layout.getAdapter();
adapter.setOnItemSelectedListener(this);
mItemGroup = (ItemGroup) adapter.getRootItemHierarchy();
// Display pSIM profile.
if (mHasPsim) {
Item item = new DisableableItem();
// Title
CharSequence title = null;
if (mRemovableSubscription != null) {
title =
SubscriptionUtil.getUniqueSubscriptionDisplayName(
mRemovableSubscription.getSubscriptionId(), this);
}
item.setTitle(TextUtils.isEmpty(title) ? getString(R.string.sim_card_label) : title);
if (mIsSwitching && mSelectedItemIndex == INDEX_PSIM) {
item.setSummary(getString(R.string.choose_sim_activating));
} else {
// Phone number
String phoneNumber =
SubscriptionUtil.getFormattedPhoneNumber(this, mRemovableSubscription);
item.setSummary(TextUtils.isEmpty(phoneNumber) ? "" : phoneNumber);
}
// pSIM profile has index -1.
item.setId(INDEX_PSIM);
mItemGroup.addChild(item);
}
// Display all eSIM profiles.
int index = 0;
for (SubscriptionInfo sub : mEmbeddedSubscriptions) {
Item item = new DisableableItem();
CharSequence title =
SubscriptionUtil.getUniqueSubscriptionDisplayName(
sub.getSubscriptionId(), this);
item.setTitle(TextUtils.isEmpty(title) ? sub.getDisplayName() : title);
if (mIsSwitching && mSelectedItemIndex == index) {
item.setSummary(getString(R.string.choose_sim_activating));
} else {
String phoneNumber = SubscriptionUtil.getFormattedPhoneNumber(this, sub);
item.setSummary(TextUtils.isEmpty(phoneNumber) ? "" : phoneNumber);
}
item.setId(index++);
mItemGroup.addChild(item);
}
// This removes the unused header artifact from GlifRecyclerLayout.
HeaderRecyclerView rv = (HeaderRecyclerView) layout.getRecyclerView();
rv.getHeader().setVisibility(View.GONE);
}
private void updateSubscriptions() {
List<SubscriptionInfo> subscriptions =
SubscriptionUtil.getSelectableSubscriptionInfoList(this);
if (subscriptions != null) {
for (SubscriptionInfo sub : subscriptions) {
if (sub == null) {
continue;
}
if (sub.isEmbedded()) {
mEmbeddedSubscriptions.add(sub);
} else {
mRemovableSubscription = sub;
}
}
}
}
private void handleEnableRemovableSimError() {
// mSelectedItemIndex will be -1 if pSIM is selected. Since pSIM is always be
// listed at index 0, we change the itemIndex to 0 if pSIM is selected.
int itemIndex = mSelectedItemIndex == INDEX_PSIM ? 0 : mSelectedItemIndex;
Item item = (Item) mItemGroup.getItemAt(itemIndex);
item.setEnabled(false);
item.setSummary(getString(R.string.choose_sim_could_not_activate));
mIsSwitching = false;
}
class DisableableItem extends Item implements Dividable {
@Override
public boolean isDividerAllowedAbove() {
return true;
}
@Override
public boolean isDividerAllowedBelow() {
return true;
}
@Override
public void onBindView(View view) {
super.onBindView(view);
TextView title = view.findViewById(R.id.sud_items_title);
TextView summary = view.findViewById(R.id.sud_items_summary);
title.setEnabled(isEnabled());
summary.setEnabled(isEnabled());
}
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.SidecarFragment;
import com.android.settings.network.EnableMultiSimSidecar;
import com.android.settings.network.telephony.ConfirmDialogFragment;
import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
/** Activity to show the enabling DSDS dialog. */
public class DsdsDialogActivity extends SubscriptionActionDialogActivity
implements SidecarFragment.Listener, ConfirmDialogFragment.OnConfirmListener {
private static final String TAG = "DsdsDialogActivity";
// Dialog tags
private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 1;
private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 2;
// Number of SIMs for DSDS
private static final int NUM_OF_SIMS_FOR_DSDS = 2;
private EnableMultiSimSidecar mEnableMultiSimSidecar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEnableMultiSimSidecar = EnableMultiSimSidecar.get(getFragmentManager());
if (savedInstanceState == null) {
showEnableDsdsConfirmDialog();
}
}
@Override
protected void onResume() {
super.onResume();
mEnableMultiSimSidecar.addListener(this);
}
@Override
protected void onPause() {
mEnableMultiSimSidecar.removeListener(this);
super.onPause();
}
@Override
public void onStateChange(SidecarFragment fragment) {
if (fragment == mEnableMultiSimSidecar) {
switch (fragment.getState()) {
case SidecarFragment.State.SUCCESS:
mEnableMultiSimSidecar.reset();
Log.i(TAG, "Enabled DSDS successfully");
dismissProgressDialog();
finish();
break;
case SidecarFragment.State.ERROR:
mEnableMultiSimSidecar.reset();
Log.e(TAG, "Failed to enable DSDS");
dismissProgressDialog();
showErrorDialog(
getString(R.string.dsds_activation_failure_title),
getString(R.string.dsds_activation_failure_body_msg2));
break;
}
}
}
@Override
public void onConfirm(int tag, boolean confirmed) {
if (!confirmed) {
Log.i(TAG, "User cancel the dialog to enable DSDS.");
startChooseSimActivity();
return;
}
TelephonyManager telephonyManager = getSystemService(TelephonyManager.class);
switch (tag) {
case DIALOG_TAG_ENABLE_DSDS_CONFIRMATION:
if (telephonyManager.doesSwitchMultiSimConfigTriggerReboot()) {
Log.i(TAG, "Device does not support reboot free DSDS.");
showRebootConfirmDialog();
return;
}
Log.i(TAG, "Enabling DSDS without rebooting.");
showProgressDialog(
getString(R.string.sim_action_enabling_sim_without_carrier_name));
mEnableMultiSimSidecar.run(NUM_OF_SIMS_FOR_DSDS);
break;
case DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION:
Log.i(TAG, "User confirmed reboot to enable DSDS.");
SimActivationNotifier.setShowSimSettingsNotification(this, true);
telephonyManager.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
break;
default:
Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
break;
}
}
private void showEnableDsdsConfirmDialog() {
ConfirmDialogFragment.show(
this,
ConfirmDialogFragment.OnConfirmListener.class,
DIALOG_TAG_ENABLE_DSDS_CONFIRMATION,
getString(R.string.sim_action_enable_dsds_title),
getString(R.string.sim_action_enable_dsds_text),
getString(R.string.sim_action_continue),
getString(R.string.sim_action_no_thanks));
}
private void showRebootConfirmDialog() {
ConfirmDialogFragment.show(
this,
ConfirmDialogFragment.OnConfirmListener.class,
DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION,
getString(R.string.sim_action_restart_title),
getString(R.string.sim_action_enable_dsds_text),
getString(R.string.sim_action_reboot),
getString(R.string.cancel));
}
private void startChooseSimActivity() {
Intent intent = ChooseSimActivity.getIntent(this);
intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, true);
startActivity(intent);
finish();
}
}

View File

@@ -26,8 +26,10 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -40,6 +42,8 @@ import com.android.settings.network.SubscriptionUtil;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nullable;
/**
* This class manages the notification of SIM activation notification including creating and
* canceling the notifications.
@@ -48,21 +52,26 @@ public class SimActivationNotifier {
private static final String TAG = "SimActivationNotifier";
private static final String SIM_SETUP_CHANNEL_ID = "sim_setup";
private static final String SWITCH_SLOT_CHANNEL_ID = "carrier_switching";
private static final String SIM_PREFS = "sim_prefs";
private static final String KEY_SHOW_SIM_SETTINGS_NOTIFICATION =
"show_sim_settings_notification";
public static final int SIM_ACTIVATION_NOTIFICATION_ID = 1;
public static final int SWITCH_TO_REMOVABLE_SLOT_NOTIFICATION_ID = 2;
/** Notification types */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
value = {
NotificationType.NETWORK_CONFIG,
NotificationType.SWITCH_TO_REMOVABLE_SLOT,
})
public @interface NotificationType {
// The notification to remind users to config network Settings.
int NETWORK_CONFIG = 1;
// The notification to notify users that the device is switched to the removable slot.
int SWITCH_TO_REMOVABLE_SLOT = 2;
}
private final Context mContext;
@@ -104,13 +113,7 @@ public class SimActivationNotifier {
/** Sends a push notification for the SIM activation. It should be called after DSDS reboot. */
public void sendNetworkConfigNotification() {
SubscriptionManager subscriptionManager =
mContext.getSystemService(SubscriptionManager.class);
SubscriptionInfo activeRemovableSub =
SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
.filter(sub -> !sub.isEmbedded())
.findFirst()
.orElse(null);
SubscriptionInfo activeRemovableSub = getActiveRemovableSub();
if (activeRemovableSub == null) {
Log.e(TAG, "No removable subscriptions found. Do not show notification.");
@@ -143,4 +146,65 @@ public class SimActivationNotifier {
.setAutoCancel(true);
mNotificationManager.notify(SIM_ACTIVATION_NOTIFICATION_ID, builder.build());
}
/** Sends a push notification for switching to the removable slot. */
public void sendSwitchedToRemovableSlotNotification() {
String carrierName = getActiveCarrierName();
Intent clickIntent = new Intent(mContext, Settings.MobileNetworkListActivity.class);
TaskStackBuilder stackBuilder =
TaskStackBuilder.create(mContext).addNextIntent(clickIntent);
PendingIntent contentIntent =
stackBuilder.getPendingIntent(
0 /* requestCode */, PendingIntent.FLAG_UPDATE_CURRENT);
String titleText =
TextUtils.isEmpty(carrierName)
? mContext.getString(
R.string.switch_to_removable_notification_no_carrier_name)
: mContext.getString(
R.string.switch_to_removable_notification, carrierName);
Notification.Builder builder =
new Notification.Builder(mContext, SWITCH_SLOT_CHANNEL_ID)
.setContentTitle(titleText)
.setContentText(
mContext.getString(R.string.network_changed_notification_text))
.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.ic_sim_alert)
.setColor(
mContext.getResources()
.getColor(
R.color.homepage_generic_icon_background,
null /* theme */))
.setAutoCancel(true);
mNotificationManager.notify(SWITCH_TO_REMOVABLE_SLOT_NOTIFICATION_ID, builder.build());
}
@Nullable
private SubscriptionInfo getActiveRemovableSub() {
SubscriptionManager subscriptionManager =
mContext.getSystemService(SubscriptionManager.class);
return SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
.filter(sub -> !sub.isEmbedded())
.findFirst()
.orElse(null);
}
@Nullable
private String getActiveCarrierName() {
CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class);
String telName = telManager.getSimOperatorName();
if (configManager != null && configManager.getConfig() != null) {
boolean override =
configManager
.getConfig()
.getBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL);
String configName =
configManager
.getConfig()
.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
return override || TextUtils.isEmpty(telName) ? configName : telName;
}
return telName;
}
}

View File

@@ -35,6 +35,7 @@ public class SimNotificationService extends JobService {
/**
* Schedules a service to send SIM push notifications.
*
* @param context
* @param notificationType indicates which SIM notification to send.
*/
@@ -67,6 +68,9 @@ public class SimNotificationService extends JobService {
SimActivationNotifier.setShowSimSettingsNotification(this, false);
new SimActivationNotifier(this).sendNetworkConfigNotification();
break;
case SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT:
new SimActivationNotifier(this).sendSwitchedToRemovableSlotNotification();
break;
default:
Log.e(TAG, "Invalid notification type: " + notificationType);
break;

View File

@@ -0,0 +1,119 @@
/*
* 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.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.SidecarFragment;
import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
import com.android.settings.network.telephony.AlertDialogFragment;
import com.android.settings.network.telephony.ConfirmDialogFragment;
import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
/**
* Starts a confirm dialog asking the user to switch to the eSIM slot/subscription. The caller needs
* to pass in the current enabled eSIM subscription, which is also the subscription to switch to.
*/
public class SwitchToEsimConfirmDialogActivity extends SubscriptionActionDialogActivity
implements SidecarFragment.Listener, ConfirmDialogFragment.OnConfirmListener {
public static final String KEY_SUB_TO_ENABLE = "sub_to_enable";
private static final String TAG = "SwitchToEsimConfirmDialogActivity";
private static final int TAG_CONFIRM = 1;
private SubscriptionInfo mSubToEnabled = null;
private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSubToEnabled = getIntent().getParcelableExtra(KEY_SUB_TO_ENABLE);
mSwitchToEuiccSubscriptionSidecar =
SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
if (mSubToEnabled == null) {
Log.e(TAG, "Cannot find SIM to enable.");
finish();
return;
}
if (savedInstanceState == null) {
ConfirmDialogFragment.show(
this,
ConfirmDialogFragment.OnConfirmListener.class,
TAG_CONFIRM,
getString(R.string.switch_sim_dialog_title, mSubToEnabled.getDisplayName()),
getString(R.string.switch_sim_dialog_text, mSubToEnabled.getDisplayName()),
getString(R.string.okay),
getString(R.string.cancel));
}
}
@Override
public void onResume() {
super.onResume();
mSwitchToEuiccSubscriptionSidecar.addListener(this);
}
@Override
public void onPause() {
mSwitchToEuiccSubscriptionSidecar.removeListener(this);
super.onPause();
}
@Override
public void onStateChange(SidecarFragment fragment) {
if (fragment == mSwitchToEuiccSubscriptionSidecar) {
switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
case SidecarFragment.State.SUCCESS:
mSwitchToEuiccSubscriptionSidecar.reset();
Log.i(TAG, "Successfully switched to eSIM slot.");
dismissProgressDialog();
finish();
break;
case SidecarFragment.State.ERROR:
mSwitchToEuiccSubscriptionSidecar.reset();
Log.e(TAG, "Failed switching to eSIM slot.");
dismissProgressDialog();
finish();
break;
}
}
}
@Override
public void onConfirm(int tag, boolean confirmed) {
if (!confirmed) {
AlertDialogFragment.show(
this,
getString(R.string.switch_sim_dialog_no_switch_title),
getString(R.string.switch_sim_dialog_no_switch_text));
return;
}
Log.i(TAG, "User confirmed to switch to embedded slot.");
mSwitchToEuiccSubscriptionSidecar.run(mSubToEnabled.getSubscriptionId());
showProgressDialog(
getString(
R.string.sim_action_switch_sub_dialog_progress,
mSubToEnabled.getDisplayName()));
}
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.sim.receivers;
import static android.content.Context.MODE_PRIVATE;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Looper;
import android.provider.Settings;
@@ -29,6 +30,13 @@ import android.telephony.UiccSlotInfo;
import android.util.Log;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.UiccSlotUtil;
import com.android.settings.network.UiccSlotsException;
import com.android.settings.sim.ChooseSimActivity;
import com.android.settings.sim.DsdsDialogActivity;
import com.android.settings.sim.SimActivationNotifier;
import com.android.settings.sim.SimNotificationService;
import com.android.settings.sim.SwitchToEsimConfirmDialogActivity;
import com.google.common.collect.ImmutableList;
@@ -121,14 +129,13 @@ public class SimSlotChangeHandler {
return;
}
if (!hasActiveEsimSubscription()) {
if (mTelMgr.isMultiSimEnabled()) {
if (hasActiveEsimSubscription()) {
if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
Log.i(TAG, "Enabled profile exists. DSDS condition satisfied.");
// TODO(b/170508680): Display DSDS dialog to ask users whether to enable DSDS.
startDsdsDialogActivity();
} else {
Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied.");
// TODO(b/170508680): Display Choose a number to use screen for subscription
// selection.
startChooseSimActivity(true);
}
return;
}
@@ -137,7 +144,15 @@ public class SimSlotChangeHandler {
TAG,
"No enabled eSIM profile. Ready to switch to removable slot and show"
+ " notification.");
// TODO(b/170508680): Switch the slot to the removebale slot and show the notification.
try {
UiccSlotUtil.switchToRemovableSlot(
UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, mContext.getApplicationContext());
} catch (UiccSlotsException e) {
Log.e(TAG, "Failed to switch to removable slot.");
return;
}
SimNotificationService.scheduleSimNotification(
mContext, SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT);
}
private void handleSimRemove(UiccSlotInfo removableSlotInfo) {
@@ -160,14 +175,14 @@ public class SimSlotChangeHandler {
// profile.
if (groupedEmbeddedSubscriptions.size() == 1) {
Log.i(TAG, "Only 1 eSIM profile found. Ask user's consent to switch.");
// TODO(b/170508680): Display a dialog to ask users to switch.
startSwitchSlotConfirmDialogActivity(groupedEmbeddedSubscriptions.get(0));
return;
}
// If there are more than 1 eSIM profiles installed, we show a screen to let users to choose
// the number they want to use.
Log.i(TAG, "Multiple eSIM profiles found. Ask user which subscription to use.");
// TODO(b/170508680): Display a dialog to ask user which SIM to switch.
startChooseSimActivity(false);
}
private int getLastRemovableSimSlotState(Context context) {
@@ -225,5 +240,25 @@ public class SimSlotChangeHandler {
.collect(Collectors.toList()));
}
private void startChooseSimActivity(boolean psimInserted) {
Intent intent = ChooseSimActivity.getIntent(mContext);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, psimInserted);
mContext.startActivity(intent);
}
private void startSwitchSlotConfirmDialogActivity(SubscriptionInfo subscriptionInfo) {
Intent intent = new Intent(mContext, SwitchToEsimConfirmDialogActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(SwitchToEsimConfirmDialogActivity.KEY_SUB_TO_ENABLE, subscriptionInfo);
mContext.startActivity(intent);
}
private void startDsdsDialogActivity() {
Intent intent = new Intent(mContext, DsdsDialogActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
private SimSlotChangeHandler() {}
}

View File

@@ -48,14 +48,16 @@ public class SimSlotChangeReceiver extends BroadcastReceiver {
return;
}
final PendingResult pendingResult = goAsync();
ThreadUtils.postOnBackgroundThread(
() -> {
synchronized (mLock) {
if (!shouldHandleSlotChange(context)) {
return;
}
mSlotChangeHandler.onSlotsStatusChange(context);
mSlotChangeHandler.onSlotsStatusChange(context.getApplicationContext());
}
ThreadUtils.postOnMainThread(pendingResult::finish);
});
}

View File

@@ -25,6 +25,7 @@ import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.WindowManager;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
@@ -43,6 +44,8 @@ public class WifiScanModeActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addSystemFlags(
WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
Intent intent = getIntent();
if (savedInstanceState == null) {
if (intent != null && WifiManager.ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE

View File

@@ -0,0 +1,178 @@
/*
* 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.connecteddevice.usb;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.usb.UsbManager;
import android.os.SystemProperties;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.testutils.shadow.ShadowUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
public class UsbDetailsTranscodeMtpControllerTest {
private static final String TRANSCODE_MTP_SYS_PROP_KEY = "sys.fuse.transcode_mtp";
private Context mContext;
private PreferenceCategory mPreference;
private PreferenceManager mPreferenceManager;
private PreferenceScreen mScreen;
private UsbDetailsTranscodeMtpController mUnderTest;
@Mock
private UsbBackend mUsbBackend;
@Mock
private UsbDetailsFragment mFragment;
@Mock
private FragmentActivity mActivity;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreferenceManager = new PreferenceManager(mContext);
mScreen = mPreferenceManager.createPreferenceScreen(mContext);
when(mFragment.getActivity()).thenReturn(mActivity);
when(mActivity.getApplicationContext()).thenReturn(mContext);
when(mFragment.getContext()).thenReturn(mContext);
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
mUnderTest = new UsbDetailsTranscodeMtpController(mContext, mFragment, mUsbBackend);
mPreference = new PreferenceCategory(mContext);
mPreference.setKey(mUnderTest.getPreferenceKey());
mScreen.addPreference(mPreference);
}
@Test
public void displayRefresh_noUsbConnection_shouldDisablePrefCategory() {
mUnderTest.displayPreference(mScreen);
when(mUsbBackend.areAllRolesSupported()).thenReturn(true);
mUnderTest.refresh(false /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
DATA_ROLE_NONE);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void displayRefresh_noDataTransfer_shouldDisablePrefCategory() {
mUnderTest.displayPreference(mScreen);
when(mUsbBackend.areAllRolesSupported()).thenReturn(true);
mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_NONE, POWER_ROLE_NONE,
DATA_ROLE_NONE);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void displayRefresh_noDataRole_shouldDisablePrefCategory() throws InterruptedException {
mUnderTest.displayPreference(mScreen);
when(mUsbBackend.areAllRolesSupported()).thenReturn(true);
mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
DATA_ROLE_NONE);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void displayRefresh_fileTransfer_withAbsentProp_shouldCheck() {
mUnderTest.displayPreference(mScreen);
when(mUsbBackend.areAllRolesSupported()).thenReturn(true);
mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
DATA_ROLE_NONE);
assertThat(getSwitchPreference().isChecked()).isTrue();
}
@Test
public void displayRefresh_fileTransfer_withUnsetProp_shouldUncheck() {
mUnderTest.displayPreference(mScreen);
SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, Boolean.toString(false));
when(mUsbBackend.areAllRolesSupported()).thenReturn(true);
mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
DATA_ROLE_NONE);
assertThat(getSwitchPreference().isChecked()).isFalse();
}
@Test
public void displayRefresh_fileTransfer_withSetProp_shouldCheck() {
mUnderTest.displayPreference(mScreen);
SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, Boolean.toString(true));
when(mUsbBackend.areAllRolesSupported()).thenReturn(true);
mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
DATA_ROLE_NONE);
assertThat(getSwitchPreference().isChecked()).isTrue();
}
@Test
public void click_checked_shouldSetSystemProperty() {
mUnderTest.displayPreference(mScreen);
getSwitchPreference().performClick();
assertThat(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, false)).isTrue();
}
@Test
public void click_unChecked_shouldUnsetSystemProperty() {
mUnderTest.displayPreference(mScreen);
getSwitchPreference().performClick();
getSwitchPreference().performClick();
assertThat(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, false)).isFalse();
}
@Test
@Config(shadows = ShadowUtils.class)
public void isAvailable_isMonkey_shouldReturnFalse() {
ShadowUtils.setIsUserAMonkey(true);
assertThat(mUnderTest.isAvailable()).isFalse();
}
private SwitchPreference getSwitchPreference() {
return (SwitchPreference) mPreference.getPreference(0);
}
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright (C) 2017 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.enterprise;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.when;
import android.content.Context;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class EnterpriseSetDefaultAppsListFragmentTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceManager mPreferenceManager;
private EnterpriseSetDefaultAppsListFragment mFragment;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
when(mPreferenceManager.getContext()).thenReturn(mContext);
when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
mFragment = new EnterpriseSetDefaultAppsListFragmentTestable(mPreferenceManager, mScreen);
}
@Test
public void getMetricsCategory() {
assertThat(mFragment.getMetricsCategory())
.isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_DEFAULT_APPS);
}
@Test
public void getLogTag() {
assertThat(mFragment.getLogTag()).isEqualTo("EnterprisePrivacySettings");
}
@Test
public void getScreenResource() {
assertThat(mFragment.getPreferenceScreenResId())
.isEqualTo(R.xml.enterprise_set_default_apps_settings);
}
@Test
public void getPreferenceControllers() {
final List<AbstractPreferenceController> controllers = mFragment.createPreferenceControllers(mContext);
assertThat(controllers).isNotNull();
assertThat(controllers.size()).isEqualTo(1);
assertThat(controllers.get(0))
.isInstanceOf(EnterpriseSetDefaultAppsListPreferenceController.class);
}
private static class EnterpriseSetDefaultAppsListFragmentTestable
extends EnterpriseSetDefaultAppsListFragment {
private final PreferenceManager mPreferenceManager;
private final PreferenceScreen mPreferenceScreen;
private EnterpriseSetDefaultAppsListFragmentTestable(PreferenceManager preferenceManager,
PreferenceScreen screen) {
mPreferenceManager = preferenceManager;
mPreferenceScreen = screen;
}
@Override
public PreferenceManager getPreferenceManager() {
return mPreferenceManager;
}
@Override
public PreferenceScreen getPreferenceScreen() {
return mPreferenceScreen;
}
}
}

View File

@@ -36,6 +36,7 @@ import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Looper;
import android.telephony.SubscriptionManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
@@ -78,6 +79,7 @@ public class InternetPreferenceControllerTest {
when(wifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_DISABLED);
mController = new InternetPreferenceController(mContext, mock(Lifecycle.class));
mController.sIconMap.put(INTERNET_WIFI, 0);
if (Looper.myLooper() == null) {
Looper.prepare();
}
@@ -123,4 +125,13 @@ public class InternetPreferenceControllerTest {
assertThat(mPreference.getSummary()).isEqualTo(TEST_SUMMARY);
}
@Test
public void updateCellularSummary_getNullSubscriptionInfo_shouldNotCrash() {
final SubscriptionManager subscriptionManager = mock(SubscriptionManager.class);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(subscriptionManager);
when(subscriptionManager.getDefaultDataSubscriptionInfo()).thenReturn(null);
mController.updateCellularSummary();
}
}

View File

@@ -380,6 +380,26 @@ public class SubscriptionUtilTest {
assertEquals(CARRIER_1, name);
}
@Test
public void getUniqueDisplayName_nullSubscriptionInfo_emptyStringReturned() {
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
when(info1.getDisplayName()).thenReturn(CARRIER_1);
when(mSubMgr.getActiveSubscriptionInfoList()).thenReturn(
Arrays.asList(info1));
TelephonyManager sub1Telmgr = mock(TelephonyManager.class);
when(sub1Telmgr.getLine1Number()).thenReturn("1112223333");
when(mTelMgr.createForSubscriptionId(SUBID_1)).thenReturn(sub1Telmgr);
SubscriptionInfo info2 = null;
final CharSequence name =
SubscriptionUtil.getUniqueSubscriptionDisplayName(info2, mContext);
assertThat(name).isNotNull();
assertTrue(TextUtils.isEmpty(name));
}
@Test
public void isInactiveInsertedPSim_nullSubInfo_doesNotCrash() {
assertThat(SubscriptionUtil.isInactiveInsertedPSim(null)).isFalse();