Snap for 7991057 from 4f5f174a5e to tm-release

Change-Id: Ief02fb5fad2bb474f76e4885b46fa3c8307a0080
This commit is contained in:
Android Build Coastguard Worker
2021-12-11 04:09:49 +00:00
44 changed files with 1325 additions and 217 deletions

View File

@@ -110,6 +110,7 @@
<uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" />
<uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
<uses-permission android:name="android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".SettingsApplication"
@@ -150,6 +151,7 @@
android:taskAffinity=""
android:launchMode="singleTask"
android:exported="true"
android:enabled="false"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout"
android:permission="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK">
<intent-filter>
@@ -311,6 +313,7 @@
<receiver android:name=".search.SearchStateReceiver"
android:exported="true"
android:enabled="false"
android:permission="android.permission.READ_SEARCH_INDEXABLES">
<intent-filter>
<action android:name="com.android.settings.SEARCH_START"/>
@@ -673,12 +676,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"/>
<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"/>
<activity
android:name="Settings$TetherSettingsActivity"
@@ -4260,14 +4263,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"/>
<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"/>
<service android:name=".sim.SimNotificationService"
android:permission="android.permission.BIND_JOB_SERVICE" />

View File

@@ -0,0 +1,29 @@
<!--
~ 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.
-->
<!-- TODO(b/208624929): Update to an UX approved icon. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M18.5,1C16.01,1 14,3.01 14,5.5V8H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2h-2V5.5C16,4.12 17.12,3 18.5,3C19.88,3 21,4.12 21,5.5V6h2V5.5C23,3.01 20.99,1 18.5,1zM18,10v10H6V10H18z"/>
</vector>

View File

@@ -26,6 +26,7 @@
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/font_size_preview_text_group"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="16dp"

View File

@@ -0,0 +1,57 @@
<?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="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/Theme.Material3.DayNight.Dialog.Alert">
<TextView
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ListView
android:id="@+id/carrier_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
android:gravity="start|top"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="16dp"
android:paddingBottom="4dp"
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:paddingTop="16dp"
android:paddingBottom="8dp"
android:text="@string/sim_action_switch_sub_dialog_info_outline_for_turning_off"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
</LinearLayout>

View File

@@ -75,12 +75,6 @@
android:contentDescription="@string/font_size_make_larger_desc"
style="@style/screen_size_imageview_style"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/font_size_summary"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"/>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -74,12 +74,6 @@
android:contentDescription="@string/screen_zoom_make_larger_desc"
style="@style/screen_size_imageview_style"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/screen_zoom_summary"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"/>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -19,4 +19,6 @@
<!-- Dashboard number of columns -->
<integer name="dashboard_num_columns">2</integer>
<!-- Whether to support large screen -->
<bool name="config_supported_large_screen">true</bool>
</resources>

View File

@@ -63,6 +63,9 @@
<dimen name="confirm_credentials_side_margin">0dp</dimen>
<dimen name="confirm_credentials_top_margin">64dp</dimen>
<!-- padding for font size preview in large screen -->
<dimen name="font_size_preview_padding_start">32dp</dimen>
<!-- Padding for screen pinning -->
<dimen name="screen_pinning_padding_start">40dp</dimen>
<dimen name="screen_pinning_padding_end">40dp</dimen>

View File

@@ -532,6 +532,9 @@
<!-- Whether suw to support two panes -->
<bool name="config_suw_supported_two_panes">false</bool>
<!-- Whether to support large screen -->
<bool name="config_supported_large_screen">false</bool>
<!-- Display settings screen, Color mode options. Must be the same length and order as
config_color_mode_options_values below. Only the values that also appear in
frameworks/base/core/res/res/values/config.xml's config_availableColorModes are shown. -->

View File

@@ -26,6 +26,7 @@
<string name="menu_key_display" translatable="false">top_level_display</string>
<string name="menu_key_wallpaper" translatable="false">top_level_wallpaper</string>
<string name="menu_key_accessibility" translatable="false">top_level_accessibility</string>
<string name="menu_key_safety_center" translatable="false">top_level_safety_center</string>
<string name="menu_key_security" translatable="false">top_level_security</string>
<string name="menu_key_privacy" translatable="false">top_level_privacy</string>
<string name="menu_key_location" translatable="false">top_level_location</string>

View File

@@ -722,6 +722,12 @@
<string name="security_status_title">Security status</string>
<!-- Summary for Security settings, explaining a few important settings under it [CHAR LIMIT=NONE] -->
<string name="security_dashboard_summary">Screen lock, Find My Device, app security</string>
<!-- TODO(b/208624929): Update to an UX approved title and char limit. -->
<!-- Main Settings screen setting title for the item that takes you to the safety center [CHAR LIMIT=60] -->
<string name="safety_center_title">Security &amp; privacy</string>
<!-- TODO(b/208624929): Update to an UX approved summary and char limit. -->
<!-- Main Settings screen setting summary for the item that takes you to the safety center [CHAR LIMIT=60] -->
<string name="safety_center_summary">Permissions, screen lock, app security</string>
<!-- Face enrollment and settings --><skip />
<!-- Note: Update FaceEnrollParentalConsent.CONSENT_STRING_RESOURCES when any _consent_ strings are added or removed. -->
@@ -871,11 +877,9 @@
<!-- Dialog title shown when the user removes an enrollment [CHAR LIMIT=35] -->
<string name="security_settings_face_settings_remove_dialog_title">Delete face model?</string>
<!-- Dialog contents shown when the user removes an enrollment [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_remove_dialog_details">Your face model will be permanently and securely deleted. After deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps.</string>
<!-- Dialog title shown when the user chooses to delete an existing enrolled face model. [CHAR LIMIT=35] -->
<string name="security_settings_face_settings_remove_model_dialog_title">Delete face model?</string>
<!-- Dialog contents shown when the user chooses to delete an existing enrolled face model. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_remove_model_dialog_details">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone or for authentication in apps.</string>
<string name="security_settings_face_settings_remove_dialog_details">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps.</string>
<!-- Dialog contents shown when the user removes an enrollment when configured as a convenience [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_remove_dialog_details_convenience">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone.</string>
<!-- Subtitle shown for contextual setting face enrollment [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_context_subtitle">Use Face Unlock to unlock your phone</string>
@@ -4386,25 +4390,6 @@
<!-- Header on first screen of choose work profile pattern flow [CHAR LIMIT=40] -->
<string name="lockpassword_choose_your_profile_pattern_header">Set a work pattern</string>
<!-- Header on first screen of choose device password flow [CHAR LIMIT=NONE] -->
<string name="lockpassword_choose_password_description" product="phone">For added security, set a password to unlock the phone</string>
<!-- Header on first screen of choose device PIN flow [CHAR LIMIT=NONE] -->
<string name="lockpassword_choose_pin_description" product="phone">For added security, set a PIN to unlock the phone</string>
<!-- Header on first screen of choose device pattern flow [CHAR LIMIT=NONE] -->
<string name="lockpattern_choose_pattern_description" product="phone">For added security, set a pattern to unlock the phone</string>
<!-- Header on first screen of choose device password flow [CHAR LIMIT=NONE] -->
<string name="lockpassword_choose_password_description" product="tablet">For added security, set a password to unlock the tablet</string>
<!-- Header on first screen of choose device PIN flow [CHAR LIMIT=NONE] -->
<string name="lockpassword_choose_pin_description" product="tablet">For added security, set a PIN to unlock the tablet</string>
<!-- Header on first screen of choose device pattern flow [CHAR LIMIT=NONE] -->
<string name="lockpattern_choose_pattern_description" product="tablet">For added security, set a pattern to unlock the tablet</string>
<!-- Header on first screen of choose device password flow [CHAR LIMIT=NONE] -->
<string name="lockpassword_choose_password_description" product="default">For added security, set a password to unlock the device</string>
<!-- Header on first screen of choose device PIN flow [CHAR LIMIT=NONE] -->
<string name="lockpassword_choose_pin_description" product="default">For added security, set a PIN to unlock the device</string>
<!-- Header on first screen of choose device pattern flow [CHAR LIMIT=NONE] -->
<string name="lockpattern_choose_pattern_description" product="default">For added security, set a pattern to unlock the device</string>
<!-- Header on first screen of choose password/PIN as backup for fingerprint flow. If this string cannot be translated in under 40 characters, please translate "Set fingerprint backup" [CHAR LIMIT=40] -->
<string name="lockpassword_choose_your_password_header_for_fingerprint">To use fingerprint, set password</string>
<!-- Header on first screen of choose pattern as backup for fingerprint flow. If this string cannot be translated in under 40 characters, please translate "Set fingerprint backup" [CHAR LIMIT=40] -->
@@ -8122,11 +8107,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] -->
@@ -12936,14 +12927,22 @@
<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&#8230;</string>
<!-- Text of progress dialog indicating the subscription switch is in progress. [CHAR_LIMIT=NONE] -->

View File

@@ -116,6 +116,15 @@
settings:highlightableMenuKey="@string/menu_key_accessibility"
settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:icon="@drawable/ic_settings_safety_center"
android:key="top_level_safety_center"
android:order="-55"
android:title="@string/safety_center_title"
android:summary="@string/safety_center_summary"
settings:highlightableMenuKey="@string/menu_key_safety_center"
settings:controller="com.android.settings.safetycenter.TopLevelSafetyCenterEntryPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.security.SecuritySettings"
android:icon="@drawable/ic_settings_security_white"
@@ -133,7 +142,8 @@
android:order="-40"
android:title="@string/privacy_dashboard_title"
android:summary="@string/privacy_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_privacy"/>
settings:highlightableMenuKey="@string/menu_key_privacy"
settings:controller="com.android.settings.privacy.TopLevelPrivacyEntryPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.location.LocationSettings"

View File

@@ -120,8 +120,8 @@ public class AccessibilityScreenSizeForSetupWizardActivity extends InstrumentedA
: R.string.screen_zoom_title);
((TextView) findViewById(R.id.sud_layout_subtitle)).setText(
getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? R.string.short_summary_font_size
: R.string.screen_zoom_short_summary);
? R.string.font_size_summary
: R.string.screen_zoom_summary);
}
private boolean isSuwSupportedTwoPanes() {

View File

@@ -22,6 +22,9 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.os.storage.StorageManager;
import android.util.Log;
import android.view.Surface;
@@ -273,4 +276,17 @@ public class BiometricUtils {
public static boolean isReverseLandscape(@NonNull Context context) {
return context.getDisplay().getRotation() == Surface.ROTATION_270;
}
/**
* @param faceManager
* @return True if at least one sensor is set as a convenience.
*/
public static boolean isConvenience(@NonNull FaceManager faceManager) {
for (FaceSensorPropertiesInternal props : faceManager.getSensorPropertiesInternal()) {
if (props.sensorStrength == SensorProperties.STRENGTH_CONVENIENCE) {
return true;
}
}
return false;
}
}

View File

@@ -33,6 +33,7 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.overlay.FeatureFactory;
@@ -56,6 +57,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
public static class ConfirmRemoveDialog extends InstrumentedDialogFragment {
private boolean mIsConvenience;
private DialogInterface.OnClickListener mOnClickListener;
@Override
@@ -68,7 +70,9 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.security_settings_face_settings_remove_dialog_title)
.setMessage(R.string.security_settings_face_settings_remove_dialog_details)
.setMessage(mIsConvenience
? R.string.security_settings_face_settings_remove_dialog_details_convenience
: R.string.security_settings_face_settings_remove_dialog_details)
.setPositiveButton(R.string.delete, mOnClickListener)
.setNegativeButton(R.string.cancel, mOnClickListener);
AlertDialog dialog = builder.create();
@@ -76,6 +80,10 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
return dialog;
}
public void setIsConvenience(boolean isConvenience) {
mIsConvenience = isConvenience;
}
public void setOnClickListener(DialogInterface.OnClickListener listener) {
mOnClickListener = listener;
}
@@ -197,6 +205,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
mRemoving = true;
ConfirmRemoveDialog dialog = new ConfirmRemoveDialog();
dialog.setOnClickListener(mOnClickListener);
dialog.setIsConvenience(BiometricUtils.isConvenience(mFaceManager));
dialog.show(mActivity.getSupportFragmentManager(), ConfirmRemoveDialog.class.getName());
}
}

View File

@@ -17,6 +17,13 @@
package com.android.settings.display;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.viewpager.widget.ViewPager;
import com.android.settings.R;
@@ -33,6 +40,23 @@ public class FontSizePreferenceFragmentForSetupWizard
return SettingsEnums.SUW_ACCESSIBILITY_FONT_SIZE;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View root = super.onCreateView(inflater, container, savedInstanceState);
if (getResources().getBoolean(R.bool.config_supported_large_screen)) {
final ViewPager viewPager = root.findViewById(R.id.preview_pager);
final View view = (View) viewPager.getAdapter().instantiateItem(viewPager,
viewPager.getCurrentItem());
final LinearLayout layout = view.findViewById(R.id.font_size_preview_text_group);
final int paddingStart = getResources().getDimensionPixelSize(
R.dimen.font_size_preview_padding_start);
layout.setPaddingRelative(paddingStart, layout.getPaddingTop(),
layout.getPaddingEnd(), layout.getPaddingBottom());
}
return root;
}
@Override
public void onStop() {
// Log the final choice in value if it's different from the previous value.

View File

@@ -311,7 +311,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
}
private void launchDeepLinkIntentToRight() {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) {
if (!mIsEmbeddingActivityEnabled) {
return;
}

View File

@@ -19,12 +19,14 @@ package com.android.settings.homepage;
import static com.android.settings.search.actionbar.SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR;
import static com.android.settingslib.search.SearchIndexable.MOBILE;
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
@@ -53,6 +55,7 @@ public class TopLevelSettings extends DashboardFragment implements
private static final String SAVED_HIGHLIGHT_MIXIN = "highlight_mixin";
private static final String PREF_KEY_SUPPORT = "top_level_support";
private boolean mIsEmbeddingActivityEnabled;
private TopLevelHighlightMixin mHighlightMixin;
private boolean mFirstStarted = true;
@@ -122,7 +125,9 @@ public class TopLevelSettings extends DashboardFragment implements
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext())) {
mIsEmbeddingActivityEnabled =
ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext());
if (!mIsEmbeddingActivityEnabled) {
return;
}
@@ -138,15 +143,23 @@ public class TopLevelSettings extends DashboardFragment implements
public void onStart() {
if (mFirstStarted) {
mFirstStarted = false;
} else if (!ActivityEmbeddingUtils.isTwoPaneResolution(getActivity())) {
} else if (mIsEmbeddingActivityEnabled && isOnlyOneActivityInTask()
&& !ActivityEmbeddingUtils.isTwoPaneResolution(getActivity())) {
// Set default highlight menu key for 1-pane homepage since it will show the placeholder
// page once changing back to 2-pane.
Log.i(TAG, "Set default menu key");
setHighlightMenuKey(getString(SettingsHomepageActivity.DEFAULT_HIGHLIGHT_MENU_KEY),
/* scrollNeeded= */ false);
}
super.onStart();
}
private boolean isOnlyOneActivityInTask() {
final ActivityManager.RunningTaskInfo taskInfo = getSystemService(ActivityManager.class)
.getRunningTasks(1).get(0);
return taskInfo.numActivities == 1;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -225,8 +238,7 @@ public class TopLevelSettings extends DashboardFragment implements
@Override
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext())
|| !(getActivity() instanceof SettingsHomepageActivity)) {
if (!mIsEmbeddingActivityEnabled || !(getActivity() instanceof SettingsHomepageActivity)) {
return super.onCreateAdapter(preferenceScreen);
}
return mHighlightMixin.onCreateAdapter(this, preferenceScreen);

View File

@@ -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.");

View File

@@ -18,17 +18,30 @@ package com.android.settings.network;
import android.app.FragmentManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
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 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;
/** Returns a SwitchToEuiccSubscriptionSidecar sidecar instance. */
public static SwitchToEuiccSubscriptionSidecar get(FragmentManager fm) {
@@ -46,10 +59,119 @@ 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;
Log.i(TAG, "The SubId is " + mSubId + "The port is " + mPort);
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);
}
}

View File

@@ -38,8 +38,8 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
// Stateless members.
private SwitchToEuiccSubscriptionSidecar mSwitchToSubscriptionSidecar;
private SwitchSlotSidecar mSwitchSlotSidecar;
private int mPhysicalSlotId;
private SubscriptionInfo mRemovedSubInfo;
/** Returns a SwitchToRemovableSlotSidecar sidecar instance. */
public static SwitchToRemovableSlotSidecar get(FragmentManager fm) {
@@ -51,20 +51,17 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
super.onCreate(savedInstanceState);
mSwitchToSubscriptionSidecar =
SwitchToEuiccSubscriptionSidecar.get(getChildFragmentManager());
mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
}
@Override
public void onResume() {
super.onResume();
mSwitchToSubscriptionSidecar.addListener(this);
mSwitchSlotSidecar.addListener(this);
}
@Override
public void onPause() {
mSwitchToSubscriptionSidecar.removeListener(this);
mSwitchSlotSidecar.removeListener(this);
super.onPause();
}
@@ -90,29 +87,46 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
*
* @param physicalSlotId removable physical SIM slot ID.
*/
// ToDo: delete this api and refactor the related code.
public void run(int physicalSlotId) {
mPhysicalSlotId = physicalSlotId;
SubscriptionManager subscriptionManager =
getContext().getSystemService(SubscriptionManager.class);
if (SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
.anyMatch(SubscriptionInfo::isEmbedded)) {
// In SS mode, the esim is active, then inactivate the esim.
Log.i(TAG, "There is an active eSIM profile. Disable the profile first.");
// Use INVALID_SUBSCRIPTION_ID to disable the only active profile.
mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0, null);
} else {
Log.i(TAG, "There is no active eSIM profiles. Start to switch to removable slot.");
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, null);
}
}
/**
* Starts switching to the removable slot.
*
* @param physicalSlotId removable physical SIM slot ID.
* @param removedSubInfo if the all of slots have sims, it should remove the one of active sim.
* If the removedSubInfo is null, then use the default value.
* The default value is the removable physical SIM slot and portId 0.
*/
public void run(int physicalSlotId, SubscriptionInfo removedSubInfo) {
mPhysicalSlotId = physicalSlotId;
mRemovedSubInfo = removedSubInfo;
Log.i(TAG, "Start to switch to removable slot.");
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo);
}
private void onSwitchToSubscriptionSidecarStateChange() {
switch (mSwitchToSubscriptionSidecar.getState()) {
case State.SUCCESS:
mSwitchToSubscriptionSidecar.reset();
Log.i(
TAG,
Log.i(TAG,
"Successfully disabled eSIM profile. Start to switch to Removable slot.");
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo);
break;
case State.ERROR:
mSwitchToSubscriptionSidecar.reset();

View File

@@ -19,8 +19,10 @@ package com.android.settings.network;
import android.annotation.IntDef;
import android.content.Context;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotInfo;
import android.telephony.UiccSlotMapping;
import android.util.Log;
import com.android.settingslib.utils.ThreadUtils;
@@ -29,17 +31,21 @@ import com.google.common.collect.ImmutableList;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
// ToDo: to do the refactor for renaming
public class UiccSlotUtil {
private static final String TAG = "UiccSlotUtil";
private static final long DEFAULT_WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25 * 1000L;
;
public static final int INVALID_PHYSICAL_SLOT_ID = -1;
public static final int INVALID_PORT_ID = -1;
/**
* Mode for switching to eSIM slot which decides whether there is cleanup process, e.g.
@@ -47,9 +53,9 @@ public class UiccSlotUtil {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SwitchingEsimMode.NO_CLEANUP,
SwitchingEsimMode.ASYNC_CLEANUP,
SwitchingEsimMode.SYNC_CLEANUP
SwitchingEsimMode.NO_CLEANUP,
SwitchingEsimMode.ASYNC_CLEANUP,
SwitchingEsimMode.SYNC_CLEANUP
})
public @interface SwitchingEsimMode {
/** No cleanup process after switching to eSIM slot */
@@ -76,44 +82,108 @@ public class UiccSlotUtil {
* Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
* INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
*
* @param slotId the physical removable slot id.
* @param slotId the physical removable slot id.
* @param context the application context.
* @throws UiccSlotsException if there is an error.
*/
//ToDo: delete this api and refactor the related code.
public static synchronized void switchToRemovableSlot(int slotId, Context context)
throws UiccSlotsException {
switchToRemovableSlot(context, slotId, null);
}
/**
* Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
* INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
*
* @param slotId the physical removable slot id.
* @param context the application context.
* @param removedSubInfo In the DSDS+MEP mode, if the all of slots have sims, it should
* remove the one of active sim.
* If the removedSubInfo is null, then use the default value.
* The default value is the esim slot and portId 0.
* @throws UiccSlotsException if there is an error.
*/
public static synchronized void switchToRemovableSlot(Context context, int slotId,
SubscriptionInfo removedSubInfo) throws UiccSlotsException {
if (ThreadUtils.isMainThread()) {
throw new IllegalThreadStateException(
"Do not call switchToRemovableSlot on the main thread.");
}
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
if (telMgr.isMultiSimEnabled()) {
// If this device supports multiple active slots, don't mess with TelephonyManager.
Log.i(TAG, "Multiple active slots supported. Not calling switchSlots.");
return;
}
UiccSlotInfo[] slots = telMgr.getUiccSlotsInfo();
if (slotId == INVALID_PHYSICAL_SLOT_ID) {
for (int i = 0; i < slots.length; i++) {
if (slots[i].isRemovable()
&& !slots[i].getPorts().stream().findFirst().get().isActive()
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
performSwitchToRemovableSlot(i, context);
return;
}
}
} else {
if (slotId >= slots.length || !slots[slotId].isRemovable()) {
throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId);
}
if (!slots[slotId].getPorts().stream().findFirst().get().isActive()) {
performSwitchToRemovableSlot(slotId, context);
}
}
int inactiveRemovableSlot = getInactiveRemovableSlot(telMgr.getUiccSlotsInfo(), slotId);
performSwitchToSlot(telMgr,
prepareUiccSlotMappingsForRemovableSlot(telMgr.getSimSlotMapping(),
inactiveRemovableSlot, removedSubInfo, telMgr.isMultiSimEnabled()),
context);
}
private static void performSwitchToRemovableSlot(int slotId, Context context)
/**
* Switches to the Euicc slot. It waits for SIM_STATE_LOADED after switch.
*
* @param context the application context.
* @param slotId the Euicc slot id.
* @param port the Euicc slot port id.
* @param removedSubInfo In the DSDS+MEP mode, if the all of slots have sims, it should
* remove the one of active sim.
* If the removedSubInfo is null, then it uses the default value.
* The default value is the esim slot and portId 0.
* @throws UiccSlotsException if there is an error.
*/
public static synchronized void switchToEuiccSlot(Context context, int slotId, int port,
SubscriptionInfo removedSubInfo) throws UiccSlotsException {
if (ThreadUtils.isMainThread()) {
throw new IllegalThreadStateException(
"Do not call switchToRemovableSlot on the main thread.");
}
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
Collection<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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -16,13 +16,24 @@
package com.android.settings.network.telephony;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
import java.util.ArrayList;
/** Fragment to show a confirm dialog. The caller should implement onConfirmListener. */
public class ConfirmDialogFragment extends BaseDialogFragment
@@ -32,22 +43,27 @@ public class ConfirmDialogFragment extends BaseDialogFragment
private static final String ARG_MSG = "msg";
private static final String ARG_POS_BUTTON_STRING = "pos_button_string";
private static final String ARG_NEG_BUTTON_STRING = "neg_button_string";
private static final String ARG_LIST = "list";
/**
* Interface defining the method that will be invoked when the user has done with the dialog.
*/
public interface OnConfirmListener {
/**
* @param tag The tag in the caller.
* @param confirmed True if the user has clicked the positive button. False if the user has
* clicked the negative button or cancel the dialog.
* @param tag The tag in the caller.
* @param confirmed True if the user has clicked the positive button. False if the
* user has
* clicked the negative button or cancel the dialog.
* @param itemPosition It is the position of item, if user selects one of the list item.
* If the user select "cancel" or the dialog does not have list, then
* the value is -1.
*/
void onConfirm(int tag, boolean confirmed);
void onConfirm(int tag, boolean confirmed, int itemPosition);
}
/** Displays a confirmation dialog which has confirm and cancel buttons. */
public static <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,77 @@ public class ConfirmDialogFragment extends BaseDialogFragment
String message = getArguments().getString(ARG_MSG);
String posBtnString = getArguments().getString(ARG_POS_BUTTON_STRING);
String negBtnString = getArguments().getString(ARG_NEG_BUTTON_STRING);
ArrayList<String> list = getArguments().getStringArrayList(ARG_LIST);
Log.i("Showing dialog with title = %s", title);
Log.i(TAG, "Showing dialog with title =" + title);
AlertDialog.Builder builder =
new AlertDialog.Builder(getContext())
.setTitle(title)
.setPositiveButton(posBtnString, this)
.setNegativeButton(negBtnString, this);
if (!TextUtils.isEmpty(message)) {
builder.setMessage(message);
if (list != null && !list.isEmpty()) {
Log.i(TAG, "list =" + list.toString());
View content = LayoutInflater.from(getContext()).inflate(
R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
TextView dialogMessage = content.findViewById(R.id.msg);
if (!TextUtils.isEmpty(message) && dialogMessage != null) {
dialogMessage.setText(message);
}
final ArrayAdapter<String> arrayAdapterItems = new ArrayAdapter<String>(
getContext(), android.R.layout.select_dialog_item, list);
final ListView lvItems = content.findViewById(R.id.carrier_list);
if (lvItems != null) {
lvItems.setAdapter(arrayAdapterItems);
lvItems.setChoiceMode(ListView.CHOICE_MODE_NONE);
lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Log.i(TAG, "list onClick =" + position);
Log.i(TAG, "list item =" + list.get(position));
if (position == list.size() - 1) {
// user select the "cancel" item;
informCaller(false, -1);
} else {
informCaller(true, position);
}
}
});
}
builder.setView(content);
} else {
if (!TextUtils.isEmpty(message)) {
builder.setMessage(message);
}
}
AlertDialog dialog = builder.show();
AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
@Override
public void onClick(DialogInterface dialog, int which) {
informCaller(which == DialogInterface.BUTTON_POSITIVE);
Log.i(TAG, "dialog onClick =" + which);
informCaller(which == DialogInterface.BUTTON_POSITIVE, -1);
}
@Override
public void onCancel(DialogInterface dialog) {
informCaller(false);
informCaller(false, -1);
}
private void informCaller(boolean confirmed) {
private void informCaller(boolean confirmed, int itemPosition) {
OnConfirmListener listener = getListener(OnConfirmListener.class);
if (listener == null) {
return;
}
listener.onConfirm(getTagInCaller(), confirmed);
listener.onConfirm(getTagInCaller(), confirmed, itemPosition);
}
}

View File

@@ -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;

View File

@@ -24,10 +24,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
import com.android.settings.SidecarFragment;
import com.android.settings.network.SwitchSlotSidecar;
import java.util.concurrent.atomic.AtomicInteger;
@@ -37,7 +39,8 @@ import java.util.concurrent.atomic.AtomicInteger;
* should implement its own get() function to return an instance of that class, and implement the
* functional class like run() to actually trigger the function in EuiccManager.
*/
public abstract class EuiccOperationSidecar extends SidecarFragment {
public abstract class EuiccOperationSidecar extends SidecarFragment
implements SidecarFragment.Listener{
private static final String TAG = "EuiccOperationSidecar";
private static final int REQUEST_CODE = 0;
private static final String EXTRA_OP_ID = "op_id";
@@ -45,6 +48,9 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
new AtomicInteger((int) SystemClock.elapsedRealtime());
protected EuiccManager mEuiccManager;
protected TelephonyManager mTelephonyManager;
protected SwitchSlotSidecar mSwitchSlotSidecar;
private int mResultCode;
private int mDetailedCode;
@@ -107,6 +113,8 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEuiccManager = getContext().getSystemService(EuiccManager.class);
mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
getContext()
.getApplicationContext()
@@ -118,12 +126,42 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
Context.RECEIVER_EXPORTED);
}
@Override
public void onResume() {
super.onResume();
mSwitchSlotSidecar.addListener(this);
}
@Override
public void onPause() {
mSwitchSlotSidecar.removeListener(this);
super.onPause();
}
@Override
public void onDestroy() {
getContext().getApplicationContext().unregisterReceiver(mReceiver);
super.onDestroy();
}
@Override
public void onStateChange(SidecarFragment fragment) {
if (fragment == mSwitchSlotSidecar) {
switch (mSwitchSlotSidecar.getState()) {
case State.SUCCESS:
mSwitchSlotSidecar.reset();
Log.i(TAG, "mSwitchSlotSidecar SUCCESS");
break;
case State.ERROR:
mSwitchSlotSidecar.reset();
Log.i(TAG, "mSwitchSlotSidecar ERROR");
break;
}
} else {
Log.wtf(TAG, "Received state change from a sidecar not expected.");
}
}
public int getResultCode() {
return mResultCode;
}

View File

@@ -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

View File

@@ -23,6 +23,7 @@ import android.os.UserManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.text.TextUtils;
import android.util.Log;
@@ -40,7 +41,9 @@ import com.android.settings.sim.SimActivationNotifier;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */
public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity
@@ -55,6 +58,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION = 2;
private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 3;
private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 4;
private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP = 5;
// Number of SIMs for DSDS
private static final int NUM_OF_SIMS_FOR_DSDS = 2;
// Support RTL mode
@@ -85,11 +90,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private boolean mIsEsimOperation;
private TelephonyManager mTelMgr;
private boolean isRtlMode;
private List<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);
@@ -107,6 +112,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return;
}
mActiveSubInfos = SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded();
mSwitchToEuiccSubscriptionSidecar =
@@ -116,6 +122,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
mEnable = intent.getBooleanExtra(ARG_enable, true);
isRtlMode = getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_RTL;
Log.i(TAG, "isMultipleEnabledProfilesSupported():" + isMultipleEnabledProfilesSupported());
if (savedInstanceState == null) {
if (mEnable) {
@@ -154,7 +161,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
@Override
public void onConfirm(int tag, boolean confirmed) {
public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed
&& tag != DIALOG_TAG_ENABLE_DSDS_CONFIRMATION
&& tag != DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION) {
@@ -162,14 +169,16 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return;
}
SubscriptionInfo removedSubInfo = null;
switch (tag) {
case DIALOG_TAG_DISABLE_SIM_CONFIRMATION:
if (mIsEsimOperation) {
Log.i(TAG, "Disabling the eSIM profile.");
showProgressDialog(
getString(R.string.privileged_action_disable_sub_dialog_progress));
int port = mSubInfo != null ? mSubInfo.getPortIndex() : 0;
mSwitchToEuiccSubscriptionSidecar.run(
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
SubscriptionManager.INVALID_SUBSCRIPTION_ID, port, null);
return;
}
Log.i(TAG, "Disabling the pSIM profile.");
@@ -201,6 +210,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
SimActivationNotifier.setShowSimSettingsNotification(this, true);
mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
break;
case DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP:
if (itemPosition != -1) {
removedSubInfo = (mActiveSubInfos != null) ? mActiveSubInfos.get(itemPosition)
: null;
}
case DIALOG_TAG_ENABLE_SIM_CONFIRMATION:
Log.i(TAG, "User confirmed to enable the subscription.");
if (mIsEsimOperation) {
@@ -209,12 +223,15 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
R.string.sim_action_switch_sub_dialog_progress,
SubscriptionUtil.getUniqueSubscriptionDisplayName(
mSubInfo, this)));
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
UiccSlotUtil.INVALID_PORT_ID,
removedSubInfo);
return;
}
showProgressDialog(
getString(R.string.sim_action_enabling_sim_without_carrier_name));
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID);
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
removedSubInfo);
break;
default:
Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
@@ -225,8 +242,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private void handleSwitchToEuiccSubscriptionSidecarStateChange() {
switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
case SidecarFragment.State.SUCCESS:
Log.i(
TAG,
Log.i(TAG,
String.format(
"Successfully %s the eSIM profile.",
mEnable ? "enable" : "disable"));
@@ -235,8 +251,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
finish();
break;
case SidecarFragment.State.ERROR:
Log.i(
TAG,
Log.i(TAG,
String.format(
"Failed to %s the eSIM profile.", mEnable ? "enable" : "disable"));
mSwitchToEuiccSubscriptionSidecar.reset();
@@ -290,7 +305,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
if (mIsEsimOperation) {
Log.i(TAG, "DSDS enabled, start to enable profile: " + mSubInfo.getSubscriptionId());
// For eSIM operations, we simply switch to the selected eSIM profile.
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
UiccSlotUtil.INVALID_PORT_ID, null);
return;
}
@@ -305,10 +321,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable);
finish();
} else {
Log.i(
TAG,
"The device does not support toggling pSIM. It is enough to just "
+ "enable the removable slot.");
Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
+ "enable the removable slot.");
}
}
@@ -319,7 +333,10 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
showEnableDsdsConfirmDialog();
return;
}
if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()) {
if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()
&& isRemovableSimEnabled()) {
// This case is for switching on psim when device is not multiple enable profile
// supported.
Log.i(TAG, "Toggle on pSIM, no dialog displayed.");
handleTogglePsimAction();
finish();
@@ -372,27 +389,55 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
private void showEnableSimConfirmDialog() {
List<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));
}
@@ -408,6 +453,35 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
getString(R.string.sim_action_cancel));
}
private void showMepSwitchSimConfirmDialog() {
Log.i(TAG, "showMepSwitchSimConfirmDialog");
final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
mSubInfo, this);
String title = getString(R.string.sim_action_switch_sub_dialog_mep_title, displayName);
final StringBuilder switchDialogMsg = new StringBuilder();
switchDialogMsg.append(
getString(R.string.sim_action_switch_sub_dialog_mep_text, displayName));
if (isRtlMode) {
/* There are two lines of message in the dialog, and the RTL symbols must be added
* before and after each sentence, so use the line break symbol to find the position.
* (Each message are all with two line break symbols)
*/
switchDialogMsg.insert(0, RTL_MARK)
.insert(switchDialogMsg.indexOf(LINE_BREAK) - LINE_BREAK_OFFSET_ONE, RTL_MARK)
.insert(switchDialogMsg.indexOf(LINE_BREAK) + LINE_BREAK_OFFSET_TWO, RTL_MARK)
.insert(switchDialogMsg.length(), RTL_MARK);
}
ConfirmDialogFragment.show(
this,
ConfirmDialogFragment.OnConfirmListener.class,
DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP,
title,
switchDialogMsg.toString(),
null,
null,
getSwitchDialogBodyList());
}
private String getSwitchDialogPosBtnText() {
return mIsEsimOperation
? getString(
@@ -468,6 +542,20 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return switchDialogMsg.toString();
}
private ArrayList<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() {
if (mTelMgr.isMultiSimEnabled()) {
Log.i(TAG, "DSDS is already enabled. Condition not satisfied.");
@@ -477,17 +565,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
Log.i(TAG, "Hardware does not support DSDS.");
return false;
}
ImmutableList<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;
@@ -496,13 +574,36 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream()
.anyMatch(SubscriptionInfo::isEmbedded);
if (!mIsEsimOperation && isEsimProfileEnabled) {
Log.i(
TAG,
"Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ " satisfied.");
Log.i(TAG, "Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ " satisfied.");
return true;
}
Log.i(TAG, "DSDS condition not satisfied.");
return false;
}
private boolean isRemovableSimEnabled() {
ImmutableList<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());
}
}

View File

@@ -59,6 +59,7 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mContext = getActivity();
final Intent intent = getActivity().getIntent();
@@ -81,7 +82,6 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase {
return;
}
super.onCreate(icicle);
mCustomBehaviorPreference = getPreferenceScreen().findPreference(CUSTOM_BEHAVIOR_KEY);
mCustomBehaviorPreference.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {

View File

@@ -268,9 +268,7 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_choose_your_pin_header_for_fingerprint,
R.string.lockpassword_choose_your_pin_header_for_face,
R.string.lockpassword_choose_your_pin_header_for_biometrics,
R.string.lockpassword_choose_password_description,
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_pin_description,
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.next_label),
@@ -287,8 +285,6 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_confirm_your_pin_header,
0,
0,
0,
0,
R.string.lockpassword_confirm_label),
ConfirmWrong(
@@ -304,8 +300,6 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_confirm_pins_dont_match,
0,
0,
0,
0,
R.string.lockpassword_confirm_label);
Stage(int hintInAlpha,
@@ -318,9 +312,7 @@ public class ChooseLockPassword extends SettingsActivity {
int hintInNumericForFingerprint,
int hintInNumericForFace,
int hintInNumericForBiometrics,
int messageInAlpha,
int messageInAlphaForBiometrics,
int messageInNumeric,
int messageInNumericForBiometrics,
int nextButtonText) {
@@ -336,10 +328,7 @@ public class ChooseLockPassword extends SettingsActivity {
this.numericHintForFace = hintInNumericForFace;
this.numericHintForBiometrics = hintInNumericForBiometrics;
this.alphaMessage = messageInAlpha;
this.alphaMessageForBiometrics = messageInAlphaForBiometrics;
this.numericMessage = messageInNumeric;
this.numericMessageForBiometrics = messageInNumericForBiometrics;
this.buttonText = nextButtonText;
@@ -365,11 +354,9 @@ public class ChooseLockPassword extends SettingsActivity {
public final int numericHintForBiometrics;
// Password description
public final int alphaMessage;
public final int alphaMessageForBiometrics;
// PIN description
public final int numericMessage;
public final int numericMessageForBiometrics;
public final int buttonText;
@@ -407,7 +394,7 @@ public class ChooseLockPassword extends SettingsActivity {
case TYPE_NONE:
default:
return isAlpha ? alphaMessage : numericMessage;
return 0;
}
}
}
@@ -869,12 +856,17 @@ public class ChooseLockPassword extends SettingsActivity {
setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
}
int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
if (message != 0) {
mMessage.setVisibility(View.VISIBLE);
mMessage.setText(message);
final int stage = getStageType();
if (getStageType() != Stage.TYPE_NONE) {
int message = mUiStage.getMessage(mIsAlphaMode, stage);
if (message != 0) {
mMessage.setVisibility(View.VISIBLE);
mMessage.setText(message);
} else {
mMessage.setVisibility(View.INVISIBLE);
}
} else {
mMessage.setVisibility(View.INVISIBLE);
mMessage.setVisibility(View.GONE);
}
setNextText(mUiStage.buttonText);

View File

@@ -363,54 +363,49 @@ public class ChooseLockPattern extends SettingsActivity {
Introduction(
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpattern_choose_pattern_description,
R.string.lockpattern_recording_intro_header,
LeftButtonMode.Gone, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
HelpScreen(
ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record,
ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record,
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
ChoiceTooShort(
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpattern_choose_pattern_description,
R.string.lockpattern_recording_incorrect_too_short,
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
FirstChoiceValid(
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpattern_choose_pattern_description,
R.string.lockpattern_pattern_entered_header,
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
NeedToConfirm(
ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_confirm,
ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_confirm,
LeftButtonMode.Gone, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ConfirmWrong(
ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_unlock_wrong,
ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_unlock_wrong,
LeftButtonMode.Gone, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ChoiceConfirmed(
ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_pattern_confirmed_header,
ID_EMPTY_MESSAGE, R.string.lockpattern_pattern_confirmed_header,
LeftButtonMode.Gone, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false);
/**
* @param messageForBiometrics The message displayed at the top, above header for
* fingerprint flow.
* @param message The message displayed at the top.
* @param headerMessage The message displayed at the top.
* @param leftMode The mode of the left button.
* @param rightMode The mode of the right button.
* @param footerMessage The footer message.
* @param patternEnabled Whether the pattern widget is enabled.
*/
Stage(int messageForBiometrics, int message, int headerMessage,
Stage(int messageForBiometrics, int headerMessage,
LeftButtonMode leftMode,
RightButtonMode rightMode,
int footerMessage, boolean patternEnabled) {
this.headerMessage = headerMessage;
this.messageForBiometrics = messageForBiometrics;
this.message = message;
this.leftMode = leftMode;
this.rightMode = rightMode;
this.footerMessage = footerMessage;
@@ -419,7 +414,6 @@ public class ChooseLockPattern extends SettingsActivity {
final int headerMessage;
final int messageForBiometrics;
final int message;
final LeftButtonMode leftMode;
final RightButtonMode rightMode;
final int footerMessage;
@@ -735,11 +729,14 @@ public class ChooseLockPattern extends SettingsActivity {
}
final GlifLayout layout = getActivity().findViewById(R.id.setup_wizard_layout);
final boolean forAnyBiometric = mForFingerprint || mForFace || mForBiometrics;
int message = forAnyBiometric ? stage.messageForBiometrics : stage.message;
if (message == ID_EMPTY_MESSAGE) {
layout.setDescriptionText("");
if (forAnyBiometric) {
if (stage.messageForBiometrics == ID_EMPTY_MESSAGE) {
layout.setDescriptionText("");
} else {
layout.setDescriptionText(stage.messageForBiometrics);
}
} else {
layout.setDescriptionText(message);
layout.getDescriptionTextView().setVisibility(View.GONE);
}
if (stage.footerMessage == ID_EMPTY_MESSAGE) {
mFooterText.setText("");

View File

@@ -142,14 +142,8 @@ public class SetupChooseLockPattern extends ChooseLockPattern {
mLeftButtonIsSkip = false;
}
// Show generic pattern message when pattern lock screen launch in Setup wizard flow
// before fingerprint and face setup.
final GlifLayout layout = getActivity().findViewById(R.id.setup_wizard_layout);
if (stage.message == ID_EMPTY_MESSAGE) {
layout.setDescriptionText("");
} else {
layout.setDescriptionText(stage.message);
}
layout.setDescriptionText("");
}
@Override

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.privacy;
import android.annotation.NonNull;
import android.content.Context;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.safetycenter.SafetyCenterStatus;
/** The preference controller for the top level privacy tile. */
public class TopLevelPrivacyEntryPreferenceController extends BasePreferenceController {
public TopLevelPrivacyEntryPreferenceController(@NonNull Context context, @NonNull String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
if (!SafetyCenterStatus.isEnabled()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.safetycenter;
import android.provider.DeviceConfig;
import com.android.internal.annotations.VisibleForTesting;
/** Knows whether safety center is enabled or disabled. */
public class SafetyCenterStatus {
/** Whether SafetyCenter page is enabled. */
@VisibleForTesting
public static final String SAFETY_CENTER_IS_ENABLED = "safety_center_is_enabled";
/** Returns true is SafetyCenter page is enabled, false otherwise. */
public static boolean isEnabled() {
// TODO(b/208625216): use SafetyManager API instead
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY, SAFETY_CENTER_IS_ENABLED, false);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.safetycenter;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import androidx.preference.Preference;
import com.android.settings.core.BasePreferenceController;
/** Controller for the SafetyCenter entry in top level Settings. */
public class TopLevelSafetyCenterEntryPreferenceController extends BasePreferenceController {
private static final String TAG = "TopLevelSafetyCenterEntryPreferenceController";
public TopLevelSafetyCenterEntryPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
if (SafetyCenterStatus.isEnabled()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
return super.handlePreferenceTreeClick(preference);
}
try {
mContext.startActivity(new Intent(Intent.ACTION_SAFETY_CENTER));
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Unable to open safety center", e);
return false;
}
return true;
}
}

View File

@@ -24,6 +24,7 @@ import androidx.preference.Preference;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.safetycenter.SafetyCenterStatus;
public class TopLevelSecurityEntryPreferenceController extends BasePreferenceController {
@@ -37,7 +38,10 @@ public class TopLevelSecurityEntryPreferenceController extends BasePreferenceCon
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
if (!SafetyCenterStatus.isEnabled()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
@Override

View File

@@ -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();

View File

@@ -160,8 +160,10 @@ public class SimDialogActivity extends FragmentActivity {
final TelephonyManager telephonyManager = getSystemService(
TelephonyManager.class).createForSubscriptionId(subId);
subscriptionManager.setDefaultDataSubId(subId);
telephonyManager.setDataEnabled(true);
Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
telephonyManager.setDataEnabled(true);
Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
}
}
private void setDefaultCallsSubId(final int subId) {

View File

@@ -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,

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.privacy;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.DeviceConfig;
import android.provider.Settings;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.safetycenter.SafetyCenterStatus;
import com.android.settings.security.TopLevelSecurityEntryPreferenceController;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
public class TopLevelPrivacyEntryPreferenceControllerTest {
private static final String PREFERENCE_KEY = "top_level_privacy";
private TopLevelPrivacyEntryPreferenceController mTopLevelPrivacyEntryPreferenceController;
@Mock
private Context mContext;
@Before
public void setUp() {
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
mTopLevelPrivacyEntryPreferenceController =
new TopLevelPrivacyEntryPreferenceController(mContext, PREFERENCE_KEY);
}
@After
public void tearDown() {
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
}
@Test
public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsUnavailable() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(true),
/* makeDefault = */ false);
assertThat(mTopLevelPrivacyEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsAvailable() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(false),
/* makeDefault = */ false);
assertThat(mTopLevelPrivacyEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.AVAILABLE);
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.safetycenter;
import static com.google.common.truth.Truth.assertThat;
import android.provider.DeviceConfig;
import android.provider.Settings;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class SafetyCenterStatusTest {
@Before
public void setUp() {
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
}
@After
public void tearDown() {
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
}
@Test
public void isEnabled_whenFlagTrue_returnsTrue() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(true),
/* makeDefault = */ false);
assertThat(SafetyCenterStatus.isEnabled()).isTrue();
}
@Test
public void isEnabled_whenFlagFalse_returnsFalse() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(false),
/* makeDefault = */ false);
assertThat(SafetyCenterStatus.isEnabled()).isFalse();
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.safetycenter;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.provider.DeviceConfig;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class TopLevelSafetyCenterEntryPreferenceControllerTest {
private static final String PREFERENCE_KEY = "top_level_safety_center";
private TopLevelSafetyCenterEntryPreferenceController
mTopLevelSafetyCenterEntryPreferenceController;
private Preference mPreference;
@Mock
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mPreference = new Preference(ApplicationProvider.getApplicationContext());
mPreference.setKey(PREFERENCE_KEY);
doNothing().when(mContext).startActivity(any(Intent.class));
mTopLevelSafetyCenterEntryPreferenceController =
new TopLevelSafetyCenterEntryPreferenceController(mContext, PREFERENCE_KEY);
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
}
@After
public void tearDown() {
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
}
@Test
public void handlePreferenceTreeClick_forDifferentPreferenceKey_isNotHandled() {
Preference preference = new Preference(ApplicationProvider.getApplicationContext());
preference.setKey("some_other_preference");
boolean preferenceHandled =
mTopLevelSafetyCenterEntryPreferenceController.handlePreferenceTreeClick(
preference);
assertThat(preferenceHandled).isFalse();
}
@Test
public void handlePreferenceTreeClick_launchesIntendedIntent() {
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
boolean preferenceHandled = mTopLevelSafetyCenterEntryPreferenceController
.handlePreferenceTreeClick(mPreference);
assertThat(preferenceHandled).isTrue();
verify(mContext).startActivity(intentCaptor.capture());
assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_SAFETY_CENTER);
}
@Test
public void handlePreferenceTreeClick_onStartActivityThrows_returnsFalse() {
doThrow(ActivityNotFoundException.class)
.when(mContext).startActivity(any(Intent.class));
boolean preferenceHandled = mTopLevelSafetyCenterEntryPreferenceController
.handlePreferenceTreeClick(mPreference);
assertThat(preferenceHandled).isFalse();
}
@Test
public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsUnavailable() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(false),
/* makeDefault = */ false);
assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSafetyCenterEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsAvailable() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(true),
/* makeDefault = */ false);
assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSafetyCenterEntryPreferenceController.AVAILABLE);
}
}

View File

@@ -25,14 +25,18 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.provider.DeviceConfig;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.SettingsActivity;
import com.android.settings.safetycenter.SafetyCenterStatus;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,6 +64,9 @@ public class TopLevelSecurityEntryPreferenceControllerTest {
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSecuritySettingsFeatureProvider = mFeatureFactory.getSecuritySettingsFeatureProvider();
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
mPreference = new Preference(ApplicationProvider.getApplicationContext());
mPreference.setKey(PREFERENCE_KEY);
@@ -68,6 +75,12 @@ public class TopLevelSecurityEntryPreferenceControllerTest {
new TopLevelSecurityEntryPreferenceController(mContext, PREFERENCE_KEY);
}
@After
public void tearDown() {
DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
DeviceConfig.NAMESPACE_PRIVACY);
}
@Test
public void handlePreferenceTreeClick_forDifferentPreferenceKey_isNotHandled() {
Preference preference = new Preference(ApplicationProvider.getApplicationContext());
@@ -121,4 +134,28 @@ public class TopLevelSecurityEntryPreferenceControllerTest {
assertThat(preferenceHandled).isFalse();
}
@Test
public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsUnavailable() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(true),
/* makeDefault = */ false);
assertThat(mTopLevelSecurityEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsAvailable() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED,
/* value = */ Boolean.toString(false),
/* makeDefault = */ false);
assertThat(mTopLevelSecurityEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.AVAILABLE);
}
}