diff options
8 files changed, 204 insertions, 90 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java index e4539b75f317..0b13900d826b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java @@ -45,7 +45,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.media.AudioManager; -import android.os.Handler; import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.testing.TestableLooper; @@ -75,6 +74,8 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.phone.SystemUIDialogManager; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.After; import org.junit.Before; @@ -107,6 +108,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { private static final String TEST_LABEL = "label"; private static final int TEST_PRESET_INDEX = 1; private static final String TEST_PRESET_NAME = "test_preset"; + private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); @Mock private SystemUIDialogManager mSystemUIDialogManager; @@ -150,11 +152,9 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { private SystemUIDialog mDialog; private SystemUIDialog.Factory mDialogFactory; private HearingDevicesDialogDelegate mDialogDelegate; - private TestableLooper mTestableLooper; @Before public void setUp() { - mTestableLooper = TestableLooper.get(this); when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager); when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile); @@ -186,6 +186,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { public void clickPairNewDeviceButton_intentActionMatch() { setUpDeviceDialogWithPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); getPairNewDeviceButton(mDialog).performClick(); @@ -232,6 +233,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); assertToolsUi(0); } @@ -246,6 +248,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); assertToolsUi(1); } @@ -267,6 +270,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); assertToolsUi(2); } @@ -278,6 +282,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); ViewGroup presetLayout = getPresetLayout(mDialog); assertThat(presetLayout.getVisibility()).isEqualTo(View.GONE); @@ -291,7 +296,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); - mTestableLooper.processAllMessages(); + mExecutor.runAllReady(); ViewGroup presetLayout = getPresetLayout(mDialog); assertThat(presetLayout.getVisibility()).isEqualTo(View.VISIBLE); @@ -306,6 +311,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); ViewGroup ambientLayout = getAmbientLayout(mDialog); assertThat(ambientLayout.getVisibility()).isEqualTo(View.GONE); @@ -318,6 +324,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); ViewGroup ambientLayout = getAmbientLayout(mDialog); assertThat(ambientLayout.getVisibility()).isEqualTo(View.GONE); @@ -343,6 +350,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { public void onActiveDeviceChanged_presetExist_presetSelected() { setUpDeviceDialogWithoutPairNewDeviceButton(); mDialog.show(); + mExecutor.runAllReady(); BluetoothHapPresetInfo info = getTestPresetInfo(); when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info)); when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX); @@ -351,7 +359,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { assertThat(spinner.getSelectedItemPosition()).isEqualTo(-1); mDialogDelegate.onActiveDeviceChanged(mCachedDevice, BluetoothProfile.LE_AUDIO); - mTestableLooper.processAllMessages(); + mExecutor.runAllReady(); ViewGroup presetLayout = getPresetLayout(mDialog); assertThat(presetLayout.getVisibility()).isEqualTo(View.VISIBLE); @@ -381,7 +389,8 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { mActivityStarter, mDialogTransitionAnimator, mLocalBluetoothManager, - new Handler(mTestableLooper.getLooper()), + mExecutor, + mExecutor, mAudioManager, mUiEventLogger ); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java index 438184d4d2d6..22ecb0af8c18 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java @@ -30,7 +30,6 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.media.AudioManager; import android.os.Bundle; -import android.os.Handler; import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; @@ -49,6 +48,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -57,7 +57,6 @@ import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; -import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.accessibility.hearingaid.HearingDevicesListAdapter.HearingDeviceItemCallback; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.bluetooth.qsdialog.ActiveHearingDeviceItemFactory; @@ -67,6 +66,7 @@ import com.android.systemui.bluetooth.qsdialog.DeviceItem; import com.android.systemui.bluetooth.qsdialog.DeviceItemFactory; import com.android.systemui.bluetooth.qsdialog.DeviceItemType; import com.android.systemui.bluetooth.qsdialog.SavedHearingDeviceItemFactory; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.res.R; @@ -79,6 +79,7 @@ import dagger.assisted.AssistedInject; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.stream.Collectors; /** @@ -101,7 +102,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, private final DialogTransitionAnimator mDialogTransitionAnimator; private final ActivityStarter mActivityStarter; private final LocalBluetoothManager mLocalBluetoothManager; - private final Handler mMainHandler; + private final Executor mMainExecutor; + private final Executor mBgExecutor; private final AudioManager mAudioManager; private final LocalBluetoothProfileManager mProfileManager; private final HearingDevicesUiEventLogger mUiEventLogger; @@ -109,8 +111,6 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, private final int mLaunchSourceId; private SystemUIDialog mDialog; - - private List<DeviceItem> mHearingDeviceItemList; private HearingDevicesListAdapter mDeviceListAdapter; private View mPresetLayout; @@ -122,14 +122,14 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, @Override public void onPresetInfoUpdated(List<BluetoothHapPresetInfo> presetInfos, int activePresetIndex) { - mMainHandler.post( + mMainExecutor.execute( () -> refreshPresetUi(presetInfos, activePresetIndex)); } @Override public void onPresetCommandFailed(int reason) { mPresetController.refreshPresetInfo(); - mMainHandler.post(() -> { + mMainExecutor.execute(() -> { showErrorToast(R.string.hearing_devices_presets_error); }); } @@ -166,7 +166,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, ActivityStarter activityStarter, DialogTransitionAnimator dialogTransitionAnimator, @Nullable LocalBluetoothManager localBluetoothManager, - @Main Handler handler, + @Main Executor mainExecutor, + @Background Executor bgExecutor, AudioManager audioManager, HearingDevicesUiEventLogger uiEventLogger) { mShowPairNewDevice = showPairNewDevice; @@ -174,7 +175,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, mActivityStarter = activityStarter; mDialogTransitionAnimator = dialogTransitionAnimator; mLocalBluetoothManager = localBluetoothManager; - mMainHandler = handler; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; mAudioManager = audioManager; mProfileManager = localBluetoothManager.getProfileManager(); mUiEventLogger = uiEventLogger; @@ -227,9 +229,10 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, @Override public void onActiveDeviceChanged(@Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) { - refreshDeviceUi(); - mMainHandler.post(() -> { - CachedBluetoothDevice device = getActiveHearingDevice(); + List<DeviceItem> hearingDeviceItemList = getHearingDeviceItemList(); + refreshDeviceUi(hearingDeviceItemList); + mMainExecutor.execute(() -> { + CachedBluetoothDevice device = getActiveHearingDevice(hearingDeviceItemList); if (mPresetController != null) { mPresetController.setDevice(device); mPresetLayout.setVisibility( @@ -244,13 +247,15 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, @Override public void onProfileConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice, int state, int bluetoothProfile) { - refreshDeviceUi(); + List<DeviceItem> hearingDeviceItemList = getHearingDeviceItemList(); + refreshDeviceUi(hearingDeviceItemList); } @Override public void onAclConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice, int state) { - refreshDeviceUi(); + List<DeviceItem> hearingDeviceItemList = getHearingDeviceItemList(); + refreshDeviceUi(hearingDeviceItemList); } @Override @@ -280,18 +285,25 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DIALOG_SHOW, mLaunchSourceId); - setupDeviceListView(dialog); - setupPairNewDeviceButton(dialog); - setupPresetSpinner(dialog); - if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) { - setupAmbientControls(); - } - setupRelatedToolsView(dialog); + mBgExecutor.execute(() -> { + List<DeviceItem> hearingDeviceItemList = getHearingDeviceItemList(); + CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice( + hearingDeviceItemList); + mMainExecutor.execute(() -> { + setupDeviceListView(dialog, hearingDeviceItemList); + setupPairNewDeviceButton(dialog); + setupPresetSpinner(dialog, activeHearingDevice); + if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) { + setupAmbientControls(activeHearingDevice); + } + setupRelatedToolsView(dialog); + }); + }); } @Override public void onStart(@NonNull SystemUIDialog dialog) { - ThreadUtils.postOnBackgroundThread(() -> { + mBgExecutor.execute(() -> { if (mLocalBluetoothManager != null) { mLocalBluetoothManager.getEventManager().registerCallback(this); } @@ -306,7 +318,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, @Override public void onStop(@NonNull SystemUIDialog dialog) { - ThreadUtils.postOnBackgroundThread(() -> { + mBgExecutor.execute(() -> { if (mLocalBluetoothManager != null) { mLocalBluetoothManager.getEventManager().unregisterCallback(this); } @@ -319,17 +331,18 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, }); } - private void setupDeviceListView(SystemUIDialog dialog) { + private void setupDeviceListView(SystemUIDialog dialog, + List<DeviceItem> hearingDeviceItemList) { final RecyclerView deviceList = dialog.requireViewById(R.id.device_list); deviceList.setLayoutManager(new LinearLayoutManager(dialog.getContext())); - mHearingDeviceItemList = getHearingDeviceItemList(); - mDeviceListAdapter = new HearingDevicesListAdapter(mHearingDeviceItemList, this); + mDeviceListAdapter = new HearingDevicesListAdapter(hearingDeviceItemList, this); deviceList.setAdapter(mDeviceListAdapter); } - private void setupPresetSpinner(SystemUIDialog dialog) { + private void setupPresetSpinner(SystemUIDialog dialog, + CachedBluetoothDevice activeHearingDevice) { mPresetController = new HearingDevicesPresetsController(mProfileManager, mPresetCallback); - mPresetController.setDevice(getActiveHearingDevice()); + mPresetController.setDevice(activeHearingDevice); mPresetSpinner = dialog.requireViewById(R.id.preset_spinner); mPresetInfoAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext()); @@ -367,12 +380,12 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, mPresetLayout.setVisibility(mPresetController.isPresetControlAvailable() ? VISIBLE : GONE); } - private void setupAmbientControls() { + private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) { final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout); mAmbientController = new AmbientVolumeUiController( mDialog.getContext(), mLocalBluetoothManager, ambientLayout); mAmbientController.setShowUiWhenLocalDataExist(false); - mAmbientController.loadDevice(getActiveHearingDevice()); + mAmbientController.loadDevice(activeHearingDevice); } private void setupPairNewDeviceButton(SystemUIDialog dialog) { @@ -429,10 +442,11 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, } } - private void refreshDeviceUi() { - mHearingDeviceItemList = getHearingDeviceItemList(); - mMainHandler.post(() -> { - mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList); + private void refreshDeviceUi(List<DeviceItem> hearingDeviceItemList) { + mMainExecutor.execute(() -> { + if (mDeviceListAdapter != null) { + mDeviceListAdapter.refreshDeviceItemList(hearingDeviceItemList); + } }); } @@ -463,21 +477,27 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, } @Nullable - private CachedBluetoothDevice getActiveHearingDevice() { - return mHearingDeviceItemList.stream() + private static CachedBluetoothDevice getActiveHearingDevice( + List<DeviceItem> hearingDeviceItemList) { + return hearingDeviceItemList.stream() .filter(item -> item.getType() == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE) .map(DeviceItem::getCachedBluetoothDevice) .findFirst() .orElse(null); } + @WorkerThread private DeviceItem createHearingDeviceItem(CachedBluetoothDevice cachedDevice) { final Context context = mDialog.getContext(); if (cachedDevice == null) { return null; } + int mode = mAudioManager.getMode(); + boolean isOngoingCall = mode == AudioManager.MODE_RINGTONE + || mode == AudioManager.MODE_IN_CALL + || mode == AudioManager.MODE_IN_COMMUNICATION; for (DeviceItemFactory itemFactory : mHearingDeviceItemFactoryList) { - if (itemFactory.isFilterMatched(context, cachedDevice, mAudioManager)) { + if (itemFactory.isFilterMatched(context, cachedDevice, isOngoingCall)) { return itemFactory.create(context, cachedDevice); } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModel.kt index ff2d9efa1b58..1c9cf8d14f74 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModel.kt @@ -31,6 +31,7 @@ import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor import com.android.systemui.Prefs import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator @@ -71,6 +72,7 @@ constructor( private val bluetoothStateInteractor: BluetoothStateInteractor, private val bluetoothAutoOnInteractor: BluetoothAutoOnInteractor, private val audioSharingInteractor: AudioSharingInteractor, + private val audioModeInteractor: AudioModeInteractor, private val audioSharingButtonViewModelFactory: AudioSharingButtonViewModel.Factory, private val bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor, private val dialogTransitionAnimator: DialogTransitionAnimator, @@ -167,6 +169,7 @@ constructor( // the device item list and animate the progress bar. merge( deviceItemInteractor.deviceItemUpdateRequest, + audioModeInteractor.isOngoingCall, bluetoothDeviceMetadataInteractor.metadataUpdate, if ( audioSharingInteractor.audioSharingAvailable() && diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt index 095e6e741584..bfbc27d3a086 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt @@ -18,7 +18,6 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothDevice import android.content.Context -import android.media.AudioManager import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager @@ -43,7 +42,7 @@ abstract class DeviceItemFactory { abstract fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean @@ -51,8 +50,8 @@ abstract class DeviceItemFactory { fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, - ): Boolean = isFilterMatched(context, cachedDevice, audioManager, false) + isOngoingCall: Boolean, + ): Boolean = isFilterMatched(context, cachedDevice, isOngoingCall, false) abstract fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem @@ -88,11 +87,11 @@ internal open class ActiveMediaDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return BluetoothUtils.isActiveMediaDevice(cachedDevice) && - BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager) + BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, isOngoingCall) } override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { @@ -113,10 +112,11 @@ internal class AudioSharingMediaDeviceItemFactory( override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return audioSharingAvailable && + !isOngoingCall && BluetoothUtils.hasConnectedBroadcastSource(cachedDevice, localBluetoothManager) } @@ -140,11 +140,12 @@ internal class AvailableAudioSharingMediaDeviceItemFactory( override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return audioSharingAvailable && - super.isFilterMatched(context, cachedDevice, audioManager, true) && + !isOngoingCall && + super.isFilterMatched(context, cachedDevice, false, true) && BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice( cachedDevice, localBluetoothManager, @@ -170,7 +171,7 @@ internal class ActiveHearingDeviceItemFactory : ActiveMediaDeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return BluetoothUtils.isActiveMediaDevice(cachedDevice) && @@ -182,11 +183,11 @@ open class AvailableMediaDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return !BluetoothUtils.isActiveMediaDevice(cachedDevice) && - BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager) + BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, isOngoingCall) } override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { @@ -206,7 +207,7 @@ internal class AvailableHearingDeviceItemFactory : AvailableMediaDeviceItemFacto override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return !BluetoothUtils.isActiveMediaDevice(cachedDevice) && @@ -218,14 +219,14 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) && - BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager) + BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, isOngoingCall) } else { - BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager) + BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, isOngoingCall) } } @@ -246,7 +247,7 @@ internal open class SavedDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { @@ -275,7 +276,7 @@ internal class SavedHearingDeviceItemFactory : SavedDeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt index 1e0ba8e75714..b606c19b3503 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt @@ -19,10 +19,10 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.content.Context -import android.media.AudioManager import com.android.settingslib.bluetooth.BluetoothCallback import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.isActive @@ -51,7 +52,6 @@ class DeviceItemInteractor constructor( private val bluetoothTileDialogRepository: BluetoothTileDialogRepository, private val audioSharingInteractor: AudioSharingInteractor, - private val audioManager: AudioManager, private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter(), private val localBluetoothManager: LocalBluetoothManager?, private val systemClock: SystemClock, @@ -60,6 +60,7 @@ constructor( private val deviceItemDisplayPriority: List<@JvmSuppressWildcards DeviceItemType>, @Application private val coroutineScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, + private val audioModeInteractor: AudioModeInteractor, ) { private val mutableDeviceItemUpdate: MutableSharedFlow<List<DeviceItem>> = @@ -118,8 +119,12 @@ constructor( internal suspend fun updateDeviceItems(context: Context, trigger: DeviceFetchTrigger) { withContext(backgroundDispatcher) { + if (!isActive) { + return@withContext + } val start = systemClock.elapsedRealtime() val audioSharingAvailable = audioSharingInteractor.audioSharingAvailable() + val isOngoingCall = audioModeInteractor.isOngoingCall.first() val deviceItems = bluetoothTileDialogRepository.cachedDevices .mapNotNull { cachedDevice -> @@ -128,7 +133,7 @@ constructor( it.isFilterMatched( context, cachedDevice, - audioManager, + isOngoingCall, audioSharingAvailable, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt index bfc5361b6129..f04fc86d7230 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.volume.domain.interactor.audioModeInteractor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableSharedFlow @@ -146,6 +147,7 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() { ) ), kosmos.audioSharingInteractor, + kosmos.audioModeInteractor, kosmos.audioSharingButtonViewModelFactory, bluetoothDeviceMetadataInteractor, mDialogTransitionAnimator, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt index 0aa5199cb20e..7c8822bc11ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothDevice import android.graphics.drawable.Drawable -import android.media.AudioManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper @@ -61,8 +60,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { private val connectedDeviceItemFactory = ConnectedDeviceItemFactory() private val savedDeviceItemFactory = SavedDeviceItemFactory() - private val audioManager = context.getSystemService(AudioManager::class.java)!! - @Before fun setup() { mockitoSession = @@ -132,7 +129,12 @@ class DeviceItemFactoryTest : SysuiTestCase() { fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_flagOff_returnsFalse() { assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager, false) + .isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + audioSharingAvailable = false, + ) ) .isFalse() } @@ -143,7 +145,12 @@ class DeviceItemFactoryTest : SysuiTestCase() { assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager, true) + .isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + audioSharingAvailable = true, + ) ) .isFalse() } @@ -157,7 +164,26 @@ class DeviceItemFactoryTest : SysuiTestCase() { assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager, true) + .isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + audioSharingAvailable = true, + ) + ) + .isFalse() + } + + @Test + fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_inCall_false() { + assertThat( + AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) + .isFilterMatched( + context, + cachedDevice, + isOngoingCall = true, + audioSharingAvailable = true, + ) ) .isFalse() } @@ -171,7 +197,12 @@ class DeviceItemFactoryTest : SysuiTestCase() { assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager, true) + .isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + audioSharingAvailable = true, + ) ) .isTrue() } @@ -182,7 +213,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) `when`(cachedDevice.isConnected).thenReturn(false) - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) + ) .isTrue() } @@ -192,7 +225,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) `when`(cachedDevice.isConnected).thenReturn(true) - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) + ) .isFalse() } @@ -201,7 +236,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() { `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE) - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) + ) .isFalse() } @@ -211,7 +248,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true) - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) + ) .isFalse() } @@ -223,7 +262,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) `when`(cachedDevice.isConnected).thenReturn(false) - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) + ) .isTrue() } @@ -235,7 +276,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) `when`(cachedDevice.isConnected).thenReturn(true) - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) + ) .isFalse() } @@ -244,14 +287,26 @@ class DeviceItemFactoryTest : SysuiTestCase() { fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() { `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true) - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + connectedDeviceItemFactory.isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + ) + ) .isTrue() } @Test @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() { - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + connectedDeviceItemFactory.isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + ) + ) .isFalse() } @@ -261,7 +316,13 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true) - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + connectedDeviceItemFactory.isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + ) + ) .isFalse() } @@ -272,7 +333,13 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true) - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + connectedDeviceItemFactory.isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + ) + ) .isTrue() } @@ -283,7 +350,13 @@ class DeviceItemFactoryTest : SysuiTestCase() { `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(false) - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) + assertThat( + connectedDeviceItemFactory.isFilterMatched( + context, + cachedDevice, + isOngoingCall = false, + ) + ) .isFalse() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt index 42dc50d77d05..943b89aa10f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.testKosmos import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.volume.domain.interactor.audioModeInteractor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.test.TestScope @@ -102,7 +103,6 @@ class DeviceItemInteractorTest : SysuiTestCase() { DeviceItemInteractor( bluetoothTileDialogRepository, kosmos.audioSharingInteractor, - audioManager, adapter, localBluetoothManager, fakeSystemClock, @@ -111,6 +111,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { emptyList(), testScope.backgroundScope, dispatcher, + kosmos.audioModeInteractor, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -130,7 +131,6 @@ class DeviceItemInteractorTest : SysuiTestCase() { DeviceItemInteractor( bluetoothTileDialogRepository, kosmos.audioSharingInteractor, - audioManager, adapter, localBluetoothManager, fakeSystemClock, @@ -139,6 +139,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { emptyList(), testScope.backgroundScope, dispatcher, + kosmos.audioModeInteractor, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -158,7 +159,6 @@ class DeviceItemInteractorTest : SysuiTestCase() { DeviceItemInteractor( bluetoothTileDialogRepository, kosmos.audioSharingInteractor, - audioManager, adapter, localBluetoothManager, fakeSystemClock, @@ -167,6 +167,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { emptyList(), testScope.backgroundScope, dispatcher, + kosmos.audioModeInteractor, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -186,7 +187,6 @@ class DeviceItemInteractorTest : SysuiTestCase() { DeviceItemInteractor( bluetoothTileDialogRepository, kosmos.audioSharingInteractor, - audioManager, adapter, localBluetoothManager, fakeSystemClock, @@ -198,6 +198,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { emptyList(), testScope.backgroundScope, dispatcher, + kosmos.audioModeInteractor, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -217,7 +218,6 @@ class DeviceItemInteractorTest : SysuiTestCase() { DeviceItemInteractor( bluetoothTileDialogRepository, kosmos.audioSharingInteractor, - audioManager, adapter, localBluetoothManager, fakeSystemClock, @@ -238,6 +238,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { ), testScope.backgroundScope, dispatcher, + kosmos.audioModeInteractor, ) `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) @@ -259,7 +260,6 @@ class DeviceItemInteractorTest : SysuiTestCase() { DeviceItemInteractor( bluetoothTileDialogRepository, kosmos.audioSharingInteractor, - audioManager, adapter, localBluetoothManager, fakeSystemClock, @@ -277,6 +277,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE), testScope.backgroundScope, dispatcher, + kosmos.audioModeInteractor, ) `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) @@ -300,7 +301,6 @@ class DeviceItemInteractorTest : SysuiTestCase() { DeviceItemInteractor( bluetoothTileDialogRepository, kosmos.audioSharingInteractor, - audioManager, adapter, localBluetoothManager, fakeSystemClock, @@ -309,6 +309,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { emptyList(), testScope.backgroundScope, dispatcher, + kosmos.audioModeInteractor, ) val latest by collectLastValue(interactor.deviceItemUpdate) val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate) @@ -327,7 +328,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager, + isOngoingCall: Boolean, audioSharingAvailable: Boolean, ) = isFilterMatchFunc(cachedDevice) |