Merge "Fix startActivity for multi-user issue HSUM mode" into main
diff --git a/aconfig/catalyst/system.aconfig b/aconfig/catalyst/system.aconfig
new file mode 100644
index 0000000..f87ff44
--- /dev/null
+++ b/aconfig/catalyst/system.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.settings.flags"
+container: "system"
+
+flag {
+ name: "catalyst_language_setting"
+ namespace: "android_settings"
+ description: "Flag for System -> Languages screen"
+ bug: "323791114"
+}
diff --git a/aconfig/settings_telephony_flag_declarations.aconfig b/aconfig/settings_telephony_flag_declarations.aconfig
index dab1b45..0279125 100644
--- a/aconfig/settings_telephony_flag_declarations.aconfig
+++ b/aconfig/settings_telephony_flag_declarations.aconfig
@@ -14,3 +14,19 @@
description: "Control the Dual SIM onobarding feature"
bug: "298898436"
}
+
+# OWNER=yomna TARGET=25Q2
+flag {
+ name: "mobile_network_security_2g"
+ namespace: "cellular_security"
+ description: "Exposing 2G toggles in Mobile Network Security page"
+ bug: "355062720"
+}
+
+# OWNER=yomna TARGET=25Q2
+flag {
+ name: "add_security_transparency_to_eng_menu"
+ namespace: "cellular_security"
+ description: "Exposing security transparency features to field engineering menu"
+ bug: "355062720"
+}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2ee6d60..75e901e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -260,6 +260,13 @@
<string name="bluetooth_disable_leaudio_summary">Disables Bluetooth LE audio feature if the device supports LE audio hardware capabilities.</string>
<!-- Setting toggle title for switch Bluetooth LE Audio mode. [CHAR LIMIT=40] -->
<string name="bluetooth_leaudio_mode">Bluetooth LE Audio mode</string>
+
+ <!-- Setting toggle title for enabling Bluetooth LE Audio UI preview. [CHAR LIMIT=none] -->
+ <string name="bluetooth_leaudio_broadcast_ui">Enable Bluetooth LE Audio Broadcast UI preview</string>
+ <!-- Summary of toggle for enabling Bluetooth LE Audio UI preview. [CHAR LIMIT=none]-->
+ <string name="bluetooth_leaudio_broadcast_ui_summary">Enables the LE Audio Sharing UI preview
+ including personal audio sharing and private broadcast</string>
+
<!-- Setting toggle title for enabling Bluetooth LE Audio toggle in Device Details. [CHAR LIMIT=40] -->
<string name="bluetooth_show_leaudio_device_details">Show LE audio toggle in Device Details</string>
@@ -5995,6 +6002,10 @@
<!-- Title for the battery summary tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_summary_title">Apps are running normally</string>
+ <!-- Title for the battery replacement tip [CHAR LIMIT=NONE] -->
+ <string name="battery_tip_replacement_title">Battery replacement recommended</string>
+ <!-- Summary for the battery replacement tip [CHAR LIMIT=NONE] -->
+ <string name="battery_tip_replacement_summary">Battery capacity and charging performance are reduced, and battery replacement is recommended.</string>
<!-- Title for the low battery tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_low_battery_title">Battery level low</string>
<!-- Summary for the low battery tip [CHAR LIMIT=NONE] -->
@@ -7417,6 +7428,8 @@
<string name="help_url_battery" translatable="false"></string>
<!-- Help URL, Battery Defender [DO NOT TRANSLATE] -->
<string name="help_url_battery_defender" translatable="false"></string>
+ <!-- Help URL, Battery Replacement [DO NOT TRANSLATE] -->
+ <string name="help_url_battery_replacement" translatable="false"></string>
<!-- Help URL, Dock Defender [DO NOT TRANSLATE] -->
<string name="help_url_dock_defender" translatable="false"></string>
<!-- Help URL, Incompatible charging [DO NOT TRANSLATE] -->
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index a4addf6..dde397b 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -394,6 +394,11 @@
android:entryValues="@array/bluetooth_leaudio_mode_values"/>
<SwitchPreferenceCompat
+ android:key="bluetooth_leaudio_broadcast_ui"
+ android:title="@string/bluetooth_leaudio_broadcast_ui"
+ android:summary="@string/bluetooth_leaudio_broadcast_ui_summary"/>
+
+ <SwitchPreferenceCompat
android:key="bluetooth_show_leaudio_device_details"
android:title="@string/bluetooth_show_leaudio_device_details"/>
diff --git a/res/xml/language_settings.xml b/res/xml/language_settings.xml
index 4613cb0..7618399 100644
--- a/res/xml/language_settings.xml
+++ b/res/xml/language_settings.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/languages_settings"
- android:key="languages_settings">
+ android:key="language_settings">
<PreferenceCategory
android:key="languages_category"
android:title="@string/locale_picker_category_title">
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 481ad65..adda094 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1516,13 +1516,13 @@
final UserManager userManager = context.getSystemService(
UserManager.class);
final int status = biometricManager.canAuthenticate(getEffectiveUserId(
- userManager, userId), BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ userManager, userId), BiometricManager.Authenticators.IDENTITY_CHECK);
switch(status) {
case BiometricManager.BIOMETRIC_SUCCESS:
return BiometricStatus.OK;
case BiometricManager.BIOMETRIC_ERROR_LOCKOUT:
return BiometricStatus.LOCKOUT;
- case BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE:
+ case BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE:
case BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS:
return BiometricStatus.NOT_ACTIVE;
default:
@@ -1582,7 +1582,7 @@
final Intent intent = new Intent();
if (android.hardware.biometrics.Flags.mandatoryBiometrics()) {
intent.putExtra(BIOMETRIC_PROMPT_AUTHENTICATORS,
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ BiometricManager.Authenticators.IDENTITY_CHECK);
}
intent.putExtra(BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT,
resources.getString(R.string.cancel));
diff --git a/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
index 50ac3cd..030aadb 100644
--- a/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
@@ -45,6 +45,8 @@
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrolledFingerprintsInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollStageCountInteractor
+import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollStageThresholdInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
@@ -113,6 +115,12 @@
fun createCanEnrollFingerprintsInteractor(): CanEnrollFingerprintsInteractor =
CanEnrollFingerprintsInteractorImpl(fingerprintEnrollmentRepository)
+ fun createFingerprintEnrollStageCountInteractor(): FingerprintEnrollStageCountInteractor =
+ FingerprintEnrollStageCountInteractor(fingerprintEnrollmentRepository)
+
+ fun createFingerprintEnrollStageThresholdInteractor(): FingerprintEnrollStageThresholdInteractor =
+ FingerprintEnrollStageThresholdInteractor(fingerprintEnrollmentRepository)
+
fun createGenerateChallengeInteractor(): GenerateChallengeInteractor =
GenerateChallengeInteractorImpl(fingerprintManager, context.userId, gateKeeperPasswordProvider)
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
index 0bb4eea..d01a49c 100644
--- a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
@@ -46,6 +46,16 @@
/** Indicates if a user can enroll another fingerprint */
val canEnrollUser: Flow<Boolean>
+ val enrollStageCount: Int
+
+ /**
+ * Returns the threshold for the given stage of fingerprint enrollment.
+ *
+ * @param index The index of the enrollment stage.
+ * @return The threshold for the enrollment stage.
+ */
+ fun getEnrollStageThreshold(index: Int): Float
+
/**
* Indicates if we should use the default settings for maximum enrollments or the sensor props
* from the fingerprint sensor
@@ -115,4 +125,10 @@
?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
?.toList()
}
+
+ override val enrollStageCount: Int
+ get() = fingerprintManager.enrollStageCount
+
+ override fun getEnrollStageThreshold(index: Int): Float =
+ fingerprintManager.getEnrollStageThreshold(index)
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollStageCountInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollStageCountInteractor.kt
new file mode 100644
index 0000000..6148158
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollStageCountInteractor.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.biometrics.fingerprint2.domain.interactor
+
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepository
+
+/**
+ * Interactor class for retrieving the total number of enrollment stages.
+ *
+ * This class interacts with the `FingerprintsRepository` to obtain the count
+ * of stages involved in the fingerprint enrollment process.
+ */
+class FingerprintEnrollStageCountInteractor (
+ fingerprintEnrollmentRepository: FingerprintEnrollmentRepository
+) {
+ /** The total number of enrollment stages. */
+ val count: Int = fingerprintEnrollmentRepository.enrollStageCount
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollStageThresholdInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollStageThresholdInteractor.kt
new file mode 100644
index 0000000..a96e4ef
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollStageThresholdInteractor.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.biometrics.fingerprint2.domain.interactor
+
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepository
+
+/**
+ * Interactor class for retrieving the enrollment stage threshold.
+ *
+ * This class interacts with the `fingerprintEnrollmentRepository` to fetch the threshold value
+ * for a specific enrollment stage.
+ */
+class FingerprintEnrollStageThresholdInteractor(
+ private val fingerprintEnrollmentRepository: FingerprintEnrollmentRepository,
+) {
+ /**
+ * Retrieves the enrollment stage threshold for the given index.
+ *
+ * @param index The index of the enrollment stage.
+ * @return The threshold value for the specified stage.
+ */
+ fun getThreshold(index: Int): Float = fingerprintEnrollmentRepository.getEnrollStageThreshold(index)
+}
\ No newline at end of file
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 2860ce8..a1a22be 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -123,6 +123,14 @@
}
}
}
+
+ @Override
+ public void onDeviceBondStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int bondState) {
+ if (cachedDevice.equals(mCachedDevice)) {
+ finishFragmentIfNecessary();
+ }
+ }
};
private final BluetoothAdapter.OnMetadataChangedListener mExtraControlMetadataListener =
diff --git a/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractor.kt b/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractor.kt
index 4b91716a..cade566 100644
--- a/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractor.kt
+++ b/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractor.kt
@@ -30,10 +30,13 @@
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -41,9 +44,7 @@
/** Provides device setting for spatial audio. */
interface SpatialAudioInteractor {
/** Gets device setting for spatial audio */
- fun getDeviceSetting(
- cachedDevice: CachedBluetoothDevice,
- ): Flow<DeviceSettingModel?>
+ fun getDeviceSetting(cachedDevice: CachedBluetoothDevice): Flow<DeviceSettingModel?>
}
class SpatialAudioInteractorImpl(
@@ -56,33 +57,55 @@
private val spatialAudioOffToggle =
ToggleModel(
context.getString(R.string.spatial_audio_multi_toggle_off),
- DeviceSettingIcon.ResourceIcon(R.drawable.ic_spatial_audio_off))
+ DeviceSettingIcon.ResourceIcon(R.drawable.ic_spatial_audio_off),
+ )
private val spatialAudioOnToggle =
ToggleModel(
context.getString(R.string.spatial_audio_multi_toggle_on),
- DeviceSettingIcon.ResourceIcon(R.drawable.ic_spatial_audio))
+ DeviceSettingIcon.ResourceIcon(R.drawable.ic_spatial_audio),
+ )
private val headTrackingOnToggle =
ToggleModel(
context.getString(R.string.spatial_audio_multi_toggle_head_tracking_on),
- DeviceSettingIcon.ResourceIcon(R.drawable.ic_head_tracking))
+ DeviceSettingIcon.ResourceIcon(R.drawable.ic_head_tracking),
+ )
private val changes = MutableSharedFlow<Unit>()
- override fun getDeviceSetting(
- cachedDevice: CachedBluetoothDevice,
- ): Flow<DeviceSettingModel?> =
+ override fun getDeviceSetting(cachedDevice: CachedBluetoothDevice): Flow<DeviceSettingModel?> =
changes
.onStart { emit(Unit) }
- .map { getSpatialAudioDeviceSettingModel(cachedDevice) }
+ .combine(
+ isDeviceConnected(cachedDevice),
+ ) { _, connected ->
+ if (connected) {
+ getSpatialAudioDeviceSettingModel(cachedDevice)
+ } else {
+ null
+ }
+ }
+ .flowOn(backgroundCoroutineContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = null)
+ private fun isDeviceConnected(cachedDevice: CachedBluetoothDevice): Flow<Boolean> =
+ callbackFlow {
+ val listener =
+ CachedBluetoothDevice.Callback { launch { send(cachedDevice.isConnected) } }
+ cachedDevice.registerCallback(context.mainExecutor, listener)
+ awaitClose { cachedDevice.unregisterCallback(listener) }
+ }
+ .onStart { emit(cachedDevice.isConnected) }
+ .flowOn(backgroundCoroutineContext)
+
private suspend fun getSpatialAudioDeviceSettingModel(
- cachedDevice: CachedBluetoothDevice,
+ cachedDevice: CachedBluetoothDevice
): DeviceSettingModel? {
// TODO(b/343317785): use audio repository instead of calling AudioManager directly.
Log.i(TAG, "CachedDevice: $cachedDevice profiles: ${cachedDevice.profiles}")
val attributes =
BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
- cachedDevice, audioManager.getBluetoothAudioDeviceCategory(cachedDevice.address))
+ cachedDevice,
+ audioManager.getBluetoothAudioDeviceCategory(cachedDevice.address),
+ )
?: run {
Log.i(TAG, "No audio profiles in cachedDevice: ${cachedDevice.address}.")
return null
@@ -116,7 +139,8 @@
TAG,
"Head tracking available: $headTrackingAvailable, " +
"spatial audio enabled: $spatialAudioEnabled, " +
- "head tracking enabled: $headTrackingEnabled")
+ "head tracking enabled: $headTrackingEnabled",
+ )
return DeviceSettingModel.MultiTogglePreference(
cachedDevice = cachedDevice,
id = DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE,
@@ -143,7 +167,8 @@
}
changes.emit(Unit)
}
- })
+ },
+ )
}
companion object {
diff --git a/src/com/android/settings/development/BluetoothLeAudioModePreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioModePreferenceController.java
index 739258d..bf5efa7 100644
--- a/src/com/android/settings/development/BluetoothLeAudioModePreferenceController.java
+++ b/src/com/android/settings/development/BluetoothLeAudioModePreferenceController.java
@@ -34,12 +34,10 @@
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+import java.util.Objects;
-/**
- * Preference controller to control Bluetooth LE audio mode
- */
-public class BluetoothLeAudioModePreferenceController
- extends DeveloperOptionsPreferenceController
+/** Preference controller to control Bluetooth LE audio mode */
+public class BluetoothLeAudioModePreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String PREFERENCE_KEY = "bluetooth_leaudio_mode";
@@ -51,15 +49,13 @@
private final String[] mListValues;
private final String[] mListSummaries;
- @VisibleForTesting
- @Nullable String mNewMode;
- @VisibleForTesting
- BluetoothAdapter mBluetoothAdapter;
+ @VisibleForTesting @Nullable String mNewMode;
+ @VisibleForTesting BluetoothAdapter mBluetoothAdapter;
boolean mChanged = false;
- public BluetoothLeAudioModePreferenceController(@NonNull Context context,
- @Nullable DevelopmentSettingsDashboardFragment fragment) {
+ public BluetoothLeAudioModePreferenceController(
+ @NonNull Context context, @Nullable DevelopmentSettingsDashboardFragment fragment) {
super(context);
mFragment = fragment;
mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter();
@@ -69,7 +65,8 @@
}
@Override
- @NonNull public String getPreferenceKey() {
+ @NonNull
+ public String getPreferenceKey() {
return PREFERENCE_KEY;
}
@@ -125,20 +122,25 @@
}
}
- /**
- * Called when the RebootDialog confirm is clicked.
- */
+ /** Called when the RebootDialog confirm is clicked. */
public void onRebootDialogConfirmed() {
if (!mChanged) {
return;
}
SystemProperties.set(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, mNewMode);
+ if (mFragment != null && !Objects.equals(mNewMode, "broadcast")) {
+ mFragment.onBroadcastDisabled();
+ }
}
- /**
- * Called when the RebootDialog cancel is clicked.
- */
+ /** Called when the RebootDialog cancel is clicked. */
public void onRebootDialogCanceled() {
mChanged = false;
}
+
+ public interface OnModeChangeListener {
+
+ /** Called when the broadcast mode is disabled. */
+ void onBroadcastDisabled();
+ }
}
diff --git a/src/com/android/settings/development/BluetoothLeAudioUiPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioUiPreferenceController.java
new file mode 100644
index 0000000..f2ae55f
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothLeAudioUiPreferenceController.java
@@ -0,0 +1,144 @@
+/*
+ * 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.development;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothStatusCodes;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.sysprop.BluetoothProperties;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreferenceCompat;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+import com.android.settingslib.flags.Flags;
+import com.android.settingslib.utils.ThreadUtils;
+
+/** Preference controller to enable / disable the Bluetooth LE audio sharing UI flow */
+public class BluetoothLeAudioUiPreferenceController extends DeveloperOptionsPreferenceController
+ implements Preference.OnPreferenceChangeListener,
+ PreferenceControllerMixin,
+ BluetoothLeAudioModePreferenceController.OnModeChangeListener {
+ private static final String TAG = "BluetoothLeAudioUiPreferenceController";
+ private static final String PREFERENCE_KEY = "bluetooth_leaudio_broadcast_ui";
+
+ @VisibleForTesting
+ static final String VALUE_KEY = "bluetooth_le_audio_sharing_ui_preview_enabled";
+
+ @VisibleForTesting static final int VALUE_OFF = 0;
+ @VisibleForTesting static final int VALUE_ON = 1;
+ @VisibleForTesting static final int VALUE_UNSET = -1;
+ @Nullable private final DevelopmentSettingsDashboardFragment mFragment;
+ private final BluetoothAdapter mBluetoothAdapter;
+ private boolean mCurrentSettingsValue = false;
+ private boolean mShouldToggleCurrentValue = false;
+
+ public BluetoothLeAudioUiPreferenceController(
+ @NonNull Context context, @Nullable DevelopmentSettingsDashboardFragment fragment) {
+ super(context);
+ mFragment = fragment;
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return Flags.audioSharingDeveloperOption()
+ && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)
+ && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false);
+ }
+
+ @Override
+ public boolean onPreferenceChange(@NonNull Preference preference, @Nullable Object newValue) {
+ if (mFragment != null && newValue != null && (boolean) newValue != mCurrentSettingsValue) {
+ mShouldToggleCurrentValue = true;
+ BluetoothRebootDialog.show(mFragment);
+ }
+ return false;
+ }
+
+ @Override
+ public void updateState(@NonNull Preference preference) {
+ if (mBluetoothAdapter == null) {
+ return;
+ }
+ var unused = ThreadUtils.postOnBackgroundThread(
+ () -> {
+ boolean shouldEnable =
+ mBluetoothAdapter.isEnabled()
+ && mBluetoothAdapter.isLeAudioBroadcastSourceSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED
+ && mBluetoothAdapter.isLeAudioBroadcastAssistantSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED;
+ boolean valueOn =
+ Settings.Global.getInt(
+ mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET)
+ == VALUE_ON;
+ mContext.getMainExecutor()
+ .execute(
+ () -> {
+ if (!shouldEnable && valueOn) {
+ Log.e(
+ TAG,
+ "Error state: toggle disabled but current"
+ + " settings value is true.");
+ }
+ mCurrentSettingsValue = valueOn;
+ preference.setEnabled(shouldEnable);
+ ((SwitchPreferenceCompat) preference).setChecked(valueOn);
+ });
+ });
+ }
+
+ @Override
+ public @NonNull String getPreferenceKey() {
+ return PREFERENCE_KEY;
+ }
+
+ /** Called when the RebootDialog confirm is clicked. */
+ public void onRebootDialogConfirmed() {
+ if (isAvailable() && mShouldToggleCurrentValue) {
+ // Blocking, ensure reboot happens after value is saved.
+ Log.d(TAG, "onRebootDialogConfirmed(): setting value to " + !mCurrentSettingsValue);
+ toggleSetting(mContext.getContentResolver(), !mCurrentSettingsValue);
+ }
+ }
+
+ /** Called when the RebootDialog cancel is clicked. */
+ public void onRebootDialogCanceled() {
+ mShouldToggleCurrentValue = false;
+ }
+
+ @Override
+ public void onBroadcastDisabled() {
+ if (isAvailable() && mCurrentSettingsValue) {
+ Log.d(TAG, "onBroadcastDisabled(): setting value to false");
+ // Blocking, ensure reboot happens after value is saved.
+ toggleSetting(mContext.getContentResolver(), false);
+ }
+ }
+
+ private static void toggleSetting(ContentResolver contentResolver, boolean valueOn) {
+ Settings.Global.putInt(contentResolver, VALUE_KEY, valueOn ? VALUE_ON : VALUE_OFF);
+ }
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 8a970fb..b453de1 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -99,7 +99,9 @@
AdbClearKeysDialogHost, LogPersistDialogHost,
BluetoothRebootDialog.OnRebootDialogListener,
AbstractBluetoothPreferenceController.Callback,
- NfcRebootDialog.OnNfcRebootDialogConfirmedListener, BluetoothSnoopLogHost {
+ NfcRebootDialog.OnNfcRebootDialogConfirmedListener,
+ BluetoothSnoopLogHost,
+ BluetoothLeAudioModePreferenceController.OnModeChangeListener {
private static final String TAG = "DevSettingsDashboard";
@VisibleForTesting static final int REQUEST_BIOMETRIC_PROMPT = 100;
@@ -498,6 +500,10 @@
getDevelopmentOptionsController(
BluetoothLeAudioModePreferenceController.class);
leAudioModeController.onRebootDialogConfirmed();
+
+ final BluetoothLeAudioUiPreferenceController leAudioUiController =
+ getDevelopmentOptionsController(BluetoothLeAudioUiPreferenceController.class);
+ leAudioUiController.onRebootDialogConfirmed();
}
@Override
@@ -520,6 +526,10 @@
getDevelopmentOptionsController(
BluetoothLeAudioModePreferenceController.class);
leAudioModeController.onRebootDialogCanceled();
+
+ final BluetoothLeAudioUiPreferenceController leAudioUiController =
+ getDevelopmentOptionsController(BluetoothLeAudioUiPreferenceController.class);
+ leAudioUiController.onRebootDialogCanceled();
}
@Override
@@ -741,6 +751,7 @@
controllers.add(new BluetoothMapVersionPreferenceController(context));
controllers.add(new BluetoothLeAudioPreferenceController(context, fragment));
controllers.add(new BluetoothLeAudioModePreferenceController(context, fragment));
+ controllers.add(new BluetoothLeAudioUiPreferenceController(context, fragment));
controllers.add(new BluetoothLeAudioDeviceDetailsPreferenceController(context));
controllers.add(new BluetoothLeAudioAllowListPreferenceController(context));
controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
@@ -858,6 +869,15 @@
}
}
+ @Override
+ public void onBroadcastDisabled() {
+ for (AbstractPreferenceController controller : mPreferenceControllers) {
+ if (controller instanceof BluetoothLeAudioUiPreferenceController) {
+ ((BluetoothLeAudioUiPreferenceController) controller).onBroadcastDisabled();
+ }
+ }
+ }
+
/**
* For Search.
*/
diff --git a/src/com/android/settings/language/LanguageSettingScreen.kt b/src/com/android/settings/language/LanguageSettingScreen.kt
new file mode 100644
index 0000000..09ca11b
--- /dev/null
+++ b/src/com/android/settings/language/LanguageSettingScreen.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.language
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import com.android.settings.R
+import com.android.settings.Settings.LanguageSettingsActivity
+import com.android.settings.flags.Flags
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.ProvidePreferenceScreen
+import com.android.settingslib.metadata.preferenceHierarchy
+import com.android.settingslib.preference.PreferenceScreenCreator
+
+@ProvidePreferenceScreen
+class LanguageSettingScreen: PreferenceScreenCreator {
+ override val key: String
+ get() = KEY
+
+ override val title: Int
+ get() = R.string.languages_settings
+
+ override val summary: Int
+ get() = R.string.languages_setting_summary
+
+ override val icon: Int
+ get() = R.drawable.ic_settings_languages
+
+ override fun isFlagEnabled(context: Context) = Flags.catalystLanguageSetting()
+
+ override fun hasCompleteHierarchy() = false
+
+ override fun fragmentClass() = LanguageSettings::class.java
+
+ override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
+
+ companion object {
+ const val KEY = "language_setting"
+ }
+}
diff --git a/src/com/android/settings/language/LanguageSettings.java b/src/com/android/settings/language/LanguageSettings.java
index a5adb02..d992ff2 100644
--- a/src/com/android/settings/language/LanguageSettings.java
+++ b/src/com/android/settings/language/LanguageSettings.java
@@ -67,6 +67,11 @@
}
@Override
+ public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
+ return LanguageSettingScreen.KEY;
+ }
+
+ @Override
protected int getPreferenceScreenResId() {
return R.xml.language_settings;
}
diff --git a/src/com/android/settings/notification/CallVolumePreference.kt b/src/com/android/settings/notification/CallVolumePreference.kt
new file mode 100644
index 0000000..9b593ad
--- /dev/null
+++ b/src/com/android/settings/notification/CallVolumePreference.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.notification
+
+import android.content.Context
+import android.media.AudioManager
+import android.media.AudioManager.STREAM_BLUETOOTH_SCO
+import android.media.AudioManager.STREAM_VOICE_CALL
+import android.os.UserHandle
+import android.os.UserManager.DISALLOW_ADJUST_VOLUME
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.datastore.NoOpKeyedObservable
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.PreferenceIconProvider
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceRestrictionProvider
+import com.android.settingslib.metadata.RangeValue
+import com.android.settingslib.preference.PreferenceBinding
+
+// LINT.IfChange
+open class CallVolumePreference :
+ PreferenceMetadata,
+ PreferenceBinding,
+ PersistentPreference<Int>,
+ RangeValue,
+ PreferenceAvailabilityProvider,
+ PreferenceIconProvider,
+ PreferenceRestrictionProvider {
+ override val key: String
+ get() = KEY
+
+ override val title: Int
+ get() = R.string.call_volume_option_title
+
+ override fun getIcon(context: Context) = R.drawable.ic_local_phone_24_lib
+
+ override fun order(context: Context) = -170
+
+ override fun isAvailable(context: Context) =
+ context.resources.getBoolean(R.bool.config_show_call_volume) &&
+ !createAudioHelper(context).isSingleVolume()
+
+ override fun isRestricted(context: Context) =
+ RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ context,
+ DISALLOW_ADJUST_VOLUME,
+ UserHandle.myUserId()
+ ) || RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ DISALLOW_ADJUST_VOLUME,
+ UserHandle.myUserId()
+ ) != null
+
+ override fun storage(context: Context): KeyValueStore {
+ val helper = createAudioHelper(context)
+ return object : NoOpKeyedObservable<String>(), KeyValueStore {
+ override fun contains(key: String) = key == KEY
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+ helper.getStreamVolume(getAudioStream(context)) as T
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ helper.setStreamVolume(getAudioStream(context), value as Int)
+ }
+ }
+ }
+
+ override fun getMinValue(context: Context) =
+ createAudioHelper(context).getMinVolume(getAudioStream(context))
+
+ override fun getMaxValue(context: Context) =
+ createAudioHelper(context).getMaxVolume(getAudioStream(context))
+
+ override fun createWidget(context: Context) = VolumeSeekBarPreference(context)
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ (preference as VolumeSeekBarPreference).setStream(getAudioStream(preference.context))
+ }
+
+ open fun createAudioHelper(context: Context) = AudioHelper(context)
+
+ @Suppress("DEPRECATION")
+ fun getAudioStream(context: Context): Int {
+ val audioManager = context.getSystemService(AudioManager::class.java)
+ return when {
+ audioManager.isBluetoothScoOn() -> STREAM_BLUETOOTH_SCO
+ else -> STREAM_VOICE_CALL
+ }
+ }
+
+ companion object {
+ const val KEY = "call_volume"
+ }
+}
+// LINT.ThenChange(CallVolumePreferenceController.java)
diff --git a/src/com/android/settings/notification/CallVolumePreferenceController.java b/src/com/android/settings/notification/CallVolumePreferenceController.java
index d505227..d7c5ddc 100644
--- a/src/com/android/settings/notification/CallVolumePreferenceController.java
+++ b/src/com/android/settings/notification/CallVolumePreferenceController.java
@@ -22,7 +22,7 @@
import com.android.settings.R;
-
+// LINT.IfChange
public class CallVolumePreferenceController extends VolumeSeekBarPreferenceController {
private AudioManager mAudioManager;
@@ -69,3 +69,4 @@
}
}
+// LINT.ThenChange(CallVolumePreference.kt)
diff --git a/src/com/android/settings/notification/SoundScreen.kt b/src/com/android/settings/notification/SoundScreen.kt
index 6b60967..f29437b 100644
--- a/src/com/android/settings/notification/SoundScreen.kt
+++ b/src/com/android/settings/notification/SoundScreen.kt
@@ -49,6 +49,7 @@
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(this) {
+ +CallVolumePreference()
+DialPadTonePreference()
}
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index b069c7e..fffb784 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -113,6 +113,14 @@
if (phoneRingTonePreference != null && openPhoneRingtonePicker) {
onPreferenceTreeClick(phoneRingTonePreference);
}
+ if (isCatalystEnabled()) {
+ for (String key : getPreferenceKeysInHierarchy()) {
+ Preference preference = findPreference(key);
+ if (preference instanceof VolumeSeekBarPreference) {
+ ((VolumeSeekBarPreference) preference).setCallback(mVolumeCallback);
+ }
+ }
+ }
}
@Override
diff --git a/src/com/android/settings/notification/app/BundleListPreferenceController.java b/src/com/android/settings/notification/app/BundleListPreferenceController.java
index 82e910c..9ada049 100644
--- a/src/com/android/settings/notification/app/BundleListPreferenceController.java
+++ b/src/com/android/settings/notification/app/BundleListPreferenceController.java
@@ -91,14 +91,27 @@
public void updateState(Preference preference) {
PreferenceCategory category = (PreferenceCategory) preference;
- createOrUpdatePrefForChannel(category,
- mBackend.getChannel(mAppRow.pkg, mAppRow.uid, PROMOTIONS_ID));
- createOrUpdatePrefForChannel(category,
- mBackend.getChannel(mAppRow.pkg, mAppRow.uid, RECS_ID));
- createOrUpdatePrefForChannel(category,
- mBackend.getChannel(mAppRow.pkg, mAppRow.uid, SOCIAL_MEDIA_ID));
- createOrUpdatePrefForChannel(category,
- mBackend.getChannel(mAppRow.pkg, mAppRow.uid, NEWS_ID));
+ NotificationChannel promos = mBackend.getChannel(mAppRow.pkg, mAppRow.uid, PROMOTIONS_ID);
+ if (promos != null) {
+ createOrUpdatePrefForChannel(category, promos);
+ }
+ NotificationChannel recs = mBackend.getChannel(mAppRow.pkg, mAppRow.uid, RECS_ID);
+ if (recs != null) {
+ createOrUpdatePrefForChannel(category, recs);
+ }
+ NotificationChannel social = mBackend.getChannel(mAppRow.pkg, mAppRow.uid, SOCIAL_MEDIA_ID);
+ if (social != null) {
+ createOrUpdatePrefForChannel(category, social);
+ }
+ NotificationChannel news = mBackend.getChannel(mAppRow.pkg, mAppRow.uid, NEWS_ID);
+ if (news != null) {
+ createOrUpdatePrefForChannel(category, news);
+ }
+
+ int preferenceCount = ((PreferenceGroup) preference).getPreferenceCount();
+ if (preferenceCount == 0) {
+ preference.setVisible(false);
+ }
}
@NonNull
@@ -167,5 +180,4 @@
icon.setTintList(Utils.getColorAccent(mContext));
return icon;
}
-
}
diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
index 014a190..13d5c6e 100644
--- a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
@@ -40,6 +40,7 @@
import androidx.preference.Preference;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
@@ -108,7 +109,9 @@
tryParseScheduleConditionId(mode.getRule().getConditionId());
if (schedule != null) {
preference.setTitle(SystemZenRules.getTimeSummary(mContext, schedule));
- preference.setSummary(SystemZenRules.getShortDaysSummary(mContext, schedule));
+ preference.setSummary(Utils.createAccessibleSequence(
+ SystemZenRules.getDaysOfWeekShort(mContext, schedule),
+ SystemZenRules.getDaysOfWeekFull(mContext, schedule)));
} else {
// Fallback, but shouldn't happen.
Log.wtf(TAG, "SCHEDULE_TIME mode without schedule: " + mode);
diff --git a/tests/robotests/src/com/android/settings/MainClearTest.java b/tests/robotests/src/com/android/settings/MainClearTest.java
index 0f823d6..4c2b266 100644
--- a/tests/robotests/src/com/android/settings/MainClearTest.java
+++ b/tests/robotests/src/com/android/settings/MainClearTest.java
@@ -149,8 +149,8 @@
doReturn(mMockActivity).when(mMainClear).getActivity();
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mBiometricManager.canAuthenticate(anyInt(),
- eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
- .thenReturn(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
+ eq(BiometricManager.Authenticators.IDENTITY_CHECK)))
+ .thenReturn(BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE);
}
@After
@@ -379,7 +379,7 @@
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mResources.getString(anyInt())).thenReturn(TEST_ACCOUNT_NAME);
when(mBiometricManager.canAuthenticate(anyInt(),
- eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
+ eq(BiometricManager.Authenticators.IDENTITY_CHECK)))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
doReturn(true).when(mMainClear).isValidRequestCode(eq(MainClear.KEYGUARD_REQUEST));
doNothing().when(mMainClear).startActivityForResult(any(), anyInt());
@@ -406,7 +406,7 @@
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mResources.getString(anyInt())).thenReturn(TEST_ACCOUNT_NAME);
when(mBiometricManager.canAuthenticate(anyInt(),
- eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
+ eq(BiometricManager.Authenticators.IDENTITY_CHECK)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_LOCKOUT);
doReturn(true).when(mMainClear).isValidRequestCode(eq(MainClear.KEYGUARD_REQUEST));
doNothing().when(mMainClear).startActivityForResult(any(), anyInt());
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index 8f4b83e..b91ad6d 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -541,7 +541,7 @@
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testRequestBiometricAuthentication_biometricManagerReturnsSuccess_shouldReturnOk() {
when(mBiometricManager.canAuthenticate(USER_ID,
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
+ BiometricManager.Authenticators.IDENTITY_CHECK))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
final Utils.BiometricStatus requestBiometricAuthenticationForMandatoryBiometrics =
Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
@@ -554,7 +554,7 @@
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testRequestBiometricAuthentication_biometricManagerReturnsError_shouldReturnError() {
when(mBiometricManager.canAuthenticate(anyInt(),
- eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
+ eq(BiometricManager.Authenticators.IDENTITY_CHECK)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
false /* biometricsAuthenticationRequested */, USER_ID)).isEqualTo(
@@ -567,10 +567,10 @@
when(mContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager);
when(mMockUserManager.getCredentialOwnerProfile(USER_ID)).thenReturn(USER_ID);
when(mBiometricManager.canAuthenticate(anyInt(),
- eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
+ eq(BiometricManager.Authenticators.IDENTITY_CHECK)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
when(mBiometricManager.canAuthenticate(0 /* userId */,
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
+ BiometricManager.Authenticators.IDENTITY_CHECK))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
false /* biometricsAuthenticationRequested */, USER_ID)).isEqualTo(
@@ -594,7 +594,7 @@
final Intent intent = intentArgumentCaptor.getValue();
assertThat(intent.getExtra(BIOMETRIC_PROMPT_AUTHENTICATORS)).isEqualTo(
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ BiometricManager.Authenticators.IDENTITY_CHECK);
assertThat(intent.getExtra(BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT)).isNotNull();
assertThat(intent.getExtra(KeyguardManager.EXTRA_DESCRIPTION)).isNotNull();
assertThat(intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, false))
diff --git a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java
index b4605c7..3dc011e 100644
--- a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java
@@ -128,8 +128,8 @@
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mBiometricManager).when(mActivity).getSystemService(BiometricManager.class);
when(mBiometricManager.canAuthenticate(anyInt(),
- eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
- .thenReturn(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
+ eq(BiometricManager.Authenticators.IDENTITY_CHECK)))
+ .thenReturn(BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE);
ReflectionHelpers.setField(mFragment, "mDashboardFeatureProvider",
FakeFeatureFactory.setupForTest().dashboardFeatureProvider);
@@ -182,7 +182,7 @@
ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
doNothing().when(mFragment).startActivityForResult(any(), anyInt());
when(mBiometricManager.canAuthenticate(anyInt(),
- eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
+ eq(BiometricManager.Authenticators.IDENTITY_CHECK)))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
mFragment.onAttach(mContext);
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
index 0e1bcf6..1086f85 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
@@ -149,7 +149,7 @@
doReturn(true).when(mFingerprintManager).isHardwareDetected();
doReturn(mVibrator).when(mContext).getSystemService(Vibrator.class);
when(mBiometricManager.canAuthenticate(PRIMARY_USER_ID,
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
+ BiometricManager.Authenticators.IDENTITY_CHECK))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
}
@@ -176,7 +176,7 @@
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testLaunchBiometricPromptForFingerprint() {
when(mBiometricManager.canAuthenticate(PRIMARY_USER_ID,
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
+ BiometricManager.Authenticators.IDENTITY_CHECK))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
doNothing().when(mFingerprintManager).generateChallenge(anyInt(), any());
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index fc19728..9609af4 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -40,6 +40,7 @@
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -84,6 +85,7 @@
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock private LocalBluetoothProfileManager mProfileManager;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
+ @Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
private Context mContext;
@@ -102,6 +104,7 @@
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
+ when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager);
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
@@ -267,8 +270,7 @@
}
@Test
- public void
- onProfileConnectionStateChanged_leaConnected_notInCallSharingFlagOff_addsPreference() {
+ public void onProfileConnectionStateChanged_leaConnected_notInCallSharingFlagOff_addPref() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
@@ -288,8 +290,7 @@
}
@Test
- public void
- onProfileConnectionStateChanged_leaConnected_notInCallNotInSharing_addsPreference() {
+ public void onProfileConnectionStateChanged_leaConnected_notInCallNotInSharing_addPref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
@@ -306,16 +307,13 @@
}
@Test
- public void onProfileConnectionStateChanged_leaConnected_inCallSharingFlagOff_addsPreference() {
+ public void onProfileConnectionStateChanged_leaConnected_inCallSharingFlagOff_addPref() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
- when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mBroadcastReceiveState));
- List<Long> bisSyncState = new ArrayList<>();
- bisSyncState.add(1L);
- when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+ when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
@@ -326,7 +324,7 @@
}
@Test
- public void onProfileConnectionStateChanged_leaConnected_inCallNotInSharing_addsPreference() {
+ public void onProfileConnectionStateChanged_leaConnected_inCallNotInSharing_addPref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
@@ -343,9 +341,9 @@
}
@Test
- public void
- onProfileConnectionStateChanged_leaDeviceConnected_notInCallInSharing_removesPref() {
+ public void onProfileConnectionStateChanged_leaConnected_notInCallInSharing_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
@@ -365,8 +363,31 @@
}
@Test
- public void onProfileConnectionStateChanged_leaDeviceConnected_inCallInSharing_removesPref() {
+ public void
+ onProfileConnectionStateChanged_leaConnected_noInCallInSharing_hysteresis_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
+ .thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mBroadcastReceiveState));
+ when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
+ when(mBroadcastReceiveState.getBroadcastId()).thenReturn(1);
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_leaConnected_inCallSharing_removePref() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
@@ -386,6 +407,27 @@
}
@Test
+ public void onProfileConnectionStateChanged_leaConnected_inCallSharing_hysteresis_removePref() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
+ .thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
+ when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mBroadcastReceiveState));
+ when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
+ when(mBroadcastReceiveState.getBroadcastId()).thenReturn(1);
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
+
+ @Test
public void
onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovePreference() {
mAudioManager.setMode(AudioManager.MODE_NORMAL);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractorTest.kt b/tests/robotests/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractorTest.kt
index a83b7c2..28e0581 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractorTest.kt
+++ b/tests/robotests/src/com/android/settings/bluetooth/domain/interactor/SpatialAudioInteractorTest.kt
@@ -83,6 +83,7 @@
@Test
fun getDeviceSetting_noAudioProfile_returnNull() {
testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(true)
val setting = getLatestValue(underTest.getDeviceSetting(cachedDevice))
assertThat(setting).isNull()
@@ -93,6 +94,7 @@
@Test
fun getDeviceSetting_audioProfileNotEnabled_returnNull() {
testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(true)
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(false)
@@ -104,8 +106,23 @@
}
@Test
+ fun getDeviceSetting_deviceNotConnected_returnNull() {
+ testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(false)
+ `when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
+ `when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
+
+ val setting = getLatestValue(underTest.getDeviceSetting(cachedDevice))
+
+ assertThat(setting).isNull()
+ verifyNoInteractions(spatializerRepository)
+ }
+ }
+
+ @Test
fun getDeviceSetting_spatialAudioNotSupported_returnNull() {
testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(true)
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
@@ -122,6 +139,7 @@
@Test
fun getDeviceSetting_spatialAudioSupported_returnTwoToggles() {
testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(true)
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
@@ -150,6 +168,7 @@
@Test
fun getDeviceSetting_headTrackingSupported_returnThreeToggles() {
testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(true)
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
@@ -178,6 +197,7 @@
@Test
fun getDeviceSetting_updateState_enableSpatialAudio() {
testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(true)
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
@@ -207,6 +227,7 @@
@Test
fun getDeviceSetting_updateState_enableHeadTracking() {
testScope.runTest {
+ `when`(cachedDevice.isConnected).thenReturn(true)
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java
index 11e31b6..12e03d4 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java
@@ -51,6 +51,7 @@
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -99,6 +100,7 @@
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock private LocalBluetoothProfileManager mLocalBtProfileManager;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
+ @Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private BluetoothLeBroadcastReceiveState mState;
private Context mContext;
@@ -122,9 +124,7 @@
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
- List<Long> bisSyncState = new ArrayList<>();
- bisSyncState.add(1L);
- when(mState.getBisSyncState()).thenReturn(bisSyncState);
+ when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
Pair<Drawable, String> pairs = new Pair<>(mDrawable, TEST_DEVICE_NAME);
doReturn(TEST_DEVICE_NAME).when(mCachedBluetoothDevice).getName();
doReturn(mBluetoothDevice).when(mCachedBluetoothDevice).getDevice();
@@ -152,7 +152,7 @@
@Test
public void onProfileConnectionStateChanged_leaDeviceConnected_flagOff_removesPref() {
- setupPreferenceMapWithDevice();
+ setupPreferenceMapWithDevice(false);
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
@@ -170,8 +170,46 @@
}
@Test
- public void onProfileConnectionStateChanged_leaDeviceConnected_noSource_removesPref() {
- setupPreferenceMapWithDevice();
+ public void onProfileConnectionStateChanged_leaConnected_flagOff_hysteresisMode_removesPref() {
+ setupPreferenceMapWithDevice(true);
+
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+
+ mDeviceUpdater.onProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.LE_AUDIO);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mDevicePreferenceCallback).onDeviceRemoved(captor.capture());
+ assertThat(captor.getValue() instanceof BluetoothDevicePreference).isTrue();
+ assertThat(((BluetoothDevicePreference) captor.getValue()).getBluetoothDevice())
+ .isEqualTo(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_leaConnected_noSource_removesPref() {
+ setupPreferenceMapWithDevice(false);
+
+ when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of());
+ ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+
+ mDeviceUpdater.onProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.LE_AUDIO);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mDevicePreferenceCallback).onDeviceRemoved(captor.capture());
+ assertThat(captor.getValue() instanceof BluetoothDevicePreference).isTrue();
+ assertThat(((BluetoothDevicePreference) captor.getValue()).getBluetoothDevice())
+ .isEqualTo(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_leaConnected_noSource_hysteresisMode_removesPref() {
+ setupPreferenceMapWithDevice(true);
when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of());
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
@@ -190,7 +228,7 @@
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_removesPref() {
- setupPreferenceMapWithDevice();
+ setupPreferenceMapWithDevice(false);
mCachedDevices.clear();
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
@@ -209,8 +247,28 @@
}
@Test
- public void onProfileConnectionStateChanged_leaDeviceDisconnected_removesPref() {
- setupPreferenceMapWithDevice();
+ public void onProfileConnectionStateChanged_deviceIsNotInList_hysteresisMode_removesPref() {
+ setupPreferenceMapWithDevice(true);
+
+ mCachedDevices.clear();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
+ ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+
+ mDeviceUpdater.onProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.LE_AUDIO);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mDevicePreferenceCallback).onDeviceRemoved(captor.capture());
+ assertThat(captor.getValue() instanceof BluetoothDevicePreference).isTrue();
+ assertThat(((BluetoothDevicePreference) captor.getValue()).getBluetoothDevice())
+ .isEqualTo(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_leaDisconnected_removesPref() {
+ setupPreferenceMapWithDevice(false);
when(mDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(false);
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
@@ -228,8 +286,27 @@
}
@Test
- public void onProfileConnectionStateChanged_leaDeviceDisconnecting_removesPref() {
- setupPreferenceMapWithDevice();
+ public void onProfileConnectionStateChanged_leaDisconnected_hysteresisMode_removesPref() {
+ setupPreferenceMapWithDevice(true);
+
+ when(mDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(false);
+ ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+
+ mDeviceUpdater.onProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.LE_AUDIO);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mDevicePreferenceCallback).onDeviceRemoved(captor.capture());
+ assertThat(captor.getValue() instanceof BluetoothDevicePreference).isTrue();
+ assertThat(((BluetoothDevicePreference) captor.getValue()).getBluetoothDevice())
+ .isEqualTo(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_leaDisconnecting_removesPref() {
+ setupPreferenceMapWithDevice(false);
doReturn(false).when(mCachedBluetoothDevice).isConnectedLeAudioDevice();
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
@@ -246,9 +323,38 @@
}
@Test
- public void onProfileConnectionStateChanged_leaDeviceConnected_hasSource_addsPreference() {
+ public void onProfileConnectionStateChanged_leaDisconnecting_hysteresisMode_removesPref() {
+ setupPreferenceMapWithDevice(true);
+ doReturn(false).when(mCachedBluetoothDevice).isConnectedLeAudioDevice();
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
- setupPreferenceMapWithDevice();
+
+ mDeviceUpdater.onProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.LE_AUDIO);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mDevicePreferenceCallback).onDeviceRemoved(captor.capture());
+ assertThat(captor.getValue() instanceof BluetoothDevicePreference).isTrue();
+ assertThat(((BluetoothDevicePreference) captor.getValue()).getBluetoothDevice())
+ .isEqualTo(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_leaConnected_hasSource_addsPref() {
+ ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+ setupPreferenceMapWithDevice(false);
+
+ verify(mDevicePreferenceCallback).onDeviceAdded(captor.capture());
+ assertThat(captor.getValue() instanceof BluetoothDevicePreference).isTrue();
+ assertThat(((BluetoothDevicePreference) captor.getValue()).getBluetoothDevice())
+ .isEqualTo(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_leaConnected_hasSource_hysteresisMode_addsPref() {
+ ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+ setupPreferenceMapWithDevice(true);
verify(mDevicePreferenceCallback).onDeviceAdded(captor.capture());
assertThat(captor.getValue() instanceof BluetoothDevicePreference).isTrue();
@@ -275,9 +381,19 @@
verify(mDevicePreferenceCallback).onDeviceClick(preference);
}
- private void setupPreferenceMapWithDevice() {
+ private void setupPreferenceMapWithDevice(boolean hysteresisModeOn) {
// Add device to preferenceMap
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ if (hysteresisModeOn) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
+ when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
+ when(mState.getBroadcastId()).thenReturn(1);
+ } else {
+ mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
+ List<Long> bisSyncState = new ArrayList<>();
+ bisSyncState.add(1L);
+ when(mState.getBisSyncState()).thenReturn(bisSyncState);
+ }
when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of(mState));
when(mDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
doReturn(true).when(mCachedBluetoothDevice).isConnectedLeAudioDevice();
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothLeAudioUiPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioUiPreferenceControllerTest.java
new file mode 100644
index 0000000..a4462e4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioUiPreferenceControllerTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.development;
+
+import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_KEY;
+import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_OFF;
+import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_ON;
+import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_UNSET;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothStatusCodes;
+import android.content.Context;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreferenceCompat;
+
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.flags.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+ shadows = {
+ ShadowBluetoothAdapter.class,
+ BluetoothLeAudioUiPreferenceControllerTest.ShadowBluetoothRebootDialogFragment.class
+ })
+public class BluetoothLeAudioUiPreferenceControllerTest {
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private static final String SOURCE_SYSTEM_PROP_KEY =
+ "bluetooth.profile.bap.broadcast.source.enabled";
+ private static final String ASSIST_SYSTEM_PROP_KEY =
+ "bluetooth.profile.bap.broadcast.assist.enabled";
+ @Mock private PreferenceScreen mPreferenceScreen;
+ @Mock private DevelopmentSettingsDashboardFragment mFragment;
+ @Mock private SwitchPreferenceCompat mPreference;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private Context mContext;
+ private BluetoothLeAudioUiPreferenceController mController;
+
+ @Before
+ public void setup() {
+ mContext = RuntimeEnvironment.getApplication();
+ SystemProperties.set(SOURCE_SYSTEM_PROP_KEY, "true");
+ SystemProperties.set(ASSIST_SYSTEM_PROP_KEY, "true");
+ // Reset value
+ Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mShadowBluetoothAdapter.setEnabled(true);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mController = spy(new BluetoothLeAudioUiPreferenceController(mContext, mFragment));
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreference);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void isAvailable_flagOff_returnFalse() {
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void isAvailable_flagOn_returnFalse() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void isAvailable_flagOn_propertyOff_returnFalse() {
+ SystemProperties.set(SOURCE_SYSTEM_PROP_KEY, "false");
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void updateState_settingEnabled_checked() {
+ Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_ON);
+ mController.updateState(mPreference);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mPreference).setChecked(true);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void updateState_settingDisabled_notChecked() {
+ Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_OFF);
+ mController.updateState(mPreference);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void updateState_featureSupported_enabled() {
+ mController.updateState(mPreference);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mPreference).setEnabled(true);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void updateState_featureUnsupported_disabled() {
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
+ mController.updateState(mPreference);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mPreference).setEnabled(false);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void onRebootDialogConfirmed_noChange_doNothing() {
+ mController.onRebootDialogConfirmed();
+
+ int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET);
+ assertThat(result).isEqualTo(VALUE_UNSET);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void onRebootDialogConfirmed_hasChange_turnOn() {
+ mController.onPreferenceChange(mPreference, true);
+ mController.onRebootDialogConfirmed();
+
+ int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET);
+ assertThat(result).isEqualTo(VALUE_ON);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void onRebootDialogCanceled_hasChange_doNothing() {
+ mController.onPreferenceChange(mPreference, true);
+ mController.onRebootDialogCanceled();
+
+ int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET);
+ assertThat(result).isEqualTo(VALUE_UNSET);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void onBroadcastDisabled_currentValueOn_turnOff() {
+ Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_ON);
+ mController.updateState(mPreference);
+ shadowOf(Looper.getMainLooper()).idle();
+ mController.onBroadcastDisabled();
+
+ int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET);
+ assertThat(result).isEqualTo(VALUE_OFF);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION)
+ public void onBroadcastDisabled_currentValueUnset_doNothing() {
+ mController.updateState(mPreference);
+ mController.onBroadcastDisabled();
+ shadowOf(Looper.getMainLooper()).idle();
+
+ int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET);
+ assertThat(result).isEqualTo(VALUE_UNSET);
+ }
+
+ @Implements(BluetoothRebootDialog.class)
+ public static class ShadowBluetoothRebootDialogFragment {
+
+ /** Shadow implementation of BluetoothRebootDialog#show */
+ @Implementation
+ public static void show(DevelopmentSettingsDashboardFragment host) {
+ // Do nothing.
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java
index ed65d5b..f9f3be7 100644
--- a/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java
@@ -36,6 +36,7 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+// LINT.IfChange
@RunWith(RobolectricTestRunner.class)
public class CallVolumePreferenceControllerTest {
private static final String TEST_KEY = "Test_Key";
@@ -108,3 +109,4 @@
assertThat(mController.isPublicSlice()).isTrue();
}
}
+// LINT.ThenChange(CallVolumePreferenceTest.kt)
diff --git a/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceTest.kt
new file mode 100644
index 0000000..d6bc6d0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.notification
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.res.Resources
+import android.media.AudioManager
+import android.media.AudioManager.STREAM_BLUETOOTH_SCO
+import android.media.AudioManager.STREAM_VOICE_CALL
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+// LINT.IfChange
+@RunWith(AndroidJUnit4::class)
+class CallVolumePreferenceTest {
+ private var audioHelper = mock<AudioHelper>()
+ private var mockResources = mock<Resources>()
+
+ private var audioManager: AudioManager? = null
+
+ private var callVolumePreference = CallVolumePreference()
+ private val context = object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+ override fun getSystemService(name: String): Any? =
+ when (name) {
+ Context.AUDIO_SERVICE -> audioManager
+ else -> super.getSystemService(name)
+ }
+
+ override fun getResources(): Resources = mockResources
+ }
+
+ @Test
+ fun isAvailable_configTrueAndNoSingleVolume_shouldReturnTrue() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+ audioHelper = mock { on { isSingleVolume } doReturn false }
+ callVolumePreference = spy(callVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(callVolumePreference.isAvailable(context)).isTrue()
+ }
+
+ @Test
+ fun isAvailable_configTrueAndSingleVolume_shouldReturnFalse() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+ audioHelper = mock { on { isSingleVolume } doReturn true }
+ callVolumePreference = spy(callVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(callVolumePreference.isAvailable(context)).isFalse()
+ }
+
+ @Test
+ fun isAvailable_configFalse_shouldReturnFalse() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn false }
+
+ assertThat(callVolumePreference.isAvailable(context)).isFalse()
+ }
+
+ @Test
+ @Suppress("DEPRECATION")
+ fun getAudioStream_onBluetoothScoOn_shouldEqualToStreamBluetoothSco() {
+ audioManager = mock { on { isBluetoothScoOn } doReturn true }
+
+ assertThat(callVolumePreference.getAudioStream(context)).isEqualTo(STREAM_BLUETOOTH_SCO)
+ }
+
+ @Test
+ @Suppress("DEPRECATION")
+ fun getAudioStream_onBluetoothScoOff_shouldEqualToStreamVoiceCall() {
+ audioManager = mock { on { isBluetoothScoOn } doReturn false }
+
+ assertThat(callVolumePreference.getAudioStream(context)).isEqualTo(STREAM_VOICE_CALL)
+ }
+}
+// LINT.ThenChange(CallVolumePreferenceControllerTest.java)
diff --git a/tests/robotests/src/com/android/settings/notification/app/BundleListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BundleListPreferenceControllerTest.java
index 8b8c77e..a8de8ef 100644
--- a/tests/robotests/src/com/android/settings/notification/app/BundleListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/BundleListPreferenceControllerTest.java
@@ -89,15 +89,6 @@
mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
mGroupList = new PreferenceCategory(mContext);
mPreferenceScreen.addPreference(mGroupList);
-
- when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, PROMOTIONS_ID)).thenReturn(
- new NotificationChannel(PROMOTIONS_ID, PROMOTIONS_ID, 2));
- when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, NEWS_ID)).thenReturn(
- new NotificationChannel(NEWS_ID, NEWS_ID, 2));
- when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, SOCIAL_MEDIA_ID)).thenReturn(
- new NotificationChannel(SOCIAL_MEDIA_ID, SOCIAL_MEDIA_ID, 2));
- when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, RECS_ID)).thenReturn(
- new NotificationChannel(RECS_ID, RECS_ID, 2));
}
@Test
@@ -132,6 +123,14 @@
@Test
public void updateState() {
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, PROMOTIONS_ID)).thenReturn(
+ new NotificationChannel(PROMOTIONS_ID, PROMOTIONS_ID, 2));
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, NEWS_ID)).thenReturn(
+ new NotificationChannel(NEWS_ID, NEWS_ID, 2));
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, SOCIAL_MEDIA_ID)).thenReturn(
+ new NotificationChannel(SOCIAL_MEDIA_ID, SOCIAL_MEDIA_ID, 2));
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, RECS_ID)).thenReturn(
+ new NotificationChannel(RECS_ID, RECS_ID, 2));
mController.updateState(mGroupList);
assertThat(mGroupList.getPreferenceCount()).isEqualTo(4);
assertThat(mGroupList.findPreference(PROMOTIONS_ID).getTitle()).isEqualTo(PROMOTIONS_ID);
@@ -142,19 +141,38 @@
}
@Test
- public void updateState_updateChildren() {
+ public void updateState_noBundles() {
mController.updateState(mGroupList);
- assertThat(mGroupList.getPreferenceCount()).isEqualTo(4);
+ assertThat(mGroupList.getPreferenceCount()).isEqualTo(0);
+ assertThat(mGroupList.isVisible()).isFalse();
+ }
+ @Test
+ public void updateState_onlySomeBundlesUsed() {
when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, PROMOTIONS_ID)).thenReturn(
new NotificationChannel(PROMOTIONS_ID, PROMOTIONS_ID, 2));
+ mController.updateState(mGroupList);
+ assertThat(mGroupList.getPreferenceCount()).isEqualTo(1);
+ assertThat(mGroupList.findPreference(PROMOTIONS_ID).getTitle()).isEqualTo(PROMOTIONS_ID);
+ }
+
+ @Test
+ public void updateState_noDuplicateChannelsOnReload() {
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, PROMOTIONS_ID)).thenReturn(
+ new NotificationChannel(PROMOTIONS_ID, PROMOTIONS_ID, 2));
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, NEWS_ID)).thenReturn(
+ new NotificationChannel(NEWS_ID, NEWS_ID, 2));
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, SOCIAL_MEDIA_ID)).thenReturn(
+ new NotificationChannel(SOCIAL_MEDIA_ID, SOCIAL_MEDIA_ID, 2));
+ when(mBackend.getChannel(mAppRow.pkg, mAppRow.uid, RECS_ID)).thenReturn(
+ new NotificationChannel(RECS_ID, RECS_ID, 2));
mController.updateState(mGroupList);
assertThat(mGroupList.getPreferenceCount()).isEqualTo(4);
+ mController.updateState(mGroupList);
+ assertThat(mGroupList.getPreferenceCount()).isEqualTo(4);
assertThat(((PrimarySwitchPreference) mGroupList.findPreference(NEWS_ID)).isChecked())
.isEqualTo(false);
- assertThat(((PrimarySwitchPreference) mGroupList.findPreference(NEWS_ID)).isChecked())
- .isEqualTo(false);
}
}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java
index b7af71b..d916dcf 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java
@@ -44,6 +44,8 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
+import android.text.Spanned;
+import android.text.style.TtsSpan;
import android.widget.TextView;
import androidx.preference.PreferenceManager;
@@ -293,7 +295,14 @@
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getTitle()).isEqualTo("1:00 AM - 3:00 PM");
- assertThat(mPreference.getSummary()).isEqualTo("Mon - Tue, Thu");
+ Spanned summary = (Spanned) mPreference.getSummary();
+ assertThat(summary.toString()).isEqualTo("Mon - Tue, Thu");
+ TtsSpan[] ttsSpans = summary.getSpans(0, summary.length(), TtsSpan.class);
+ assertThat(ttsSpans).hasLength(1);
+ assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_TEXT);
+ assertThat(ttsSpans[0].getArgs().getString(TtsSpan.ARG_TEXT)).isEqualTo(
+ "Monday to Tuesday, Thursday");
+
// Destination as written into the intent by SubSettingLauncher
assertThat(
mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index be43f8e..07df3c8 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -26,11 +26,14 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"flag-junit",
+ "kotlin-test",
+ "mockito-kotlin2",
"mockito-target-minus-junit4",
"platform-test-annotations",
"platform-test-rules",
"truth",
"kotlinx_coroutines_test",
+ "SettingsLibPreference-testutils",
"Settings-testutils2",
"servicestests-utils",
// Don't add SettingsLib libraries here - you can use them directly as they are in the
diff --git a/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
index 7e942d9..9a09bf1 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
@@ -93,7 +93,7 @@
doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mBiometricManager.canAuthenticate(mContext.getUserId(),
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
+ BiometricManager.Authenticators.IDENTITY_CHECK))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
mFactory = FakeFeatureFactory.setupForTest();
@@ -213,7 +213,7 @@
public void onActivityResult_confirmPasswordRequestCompleted_launchBiometricPrompt() {
when(mUserManager.isAdminUser()).thenReturn(true);
when(mBiometricManager.canAuthenticate(mContext.getUserId(),
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
+ BiometricManager.Authenticators.IDENTITY_CHECK))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
final boolean activityResultHandled = mController.onActivityResult(
@@ -233,8 +233,8 @@
public void onActivityResult_confirmPasswordRequestCompleted_mandatoryBiometricsError() {
when(mUserManager.isAdminUser()).thenReturn(true);
when(mBiometricManager.canAuthenticate(mContext.getUserId(),
- BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
- .thenReturn(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
+ BiometricManager.Authenticators.IDENTITY_CHECK))
+ .thenReturn(BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE);
final boolean activityResultHandled = mController.onActivityResult(
BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF,
diff --git a/tests/unit/src/com/android/settings/language/LanguageSettingScreenTest.kt b/tests/unit/src/com/android/settings/language/LanguageSettingScreenTest.kt
new file mode 100644
index 0000000..7b519a2
--- /dev/null
+++ b/tests/unit/src/com/android/settings/language/LanguageSettingScreenTest.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.language
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import com.android.settings.Settings.LanguageSettingsActivity
+import com.android.settings.flags.Flags
+import com.android.settingslib.preference.CatalystScreenTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
+import org.junit.Test
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+class LanguageSettingScreenTest: CatalystScreenTestCase() {
+ override val preferenceScreenCreator = LanguageSettingScreen()
+
+ override val flagName: String
+ get() = Flags.FLAG_CATALYST_LANGUAGE_SETTING
+
+ @Test
+ fun key() {
+ assertThat(preferenceScreenCreator.key).isEqualTo(LanguageSettingScreen.KEY)
+ }
+
+ override fun migration() {}
+}