From 1b64234f4ff6946c259a90c7cc62c41c1fd03b41 Mon Sep 17 00:00:00 2001 From: William Escande Date: Thu, 3 Nov 2022 12:09:55 -0700 Subject: [PATCH 1/4] Fetch currently loaded bluetooth name Test: m ROBOTEST_FILTER="BluetoothMaxConnectedAudioDevicesPreferenceControllerTest" RunSettingsRoboTests -j40 Test: Open developper settings Bug: 257158801 Change-Id: Ib40a6264d8d6908103d76b6401ddcfd3ffa7dd88 (cherry picked from commit e2c6d1f60dde927daeb84226a1b025a582b2a9be) Merged-In: Ib40a6264d8d6908103d76b6401ddcfd3ffa7dd88 --- .../bluetooth/BluetoothPermissionRequest.java | 10 +++- src/com/android/settings/bluetooth/Utils.java | 51 +++++++++++++++++++ ...ectedAudioDevicesPreferenceController.java | 18 +++---- ...dAudioDevicesPreferenceControllerTest.java | 21 ++++---- 4 files changed, 78 insertions(+), 22 deletions(-) mode change 100755 => 100644 src/com/android/settings/bluetooth/Utils.java diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java index 8542fcd2461..a62bbe10e0c 100644 --- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java +++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.PowerManager; import android.os.UserManager; import android.util.Log; @@ -125,8 +126,15 @@ public final class BluetoothPermissionRequest extends BroadcastReceiver { // Create an intent triggered by clicking on the // "Clear All Notifications" button + String bluetoothName; + try { + bluetoothName = Utils.findBluetoothPackageName(context); + } catch (NameNotFoundException e) { + e.printStackTrace(); + return; + } Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); - deleteIntent.setPackage("com.android.bluetooth"); + deleteIntent.setPackage(bluetoothName); deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, BluetoothDevice.CONNECTION_ACCESS_NO); diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java old mode 100755 new mode 100644 index ca8f9d39a8c..24fe4e151ff --- a/src/com/android/settings/bluetooth/Utils.java +++ b/src/com/android/settings/bluetooth/Utils.java @@ -16,11 +16,18 @@ package com.android.settings.bluetooth; +import static android.os.Process.BLUETOOTH_UID; + import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; @@ -189,4 +196,48 @@ public final class Utils { } return false; } + + /** + * Returns the Bluetooth Package name + */ + public static String findBluetoothPackageName(Context context) + throws NameNotFoundException { + // this activity will always be in the package where the rest of Bluetooth lives + String sentinelActivity = "com.android.bluetooth.opp.BluetoothOppLauncherActivity"; + PackageManager packageManager = context.createContextAsUser(UserHandle.SYSTEM, 0) + .getPackageManager(); + String[] allPackages = packageManager.getPackagesForUid(BLUETOOTH_UID); + String matchedPackage = null; + for (String candidatePackage : allPackages) { + PackageInfo packageInfo; + try { + packageInfo = + packageManager.getPackageInfo( + candidatePackage, + PackageManager.GET_ACTIVITIES + | PackageManager.MATCH_ANY_USER + | PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS); + } catch (NameNotFoundException e) { + // rethrow + throw e; + } + if (packageInfo.activities == null) { + continue; + } + for (ActivityInfo activity : packageInfo.activities) { + if (sentinelActivity.equals(activity.name)) { + if (matchedPackage == null) { + matchedPackage = candidatePackage; + } else { + throw new NameNotFoundException("multiple main bluetooth packages found"); + } + } + } + } + if (matchedPackage != null) { + return matchedPackage; + } + throw new NameNotFoundException("Could not find main bluetooth package"); + } } diff --git a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java index bd8169a80fe..f1677f29509 100644 --- a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java +++ b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java @@ -16,9 +16,8 @@ package com.android.settings.development; +import android.bluetooth.BluetoothManager; import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; import android.os.SystemProperties; import androidx.annotation.VisibleForTesting; @@ -42,18 +41,15 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceController extends private int mDefaultMaxConnectedAudioDevices = 0; + private final BluetoothManager mBluetoothManager; + public BluetoothMaxConnectedAudioDevicesPreferenceController(Context context) { super(context); - try { - Resources res = context.getPackageManager().getResourcesForApplication( - "com.android.bluetooth"); - mDefaultMaxConnectedAudioDevices = res.getInteger(res.getIdentifier( - "config_bluetooth_max_connected_audio_devices", - "integer", "com.android.bluetooth")); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } + mBluetoothManager = context.getSystemService(BluetoothManager.class); + + mDefaultMaxConnectedAudioDevices = + mBluetoothManager.getAdapter().getMaxConnectedAudioDevices(); } @Override diff --git a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java index 72477b94585..7ab311fe2ab 100644 --- a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java @@ -24,9 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; import android.os.SystemProperties; @@ -54,6 +54,11 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest { @Spy private Context mSpyContext = RuntimeEnvironment.application; + @Mock + private BluetoothManager mBluetoothManager; + @Mock + private BluetoothAdapter mBluetoothAdapter; + private ListPreference mPreference; private BluetoothMaxConnectedAudioDevicesPreferenceController mController; @@ -63,19 +68,15 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest { @Before public void setup() { MockitoAnnotations.initMocks(this); + doReturn(mBluetoothManager).when(mSpyContext).getSystemService(BluetoothManager.class); + doReturn(mBluetoothAdapter).when(mBluetoothManager).getAdapter(); // Get XML values without mock // Setup test list preference using XML values mPreference = new ListPreference(mSpyContext); mPreference.setEntries(R.array.bluetooth_max_connected_audio_devices); mPreference.setEntryValues(R.array.bluetooth_max_connected_audio_devices_values); - // Retrieve default max connected audio devices to a test controlled value - try { - Resources res = mSpyContext.getPackageManager().getResourcesForApplication("com.android.bluetooth"); - TEST_MAX_CONNECTED_AUDIO_DEVICES = res.getInteger(res.getIdentifier("config_bluetooth_max_connected_audio_devices", "integer", "com.android.bluetooth")); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - + doReturn(TEST_MAX_CONNECTED_AUDIO_DEVICES).when(mBluetoothAdapter) + .getMaxConnectedAudioDevices(); // Init the actual controller mController = new BluetoothMaxConnectedAudioDevicesPreferenceController(mSpyContext); // Construct preference in the controller via a mocked preference screen object From 2e3e67389239d94acebef7232c0c4216d9f2e58e Mon Sep 17 00:00:00 2001 From: William Escande Date: Thu, 3 Nov 2022 12:09:55 -0700 Subject: [PATCH 2/4] Fetch currently loaded bluetooth name Test: m ROBOTEST_FILTER="BluetoothMaxConnectedAudioDevicesPreferenceControllerTest" RunSettingsRoboTests -j40 Test: Open developper settings Bug: 257158801 Change-Id: Ib40a6264d8d6908103d76b6401ddcfd3ffa7dd88 (cherry picked from commit e2c6d1f60dde927daeb84226a1b025a582b2a9be) Merged-In: Ib40a6264d8d6908103d76b6401ddcfd3ffa7dd88 --- .../bluetooth/BluetoothPermissionRequest.java | 10 +++- src/com/android/settings/bluetooth/Utils.java | 51 +++++++++++++++++++ ...ectedAudioDevicesPreferenceController.java | 18 +++---- ...dAudioDevicesPreferenceControllerTest.java | 21 ++++---- 4 files changed, 78 insertions(+), 22 deletions(-) mode change 100755 => 100644 src/com/android/settings/bluetooth/Utils.java diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java index 8542fcd2461..a62bbe10e0c 100644 --- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java +++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.PowerManager; import android.os.UserManager; import android.util.Log; @@ -125,8 +126,15 @@ public final class BluetoothPermissionRequest extends BroadcastReceiver { // Create an intent triggered by clicking on the // "Clear All Notifications" button + String bluetoothName; + try { + bluetoothName = Utils.findBluetoothPackageName(context); + } catch (NameNotFoundException e) { + e.printStackTrace(); + return; + } Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); - deleteIntent.setPackage("com.android.bluetooth"); + deleteIntent.setPackage(bluetoothName); deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, BluetoothDevice.CONNECTION_ACCESS_NO); diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java old mode 100755 new mode 100644 index ca8f9d39a8c..24fe4e151ff --- a/src/com/android/settings/bluetooth/Utils.java +++ b/src/com/android/settings/bluetooth/Utils.java @@ -16,11 +16,18 @@ package com.android.settings.bluetooth; +import static android.os.Process.BLUETOOTH_UID; + import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; @@ -189,4 +196,48 @@ public final class Utils { } return false; } + + /** + * Returns the Bluetooth Package name + */ + public static String findBluetoothPackageName(Context context) + throws NameNotFoundException { + // this activity will always be in the package where the rest of Bluetooth lives + String sentinelActivity = "com.android.bluetooth.opp.BluetoothOppLauncherActivity"; + PackageManager packageManager = context.createContextAsUser(UserHandle.SYSTEM, 0) + .getPackageManager(); + String[] allPackages = packageManager.getPackagesForUid(BLUETOOTH_UID); + String matchedPackage = null; + for (String candidatePackage : allPackages) { + PackageInfo packageInfo; + try { + packageInfo = + packageManager.getPackageInfo( + candidatePackage, + PackageManager.GET_ACTIVITIES + | PackageManager.MATCH_ANY_USER + | PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS); + } catch (NameNotFoundException e) { + // rethrow + throw e; + } + if (packageInfo.activities == null) { + continue; + } + for (ActivityInfo activity : packageInfo.activities) { + if (sentinelActivity.equals(activity.name)) { + if (matchedPackage == null) { + matchedPackage = candidatePackage; + } else { + throw new NameNotFoundException("multiple main bluetooth packages found"); + } + } + } + } + if (matchedPackage != null) { + return matchedPackage; + } + throw new NameNotFoundException("Could not find main bluetooth package"); + } } diff --git a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java index bd8169a80fe..f1677f29509 100644 --- a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java +++ b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java @@ -16,9 +16,8 @@ package com.android.settings.development; +import android.bluetooth.BluetoothManager; import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; import android.os.SystemProperties; import androidx.annotation.VisibleForTesting; @@ -42,18 +41,15 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceController extends private int mDefaultMaxConnectedAudioDevices = 0; + private final BluetoothManager mBluetoothManager; + public BluetoothMaxConnectedAudioDevicesPreferenceController(Context context) { super(context); - try { - Resources res = context.getPackageManager().getResourcesForApplication( - "com.android.bluetooth"); - mDefaultMaxConnectedAudioDevices = res.getInteger(res.getIdentifier( - "config_bluetooth_max_connected_audio_devices", - "integer", "com.android.bluetooth")); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } + mBluetoothManager = context.getSystemService(BluetoothManager.class); + + mDefaultMaxConnectedAudioDevices = + mBluetoothManager.getAdapter().getMaxConnectedAudioDevices(); } @Override diff --git a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java index 72477b94585..7ab311fe2ab 100644 --- a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java @@ -24,9 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; import android.os.SystemProperties; @@ -54,6 +54,11 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest { @Spy private Context mSpyContext = RuntimeEnvironment.application; + @Mock + private BluetoothManager mBluetoothManager; + @Mock + private BluetoothAdapter mBluetoothAdapter; + private ListPreference mPreference; private BluetoothMaxConnectedAudioDevicesPreferenceController mController; @@ -63,19 +68,15 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest { @Before public void setup() { MockitoAnnotations.initMocks(this); + doReturn(mBluetoothManager).when(mSpyContext).getSystemService(BluetoothManager.class); + doReturn(mBluetoothAdapter).when(mBluetoothManager).getAdapter(); // Get XML values without mock // Setup test list preference using XML values mPreference = new ListPreference(mSpyContext); mPreference.setEntries(R.array.bluetooth_max_connected_audio_devices); mPreference.setEntryValues(R.array.bluetooth_max_connected_audio_devices_values); - // Retrieve default max connected audio devices to a test controlled value - try { - Resources res = mSpyContext.getPackageManager().getResourcesForApplication("com.android.bluetooth"); - TEST_MAX_CONNECTED_AUDIO_DEVICES = res.getInteger(res.getIdentifier("config_bluetooth_max_connected_audio_devices", "integer", "com.android.bluetooth")); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - + doReturn(TEST_MAX_CONNECTED_AUDIO_DEVICES).when(mBluetoothAdapter) + .getMaxConnectedAudioDevices(); // Init the actual controller mController = new BluetoothMaxConnectedAudioDevicesPreferenceController(mSpyContext); // Construct preference in the controller via a mocked preference screen object From 5b9653f58100a47130618bcbdb7422b2030fd459 Mon Sep 17 00:00:00 2001 From: Diya Bera Date: Mon, 9 Jan 2023 23:52:34 +0000 Subject: [PATCH 3/4] Fixes background color in SUW fingerprint enrollment Test: Manual (Enroll fingerprint in SUW, background color remains the same) Bug: 246231650 Bug: 250464527 Change-Id: Iffd1543d8b230199c3b925c6538bd418a381c96a (cherry picked from commit 00dfcd83dc9acab5a49d24773bd23d5969257b36) Merged-In: Iffd1543d8b230199c3b925c6538bd418a381c96a --- .../android/settings/biometrics/BiometricUtils.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java index 4cd2f790620..9cc656cda23 100644 --- a/src/com/android/settings/biometrics/BiometricUtils.java +++ b/src/com/android/settings/biometrics/BiometricUtils.java @@ -39,6 +39,7 @@ import com.android.settings.SetupWizardUtils; import com.android.settings.biometrics.face.FaceEnrollIntroduction; import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor; import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction; +import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor; import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; @@ -152,9 +153,13 @@ public class BiometricUtils { */ public static Intent getFingerprintFindSensorIntent(@NonNull Context context, @NonNull Intent activityIntent) { - Intent intent = new Intent(context, FingerprintEnrollFindSensor.class); - SetupWizardUtils.copySetupExtras(activityIntent, intent); - return intent; + if (WizardManagerHelper.isAnySetupWizard(activityIntent)) { + Intent intent = new Intent(context, SetupFingerprintEnrollFindSensor.class); + SetupWizardUtils.copySetupExtras(activityIntent, intent); + return intent; + } else { + return new Intent(context, FingerprintEnrollFindSensor.class); + } } /** From a61c636eabe5da66d2acac9c5750d2358dc383a1 Mon Sep 17 00:00:00 2001 From: Behnam Heydarshahi Date: Wed, 25 Jan 2023 20:53:53 +0000 Subject: [PATCH 4/4] DO NOT MERGE ANYWHERE Revert "Read flag to show/hide notification slider" Revert submission 20180104-flag_for_b38477228 Reason for revert: b/261809910 fixing for qpr2 Reverted changes: /q/submissionid:20180104-flag_for_b38477228 Change-Id: Id3ebc6627d6b46172cae1ce9e5c7500a652e719d (cherry picked from commit b1195633da9d203056d29df8f119820425a33254) Merged-In: Id3ebc6627d6b46172cae1ce9e5c7500a652e719d --- ...otificationVolumePreferenceController.java | 69 +++----------- .../RingVolumePreferenceController.java | 95 ++++++------------- .../VolumeSeekBarPreferenceController.java | 12 +-- ...icationVolumePreferenceControllerTest.java | 92 +----------------- .../RingVolumePreferenceControllerTest.java | 29 +++--- 5 files changed, 63 insertions(+), 234 deletions(-) diff --git a/src/com/android/settings/notification/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java index 112debca647..322bb6c229b 100644 --- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java +++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java @@ -16,7 +16,6 @@ package com.android.settings.notification; -import android.app.ActivityThread; import android.app.INotificationManager; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -30,32 +29,26 @@ import android.os.Looper; import android.os.Message; import android.os.ServiceManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.Log; import androidx.lifecycle.OnLifecycleEvent; -import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.Objects; -import java.util.Set; /** - * Update notification volume icon in Settings in response to user adjusting volume. + * Update notification volume icon in Settings in response to user adjusting volume */ public class NotificationVolumePreferenceController extends VolumeSeekBarPreferenceController { private static final String TAG = "NotificationVolumePreferenceController"; private static final String KEY_NOTIFICATION_VOLUME = "notification_volume"; - private static final boolean CONFIG_DEFAULT_VAL = false; - private boolean mSeparateNotification; private Vibrator mVibrator; private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; @@ -63,74 +56,39 @@ public class NotificationVolumePreferenceController extends VolumeSeekBarPrefere private final RingReceiver mReceiver = new RingReceiver(); private final H mHandler = new H(); private INotificationManager mNoMan; + + private int mMuteIcon; private final int mNormalIconId = R.drawable.ic_notifications; private final int mVibrateIconId = R.drawable.ic_volume_ringer_vibrate; private final int mSilentIconId = R.drawable.ic_notifications_off_24dp; + private final boolean mRingNotificationAliased; + + public NotificationVolumePreferenceController(Context context) { this(context, KEY_NOTIFICATION_VOLUME); } public NotificationVolumePreferenceController(Context context, String key) { super(context, key); - mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); if (mVibrator != null && !mVibrator.hasVibrator()) { mVibrator = null; } + mRingNotificationAliased = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types); updateRingerMode(); } - /** - * Allow for notification slider to be enabled in the scenario where the config switches on - * while settings page is already on the screen by always configuring the preference, even if it - * is currently inactive. - */ - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - if (mPreference == null) { - setupVolPreference(screen); - } - mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - if (mPreference != null) { - mPreference.setVisible(getAvailabilityStatus() == AVAILABLE); - } - updateEffectsSuppressor(); - updatePreferenceIconAndSliderState(); - } - - /** - * Only display the notification slider when the corresponding device config flag is set - */ - private void onDeviceConfigChange(DeviceConfig.Properties properties) { - Set changeSet = properties.getKeyset(); - - if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) { - boolean newVal = properties.getBoolean( - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - if (newVal != mSeparateNotification) { - mSeparateNotification = newVal; - // manually hiding the preference because being unavailable does not do the job - if (mPreference != null) { - mPreference.setVisible(getAvailabilityStatus() == AVAILABLE); - } - } - } - } - - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @Override public void onResume() { super.onResume(); mReceiver.register(true); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - ActivityThread.currentApplication().getMainExecutor(), - this::onDeviceConfigChange); + updateEffectsSuppressor(); + updatePreferenceIconAndSliderState(); } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) @@ -138,17 +96,16 @@ public class NotificationVolumePreferenceController extends VolumeSeekBarPrefere public void onPause() { super.onPause(); mReceiver.register(false); - DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange); } @Override public int getAvailabilityStatus() { - boolean separateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false); + // Show separate notification slider if ring/notification are not aliased by AudioManager -- + // if they are, notification volume is controlled by RingVolumePreferenceController. return mContext.getResources().getBoolean(R.bool.config_show_notification_volume) + && (!mRingNotificationAliased || !Utils.isVoiceCapable(mContext)) && !mHelper.isSingleVolume() - && (separateNotification || !Utils.isVoiceCapable(mContext)) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } diff --git a/src/com/android/settings/notification/RingVolumePreferenceController.java b/src/com/android/settings/notification/RingVolumePreferenceController.java index 7fdb1e16141..a78689f5a0a 100644 --- a/src/com/android/settings/notification/RingVolumePreferenceController.java +++ b/src/com/android/settings/notification/RingVolumePreferenceController.java @@ -16,7 +16,6 @@ package com.android.settings.notification; -import android.app.ActivityThread; import android.app.INotificationManager; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -30,7 +29,6 @@ import android.os.Looper; import android.os.Message; import android.os.ServiceManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.Log; @@ -38,13 +36,11 @@ import android.util.Log; import androidx.lifecycle.OnLifecycleEvent; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.Objects; -import java.util.Set; /** * This slider can represent both ring and notification, if the corresponding streams are aliased, @@ -63,21 +59,24 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr private int mMuteIcon; - private int mNormalIconId; + /* + * Whether ring and notification streams are aliased together by AudioManager. + * If they are, we'll present one volume control for both. + * If not, we'll present separate volume controls. + */ + private final boolean mRingAliasNotif; + + private final int mNormalIconId; @VisibleForTesting - int mVibrateIconId; + final int mVibrateIconId; @VisibleForTesting - int mSilentIconId; + final int mSilentIconId; @VisibleForTesting - int mTitleId; - - private boolean mSeparateNotification; + final int mTitleId; private INotificationManager mNoMan; - private static final boolean CONFIG_DEFAULT_VAL = false; - public RingVolumePreferenceController(Context context) { this(context, KEY_RING_VOLUME); } @@ -88,56 +87,29 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr if (mVibrator != null && !mVibrator.hasVibrator()) { mVibrator = null; } - mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - loadPreferenceIconResources(mSeparateNotification); - updateRingerMode(); - } - private void loadPreferenceIconResources(boolean separateNotification) { - if (separateNotification) { - mTitleId = R.string.separate_ring_volume_option_title; - mNormalIconId = R.drawable.ic_ring_volume; - mSilentIconId = R.drawable.ic_ring_volume_off; - } else { + mRingAliasNotif = isRingAliasNotification(); + if (mRingAliasNotif) { mTitleId = R.string.ring_volume_option_title; + mNormalIconId = R.drawable.ic_notifications; mSilentIconId = R.drawable.ic_notifications_off_24dp; + } else { + mTitleId = R.string.separate_ring_volume_option_title; + + mNormalIconId = R.drawable.ic_ring_volume; + mSilentIconId = R.drawable.ic_ring_volume_off; } // todo: set a distinct vibrate icon for ring vs notification mVibrateIconId = R.drawable.ic_volume_ringer_vibrate; + + updateRingerMode(); } - /** - * As the responsibility of this slider changes, so should its title & icon - */ - public void onDeviceConfigChange(DeviceConfig.Properties properties) { - Set changeSet = properties.getKeyset(); - if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) { - boolean valueUpdated = readSeparateNotificationVolumeConfig(); - if (valueUpdated) { - updateEffectsSuppressor(); - selectPreferenceIconState(); - setPreferenceTitle(); - } - } - } - - /** - * side effect: updates the cached value of the config, and also the icon - * @return has the config changed? - */ - private boolean readSeparateNotificationVolumeConfig() { - boolean newVal = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - - boolean valueUpdated = newVal != mSeparateNotification; - if (valueUpdated) { - mSeparateNotification = newVal; - loadPreferenceIconResources(newVal); - } - - return valueUpdated; + @VisibleForTesting + boolean isRingAliasNotification() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types); } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @@ -145,11 +117,8 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr public void onResume() { super.onResume(); mReceiver.register(true); - readSeparateNotificationVolumeConfig(); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - ActivityThread.currentApplication().getMainExecutor(), this::onDeviceConfigChange); updateEffectsSuppressor(); - selectPreferenceIconState(); + updatePreferenceIcon(); setPreferenceTitle(); } @@ -158,7 +127,6 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr public void onPause() { super.onPause(); mReceiver.register(false); - DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange); } @Override @@ -202,7 +170,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr final int ringerMode = mHelper.getRingerModeInternal(); if (mRingerMode == ringerMode) return; mRingerMode = ringerMode; - selectPreferenceIconState(); + updatePreferenceIcon(); } private void updateEffectsSuppressor() { @@ -222,8 +190,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr return; } - if (hintsMatch(hints, DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false))) { + if (hintsMatch(hints, mRingAliasNotif)) { mSuppressor = suppressor; if (mPreference != null) { final String text = SuppressorHelper.getSuppressionText(mContext, suppressor); @@ -233,11 +200,11 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr } @VisibleForTesting - boolean hintsMatch(int hints, boolean notificationSeparated) { + boolean hintsMatch(int hints, boolean ringNotificationAliased) { return (hints & NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS) != 0 || (hints & NotificationListenerService.HINT_HOST_DISABLE_EFFECTS) != 0 || ((hints & NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) - != 0 && !notificationSeparated); + != 0 && ringNotificationAliased); } @VisibleForTesting @@ -250,7 +217,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr mVibrator = vibrator; } - private void selectPreferenceIconState() { + private void updatePreferenceIcon() { if (mPreference != null) { if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { mPreference.showIcon(mNormalIconId); diff --git a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java index 0414565721e..d1701599c34 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java @@ -55,17 +55,13 @@ public abstract class VolumeSeekBarPreferenceController extends public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); if (isAvailable()) { - setupVolPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + mPreference.setCallback(mVolumePreferenceCallback); + mPreference.setStream(getAudioStream()); + mPreference.setMuteIcon(getMuteIcon()); } } - protected void setupVolPreference(PreferenceScreen screen) { - mPreference = screen.findPreference(getPreferenceKey()); - mPreference.setCallback(mVolumePreferenceCallback); - mPreference.setStream(getAudioStream()); - mPreference.setMuteIcon(getMuteIcon()); - } - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onResume() { if (mPreference != null) { diff --git a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java index 7e7ad10d8c1..96b9e6219c8 100644 --- a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java @@ -18,7 +18,6 @@ package com.android.settings.notification; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -26,17 +25,10 @@ import android.content.Context; import android.content.res.Resources; import android.media.AudioManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.telephony.TelephonyManager; -import androidx.preference.PreferenceManager; -import androidx.preference.PreferenceScreen; -import androidx.test.core.app.ApplicationProvider; - -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; -import com.android.settings.core.BasePreferenceController; -import com.android.settings.testutils.shadow.ShadowDeviceConfig; +import com.android.internal.R; import org.junit.Before; import org.junit.Test; @@ -45,12 +37,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowDeviceConfig.class}) public class NotificationVolumePreferenceControllerTest { + @Mock private AudioHelper mHelper; @Mock @@ -61,11 +52,6 @@ public class NotificationVolumePreferenceControllerTest { private Vibrator mVibrator; @Mock private Resources mResources; - @Mock - private PreferenceManager mPreferenceManager; - - private static final String READ_DEVICE_CONFIG_PERMISSION = - "android.permission.READ_DEVICE_CONFIG"; private Context mContext; private NotificationVolumePreferenceController mController; @@ -101,9 +87,7 @@ public class NotificationVolumePreferenceControllerTest { public void isAvailable_voiceCapable_aliasedWithRing_shouldReturnFalse() { when(mResources.getBoolean( com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); + when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(true); NotificationVolumePreferenceController controller = new NotificationVolumePreferenceController(mContext); @@ -121,9 +105,7 @@ public class NotificationVolumePreferenceControllerTest { public void isAvailable_voiceCapable_separatedFromRing_shouldReturnTrue() { when(mResources.getBoolean( com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false); + when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(false); NotificationVolumePreferenceController controller = new NotificationVolumePreferenceController(mContext); @@ -188,70 +170,4 @@ public class NotificationVolumePreferenceControllerTest { .isTrue(); } - @Test - public void enableSeparateNotificationConfig_controllerBecomesAvailable() { - PreferenceScreen screen = spy(new PreferenceScreen(mContext, null)); - VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class); - when(screen.getPreferenceManager()).thenReturn(mPreferenceManager); - when(screen.getContext()).thenReturn(mContext); - when(mResources.getBoolean( - com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - // block the alternative condition to enable controller - when(mTelephonyManager.isVoiceCapable()).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); - - NotificationVolumePreferenceController controller = - new NotificationVolumePreferenceController(mContext); - when(screen.findPreference(controller.getPreferenceKey())) - .thenReturn(volumeSeekBarPreference); - - // allow the controller to subscribe - Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext()) - .grantPermissions(READ_DEVICE_CONFIG_PERMISSION); - controller.onResume(); - controller.displayPreference(screen); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, Boolean.toString(true), - false); - - assertThat(controller.getAvailabilityStatus() - == BasePreferenceController.AVAILABLE).isTrue(); - } - - @Test - public void disableSeparateNotificationConfig_controllerBecomesUnavailable() { - PreferenceScreen screen = spy(new PreferenceScreen(mContext, null)); - VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class); - when(screen.getPreferenceManager()).thenReturn(mPreferenceManager); - when(screen.getContext()).thenReturn(mContext); - when(mResources.getBoolean( - com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - - // block the alternative condition to enable controller - when(mTelephonyManager.isVoiceCapable()).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false); - - NotificationVolumePreferenceController controller = - new NotificationVolumePreferenceController(mContext); - - when(screen.findPreference(controller.getPreferenceKey())) - .thenReturn(volumeSeekBarPreference); - - Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext()) - .grantPermissions(READ_DEVICE_CONFIG_PERMISSION); - controller.onResume(); - controller.displayPreference(screen); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); - - assertThat(controller.getAvailabilityStatus() - == BasePreferenceController.UNSUPPORTED_ON_DEVICE).isTrue(); - } - } diff --git a/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java index 1ad26c71546..02757d52874 100644 --- a/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java @@ -27,13 +27,10 @@ import android.content.Context; import android.content.res.Resources; import android.media.AudioManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.telephony.TelephonyManager; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.settings.R; -import com.android.settings.testutils.shadow.ShadowDeviceConfig; import org.junit.Before; import org.junit.Test; @@ -42,11 +39,9 @@ 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; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowDeviceConfig.class}) public class RingVolumePreferenceControllerTest { @Mock @@ -129,10 +124,9 @@ public class RingVolumePreferenceControllerTest { // todo: verify that the title change is displayed, by examining the underlying preference @Test public void ringNotificationStreamsNotAliased_sliderTitleSetToRingOnly() { - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false); - + when(mResources.getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types)) + .thenReturn(false); final RingVolumePreferenceController controller = new RingVolumePreferenceController(mContext); @@ -144,9 +138,8 @@ public class RingVolumePreferenceControllerTest { @Test public void ringNotificationStreamsAliased_sliderTitleIncludesBothRingNotification() { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); - + when(mResources.getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types)).thenReturn(true); final RingVolumePreferenceController control = new RingVolumePreferenceController(mContext); int expectedTitleId = R.string.ring_volume_option_title; @@ -157,39 +150,39 @@ public class RingVolumePreferenceControllerTest { @Test public void setHintsRing_aliased_Matches() { assertThat(mController.hintsMatch( - NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue(); + NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue(); } @Test public void setHintsRingNotification_aliased_Matches() { assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS, - false)).isTrue(); + true)).isTrue(); } @Test public void setHintNotification_aliased_Matches() { assertThat(mController .hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS, - false)).isTrue(); + true)).isTrue(); } @Test public void setHintsRing_unaliased_Matches() { assertThat(mController.hintsMatch( - NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue(); + NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue(); } @Test public void setHintsRingNotification_unaliased_Matches() { assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS, - true)).isTrue(); + false)).isTrue(); } @Test public void setHintNotification_unaliased_doesNotMatch() { assertThat(mController .hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS, - true)).isFalse(); + false)).isFalse(); } @Test