From 79b289b71ea38575326f8db679603745aa0e0610 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 12 Jan 2024 12:04:29 +0800 Subject: [PATCH 01/20] Updaing ShadowAccountManager to extending from official shadow library Bug: 319052511 Test: atest SettingsRoboTests Change-Id: I53ec4261351f49305bb90ff10a6c00549a31e428 --- ...ChooseAccountPreferenceControllerTest.java | 25 ++++++++++++------- .../shadow/ShadowAccountManager.java | 5 ++-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java index 783eebe0da1..59e656c6a39 100644 --- a/tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; import android.app.Activity; import android.app.admin.DevicePolicyManager; @@ -35,6 +36,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; @@ -44,33 +46,38 @@ import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class, ShadowRestrictedLockUtilsInternal.class}) public class ChooseAccountPreferenceControllerTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private Context mContext; private ChooseAccountPreferenceController mController; private Activity mActivity; private PreferenceManager mPreferenceManager; private PreferenceScreen mPreferenceScreen; + private ShadowAccountManager mAccountManager; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); mController = spy(new ChooseAccountPreferenceController(mContext, "controller_key")); mActivity = Robolectric.setupActivity(FragmentActivity.class); mPreferenceManager = new PreferenceManager(mContext); mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext); + mAccountManager = (ShadowAccountManager) Shadows.shadowOf(AccountManager.get(mContext)); } @After @@ -108,7 +115,7 @@ public class ChooseAccountPreferenceControllerTest { final AuthenticatorDescription authDesc = new AuthenticatorDescription("com.acct1", "com.android.settings", R.string.header_add_an_account, 0, 0, 0, false); - ShadowAccountManager.addAuthenticator(authDesc); + mAccountManager.addAuthenticator(authDesc); final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, "com.acct1" /* accountType */, false /* userVisible */, @@ -133,7 +140,7 @@ public class ChooseAccountPreferenceControllerTest { final AuthenticatorDescription authDesc = new AuthenticatorDescription("com.acct1", "com.android.settings", R.string.header_add_an_account, 0, 0, 0, false); - ShadowAccountManager.addAuthenticator(authDesc); + mAccountManager.addAuthenticator(authDesc); final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, "com.acct1" /* accountType */, false /* userVisible */, @@ -158,7 +165,7 @@ public class ChooseAccountPreferenceControllerTest { final AuthenticatorDescription authDesc = new AuthenticatorDescription("com.acct1", "com.android.settings", R.string.header_add_an_account, 0, 0, 0, false); - ShadowAccountManager.addAuthenticator(authDesc); + mAccountManager.addAuthenticator(authDesc); final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, "com.acct1" /* accountType */, false /* userVisible */, @@ -184,8 +191,8 @@ public class ChooseAccountPreferenceControllerTest { final AuthenticatorDescription authDesc2 = new AuthenticatorDescription("com.acct2", "com.android.settings", R.string.header_add_an_account, 0, 0, 0, false); - ShadowAccountManager.addAuthenticator(authDesc); - ShadowAccountManager.addAuthenticator(authDesc2); + mAccountManager.addAuthenticator(authDesc); + mAccountManager.addAuthenticator(authDesc2); final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, "com.acct1" /* accountType */, false /* userVisible */, diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java index dae17bc41e3..aa2961ce98b 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java @@ -31,7 +31,7 @@ import java.util.List; import java.util.Map; @Implements(AccountManager.class) -public class ShadowAccountManager { +public class ShadowAccountManager extends org.robolectric.shadows.ShadowAccountManager { private static final Map sAuthenticators = new HashMap<>(); private static final Map> sAccountsByUserId = new HashMap<>(); @@ -41,7 +41,8 @@ public class ShadowAccountManager { return sAuthenticators.values().toArray(new AuthenticatorDescription[sAuthenticators.size()]); } - public static void addAuthenticator(AuthenticatorDescription authenticator) { + @Override + public void addAuthenticator(AuthenticatorDescription authenticator) { sAuthenticators.put(authenticator.type, authenticator); } From ac717e4dc29e7e225b437f325317fcdd16d2d25a Mon Sep 17 00:00:00 2001 From: Anna Bauza Date: Wed, 17 Jan 2024 16:29:07 +0000 Subject: [PATCH 02/20] Avatar sync functionality Send information to avatar sync service that user selected confirm or cancel on edit user info dialog Bug: 320656026 Test: manual Change-Id: I84356b844d47ea7c07f662691f1e48eaca56b7d8 --- res/values/config.xml | 4 ++++ src/com/android/settings/users/UserSettings.java | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/res/values/config.xml b/res/values/config.xml index 0b4851563f2..da235a2bbe8 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -42,6 +42,10 @@ com.android.avatarpicker.FULL_SCREEN_ACTIVITY + + + com.android.avatarpicker + com.android.settings diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 29d136fd9cc..d39d980f29d 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -35,6 +35,7 @@ import android.graphics.BitmapFactory; import android.graphics.BlendMode; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import android.multiuser.Flags; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; @@ -892,13 +893,24 @@ public class UserSettings extends SettingsPreferenceFragment UserIcons.convertToBitmapAtUserIconSize( activity.getResources(), newUserIcon))); mMePreference.setIcon(newUserIcon); + if (Flags.avatarSync()) { + final String pkg = getString(R.string.config_avatar_picker_package); + final String action = pkg + ".set.confirm"; + activity.sendBroadcast(new Intent(action).setPackage(pkg)); + } } if (!TextUtils.isEmpty(newUserName) && !newUserName.equals(user.name)) { mMePreference.setTitle(newUserName); mUserManager.setUserName(user.id, newUserName); } - }, null); + }, () -> { + if (Flags.avatarSync()) { + final String pkg = getString(R.string.config_avatar_picker_package); + final String action = pkg + ".set.cancel"; + activity.sendBroadcast(new Intent(action).setPackage(pkg)); + } + }); } private Dialog buildAddUserDialog(int userType) { From d581696e194b2aca801cff04c4fd48fc63aef760 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Fri, 19 Jan 2024 00:49:28 +0000 Subject: [PATCH 03/20] Updating strings as per the suggestion in b/318837875. Test: Manual (cherry picked from https://android-review.googlesource.com/q/commit:4abac5ae58cf45ccb8275f6e3f510c61023cdc88) Merged-In: I5d03a1b5b499ba7a42934b791e093d7836f67287 Change-Id: I5d03a1b5b499ba7a42934b791e093d7836f67287 --- res/values/strings.xml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 776e3f41093..64888917e17 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3370,7 +3370,7 @@ Erase eSIMs - This won’t cancel any mobile service plans. To download replacement SIMs, contact your carrier. + This won’t cancel your mobile service plan. To get a replacement SIM, contact your carrier. Reset settings @@ -3387,9 +3387,9 @@ Network settings have been reset - Can\u2019t erase SIMs + Can\u2019t erase eSIMs - eSIMs can\u2019t be erased due to an error.\n\nRestart your device and try again. + Something went wrong and your eSIMs weren\u2019t erased.\n\nRestart your device and try again. @@ -3405,7 +3405,7 @@ All of your personal information and downloaded apps will be deleted. You can\u2019t undo this action. - All of your personal information, including downloaded apps & SIMs, will be deleted. You can\u2019t undo this action. + All of your personal information, including downloaded apps & eSIMs, will be deleted. You can\u2019t undo this action. Erase all data? @@ -11362,7 +11362,7 @@ - Erase SIM + Erase eSIM Preferred network type @@ -11430,11 +11430,11 @@ Use %1$s? - Only one SIM can be active at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service. - - Only 1 eSIM can be active at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service. + Only 1 SIM can be on at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service. + + Only 1 eSIM can be on at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service. - Only one SIM can be active at a time.\n\nSwitching won\u2019t cancel your %1$s service. + Only 1 SIM can be on at a time.\n\nSwitching won\u2019t cancel your %1$s service. You can use 2 SIMs at a time. To use %1$s, turn off another SIM. @@ -11585,15 +11585,15 @@ Erase this eSIM? - Erasing this SIM removes %1$s service from this device.\n\nService for %1$s won\'t be canceled. + This removes %1$s service from this device, but your %1$s plan won\'t be canceled. Erase - Erasing SIM… + Erasing eSIM… - Can\'t erase SIM + Can\'t erase eSIM - This SIM can\'t be erased due to an error.\n\nRestart your device and try again. + Something went wrong and this eSIM wasn\'t erased.\n\nRestart your device and try again. Connect to device @@ -11998,9 +11998,9 @@ eSIMs - Active + On - Inactive + Off \u0020/ Default for %1$s From e4ebee595d948cac784950eb04e3816e1dd39036 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 19 Jan 2024 11:51:47 +0800 Subject: [PATCH 04/20] Update ShadowSecureSettings to extend from robolectric library Bug: 320820008 Test: atest SettingsRoboTests Change-Id: I278b2013e3876c461beedce7c29189df63375171 --- .../shadow/ShadowSecureSettings.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java index f2099420d63..d7731484658 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java @@ -16,6 +16,8 @@ package com.android.settings.testutils.shadow; +import static android.provider.Settings.DEFAULT_OVERRIDEABLE_BY_RESTORE; + import android.content.ContentResolver; import android.provider.Settings; @@ -24,12 +26,13 @@ import com.google.common.collect.Table; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowSettings; import java.util.Map; import java.util.WeakHashMap; @Implements(Settings.Secure.class) -public class ShadowSecureSettings { +public class ShadowSecureSettings extends ShadowSettings.ShadowSecure { private static final Map> sUserDataMap = new WeakHashMap<>(); @@ -48,6 +51,16 @@ public class ShadowSecureSettings { } } + /** + * Same implementation as Settings.Secure because robolectric.ShadowSettings.ShadowSecure + * overrides this API. + */ + @Implementation + public static boolean putString(ContentResolver resolver, String name, String value) { + return putStringForUser(resolver, name, value, null, false, + resolver.getUserId(), DEFAULT_OVERRIDEABLE_BY_RESTORE); + } + @Implementation public static String getStringForUser(ContentResolver resolver, String name, int userHandle) { final Table userTable = getUserTable(resolver); @@ -56,6 +69,15 @@ public class ShadowSecureSettings { } } + /** + * Same implementation as Settings.Secure because robolectric.ShadowSettings.ShadowSecure + * overrides this API. + */ + @Implementation + public static boolean putInt(ContentResolver resolver, String name, int value) { + return putIntForUser(resolver, name, value, resolver.getUserId()); + } + @Implementation public static boolean putIntForUser(ContentResolver resolver, String name, int value, int userHandle) { @@ -66,6 +88,15 @@ public class ShadowSecureSettings { } } + /** + * Same implementation as Settings.Secure because robolectric.ShadowSettings.ShadowSecure + * overrides this API. + */ + @Implementation + public static int getInt(ContentResolver resolver, String name, int def) { + return getIntForUser(resolver, name, def, resolver.getUserId()); + } + @Implementation public static int getIntForUser(ContentResolver resolver, String name, int def, int userHandle) { From 63f74b85677f12e929fbb5ac95384b2fa4132f4f Mon Sep 17 00:00:00 2001 From: Ling Ma Date: Mon, 22 Jan 2024 22:02:00 +0000 Subject: [PATCH 05/20] Wrap subManager creation with createForAllProfiles Since U, a sub can be associated with a particular profile, and thus in V we enforce filtering on the caller and only show the subs that are associated with the caller. However, in some cases the caller indeed needs to see all subs regardless of its association, e.g. sysUI. Therefore, a param isForAllProfile is added to indicate whether the caller intends to see all subs. Bug: 296076674 Test: voice call + data browsing Flag: ACONFIG com.android.internal.telephony.flags.enforce_subscription_user_filter DEVELOPMENT Change-Id: I7dfb324d7e08fc4c845c44cd93e6ddf7d0368c1f --- .../settings/security/SimLockPreferenceController.java | 5 +++-- .../settings/security/SimLockPreferenceControllerTest.java | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/security/SimLockPreferenceController.java b/src/com/android/settings/security/SimLockPreferenceController.java index 8429a9f352c..8cc72342e0e 100644 --- a/src/com/android/settings/security/SimLockPreferenceController.java +++ b/src/com/android/settings/security/SimLockPreferenceController.java @@ -44,8 +44,9 @@ public class SimLockPreferenceController extends BasePreferenceController { mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); - mSubscriptionManager = (SubscriptionManager) context - .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + mSubscriptionManager = ((SubscriptionManager) context + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)) + .createForAllUserProfiles(); mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); } diff --git a/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java index f38cc4948d4..10e397c1b14 100644 --- a/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java @@ -77,6 +77,7 @@ public class SimLockPreferenceControllerTest { ShadowApplication shadowApplication = ShadowApplication.getInstance(); shadowApplication.setSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE, mSubscriptionManager); + when(mSubscriptionManager.createForAllUserProfiles()).thenReturn(mSubscriptionManager); shadowApplication.setSystemService(Context.CARRIER_CONFIG_SERVICE, mCarrierManager); shadowApplication.setSystemService(Context.USER_SERVICE, mUserManager); shadowApplication.setSystemService(Context.TELEPHONY_SERVICE, mTelephonyManager); From ae8b9b5fe26b5c3a65ece0a7052f3fea5ef71515 Mon Sep 17 00:00:00 2001 From: Ling Ma Date: Mon, 22 Jan 2024 22:14:08 +0000 Subject: [PATCH 06/20] Wrap subManager creation with createForAllProfiles Since U, a sub can be associated with a particular profile, and thus in V we enforce filtering on the caller and only show the subs that are associated with the caller. However, in some cases the caller indeed needs to see all subs regardless of its association, e.g. sysUI. Therefore, a param isForAllProfile is added to indicate whether the caller intends to see all subs. Bug: 296076674 Test: voice call + data browsing Flag: ACONFIG com.android.internal.telephony.flags.enforce_subscription_user_filter DEVELOPMENT Change-Id: I88e29b445a11f36e1a3db80368bb9e46ef06eac3 --- .../settings/sim/EnableAutoDataSwitchDialogFragment.java | 2 +- .../settings/sim/SelectSpecificDataSimDialogFragment.java | 2 +- src/com/android/settings/sim/SimListDialogFragment.java | 2 +- .../settings/sim/EnableAutoDataSwitchDialogFragmentTest.java | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragment.java b/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragment.java index b1b5f8e69ed..bd6a394f597 100644 --- a/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragment.java +++ b/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragment.java @@ -195,7 +195,7 @@ public class EnableAutoDataSwitchDialogFragment extends SimDialogFragment implem } private SubscriptionManager getSubscriptionManager() { - return getContext().getSystemService(SubscriptionManager.class); + return getContext().getSystemService(SubscriptionManager.class).createForAllUserProfiles(); } @VisibleForTesting diff --git a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java index 37f5445238e..b0b65f66e2e 100644 --- a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java +++ b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java @@ -194,7 +194,7 @@ public class SelectSpecificDataSimDialogFragment extends SimDialogFragment imple @VisibleForTesting protected SubscriptionManager getSubscriptionManager() { - return getContext().getSystemService(SubscriptionManager.class); + return getContext().getSystemService(SubscriptionManager.class).createForAllUserProfiles(); } @Override diff --git a/src/com/android/settings/sim/SimListDialogFragment.java b/src/com/android/settings/sim/SimListDialogFragment.java index db2c4dc224d..fd44dc74a67 100644 --- a/src/com/android/settings/sim/SimListDialogFragment.java +++ b/src/com/android/settings/sim/SimListDialogFragment.java @@ -125,7 +125,7 @@ public class SimListDialogFragment extends SimDialogFragment { protected List getCurrentSubscriptions() { final SubscriptionManager manager = getContext().getSystemService( - SubscriptionManager.class); + SubscriptionManager.class).createForAllUserProfiles(); return manager.getActiveSubscriptionInfoList(); } diff --git a/tests/robotests/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragmentTest.java b/tests/robotests/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragmentTest.java index ad60d06dc04..24b82b2fffb 100644 --- a/tests/robotests/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragmentTest.java @@ -68,6 +68,7 @@ public class EnableAutoDataSwitchDialogFragmentTest doReturn(mContext).when(mFragment).getContext(); doReturn(mSubscriptionManager).when(mContext).getSystemService(SubscriptionManager.class); + doReturn(mSubscriptionManager).when(mSubscriptionManager).createForAllUserProfiles(); doReturn(mTelephonyManager).when(mContext).getSystemService(TelephonyManager.class); doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt()); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); From 27a8deeb4aae849720d2ded63778aac8b7442edf Mon Sep 17 00:00:00 2001 From: Charlotte Lu Date: Tue, 23 Jan 2024 12:59:23 +0800 Subject: [PATCH 07/20] Modify the format of the wep warning dialog Test: Visual Test Fix: 321855606 Change-Id: I1156f3305685f2253752a7a1d99b16d3eede55f9 --- .../network/WepNetworkDialogActivity.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/com/android/settings/network/WepNetworkDialogActivity.kt b/src/com/android/settings/network/WepNetworkDialogActivity.kt index 2fa87849ef1..d69630f6826 100644 --- a/src/com/android/settings/network/WepNetworkDialogActivity.kt +++ b/src/com/android/settings/network/WepNetworkDialogActivity.kt @@ -51,23 +51,24 @@ class WepNetworkDialogActivity : SpaBaseDialogActivity() { confirmButton = AlertDialogButton( getString(R.string.wifi_settings_ssid_block_button_close) ) { finish() }, - dismissButton = AlertDialogButton( - getString(R.string.wifi_settings_wep_networks_button_allow) - ) { - SubSettingLauncher(context) - .setTitleText(context.getText(R.string.network_and_internet_preferences_title)) - .setSourceMetricsCategory(SettingsEnums.CONFIGURE_WIFI) - .setDestination(ConfigureWifiSettings::class.java.getName()) - .launch() - finish() - }, + dismissButton = if (wifiManager?.isWepSupported == true) + AlertDialogButton( + getString(R.string.wifi_settings_wep_networks_button_allow) + ) { + SubSettingLauncher(context) + .setTitleText(context.getText(R.string.network_and_internet_preferences_title)) + .setSourceMetricsCategory(SettingsEnums.CONFIGURE_WIFI) + .setDestination(ConfigureWifiSettings::class.java.getName()) + .launch() + finish() + } else null, title = String.format( getString(R.string.wifi_settings_wep_networks_blocked_title), intent.getStringExtra(SSID) ?: SSID ), text = { Text( - if (wifiManager?.isWepSupported == false) + if (wifiManager?.isWepSupported == true) getString(R.string.wifi_settings_wep_networks_summary_toggle_off) else getString(R.string.wifi_settings_wep_networks_summary_blocked_by_carrier), modifier = Modifier.fillMaxWidth(), From 2f57f538315c335ad9649285260094fcb66b6d03 Mon Sep 17 00:00:00 2001 From: Hakjun Choi Date: Tue, 9 Jan 2024 20:00:15 +0000 Subject: [PATCH 08/20] Add Satellite Messaging activity Add satellite messaging activity into Settings for carrier satellite service Bug: 319035517 Test: manual 1. Set CarrierConfig Key KEY_SATELLITE_ATTACH_SUPPORTED_BOOL as false then verify satellite messaging menu is not shwon from Settings > Network & internet > SIMs > test sim 2. Set CarrierConfig key KEY_SATELLITE_ATTACH_SUPPORTED_BOOL as true 3. Insert test SIM 3. Get into to Settings > Network & internet > SIMs > test sim > verify Satellite messaging is shown 4. Verify whether descriptions are as directed. icons are as directed. 5. Add satellite messaging does not work when click 6. Invoke web browser with test website when click 'More about satellite messaging' 7. Replace SIM of which MCC/MNC is not test sim 8. verify no action happen when click 'More about satellite messaging' Change-Id: Iee8fad58b41cbca34f373d2a9df1d074c447cfb1 --- AndroidManifest.xml | 23 +- res/drawable/ic_block_24px.xml | 25 +++ res/drawable/ic_check_circle_24px.xml | 25 +++ res/drawable/ic_satellite_alt_24px.xml | 25 +++ .../ic_signal_cellular_nodata_24px.xml | 25 +++ ...te_more_information_background_outline.xml | 24 +++ ...ellite_setting_more_information_layout.xml | 50 +++++ res/values/strings.xml | 41 ++++ res/xml/mobile_network_settings.xml | 12 +- res/xml/satellite_setting.xml | 65 ++++++ src/com/android/settings/Settings.java | 1 + .../core/gateway/SettingsGateway.java | 2 + .../telephony/MobileNetworkSettings.java | 5 + .../network/telephony/SatelliteSetting.java | 198 ++++++++++++++++++ .../SatelliteSettingPreferenceController.java | 139 ++++++++++++ 15 files changed, 657 insertions(+), 3 deletions(-) create mode 100644 res/drawable/ic_block_24px.xml create mode 100644 res/drawable/ic_check_circle_24px.xml create mode 100644 res/drawable/ic_satellite_alt_24px.xml create mode 100644 res/drawable/ic_signal_cellular_nodata_24px.xml create mode 100644 res/drawable/satellite_more_information_background_outline.xml create mode 100644 res/layout/satellite_setting_more_information_layout.xml create mode 100644 res/xml/satellite_setting.xml create mode 100644 src/com/android/settings/network/telephony/SatelliteSetting.java create mode 100644 src/com/android/settings/network/telephony/SatelliteSettingPreferenceController.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2ba87d7099a..64455b6746f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -627,6 +627,27 @@ android:value="true" /> + + + + + + + + + + + + + + + + android:value="true" /> + + + diff --git a/res/drawable/ic_check_circle_24px.xml b/res/drawable/ic_check_circle_24px.xml new file mode 100644 index 00000000000..c0fdefbee19 --- /dev/null +++ b/res/drawable/ic_check_circle_24px.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/drawable/ic_satellite_alt_24px.xml b/res/drawable/ic_satellite_alt_24px.xml new file mode 100644 index 00000000000..f9ca7dc6679 --- /dev/null +++ b/res/drawable/ic_satellite_alt_24px.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/drawable/ic_signal_cellular_nodata_24px.xml b/res/drawable/ic_signal_cellular_nodata_24px.xml new file mode 100644 index 00000000000..9b9f3918d14 --- /dev/null +++ b/res/drawable/ic_signal_cellular_nodata_24px.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/drawable/satellite_more_information_background_outline.xml b/res/drawable/satellite_more_information_background_outline.xml new file mode 100644 index 00000000000..b11ef07507d --- /dev/null +++ b/res/drawable/satellite_more_information_background_outline.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/res/layout/satellite_setting_more_information_layout.xml b/res/layout/satellite_setting_more_information_layout.xml new file mode 100644 index 00000000000..ce2fabe23d2 --- /dev/null +++ b/res/layout/satellite_setting_more_information_layout.xml @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 35c175997c0..8eee0957150 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3256,6 +3256,10 @@ Communal settings + + + Satellite Messaging + APNs @@ -11405,6 +11409,43 @@ App data usage Invalid Network Mode %1$d. Ignore. + + Satellite messaging + + Send and receive text messages by satellite. Included with your account. + + Send and receive text messages by satellite. Non included with your account. + + Satellite messaging + + About satellite messaging + + You can send and receive text messages by satellite as part of an eligible %1$s account + + Your %1$s plan + + Satellite messaging is included with your account + + Satellite messaging isn\u2019t included with your account + + Add satellite messaging + + How it works + + When you don\u2019t have a mobile network + + Your phone will auto-connect to a satellite. For the best connection, keep a clear view of the sky. + + After your phone connects to a satellite + + You can text anyone, including emergency services. Your phone will reconnect to a mobile network when available. + + Satellite messaging may take longer and is available only in some areas, Weather and certain structures may affect your satellite connection. Calling by satellite isn\u2019t available.\n\nIt may take some time for changes to your account to show in Settings. Contact %1$s for details. + + More about satellite messaging + + + Access Point Names diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 038688e390c..b5d0c5939ed 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -208,6 +208,7 @@ + + @@ -249,7 +257,7 @@ android:title="@string/require_cellular_encryption_title" android:summary="@string/require_cellular_encryption_summary" settings:controller= - "com.android.settings.network.telephony.NullAlgorithmsPreferenceController" /> + "com.android.settings.network.telephony.NullAlgorithmsPreferenceController"/> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 3e48a9cf1e8..230c103128b 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -345,6 +345,7 @@ public class Settings extends SettingsActivity { /* empty */ } + public static class SatelliteSettingActivity extends SettingsActivity { /* empty */ } public static class ApnSettingsActivity extends SettingsActivity { /* empty */ } public static class WifiCallingSettingsActivity extends SettingsActivity { /* empty */ } public static class MemorySettingsActivity extends SettingsActivity { /* empty */ } diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 3433e12ad33..a5a21314ec8 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -143,6 +143,7 @@ import com.android.settings.network.apn.ApnEditor; import com.android.settings.network.apn.ApnSettings; import com.android.settings.network.telephony.MobileNetworkSettings; import com.android.settings.network.telephony.NetworkSelectSettings; +import com.android.settings.network.telephony.SatelliteSetting; import com.android.settings.network.tether.TetherSettings; import com.android.settings.nfc.PaymentSettings; import com.android.settings.notification.ConfigureNotificationSettings; @@ -300,6 +301,7 @@ public class SettingsGateway { AppNotificationSettings.class.getName(), NotificationAssistantPicker.class.getName(), ChannelNotificationSettings.class.getName(), + SatelliteSetting.class.getName(), ApnSettings.class.getName(), ApnEditor.class.getName(), WifiCallingSettings.class.getName(), diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index b4b40efae9b..4188f8d1ad7 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -255,6 +255,11 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme roamingPreferenceController.init(getFragmentManager(), mSubId, mMobileNetworkInfoEntity); } + final SatelliteSettingPreferenceController satelliteSettingPreferenceController = use( + SatelliteSettingPreferenceController.class); + if (satelliteSettingPreferenceController != null) { + satelliteSettingPreferenceController.init(mSubId); + } use(ApnPreferenceController.class).init(mSubId); use(CarrierPreferenceController.class).init(mSubId); use(DataUsagePreferenceController.class).init(mSubId); diff --git a/src/com/android/settings/network/telephony/SatelliteSetting.java b/src/com/android/settings/network/telephony/SatelliteSetting.java new file mode 100644 index 00000000000..ecfa8e4000a --- /dev/null +++ b/src/com/android/settings/network/telephony/SatelliteSetting.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2024 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.network.telephony; + +import android.app.Activity; +import android.app.settings.SettingsEnums; +import android.content.Intent; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.UserManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.satellite.SatelliteManager; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.style.StyleSpan; +import android.text.style.UnderlineSpan; +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; + +import com.android.settings.R; +import com.android.settings.dashboard.RestrictedDashboardFragment; +import com.android.settingslib.HelpUtils; +import com.android.settingslib.Utils; +import com.android.settingslib.widget.FooterPreference; + +import java.util.Set; + +/** Handle Satellite Setting Preference Layout. */ +public class SatelliteSetting extends RestrictedDashboardFragment { + private static final String TAG = "SatelliteSetting"; + public static final String PREF_KEY_ABOUT_SATELLITE_MESSAGING = "key_about_satellite_messaging"; + public static final String PREF_KEY_CATEGORY_YOUR_SATELLITE_PLAN = + "key_category_your_satellite_plan"; + public static final String PREF_KEY_YOUR_SATELLITE_PLAN = "key_your_satellite_plan"; + public static final String PREF_KEY_CATEGORY_HOW_IT_WORKS = "key_category_how_it_works"; + private static final String KEY_FOOTER_PREFERENCE = "satellite_setting_extra_info_footer_pref"; + public static final String SUB_ID = "sub_id"; + + private Activity mActivity; + private TelephonyManager mTelephonymanager; + private SatelliteManager mSatelliteManager; + private int mSubId; + + public SatelliteSetting() { + super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.SATELLITE_SETTING; + } + + @Override + public void onCreate(@NonNull Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mActivity = getActivity(); + mTelephonymanager = mActivity.getSystemService(TelephonyManager.class); + mSatelliteManager = mActivity.getSystemService(SatelliteManager.class); + mSubId = mActivity.getIntent().getIntExtra(SUB_ID, + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + updateDynamicPreferenceViews(); + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.satellite_setting; + } + + private void updateDynamicPreferenceViews() { + String operatorName = mTelephonymanager.getSimOperatorName(mSubId); + boolean isSatelliteEligible = isSatelliteEligible(); + + // About satellite messaging + Preference preference = findPreference(PREF_KEY_ABOUT_SATELLITE_MESSAGING); + preference.setTitle( + getResources().getString(R.string.title_about_satellite_setting, operatorName)); + + // Your mobile plan + PreferenceCategory prefCategory = findPreference(PREF_KEY_CATEGORY_YOUR_SATELLITE_PLAN); + prefCategory.setTitle(getResources().getString(R.string.category_title_your_satellite_plan, + operatorName)); + + preference = findPreference(PREF_KEY_YOUR_SATELLITE_PLAN); + Drawable icon; + if (isSatelliteEligible) { + /* In case satellite is allowed by carrier's entitlement server, the page will show + the check icon with guidance that satellite is included in user's mobile plan */ + preference.setTitle(R.string.title_have_satellite_plan); + icon = getResources().getDrawable(R.drawable.ic_check_circle_24px); + } else { + /* Or, it will show the blocked icon with the guidance that satellite is not included + in user's mobile plan */ + preference.setTitle(R.string.title_no_satellite_plan); + /* And, the link url provides more information via web page will be shown */ + SpannableString spannable = new SpannableString( + getResources().getString(R.string.summary_add_satellite_setting)); + spannable.setSpan(new UnderlineSpan(), 0, spannable.length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + spannable.setSpan(new StyleSpan(Typeface.BOLD), 0, spannable.length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + preference.setSummary(spannable); + /* The link will lead users to a guide page */ + preference.setOnPreferenceClickListener(pref -> { + String url = getResources().getString(R.string.more_info_satellite_messaging_link); + if (!url.isEmpty()) { + Uri uri = Uri.parse(url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + } + return true; + }); + icon = getResources().getDrawable(R.drawable.ic_block_24px); + } + icon.setTintList(Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)); + preference.setIcon(icon); + + /* Composes "How it works" section, which guides how users can use satellite messaging, when + satellite messaging is included in user's mobile plan, or it'll will be grey out. */ + if (!isSatelliteEligible) { + PreferenceCategory category = findPreference(PREF_KEY_CATEGORY_HOW_IT_WORKS); + category.setEnabled(false); + category.setShouldDisableView(true); + } + + // More about satellite messaging + FooterPreference footerPreference = findPreference(KEY_FOOTER_PREFERENCE); + if (footerPreference != null) { + footerPreference.setSummary( + getResources().getString(R.string.satellite_setting_summary_more_information, + operatorName)); + + final String[] link = new String[1]; + link[0] = getResources().getString(R.string.more_info_satellite_messaging_link); + footerPreference.setLearnMoreAction(view -> { + if (!link[0].isEmpty()) { + Intent helpIntent = HelpUtils.getHelpIntent(mActivity, link[0], + this.getClass().getName()); + if (helpIntent != null) { + mActivity.startActivityForResult(helpIntent, /*requestCode=*/ 0); + } + } + }); + footerPreference.setLearnMoreText( + getResources().getString(R.string.more_about_satellite_messaging)); + + // TODO : b/320467418 add rounded rectangle border line to footer preference. + } + } + + private boolean isSatelliteEligible() { + try { + Set restrictionReason = + mSatelliteManager.getSatelliteAttachRestrictionReasonsForCarrier(mSubId); + return !restrictionReason.contains( + SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT); + } catch (SecurityException | IllegalStateException | IllegalArgumentException ex) { + loge(ex.toString()); + return false; + } + } + + private static void loge(String message) { + Log.e(TAG, message); + } +} diff --git a/src/com/android/settings/network/telephony/SatelliteSettingPreferenceController.java b/src/com/android/settings/network/telephony/SatelliteSettingPreferenceController.java new file mode 100644 index 00000000000..7de7fcba64e --- /dev/null +++ b/src/com/android/settings/network/telephony/SatelliteSettingPreferenceController.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 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.network.telephony; + +import android.content.Context; +import android.content.Intent; +import android.os.PersistableBundle; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.satellite.SatelliteManager; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.network.CarrierConfigCache; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +import java.util.Set; + +/** + * Preference controller for "Satellite Setting" + */ +public class SatelliteSettingPreferenceController extends + TelephonyBasePreferenceController implements LifecycleObserver, OnStart, OnStop { + + private static final String TAG = "SatelliteSettingPreferenceController"; + + CarrierConfigCache mCarrierConfigCache; + SatelliteManager mSatelliteManager; + @Nullable private Boolean mIsSatelliteEligible = null; + + public SatelliteSettingPreferenceController(@NonNull Context context, @NonNull String key) { + super(context, key); + mCarrierConfigCache = CarrierConfigCache.getInstance(context); + mSatelliteManager = context.getSystemService(SatelliteManager.class); + } + + @Override + public int getAvailabilityStatus(int subId) { + final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId); + final boolean isSatelliteAttachSupported = carrierConfig.getBoolean( + CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL); + + return isSatelliteAttachSupported ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public void onStart() { + } + + @Override + public void onStop() { + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + } + + @Override + public void updateState(@Nullable Preference preference) { + super.updateState(preference); + if (preference != null) { + updateSummary(preference); + } + } + + @Override + public boolean handlePreferenceTreeClick(@NonNull Preference preference) { + if (getPreferenceKey().equals(preference.getKey())) { + // This activity runs in phone process, we must use intent to start + final Intent intent = new Intent(Settings.ACTION_SATELLITE_SETTING); + // This will setup the Home and Search affordance + intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, true); + intent.putExtra(SatelliteSetting.SUB_ID, mSubId); + mContext.startActivity(intent); + return true; + } + + return false; + } + + /** + * Set subId for Satellite Settings page. + * @param subId subscription ID. + */ + public void init(int subId) { + logd("init(), subId=" + subId); + mSubId = subId; + } + + private void updateSummary(Preference preference) { + try { + Set restrictionReason = + mSatelliteManager.getSatelliteAttachRestrictionReasonsForCarrier(mSubId); + boolean isSatelliteEligible = !restrictionReason.contains( + SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT); + if (mIsSatelliteEligible == null || mIsSatelliteEligible != isSatelliteEligible) { + mIsSatelliteEligible = isSatelliteEligible; + String summary = mContext.getString( + mIsSatelliteEligible ? R.string.satellite_setting_enabled_summary + : R.string.satellite_setting_disabled_summary); + preference.setSummary(summary); + } + } catch (SecurityException | IllegalStateException | IllegalArgumentException ex) { + loge(ex.toString()); + preference.setSummary(R.string.satellite_setting_disabled_summary); + } + } + + private static void logd(String message) { + Log.d(TAG, message); + } + + private static void loge(String message) { + Log.e(TAG, message); + } +} From 41b12fe8eb86768495a91e0ad00a75e359d2bf6a Mon Sep 17 00:00:00 2001 From: Ze Li Date: Thu, 11 Jan 2024 18:29:28 +0800 Subject: [PATCH 09/20] [Connected devices page] Reorder devices by most recently used. Order the saved bluetooth devices by most recently connected in connected devices settings page. Test: atest: com.android.settings.connecteddevice.SavedDeviceGroupControllerTest, com.android.settings.connecteddevice.PreviouslyConnectedDevicePreferenceControllerTest Bug: 306160434 Change-Id: Id5ad8555a026d775d96ada37f989b4346336af93 --- ..._connecteddevice_flag_declarations.aconfig | 7 + ...lyConnectedDevicePreferenceController.java | 110 ++++++++++++--- .../SavedDeviceGroupController.java | 79 ++++++++++- ...nnectedDevicePreferenceControllerTest.java | 108 ++++++++++++++- .../SavedDeviceGroupControllerTest.java | 126 +++++++++++++++++- 5 files changed, 402 insertions(+), 28 deletions(-) diff --git a/aconfig/settings_connecteddevice_flag_declarations.aconfig b/aconfig/settings_connecteddevice_flag_declarations.aconfig index 1a3afed0a1e..5ba21296a6d 100644 --- a/aconfig/settings_connecteddevice_flag_declarations.aconfig +++ b/aconfig/settings_connecteddevice_flag_declarations.aconfig @@ -27,3 +27,10 @@ flag { description: "Gates whether to require an auth challenge for changing USB preferences" bug: "317367746" } + +flag { + name: "enable_saved_devices_order_by_recency" + namespace: "pixel_cross_device_control" + description: "Order the saved bluetooth devices by most recently connected." + bug: "306160434" +} \ No newline at end of file diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java index 7a2ae04f3e1..33f8b735588 100644 --- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java @@ -17,6 +17,7 @@ package com.android.settings.connecteddevice; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -36,13 +37,16 @@ import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback { @@ -56,11 +60,12 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc private final List mDevicesList = new ArrayList<>(); private final List mDockDevicesList = new ArrayList<>(); + private final Map mDevicePreferenceMap = new HashMap<>(); + private final BluetoothAdapter mBluetoothAdapter; private PreferenceGroup mPreferenceGroup; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; private DockUpdater mSavedDockUpdater; - private BluetoothAdapter mBluetoothAdapter; @VisibleForTesting Preference mSeeAllPreference; @@ -81,7 +86,11 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mSavedDockUpdater = FeatureFactory.getFeatureFactory().getDockUpdaterFeatureProvider() .getSavedDockUpdater(context, this); mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (Flags.enableSavedDevicesOrderByRecency()) { + mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter(); + } else { + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + } } @Override @@ -114,6 +123,9 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mContext.registerReceiver(mReceiver, mIntentFilter, Context.RECEIVER_EXPORTED_UNAUDITED); mBluetoothDeviceUpdater.refreshPreference(); + if (Flags.enableSavedDevicesOrderByRecency()) { + updatePreferenceGroup(); + } } @Override @@ -131,19 +143,37 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc @Override public void onDeviceAdded(Preference preference) { - final List bluetoothDevices = - mBluetoothAdapter.getMostRecentlyConnectedDevices(); - final int index = preference instanceof BluetoothDevicePreference - ? bluetoothDevices.indexOf(((BluetoothDevicePreference) preference) - .getBluetoothDevice().getDevice()) : DOCK_DEVICE_INDEX; - if (DEBUG) { - Log.d(TAG, "onDeviceAdded() " + preference.getTitle() + ", index of : " + index); - for (BluetoothDevice device : bluetoothDevices) { - Log.d(TAG, "onDeviceAdded() most recently device : " + device.getName()); + if (Flags.enableSavedDevicesOrderByRecency()) { + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.put( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.add(preference); } + if (DEBUG) { + Log.d(TAG, "onDeviceAdded() " + preference.getTitle()); + } + updatePreferenceGroup(); + } else { + final List bluetoothDevices = + mBluetoothAdapter.getMostRecentlyConnectedDevices(); + final int index = + preference instanceof BluetoothDevicePreference + ? bluetoothDevices.indexOf( + ((BluetoothDevicePreference) preference) + .getBluetoothDevice() + .getDevice()) + : DOCK_DEVICE_INDEX; + if (DEBUG) { + Log.d(TAG, "onDeviceAdded() " + preference.getTitle() + ", index of : " + index); + for (BluetoothDevice device : bluetoothDevices) { + Log.d(TAG, "onDeviceAdded() most recently device : " + device.getName()); + } + } + addPreference(index, preference); + updatePreferenceVisibility(); } - addPreference(index, preference); - updatePreferenceVisibility(); } private void addPreference(int index, Preference preference) { @@ -194,13 +224,57 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc @Override public void onDeviceRemoved(Preference preference) { - if (preference instanceof BluetoothDevicePreference) { - mDevicesList.remove(preference); + if (Flags.enableSavedDevicesOrderByRecency()) { + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.remove( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.remove(preference); + } + if (DEBUG) { + Log.d(TAG, "onDeviceRemoved() " + preference.getTitle()); + } + updatePreferenceGroup(); } else { - mDockDevicesList.remove(preference); - } + if (preference instanceof BluetoothDevicePreference) { + mDevicesList.remove(preference); + } else { + mDockDevicesList.remove(preference); + } - addPreference(); + addPreference(); + updatePreferenceVisibility(); + } + } + + /** Sort the preferenceGroup by most recently used. */ + public void updatePreferenceGroup() { + mPreferenceGroup.removeAll(); + mPreferenceGroup.addPreference(mSeeAllPreference); + if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { + // Bluetooth is supported + int order = 0; + for (BluetoothDevice device : mBluetoothAdapter.getMostRecentlyConnectedDevices()) { + Preference preference = mDevicePreferenceMap.getOrDefault(device, null); + if (preference != null) { + preference.setOrder(order); + mPreferenceGroup.addPreference(preference); + order += 1; + } + if (order == MAX_DEVICE_NUM) { + break; + } + } + for (Preference preference : mDockDevicesList) { + if (order == MAX_DEVICE_NUM) { + break; + } + preference.setOrder(order); + mPreferenceGroup.addPreference(preference); + order += 1; + } + } updatePreferenceVisibility(); } diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java index fb35dd9f58c..c73481d3cd3 100644 --- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java +++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java @@ -15,6 +15,9 @@ */ package com.android.settings.connecteddevice; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.pm.PackageManager; @@ -23,18 +26,25 @@ import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; +import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.overlay.DockUpdaterFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Controller to maintain the {@link PreferenceGroup} for all * saved devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference} @@ -45,6 +55,10 @@ public class SavedDeviceGroupController extends BasePreferenceController private static final String KEY = "saved_device_list"; + private final Map mDevicePreferenceMap = new HashMap<>(); + private final List mDockDevicesList = new ArrayList<>(); + private final BluetoothAdapter mBluetoothAdapter; + @VisibleForTesting PreferenceGroup mPreferenceGroup; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; @@ -57,6 +71,7 @@ public class SavedDeviceGroupController extends BasePreferenceController FeatureFactory.getFeatureFactory().getDockUpdaterFeatureProvider(); mSavedDockUpdater = dockUpdaterFeatureProvider.getSavedDockUpdater(context, this); + mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter(); } @Override @@ -64,6 +79,9 @@ public class SavedDeviceGroupController extends BasePreferenceController mBluetoothDeviceUpdater.registerCallback(); mSavedDockUpdater.registerCallback(); mBluetoothDeviceUpdater.refreshPreference(); + if (Flags.enableSavedDevicesOrderByRecency()) { + updatePreferenceGroup(); + } } @Override @@ -101,17 +119,63 @@ public class SavedDeviceGroupController extends BasePreferenceController @Override public void onDeviceAdded(Preference preference) { - if (mPreferenceGroup.getPreferenceCount() == 0) { - mPreferenceGroup.setVisible(true); + if (Flags.enableSavedDevicesOrderByRecency()) { + mPreferenceGroup.addPreference(preference); + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.put( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.add(preference); + } + updatePreferenceGroup(); + } else { + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(true); + } + mPreferenceGroup.addPreference(preference); } - mPreferenceGroup.addPreference(preference); } @Override public void onDeviceRemoved(Preference preference) { - mPreferenceGroup.removePreference(preference); - if (mPreferenceGroup.getPreferenceCount() == 0) { + if (Flags.enableSavedDevicesOrderByRecency()) { + mPreferenceGroup.removePreference(preference); + if (preference instanceof BluetoothDevicePreference) { + mDevicePreferenceMap.remove( + ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), + preference); + } else { + mDockDevicesList.remove(preference); + } + updatePreferenceGroup(); + } else { + mPreferenceGroup.removePreference(preference); + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(false); + } + } + } + + /** Sort the preferenceGroup by most recently used. */ + public void updatePreferenceGroup() { + if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { + // Bluetooth is unsupported or disabled mPreferenceGroup.setVisible(false); + } else { + mPreferenceGroup.setVisible(true); + int order = 0; + for (BluetoothDevice device : mBluetoothAdapter.getMostRecentlyConnectedDevices()) { + Preference preference = mDevicePreferenceMap.getOrDefault(device, null); + if (preference != null) { + preference.setOrder(order); + order += 1; + } + } + for (Preference preference : mDockDevicesList) { + preference.setOrder(order); + order += 1; + } } } @@ -130,4 +194,9 @@ public class SavedDeviceGroupController extends BasePreferenceController public void setSavedDockUpdater(DockUpdater savedDockUpdater) { mSavedDockUpdater = savedDockUpdater; } + + @VisibleForTesting + void setPreferenceGroup(PreferenceGroup preferenceGroup) { + mPreferenceGroup = preferenceGroup; + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java index e351b71b3dc..005c1311747 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java @@ -27,9 +27,14 @@ import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.Pair; import androidx.preference.Preference; @@ -42,11 +47,13 @@ import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.widget.SingleTargetGearPreference; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -70,6 +77,9 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { private static final String FAKE_ADDRESS_4 = "AA:AA:AA:AA:AA:04"; private static final String FAKE_ADDRESS_5 = "AA:AA:AA:AA:AA:05"; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private DashboardFragment mDashboardFragment; @Mock @@ -105,6 +115,9 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Mock private Drawable mDrawable; + @Mock private BluetoothManager mBluetoothManager; + @Mock private BluetoothAdapter mBluetoothAdapter; + private Context mContext; private PreviouslyConnectedDevicePreferenceController mPreConnectedDeviceController; private PreferenceGroup mPreferenceGroup; @@ -117,10 +130,8 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { mContext = spy(RuntimeEnvironment.application); doReturn(mContext).when(mDashboardFragment).getContext(); doReturn(mPackageManager).when(mContext).getPackageManager(); - mPreConnectedDeviceController = - new PreviouslyConnectedDevicePreferenceController(mContext, KEY); - mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); - mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater); + when(mContext.getSystemService(BluetoothManager.class)).thenReturn(mBluetoothManager); + when(mBluetoothManager.getAdapter()).thenReturn(mBluetoothAdapter); mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1); @@ -145,7 +156,13 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { mMostRecentlyConnectedDevices.add(mBluetoothDevice4); mMostRecentlyConnectedDevices.add(mBluetoothDevice3); mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(mMostRecentlyConnectedDevices); + when(mBluetoothAdapter.getMostRecentlyConnectedDevices()) + .thenReturn(mMostRecentlyConnectedDevices); + mPreConnectedDeviceController = + new PreviouslyConnectedDevicePreferenceController(mContext, KEY); + mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); + mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater); mPreferenceGroup = spy(new PreferenceCategory(mContext)); doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager(); mPreferenceGroup.setVisible(false); @@ -249,6 +266,7 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) public void onDeviceAdded_addPreferenceNotExistInRecentlyDevices_noCrash() { final BluetoothDevicePreference preference = new BluetoothDevicePreference( mContext, mCachedDevice5, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT); @@ -259,6 +277,18 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void onDeviceAdded_addPreferenceNotExistInRecentlyDevices_doNothing() { + final BluetoothDevicePreference preference = new BluetoothDevicePreference( + mContext, mCachedDevice5, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT); + + mPreConnectedDeviceController.onDeviceAdded(preference); + + // 1 see all preference + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1); + } + @Test public void onDeviceRemoved_removeLastDevice_showSeeAllPreference() { final BluetoothDevicePreference preference1 = new BluetoothDevicePreference( @@ -277,6 +307,7 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Test public void updatePreferenceVisibility_bluetoothIsEnable_shouldShowCorrectText() { mShadowBluetoothAdapter.setEnabled(true); + when(mBluetoothAdapter.isEnabled()).thenReturn(true); mPreConnectedDeviceController.updatePreferenceVisibility(); verify(mSeeAllPreference).setSummary(""); @@ -285,9 +316,78 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Test public void updatePreferenceVisibility_bluetoothIsDisable_shouldShowCorrectText() { mShadowBluetoothAdapter.setEnabled(false); + when(mBluetoothAdapter.isEnabled()).thenReturn(false); mPreConnectedDeviceController.updatePreferenceVisibility(); verify(mSeeAllPreference).setSummary( mContext.getString(R.string.connected_device_see_all_summary)); } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsEnable_shouldOrderByMostRecentlyConnected() { + when(mBluetoothAdapter.isEnabled()).thenReturn(true); + final BluetoothDevicePreference preference4 = + new BluetoothDevicePreference( + mContext, + mCachedDevice4, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mPreConnectedDeviceController.onDeviceAdded(preference4); + mPreConnectedDeviceController.onDeviceAdded(preference3); + mPreConnectedDeviceController.onDeviceAdded(preference2); + + mPreConnectedDeviceController.updatePreferenceGroup(); + + // Refer to the order of {@link #mMostRecentlyConnectedDevices}, the first one is see all + // preference + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(4); + assertThat(preference2.getOrder()).isEqualTo(0); + assertThat(preference4.getOrder()).isEqualTo(1); + assertThat(preference3.getOrder()).isEqualTo(2); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsDisable_shouldShowOnlySeeAllPreference() { + when(mBluetoothAdapter.isEnabled()).thenReturn(false); + final BluetoothDevicePreference preference4 = + new BluetoothDevicePreference( + mContext, + mCachedDevice4, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mPreConnectedDeviceController.onDeviceAdded(preference4); + mPreConnectedDeviceController.onDeviceAdded(preference3); + mPreConnectedDeviceController.onDeviceAdded(preference2); + + mPreConnectedDeviceController.updatePreferenceGroup(); + + // 1 see all preference + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java index d2c44f938e4..81c0c35ccd6 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java @@ -25,29 +25,52 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.util.Pair; import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceManager; +import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.ArrayList; +import java.util.List; + @RunWith(RobolectricTestRunner.class) public class SavedDeviceGroupControllerTest { + private static final String FAKE_ADDRESS_1 = "AA:AA:AA:AA:AA:01"; + private static final String FAKE_ADDRESS_2 = "AA:AA:AA:AA:AA:02"; + private static final String FAKE_ADDRESS_3 = "AA:AA:AA:AA:AA:03"; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private DashboardFragment mDashboardFragment; @Mock @@ -56,23 +79,57 @@ public class SavedDeviceGroupControllerTest { private DockUpdater mSavedDockUpdater; @Mock private PackageManager mPackageManager; + @Mock private BluetoothManager mBluetoothManager; + @Mock private BluetoothAdapter mBluetoothAdapter; + @Mock private CachedBluetoothDevice mCachedDevice1; + @Mock private CachedBluetoothDevice mCachedDevice2; + @Mock private CachedBluetoothDevice mCachedDevice3; + @Mock private BluetoothDevice mBluetoothDevice1; + @Mock private BluetoothDevice mBluetoothDevice2; + @Mock private BluetoothDevice mBluetoothDevice3; + @Mock private Drawable mDrawable; + @Mock private PreferenceManager mPreferenceManager; private Context mContext; private SavedDeviceGroupController mSavedDeviceGroupController; private LifecycleOwner mLifecycleOwner; private Lifecycle mLifecycle; + private PreferenceGroup mPreferenceGroup; @Before public void setUp() { MockitoAnnotations.initMocks(this); + Pair pairs = new Pair<>(mDrawable, "fake_device"); mContext = spy(RuntimeEnvironment.application); mLifecycleOwner = () -> mLifecycle; mLifecycle = new Lifecycle(mLifecycleOwner); doReturn(mContext).when(mDashboardFragment).getContext(); doReturn(mPackageManager).when(mContext).getPackageManager(); + + when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1); + when(mCachedDevice1.getAddress()).thenReturn(FAKE_ADDRESS_1); + when(mCachedDevice1.getDrawableWithDescription()).thenReturn(pairs); + when(mCachedDevice2.getDevice()).thenReturn(mBluetoothDevice2); + when(mCachedDevice2.getAddress()).thenReturn(FAKE_ADDRESS_2); + when(mCachedDevice2.getDrawableWithDescription()).thenReturn(pairs); + when(mCachedDevice3.getDevice()).thenReturn(mBluetoothDevice3); + when(mCachedDevice3.getAddress()).thenReturn(FAKE_ADDRESS_3); + when(mCachedDevice3.getDrawableWithDescription()).thenReturn(pairs); + final List mMostRecentlyConnectedDevices = new ArrayList<>(); + mMostRecentlyConnectedDevices.add(mBluetoothDevice1); + mMostRecentlyConnectedDevices.add(mBluetoothDevice2); + mMostRecentlyConnectedDevices.add(mBluetoothDevice3); + when(mContext.getSystemService(BluetoothManager.class)).thenReturn(mBluetoothManager); + when(mBluetoothManager.getAdapter()).thenReturn(mBluetoothAdapter); + when(mBluetoothAdapter.getMostRecentlyConnectedDevices()) + .thenReturn(mMostRecentlyConnectedDevices); + + mPreferenceGroup = spy(new PreferenceCategory(mContext)); + when(mPreferenceGroup.getPreferenceManager()).thenReturn(mPreferenceManager); mSavedDeviceGroupController = new SavedDeviceGroupController(mContext); mSavedDeviceGroupController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); mSavedDeviceGroupController.setSavedDockUpdater(mSavedDockUpdater); + mSavedDeviceGroupController.setPreferenceGroup(mPreferenceGroup); } @Test @@ -118,4 +175,71 @@ public class SavedDeviceGroupControllerTest { assertThat(mSavedDeviceGroupController.getAvailabilityStatus()).isEqualTo( AVAILABLE); } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsEnable_shouldOrderByMostRecentlyConnected() { + when(mBluetoothAdapter.isEnabled()).thenReturn(true); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference1 = + new BluetoothDevicePreference( + mContext, + mCachedDevice1, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mSavedDeviceGroupController.onDeviceAdded(preference3); + mSavedDeviceGroupController.onDeviceAdded(preference2); + mSavedDeviceGroupController.onDeviceAdded(preference1); + + mSavedDeviceGroupController.updatePreferenceGroup(); + + // Refer to the order of {@link #mMostRecentlyConnectedDevices} + assertThat(mPreferenceGroup.isVisible()).isTrue(); + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(3); + assertThat(preference1.getOrder()).isEqualTo(0); + assertThat(preference2.getOrder()).isEqualTo(1); + assertThat(preference3.getOrder()).isEqualTo(2); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SAVED_DEVICES_ORDER_BY_RECENCY) + public void updatePreferenceGroup_bluetoothIsDisable_shouldShowNoPreference() { + when(mBluetoothAdapter.isEnabled()).thenReturn(false); + final BluetoothDevicePreference preference3 = + new BluetoothDevicePreference( + mContext, + mCachedDevice3, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference2 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + final BluetoothDevicePreference preference1 = + new BluetoothDevicePreference( + mContext, + mCachedDevice2, + true, + BluetoothDevicePreference.SortType.TYPE_NO_SORT); + mSavedDeviceGroupController.onDeviceAdded(preference3); + mSavedDeviceGroupController.onDeviceAdded(preference2); + mSavedDeviceGroupController.onDeviceAdded(preference1); + + mSavedDeviceGroupController.updatePreferenceGroup(); + + assertThat(mPreferenceGroup.isVisible()).isFalse(); + } } From f27e9e6553b8d35cde0349e1aa0a2600f6f7b6d2 Mon Sep 17 00:00:00 2001 From: josephpv Date: Thu, 18 Jan 2024 14:21:22 +0000 Subject: [PATCH 10/20] Add skip button in PS account login error screen Recording link : b/320460786#comment5 Bug: 320460786 Test: Verified skip button in shown in error screen. Change-Id: I0b3b9258ad55069c9920d7fc9fb7f17d422297f1 --- .../privatespace_main_context_nav.xml | 3 +++ res/values/strings.xml | 10 +++++--- .../PrivateSpaceAccountLoginError.java | 25 ++++++++++++++++++- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/res/navigation/privatespace_main_context_nav.xml b/res/navigation/privatespace_main_context_nav.xml index 78494581ec2..f76afac4df1 100644 --- a/res/navigation/privatespace_main_context_nav.xml +++ b/res/navigation/privatespace_main_context_nav.xml @@ -52,6 +52,9 @@ + Done Scroll down to find private space - - Sign in to set up a private space + + Sign in - You need to sign in to an account to set up a private space + Sign in to an account to use with your private space + + Not now + + Continue Choose a lock for your private space diff --git a/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java index b64335bdf43..fcb93b1c38e 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java +++ b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java @@ -31,6 +31,7 @@ import android.view.ViewGroup; import androidx.activity.OnBackPressedCallback; import androidx.annotation.Nullable; +import androidx.navigation.fragment.NavHostFragment; import com.android.settings.R; import com.android.settings.core.InstrumentedFragment; @@ -55,11 +56,20 @@ public class PrivateSpaceAccountLoginError extends InstrumentedFragment { final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class); mixin.setPrimaryButton( new FooterButton.Builder(getContext()) - .setText(R.string.private_space_tryagain_label) + .setText(R.string.private_space_continue_login_label) .setListener(nextScreen()) .setButtonType(FooterButton.ButtonType.NEXT) .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) .build()); + mixin.setSecondaryButton( + new FooterButton.Builder(getContext()) + .setText(R.string.private_space_skip_login_label) + .setListener(onSkip()) + .setButtonType(FooterButton.ButtonType.CANCEL) + .setTheme( + androidx.appcompat.R.style + .Base_TextAppearance_AppCompat_Widget_Button) + .build()); OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) { @Override @@ -96,4 +106,17 @@ public class PrivateSpaceAccountLoginError extends InstrumentedFragment { } }; } + + private View.OnClickListener onSkip() { + return v -> { + mMetricsFeatureProvider.action( + getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SKIP_ACCOUNT_LOGIN); + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_ACCOUNT_LOGIN_SUCCESS, + false); + NavHostFragment.findNavController(PrivateSpaceAccountLoginError.this) + .navigate(R.id.action_success_fragment); + }; + } } From 97d36b771ad7231e97ba1ee678949c7036bf5cca Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Tue, 23 Jan 2024 13:59:45 +0000 Subject: [PATCH 11/20] Make MoreOptionsScope abstract class Bug: 321724969 Test: m Settings Test: unit test Change-Id: Iced9df83f600c86cc409abc040fb9ace0dcedf1e --- .../src/com/android/settings/spa/app/ResetAppPreferencesTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spa_unit/src/com/android/settings/spa/app/ResetAppPreferencesTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/ResetAppPreferencesTest.kt index ffd58313f1b..b63c28184c8 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/ResetAppPreferencesTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/ResetAppPreferencesTest.kt @@ -45,7 +45,7 @@ class ResetAppPreferencesTest { } private fun setResetAppPreferences() { - val fakeMoreOptionsScope = object : MoreOptionsScope { + val fakeMoreOptionsScope = object : MoreOptionsScope() { override fun dismiss() {} } composeTestRule.setContent { From ec273aca1279e1117137a051bdaf0785b171c3cf Mon Sep 17 00:00:00 2001 From: josephpv Date: Mon, 22 Jan 2024 16:21:58 +0000 Subject: [PATCH 12/20] Remove private space biometrics on switching to device lock Bug: 320685466 Test: Verified manually Change-Id: I5fe850ada9ae53bb1a00ed171beb6f8e099fbc0d --- src/com/android/settings/Utils.java | 62 +++++++++++++++++++ .../FaceFingerprintUnlockController.java | 3 + .../PrivateSpaceFacePreferenceController.java | 1 + ...eSpaceFingerprintPreferenceController.java | 1 + 4 files changed, 67 insertions(+) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 25fd3a20f68..108e331a090 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -54,7 +54,9 @@ import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; +import android.hardware.face.Face; import android.hardware.face.FaceManager; +import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.net.ConnectivityManager; import android.net.LinkAddress; @@ -1333,4 +1335,64 @@ public final class Utils extends com.android.settingslib.Utils { return dreamsSupported && (!dreamsOnlyEnabledForDockUser || canCurrentUserDream(context)); } + + /** + * Removes fingerprint templates enrolled for a given user. + * + * @param context application context. + * @param userId the id of the relevant user + */ + public static void removeEnrolledFingerprintForUser(Context context, int userId) { + FingerprintManager fingerprintManager = getFingerprintManagerOrNull(context); + if (fingerprintManager != null && fingerprintManager.hasEnrolledTemplates(userId)) { + fingerprintManager.removeAll(userId, + fingerprintManagerRemovalCallback(userId)); + } + } + + /** + * Removes face templates enrolled for a given user. + * + * @param context application context. + * @param userId the id of the relevant user + */ + public static void removeEnrolledFaceForUser(Context context, int userId) { + FaceManager faceManager = getFaceManagerOrNull(context); + if (faceManager != null && faceManager.hasEnrolledTemplates(userId)) { + faceManager.removeAll(userId, faceManagerRemovalCallback(userId)); + } + } + + private static FaceManager.RemovalCallback faceManagerRemovalCallback(int userId) { + return new FaceManager.RemovalCallback() { + @Override + public void onRemovalError(@Nullable Face face, int errMsgId, CharSequence err) { + Log.e(TAG, "Unable to remove face template for user " + userId + ", error: " + err); + } + + @Override + public void onRemovalSucceeded(Face face, int remaining) { + if (remaining == 0) { + Log.d(TAG, "Enrolled face templates removed for user " + userId); + } + } + }; + } + + private static FingerprintManager.RemovalCallback fingerprintManagerRemovalCallback( + int userId) { + return new FingerprintManager.RemovalCallback() { + @Override + public void onRemovalError(@Nullable Fingerprint fp, int errMsgId, CharSequence err) { + Log.e(TAG, "Unable to remove fingerprint for user " + userId + " , error: " + err); + } + + @Override + public void onRemovalSucceeded(Fingerprint fp, int remaining) { + if (remaining == 0) { + Log.d(TAG, "Enrolled fingerprints removed for user " + userId); + } + } + }; + } } diff --git a/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java b/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java index 271a2195d30..2e3f2840b70 100644 --- a/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java +++ b/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java @@ -23,6 +23,7 @@ import android.util.Log; import androidx.preference.Preference; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.biometrics.combination.CombinedBiometricStatusPreferenceController; import com.android.settings.privatespace.PrivateSpaceMaintainer; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -72,6 +73,8 @@ public class FaceFingerprintUnlockController extends CombinedBiometricStatusPref super.updateState(preference); preference.setEnabled(true); } else { + Utils.removeEnrolledFaceForUser(mContext, getUserId()); + Utils.removeEnrolledFingerprintForUser(mContext, getUserId()); preference.setSummary( mContext.getString(R.string.lock_settings_profile_unified_summary)); preference.setEnabled(false); diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java index 2a5ff885dfd..b841d9aded5 100644 --- a/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java +++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java @@ -74,6 +74,7 @@ public class PrivateSpaceFacePreferenceController extends BiometricFaceStatusPre super.updateState(preference); preference.setEnabled(true); } else { + Utils.removeEnrolledFaceForUser(mContext, getUserId()); preference.setSummary( mContext.getString(R.string.lock_settings_profile_unified_summary)); preference.setEnabled(false); diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java index b6c44574fc4..d48490460b5 100644 --- a/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java +++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java @@ -76,6 +76,7 @@ public class PrivateSpaceFingerprintPreferenceController super.updateState(preference); preference.setEnabled(true); } else { + Utils.removeEnrolledFingerprintForUser(mContext, getUserId()); preference.setSummary( mContext.getString(R.string.lock_settings_profile_unified_summary)); preference.setEnabled(false); From bd942cf0d352a54427e7a2cb41640cc3b0dfdf04 Mon Sep 17 00:00:00 2001 From: Kyle Zhang Date: Thu, 16 Nov 2023 22:36:57 +0000 Subject: [PATCH 13/20] Add test ForceL3FallbackPreferenceControllerTest Bug: 301669353 Test: atest -c ForceL3FallbackPreferenceControllerTest Change-Id: I182388fa67ac408a2fcf16a6b9599d0971c895d0 --- tests/unit/Android.bp | 1 + ...rceL3FallbackPreferenceControllerTest.java | 136 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 tests/unit/src/com/android/settings/development/widevine/ForceL3FallbackPreferenceControllerTest.java diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp index 327b6aad1c4..9d2d6865d07 100644 --- a/tests/unit/Android.bp +++ b/tests/unit/Android.bp @@ -32,6 +32,7 @@ android_test { "kotlinx_coroutines_test", "flag-junit", "Settings-testutils2", + "MediaDrmSettingsFlagsLib", // Don't add SettingsLib libraries here - you can use them directly as they are in the // instrumented Settings app. ], diff --git a/tests/unit/src/com/android/settings/development/widevine/ForceL3FallbackPreferenceControllerTest.java b/tests/unit/src/com/android/settings/development/widevine/ForceL3FallbackPreferenceControllerTest.java new file mode 100644 index 00000000000..f67a4af84fb --- /dev/null +++ b/tests/unit/src/com/android/settings/development/widevine/ForceL3FallbackPreferenceControllerTest.java @@ -0,0 +1,136 @@ +/* +* Copyright (C) 2023 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.development.widevine; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.junit.Assume.assumeNoException; + +import android.content.Context; +import android.media.MediaDrm; +import android.media.UnsupportedSchemeException; +import android.media.NotProvisionedException; +import android.sysprop.WidevineProperties; +import android.util.Log; +import android.content.Context; + +import com.android.settings.media_drm.Flags; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.preference.SwitchPreference; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.UUID; + +@RunWith(AndroidJUnit4.class) +public class ForceL3FallbackPreferenceControllerTest { + + private static final String PREF_KEY = "force_l3_fallback"; + private static final UUID WIDEVINE_UUID = + new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL); + private static final String TAG = "ForceL3FallbackPreferenceControllerTest"; + + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private Context mContext; + private ForceL3FallbackPreferenceController mController; + private SwitchPreference mPreference; + + @Before + public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + mController = new ForceL3FallbackPreferenceController(mContext, PREF_KEY); + mPreference = new SwitchPreference(mContext); + WidevineProperties.forcel3_enabled(false); + } + + @Test + public void updateState_flagEnabled_checkPreference() { + mSetFlagsRule.enableFlags(Flags.FLAG_FORCE_L3_ENABLED); + mController.updateState(mPreference); + assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.isChecked()).isFalse(); + assertThat(WidevineProperties.forcel3_enabled().orElse(false)).isFalse(); + + // Toggle to true + mController.setChecked(true); + mController.updateState(mPreference); + assertThat(WidevineProperties.forcel3_enabled().orElse(false)).isTrue(); + assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.isChecked()).isTrue(); + + // Toggle to false + mController.setChecked(false); + mController.updateState(mPreference); + assertThat(WidevineProperties.forcel3_enabled().orElse(false)).isFalse(); + assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.isChecked()).isFalse(); + + // Test flag rollback + mController.setChecked(true); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isTrue(); + assertThat(WidevineProperties.forcel3_enabled().orElse(false)).isTrue(); + mSetFlagsRule.disableFlags(Flags.FLAG_FORCE_L3_ENABLED); + mController.updateState(mPreference); + assertThat(mPreference.isEnabled()).isFalse(); + assertThat(mPreference.isChecked()).isFalse(); + assertThat(WidevineProperties.forcel3_enabled().orElse(false)).isFalse(); + } + + @Test + public void updateState_flagDisabled_checkPreference() { + mSetFlagsRule.disableFlags(Flags.FLAG_FORCE_L3_ENABLED); + mController.updateState(mPreference); + assertThat(mPreference.isEnabled()).isFalse(); + } + + @Test + public void updateState_checkWidevine() throws Exception { + MediaDrm drm; + try { + drm = new MediaDrm(WIDEVINE_UUID); + assumeTrue(drm.getPropertyString("securityLevel").equals("L1")); + mSetFlagsRule.enableFlags(Flags.FLAG_FORCE_L3_ENABLED); + drm.close(); + } catch (UnsupportedSchemeException ex) { + assumeNoException(ex); + } + + // L3 enforced + mController.setChecked(true); + mController.updateState(mPreference); + assertThat(WidevineProperties.forcel3_enabled().orElse(false)).isTrue(); + assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.isChecked()).isTrue(); + drm = new MediaDrm(WIDEVINE_UUID); + assertThat(drm.getPropertyString("securityLevel")).isEqualTo("L3"); + + // Switch back to L1 + mController.setChecked(false); + mController.updateState(mPreference); + drm.close(); + drm = new MediaDrm(WIDEVINE_UUID); + assertThat(drm.getPropertyString("securityLevel")).isEqualTo("L1"); + } +} From 533e0455985089f6087cafd8ed596d765a822bf0 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Wed, 24 Jan 2024 12:35:52 +0800 Subject: [PATCH 14/20] Using new API setDataEnabledForReason for setDataEnabled Bug: 318310357 Bug: 298898436 Bug: 298891941 Test: atest MobileNetworkUtilsTest Change-Id: I30b56247370ebaa5896bec3205ddce3e5c92c7d0 --- .../network/telephony/MobileNetworkUtils.java | 9 ++++++--- .../telephony/MobileNetworkUtilsTest.java | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java index cce12ec3cb0..47515d8d6fe 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java +++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java @@ -358,7 +358,8 @@ public class MobileNetworkUtils { .createForSubscriptionId(subId); final SubscriptionManager subscriptionManager = context.getSystemService( SubscriptionManager.class).createForAllUserProfiles(); - telephonyManager.setDataEnabled(enabled); + telephonyManager.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, + enabled); if (disableOtherSubscriptions) { final List subInfoList = @@ -367,8 +368,10 @@ public class MobileNetworkUtils { for (SubscriptionInfo subInfo : subInfoList) { // We never disable mobile data for opportunistic subscriptions. if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) { - context.getSystemService(TelephonyManager.class).createForSubscriptionId( - subInfo.getSubscriptionId()).setDataEnabled(false); + context.getSystemService(TelephonyManager.class) + .createForSubscriptionId(subInfo.getSubscriptionId()) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, + false); } } } diff --git a/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java index c23b53ed35a..947ba75db66 100644 --- a/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java @@ -148,24 +148,30 @@ public class MobileNetworkUtilsTest { public void setMobileDataEnabled_setEnabled_enabled() { MobileNetworkUtils.setMobileDataEnabled(mContext, SUB_ID_1, true, false); - verify(mTelephonyManager).setDataEnabled(true); - verify(mTelephonyManager2, never()).setDataEnabled(anyBoolean()); + verify(mTelephonyManager) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, true); + verify(mTelephonyManager2, never()) + .setDataEnabledForReason(anyInt(), anyBoolean()); } @Test public void setMobileDataEnabled_setDisabled_disabled() { MobileNetworkUtils.setMobileDataEnabled(mContext, SUB_ID_2, true, false); - verify(mTelephonyManager2).setDataEnabled(true); - verify(mTelephonyManager, never()).setDataEnabled(anyBoolean()); + verify(mTelephonyManager2) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, true); + verify(mTelephonyManager, never()) + .setDataEnabledForReason(anyInt(), anyBoolean()); } @Test public void setMobileDataEnabled_disableOtherSubscriptions() { MobileNetworkUtils.setMobileDataEnabled(mContext, SUB_ID_1, true, true); - verify(mTelephonyManager).setDataEnabled(true); - verify(mTelephonyManager2).setDataEnabled(false); + verify(mTelephonyManager) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, true); + verify(mTelephonyManager2) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, false); } @Test From 0e4018f4695f9ebf72ab4514e52b2ddf4137090e Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Wed, 24 Jan 2024 13:04:25 +0800 Subject: [PATCH 15/20] [Sim UI enhancement] hide Calls & SMS when using new ui Bug: 318310357 Bug: 298898436 Bug: 298891941 Test: manually test Change-Id: Ic6e1b77a300f3e9d86225f9cd4812b0f9b426269 --- .../settings/network/NetworkProviderCallsSmsController.kt | 2 ++ .../settings/network/NetworkProviderCallsSmsFragment.java | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/network/NetworkProviderCallsSmsController.kt b/src/com/android/settings/network/NetworkProviderCallsSmsController.kt index 7346e236f68..67247c11936 100644 --- a/src/com/android/settings/network/NetworkProviderCallsSmsController.kt +++ b/src/com/android/settings/network/NetworkProviderCallsSmsController.kt @@ -28,6 +28,7 @@ import androidx.lifecycle.LifecycleOwner import androidx.preference.PreferenceScreen import com.android.settings.R import com.android.settings.core.BasePreferenceController +import com.android.settings.flags.Flags import com.android.settingslib.RestrictedPreference import com.android.settingslib.Utils import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle @@ -62,6 +63,7 @@ open class NetworkProviderCallsSmsController @JvmOverloads constructor( } override fun getAvailabilityStatus() = when { + Flags.isDualSimOnboardingEnabled() -> UNSUPPORTED_ON_DEVICE !SubscriptionUtil.isSimHardwareVisible(mContext) -> UNSUPPORTED_ON_DEVICE !mContext.userManager.isAdminUser -> DISABLED_FOR_USER else -> AVAILABLE diff --git a/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java b/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java index b5ad65a7a97..ffe5b052a2f 100644 --- a/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java +++ b/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java @@ -24,6 +24,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.network.telephony.CallsDefaultSubscriptionController; import com.android.settings.network.telephony.NetworkProviderWifiCallingPreferenceController; import com.android.settings.network.telephony.SmsDefaultSubscriptionController; @@ -91,8 +92,9 @@ public class NetworkProviderCallsSmsFragment extends DashboardFragment { @Override protected boolean isPageSearchEnabled(Context context) { - return SubscriptionUtil.isSimHardwareVisible(context) && - context.getSystemService(UserManager.class).isAdminUser(); + return !Flags.isDualSimOnboardingEnabled() + && SubscriptionUtil.isSimHardwareVisible(context) + && context.getSystemService(UserManager.class).isAdminUser(); } }; } From b4318729ca4b51b3a75174d0a931a55fbd8b9332 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Wed, 24 Jan 2024 14:43:35 +0800 Subject: [PATCH 16/20] Ignore failing zen mode related tests to bring back green postsubmit Test: atest Change-Id: Ibc7116400617b7ff8d34d4f0e767dd7bfc1609b4 --- .../settings/notification/zen/ZenModeBackendTest.java | 6 ++++-- .../settings/notification/zen/ZenModeSliceBuilderTest.java | 3 +++ .../notification/zen/ZenOnboardingActivityTest.java | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java index 247d68e4bfe..5c3b5b8357d 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java @@ -30,9 +30,8 @@ import android.database.Cursor; import android.provider.Settings; import android.service.notification.ZenModeConfig; -import com.android.settings.R; - import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -190,6 +189,7 @@ public class ZenModeBackendTest { } } + @Ignore @Test public void saveConversationSenders_importantToNone() { when(mNotificationManager.getNotificationPolicy()).thenReturn( @@ -215,6 +215,7 @@ public class ZenModeBackendTest { assertEquals(expected, captor.getValue()); } + @Ignore @Test public void saveConversationSenders_noneToAll() { when(mNotificationManager.getNotificationPolicy()).thenReturn(new Policy( @@ -239,6 +240,7 @@ public class ZenModeBackendTest { assertEquals(expected, captor.getValue()); } + @Ignore @Test public void saveSenders_doesNotChangeConversations() { when(mNotificationManager.getNotificationPolicy()).thenReturn( diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java index 71f896df7c9..8b760042b86 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java @@ -37,6 +37,7 @@ import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -95,6 +96,7 @@ public class ZenModeSliceBuilderTest { assertThat(primaryAction.getIcon()).isNull(); } + @Ignore @Test public void handleUriChange_turnOn_zenModeTurnsOn() { final Intent intent = new Intent(); @@ -107,6 +109,7 @@ public class ZenModeSliceBuilderTest { assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); } + @Ignore @Test public void handleUriChange_turnOff_zenModeTurnsOff() { final Intent intent = new Intent(); diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java index 257eaaf3a8e..6063efb702c 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java @@ -21,8 +21,7 @@ import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CA import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import static com.android.settings.notification.zen.ZenOnboardingActivity.ALWAYS_SHOW_THRESHOLD; -import static com.android.settings.notification.zen.ZenOnboardingActivity - .PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME; +import static com.android.settings.notification.zen.ZenOnboardingActivity.PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME; import static com.android.settings.notification.zen.ZenOnboardingActivity.isSuggestionComplete; import static com.google.common.truth.Truth.assertThat; @@ -40,10 +39,10 @@ import android.provider.Settings; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settings.notification.zen.ZenOnboardingActivity; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -92,6 +91,7 @@ public class ZenOnboardingActivityTest { verify(mMetricsLogger).visible(MetricsEvent.SETTINGS_ZEN_ONBOARDING); } + @Ignore @Test public void saveNewSetting() { Policy policy = new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0, SUPPRESSED_EFFECT_SCREEN_ON); From ea67ae2c510bf33f238f98b1386d3186669ffcf0 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Wed, 24 Jan 2024 14:59:49 +0800 Subject: [PATCH 17/20] Fix PlatformCompatDashboardTest Bug: 313591873 Test: atest Change-Id: Ib972f13ad194b78a32145e3879275125c43e8a3c --- .../compat/PlatformCompatDashboardTest.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java index 5a679ad56d7..86ceadc0811 100644 --- a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java +++ b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java @@ -41,7 +41,8 @@ import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; -import androidx.preference.SwitchPreference; +import androidx.preference.SwitchPreferenceCompat; +import androidx.test.core.app.ApplicationProvider; import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.CompatibilityChangeInfo; @@ -51,13 +52,13 @@ import com.android.internal.compat.OverrideAllowedState; import com.android.settings.R; import org.junit.Before; -import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.Arrays; @@ -67,6 +68,8 @@ import java.util.Set; @RunWith(RobolectricTestRunner.class) public class PlatformCompatDashboardTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private PlatformCompatDashboard mDashboard; @Mock @@ -87,7 +90,6 @@ public class PlatformCompatDashboardTest { @Before public void setUp() throws RemoteException, NameNotFoundException { - MockitoAnnotations.initMocks(this); mChanges = new CompatibilityChangeInfo[5]; mChanges[0] = new CompatibilityChangeInfo( 1L, "Default_Enabled", 0, 0, false, false, "", false); @@ -104,7 +106,7 @@ public class PlatformCompatDashboardTest { // By default, allow any change when(mOverrideValidator.getOverrideAllowedState(anyLong(),anyString())) .thenReturn(new OverrideAllowedState(ALLOWED, -1, -1)); - mContext = spy(RuntimeEnvironment.application); + mContext = spy(ApplicationProvider.getApplicationContext()); mPreferenceManager = new PreferenceManager(mContext); mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext); mApplicationInfo.packageName = APP_NAME; @@ -141,7 +143,6 @@ public class PlatformCompatDashboardTest { R.string.platform_compat_selected_app_summary, APP_NAME, 1)); } - @Ignore("b/313591873") @Test public void createPreferenceForChange_defaultEnabledChange_createCheckedEntry() { CompatibilityChangeInfo enabledChange = mChanges[0]; @@ -152,15 +153,14 @@ public class PlatformCompatDashboardTest { Preference enabledPreference = mDashboard.createPreferenceForChange(mContext, enabledChange, config); - SwitchPreference enabledSwitchPreference = (SwitchPreference) enabledPreference; + SwitchPreferenceCompat enabledSwitchPreference = (SwitchPreferenceCompat) enabledPreference; assertThat(enabledPreference.getSummary()).isEqualTo(mChanges[0].getName()); - assertThat(enabledPreference instanceof SwitchPreference).isTrue(); + assertThat(enabledPreference instanceof SwitchPreferenceCompat).isTrue(); assertThat(enabledSwitchPreference.isChecked()).isTrue(); assertThat(enabledSwitchPreference.isEnabled()).isTrue(); } - @Ignore("b/313591873") @Test public void createPreferenceForChange_defaultDisabledChange_createUncheckedEntry() { CompatibilityChangeInfo disabledChange = mChanges[1]; @@ -172,12 +172,12 @@ public class PlatformCompatDashboardTest { disabledChange, config); assertThat(disabledPreference.getSummary()).isEqualTo(mChanges[1].getName()); - SwitchPreference disabledSwitchPreference = (SwitchPreference) disabledPreference; + SwitchPreferenceCompat disabledSwitchPreference = + (SwitchPreferenceCompat) disabledPreference; assertThat(disabledSwitchPreference.isChecked()).isFalse(); assertThat(disabledSwitchPreference.isEnabled()).isTrue(); } - @Ignore("b/313591873") @Test public void createPreferenceForChange_cannotOverride_createDisabledEntry() throws RemoteException { @@ -191,15 +191,14 @@ public class PlatformCompatDashboardTest { Preference preference = mDashboard.createPreferenceForChange(mContext, enabledChange, config); - SwitchPreference switchPreference = (SwitchPreference) preference; + SwitchPreferenceCompat switchPreference = (SwitchPreferenceCompat) preference; assertThat(preference.getSummary()).isEqualTo(mChanges[0].getName()); - assertThat(preference instanceof SwitchPreference).isTrue(); + assertThat(preference instanceof SwitchPreferenceCompat).isTrue(); assertThat(switchPreference.isChecked()).isTrue(); assertThat(switchPreference.isEnabled()).isFalse(); } - @Ignore("b/313591873") @Test public void createChangeCategoryPreference_enabledAndDisabled_hasTitleAndEntries() { Set enabledChanges = new HashSet<>(); @@ -226,7 +225,7 @@ public class PlatformCompatDashboardTest { assertThat(category.getPreferenceCount()).isEqualTo(mChanges.length); for (int i = 0; i < mChanges.length; ++i) { Preference childPreference = category.getPreference(i); - assertThat(childPreference instanceof SwitchPreference).isTrue(); + assertThat(childPreference instanceof SwitchPreferenceCompat).isTrue(); } } } From f38d9c8c6430fee48035fd0702b4352dc41e1669 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Wed, 24 Jan 2024 16:34:19 +0800 Subject: [PATCH 18/20] Fix PanelSlicesAdapterTest Slice is final so cannot be mocked or spied on Bug: 313576125 Test: atest Change-Id: I14d2268b710d7525676ee4a273826f4cb141b19d --- .../panel/PanelSlicesAdapterTest.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java index f177c199cc6..87a798a2197 100644 --- a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java +++ b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java @@ -43,6 +43,7 @@ import android.widget.LinearLayout; import androidx.lifecycle.LiveData; import androidx.slice.Slice; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.panel.PanelSlicesAdapter.SliceRowViewHolder; @@ -50,14 +51,14 @@ import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; @@ -69,6 +70,8 @@ import java.util.Map; @RunWith(RobolectricTestRunner.class) @Config(shadows = PanelSlicesAdapterTest.ShadowLayoutInflater.class) public class PanelSlicesAdapterTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static LayoutInflater sLayoutInflater; @@ -81,8 +84,7 @@ public class PanelSlicesAdapterTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); mPanelFeatureProvider = spy(new PanelFeatureProviderImpl()); mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); @@ -104,8 +106,7 @@ public class PanelSlicesAdapterTest { private void addTestLiveData(Uri uri) { // Create a slice to return for the LiveData - final Slice slice = spy(new Slice()); - doReturn(uri).when(slice).getUri(); + final Slice slice = new Slice(); final LiveData liveData = mock(LiveData.class); when(liveData.getValue()).thenReturn(slice); mData.put(uri, liveData); @@ -126,7 +127,6 @@ public class PanelSlicesAdapterTest { /** * ViewHolder should load and set the action label correctly. */ - @Ignore("b/313576125") @Test public void setActionLabel_loadsActionLabel() { addTestLiveData(VOLUME_NOTIFICATION_URI); @@ -167,7 +167,6 @@ public class PanelSlicesAdapterTest { return foundLabel; } - @Ignore("b/313576125") @Test public void sizeOfAdapter_shouldNotExceedMaxNum() { for (int i = 0; i < MAX_NUM_OF_SLICES + 2; i++) { @@ -186,7 +185,6 @@ public class PanelSlicesAdapterTest { assertThat(adapter.getData().size()).isEqualTo(MAX_NUM_OF_SLICES); } - @Ignore("b/313576125") @Test public void mediaOutputIndicatorSlice_notSliderPanel_noSliderLayout() { addTestLiveData(MEDIA_OUTPUT_INDICATOR_SLICE_URI); @@ -203,7 +201,6 @@ public class PanelSlicesAdapterTest { assertThat(viewHolder.mSliceSliderLayout).isNull(); } - @Ignore("b/313576125") @Test public void onBindViewHolder_viewTypeSlider_verifyActionLabelSet() { addTestLiveData(VOLUME_NOTIFICATION_URI); From 391f37f41df54c8cb853f7b04ffe19170547dca4 Mon Sep 17 00:00:00 2001 From: SongFerng Wang Date: Wed, 24 Jan 2024 13:41:28 +0000 Subject: [PATCH 19/20] Renew the testcase for using new api setDataEnabledForReason Bug: 318310357 Change-Id: Ia800ab2727747e5f2d979051a3ffd7fe4ce92ed9 Test: test the MobileDataSliceTest --- .../settings/network/telephony/MobileDataSliceTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java index ba37c30389a..8445fe22c9f 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java @@ -114,7 +114,8 @@ public class MobileDataSliceTest { mMobileDataSlice.onNotifyChange(intent); - verify(mTelephonyManager).setDataEnabled(true); + verify(mTelephonyManager) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, true); } @Test @@ -126,7 +127,8 @@ public class MobileDataSliceTest { mMobileDataSlice.onNotifyChange(intent); - verify(mTelephonyManager).setDataEnabled(false); + verify(mTelephonyManager) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, false); } @Test @@ -138,7 +140,8 @@ public class MobileDataSliceTest { mMobileDataSlice.onNotifyChange(intent); - verify(mTelephonyManager, times(0)).setDataEnabled(true); + verify(mTelephonyManager, times(0)) + .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, true); } @Test From c16384c2aa1dffa7609477f041cbb4029daf34e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Wed, 24 Jan 2024 18:24:34 +0100 Subject: [PATCH 20/20] Adjust Settings tests to account for MODES_API Fixes: 322166166 Test: atest ZenModeBackendTest ZenModeSliceBuilderTest ZenOnboardingActivityTest Change-Id: I5bfb2ce842c1b1784c6802638512da23fd21b0c8 --- .../notification/zen/ZenModeBackendTest.java | 24 ++++++++---- .../zen/ZenModeSliceBuilderTest.java | 37 +++++++++++++------ .../zen/ZenOnboardingActivityTest.java | 17 +++++++-- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java index 5c3b5b8357d..32bf9af3010 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java @@ -16,9 +16,9 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,7 +31,6 @@ import android.provider.Settings; import android.service.notification.ZenModeConfig; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -189,7 +188,6 @@ public class ZenModeBackendTest { } } - @Ignore @Test public void saveConversationSenders_importantToNone() { when(mNotificationManager.getNotificationPolicy()).thenReturn( @@ -204,7 +202,11 @@ public class ZenModeBackendTest { mBackend.saveConversationSenders(CONVERSATION_SENDERS_NONE); ArgumentCaptor captor = ArgumentCaptor.forClass(Policy.class); - verify(mNotificationManager, times(1)).setNotificationPolicy(captor.capture()); + if (android.app.Flags.modesApi()) { + verify(mNotificationManager).setNotificationPolicy(captor.capture(), eq(true)); + } else { + verify(mNotificationManager).setNotificationPolicy(captor.capture()); + } Policy expected = new Policy( PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_ALARMS, @@ -215,7 +217,6 @@ public class ZenModeBackendTest { assertEquals(expected, captor.getValue()); } - @Ignore @Test public void saveConversationSenders_noneToAll() { when(mNotificationManager.getNotificationPolicy()).thenReturn(new Policy( @@ -229,7 +230,11 @@ public class ZenModeBackendTest { mBackend.saveConversationSenders(CONVERSATION_SENDERS_ANYONE); ArgumentCaptor captor = ArgumentCaptor.forClass(Policy.class); - verify(mNotificationManager, times(1)).setNotificationPolicy(captor.capture()); + if (android.app.Flags.modesApi()) { + verify(mNotificationManager).setNotificationPolicy(captor.capture(), eq(true)); + } else { + verify(mNotificationManager).setNotificationPolicy(captor.capture()); + } Policy expected = new Policy(PRIORITY_CATEGORY_CONVERSATIONS | PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_ALARMS, @@ -240,7 +245,6 @@ public class ZenModeBackendTest { assertEquals(expected, captor.getValue()); } - @Ignore @Test public void saveSenders_doesNotChangeConversations() { when(mNotificationManager.getNotificationPolicy()).thenReturn( @@ -255,7 +259,11 @@ public class ZenModeBackendTest { mBackend.saveSenders(PRIORITY_CATEGORY_CALLS, PRIORITY_SENDERS_ANY); ArgumentCaptor captor = ArgumentCaptor.forClass(Policy.class); - verify(mNotificationManager, times(1)).setNotificationPolicy(captor.capture()); + if (android.app.Flags.modesApi()) { + verify(mNotificationManager).setNotificationPolicy(captor.capture(), eq(true)); + } else { + verify(mNotificationManager).setNotificationPolicy(captor.capture()); + } Policy expected = new Policy(PRIORITY_CATEGORY_CONVERSATIONS | PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_ALARMS, diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java index 8b760042b86..3f9e4864a0c 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeSliceBuilderTest.java @@ -17,13 +17,18 @@ package com.android.settings.notification.zen; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; +import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; +import static android.provider.Settings.Global.ZEN_MODE_OFF; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + import android.app.NotificationManager; import android.content.Context; import android.content.Intent; -import android.provider.Settings; import androidx.slice.Slice; import androidx.slice.SliceMetadata; @@ -37,12 +42,14 @@ import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; import java.util.List; @@ -52,10 +59,17 @@ public class ZenModeSliceBuilderTest { private Context mContext; + @Mock + private NotificationManager mNm; + @Before public void setUp() { mContext = RuntimeEnvironment.application; + MockitoAnnotations.initMocks(this); + ShadowApplication shadowApplication = ShadowApplication.getInstance(); + shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm); + // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); } @@ -96,30 +110,31 @@ public class ZenModeSliceBuilderTest { assertThat(primaryAction.getIcon()).isNull(); } - @Ignore @Test public void handleUriChange_turnOn_zenModeTurnsOn() { final Intent intent = new Intent(); intent.putExtra(EXTRA_TOGGLE_STATE, true); - NotificationManager.from(mContext).setZenMode(Settings.Global.ZEN_MODE_OFF, null, ""); ZenModeSliceBuilder.handleUriChange(mContext, intent); - final int zenMode = NotificationManager.from(mContext).getZenMode(); - assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); + if (android.app.Flags.modesApi()) { + verify(mNm).setZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), any(), any(), eq(true)); + } else { + verify(mNm).setZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), any(), any()); + } } - @Ignore @Test public void handleUriChange_turnOff_zenModeTurnsOff() { final Intent intent = new Intent(); intent.putExtra(EXTRA_TOGGLE_STATE, false); - NotificationManager.from(mContext).setZenMode( - Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, ""); ZenModeSliceBuilder.handleUriChange(mContext, intent); - final int zenMode = NotificationManager.from(mContext).getZenMode(); - assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_OFF); + if (android.app.Flags.modesApi()) { + verify(mNm).setZenMode(eq(ZEN_MODE_OFF), any(), any(), eq(true)); + } else { + verify(mNm).setZenMode(eq(ZEN_MODE_OFF), any(), any()); + } } } diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java index 6063efb702c..01360fdd414 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenOnboardingActivityTest.java @@ -27,10 +27,13 @@ import static com.android.settings.notification.zen.ZenOnboardingActivity.isSugg import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Flags; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; @@ -42,7 +45,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -91,7 +93,6 @@ public class ZenOnboardingActivityTest { verify(mMetricsLogger).visible(MetricsEvent.SETTINGS_ZEN_ONBOARDING); } - @Ignore @Test public void saveNewSetting() { Policy policy = new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0, SUPPRESSED_EFFECT_SCREEN_ON); @@ -103,7 +104,11 @@ public class ZenOnboardingActivityTest { verify(mMetricsLogger).action(MetricsEvent.ACTION_ZEN_ONBOARDING_OK); ArgumentCaptor captor = ArgumentCaptor.forClass(Policy.class); - verify(mNm).setNotificationPolicy(captor.capture()); + if (android.app.Flags.modesApi()) { + verify(mNm).setNotificationPolicy(captor.capture(), eq(true)); + } else { + verify(mNm).setNotificationPolicy(captor.capture()); + } Policy actual = captor.getValue(); assertThat(actual.priorityCategories).isEqualTo(PRIORITY_CATEGORY_ALARMS @@ -123,7 +128,11 @@ public class ZenOnboardingActivityTest { mActivity.save(null); verify(mMetricsLogger).action(MetricsEvent.ACTION_ZEN_ONBOARDING_KEEP_CURRENT_SETTINGS); - verify(mNm, never()).setNotificationPolicy(any()); + if (Flags.modesApi()) { + verify(mNm, never()).setNotificationPolicy(any(), anyBoolean()); + } else { + verify(mNm, never()).setNotificationPolicy(any()); + } } @Test