Move overlay click handling back to SystemUI.

Test: atest -c com.android.systemui.bluetooth.qsdialog
Bug: 340379827
Flag: NA
Change-Id: Ieddca3ec1f05d30aff94b50e9849b6a80e153666
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 1e79bb7..fde7c2c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -368,6 +368,7 @@
         "tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
         "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
         "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt",
         "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt",
         "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt",
         "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt",
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
index c30aea0..72312b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
+import android.bluetooth.BluetoothDevice
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel.DEBUG
 import com.android.systemui.log.dagger.BluetoothTileDialogLog
@@ -103,4 +104,29 @@
 
     fun logDeviceUiUpdate(duration: Long) =
         logBuffer.log(TAG, DEBUG, { long1 = duration }, { "DeviceUiUpdate. duration=$long1" })
+
+    fun logDeviceClickInAudioSharingWhenEnabled(inAudioSharing: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = inAudioSharing.toString() },
+            { "DeviceClick. in audio sharing=$str1" }
+        )
+    }
+
+    fun logConnectedLeByGroupId(map: Map<Int, List<BluetoothDevice>>) {
+        logBuffer.log(TAG, DEBUG, { str1 = map.toString() }, { "ConnectedLeByGroupId. map=$str1" })
+    }
+
+    fun logLaunchSettingsCriteriaMatched(criteria: String, deviceItem: DeviceItem) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = criteria
+                str2 = deviceItem.toString()
+            },
+            { "$str1. deviceItem=$str2" }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
deleted file mode 100644
index 2e9169e..0000000
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.systemui.bluetooth.qsdialog
-
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
-
-@Module
-interface BluetoothTileDialogModule {
-    @Binds
-    @SysUISingleton
-    fun bindDeviceItemActionInteractor(
-        impl: DeviceItemActionInteractorImpl
-    ): DeviceItemActionInteractor
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
index b592b8e..4a358c0 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
@@ -35,7 +35,17 @@
     CONNECTED_OTHER_DEVICE_DISCONNECT(1508),
     @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617),
     @UiEvent(doc = "The audio sharing button is clicked")
-    BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700);
+    BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700),
+    @UiEvent(doc = "Currently broadcasting and a LE audio supported device is clicked")
+    LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED(1717),
+    @UiEvent(doc = "Currently broadcasting and a non-LE audio supported device is clicked")
+    LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED(1718),
+    @UiEvent(
+        doc = "Not broadcasting, having one connected, another saved LE audio device is clicked"
+    )
+    LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719),
+    @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked")
+    LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720);
 
     override fun getId() = metricId
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
index 9311760..4dafa93 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
@@ -16,32 +16,87 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
+import android.content.Intent
+import android.os.Bundle
+import android.provider.Settings
 import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.A2dpProfile
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.HeadsetProfile
+import com.android.settingslib.bluetooth.HearingAidProfile
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor.LaunchSettingsCriteria.Companion.getCurrentConnectedLeByGroupId
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.withContext
 
-/** Defines interface for click handling of a DeviceItem. */
-interface DeviceItemActionInteractor {
-    suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog)
-}
-
 @SysUISingleton
-open class DeviceItemActionInteractorImpl
+class DeviceItemActionInteractor
 @Inject
 constructor(
+    private val activityStarter: ActivityStarter,
+    private val dialogTransitionAnimator: DialogTransitionAnimator,
+    private val localBluetoothManager: LocalBluetoothManager?,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val logger: BluetoothTileDialogLogger,
     private val uiEventLogger: UiEventLogger,
-) : DeviceItemActionInteractor {
+) {
+    private val leAudioProfile: LeAudioProfile?
+        get() = localBluetoothManager?.profileManager?.leAudioProfile
 
-    override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
+    private val assistantProfile: LocalBluetoothLeBroadcastAssistant?
+        get() = localBluetoothManager?.profileManager?.leAudioBroadcastAssistantProfile
+
+    private val launchSettingsCriteriaList: List<LaunchSettingsCriteria>
+        get() =
+            listOf(
+                InSharingClickedNoSource(localBluetoothManager, backgroundDispatcher, logger),
+                NotSharingClickedNonConnect(
+                    leAudioProfile,
+                    assistantProfile,
+                    backgroundDispatcher,
+                    logger
+                ),
+                NotSharingClickedConnected(
+                    leAudioProfile,
+                    assistantProfile,
+                    backgroundDispatcher,
+                    logger
+                )
+            )
+
+    suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
         withContext(backgroundDispatcher) {
             logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
+            if (
+                BluetoothUtils.isAudioSharingEnabled() &&
+                    localBluetoothManager != null &&
+                    leAudioProfile != null &&
+                    assistantProfile != null
+            ) {
+                val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager)
+                logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing)
 
+                val criteriaMatched =
+                    launchSettingsCriteriaList.firstOrNull {
+                        it.matched(inAudioSharing, deviceItem)
+                    }
+                if (criteriaMatched != null) {
+                    uiEventLogger.log(criteriaMatched.getClickUiEvent(deviceItem))
+                    launchSettings(deviceItem.cachedBluetoothDevice.device, dialog)
+                    return@withContext
+                }
+            }
             deviceItem.cachedBluetoothDevice.apply {
                 when (deviceItem.type) {
                     DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
@@ -69,4 +124,184 @@
             }
         }
     }
+
+    private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) {
+        val intent =
+            Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply {
+                putExtra(
+                    EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+                    Bundle().apply {
+                        putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device)
+                    }
+                )
+            }
+        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
+        activityStarter.postStartActivityDismissingKeyguard(
+            intent,
+            0,
+            dialogTransitionAnimator.createActivityTransitionController(dialog)
+        )
+    }
+
+    private interface LaunchSettingsCriteria {
+        suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean
+
+        suspend fun getClickUiEvent(deviceItem: DeviceItem): BluetoothTileDialogUiEvent
+
+        companion object {
+            suspend fun getCurrentConnectedLeByGroupId(
+                leAudioProfile: LeAudioProfile,
+                assistantProfile: LocalBluetoothLeBroadcastAssistant,
+                @Background backgroundDispatcher: CoroutineDispatcher,
+                logger: BluetoothTileDialogLogger,
+            ): Map<Int, List<BluetoothDevice>> {
+                return withContext(backgroundDispatcher) {
+                    assistantProfile
+                        .getDevicesMatchingConnectionStates(
+                            intArrayOf(BluetoothProfile.STATE_CONNECTED)
+                        )
+                        ?.filterNotNull()
+                        ?.groupBy { leAudioProfile.getGroupId(it) }
+                        ?.also { logger.logConnectedLeByGroupId(it) } ?: emptyMap()
+                }
+            }
+        }
+    }
+
+    private class InSharingClickedNoSource(
+        private val localBluetoothManager: LocalBluetoothManager?,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+        private val logger: BluetoothTileDialogLogger,
+    ) : LaunchSettingsCriteria {
+        // If currently broadcasting and the clicked device is not connected to the source
+        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+            return withContext(backgroundDispatcher) {
+                val matched =
+                    inAudioSharing &&
+                        deviceItem.isMediaDevice &&
+                        !BluetoothUtils.hasConnectedBroadcastSource(
+                            deviceItem.cachedBluetoothDevice,
+                            localBluetoothManager
+                        )
+
+                if (matched) {
+                    logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem)
+                }
+
+                matched
+            }
+        }
+
+        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+            if (deviceItem.isLeAudioSupported)
+                BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED
+            else BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED
+    }
+
+    private class NotSharingClickedNonConnect(
+        private val leAudioProfile: LeAudioProfile?,
+        private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+        private val logger: BluetoothTileDialogLogger,
+    ) : LaunchSettingsCriteria {
+        // If not broadcasting, having one device connected, and clicked on a not yet connected LE
+        // audio device
+        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+            return withContext(backgroundDispatcher) {
+                val matched =
+                    leAudioProfile?.let { leAudio ->
+                        assistantProfile?.let { assistant ->
+                            !inAudioSharing &&
+                                getCurrentConnectedLeByGroupId(
+                                        leAudio,
+                                        assistant,
+                                        backgroundDispatcher,
+                                        logger
+                                    )
+                                    .size == 1 &&
+                                deviceItem.isNotConnectedLeAudioSupported
+                        }
+                    } ?: false
+
+                if (matched) {
+                    logger.logLaunchSettingsCriteriaMatched(
+                        "NotSharingClickedNonConnect",
+                        deviceItem
+                    )
+                }
+
+                matched
+            }
+        }
+
+        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED
+    }
+
+    private class NotSharingClickedConnected(
+        private val leAudioProfile: LeAudioProfile?,
+        private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+        private val logger: BluetoothTileDialogLogger,
+    ) : LaunchSettingsCriteria {
+        // If not broadcasting, having two device connected, clicked on any connected LE audio
+        // devices
+        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+            return withContext(backgroundDispatcher) {
+                val matched =
+                    leAudioProfile?.let { leAudio ->
+                        assistantProfile?.let { assistant ->
+                            !inAudioSharing &&
+                                getCurrentConnectedLeByGroupId(
+                                        leAudio,
+                                        assistant,
+                                        backgroundDispatcher,
+                                        logger
+                                    )
+                                    .size == 2 &&
+                                deviceItem.isActiveOrConnectedLeAudioSupported
+                        }
+                    } ?: false
+
+                if (matched) {
+                    logger.logLaunchSettingsCriteriaMatched(
+                        "NotSharingClickedConnected",
+                        deviceItem
+                    )
+                }
+
+                matched
+            }
+        }
+
+        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED
+    }
+
+    private companion object {
+        const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
+
+        val DeviceItem.isLeAudioSupported: Boolean
+            get() =
+                cachedBluetoothDevice.profiles.any { profile ->
+                    profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device)
+                }
+
+        val DeviceItem.isNotConnectedLeAudioSupported: Boolean
+            get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported
+
+        val DeviceItem.isActiveOrConnectedLeAudioSupported: Boolean
+            get() =
+                (type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE ||
+                    type == DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) && isLeAudioSupported
+
+        val DeviceItem.isMediaDevice: Boolean
+            get() =
+                cachedBluetoothDevice.connectableProfiles.any {
+                    it is A2dpProfile ||
+                        it is HearingAidProfile ||
+                        it is LeAudioProfile ||
+                        it is HeadsetProfile
+                }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index ea89be6..b705a03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.os.Handler;
 
-import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogModule;
 import com.android.systemui.dagger.NightDisplayListenerModule;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -61,7 +60,6 @@
  */
 @Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class},
         includes = {
-                BluetoothTileDialogModule.class,
                 MediaModule.class,
                 PanelsModule.class,
                 QSExternalModule.class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt
deleted file mode 100644
index 64bd742..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.systemui.bluetooth.qsdialog
-
-import android.bluetooth.BluetoothDevice
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@OptIn(ExperimentalCoroutinesApi::class)
-class DeviceItemActionInteractorImplTest : SysuiTestCase() {
-    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
-    private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
-    private lateinit var actionInteractorImpl: DeviceItemActionInteractor
-
-    @Mock private lateinit var dialog: SystemUIDialog
-    @Mock private lateinit var cachedDevice: CachedBluetoothDevice
-    @Mock private lateinit var device: BluetoothDevice
-    @Mock private lateinit var deviceItem: DeviceItem
-
-    @Before
-    fun setUp() {
-        actionInteractorImpl = kosmos.deviceItemActionInteractor
-        whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedDevice)
-        whenever(cachedDevice.address).thenReturn("ADDRESS")
-        whenever(cachedDevice.device).thenReturn(device)
-    }
-
-    @Test
-    fun testOnClick_connectedMedia_setActive() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type)
-                    .thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).setActive()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedDevice.address,
-                        DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_activeMedia_disconnect() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).disconnect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedDevice.address,
-                        DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_connectedOtherDevice_disconnect() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).disconnect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(cachedDevice.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_saved_connect() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).connect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(cachedDevice.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
index e8e37bc..5ff4634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
@@ -13,19 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.systemui.bluetooth.qsdialog
 
 import com.android.internal.logging.uiEventLogger
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
 import com.android.systemui.util.mockito.mock
 
 val Kosmos.bluetoothTileDialogLogger: BluetoothTileDialogLogger by Kosmos.Fixture { mock {} }
 
+val Kosmos.localBluetoothManager: LocalBluetoothManager by Kosmos.Fixture { mock {} }
+
+val Kosmos.dialogTransitionAnimator: DialogTransitionAnimator by Kosmos.Fixture { mock {} }
+
 val Kosmos.deviceItemActionInteractor: DeviceItemActionInteractor by
     Kosmos.Fixture {
-        DeviceItemActionInteractorImpl(
+        DeviceItemActionInteractor(
+            activityStarter,
+            dialogTransitionAnimator,
+            localBluetoothManager,
             testDispatcher,
             bluetoothTileDialogLogger,
             uiEventLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
new file mode 100644
index 0000000..8246506
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
@@ -0,0 +1,459 @@
+/*
+ * 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.systemui.bluetooth.qsdialog
+
+import android.bluetooth.BluetoothDevice
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
+class DeviceItemActionInteractorTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+    private lateinit var actionInteractorImpl: DeviceItemActionInteractor
+    private lateinit var mockitoSession: StaticMockitoSession
+    private lateinit var activeMediaDeviceItem: DeviceItem
+    private lateinit var notConnectedDeviceItem: DeviceItem
+    private lateinit var connectedMediaDeviceItem: DeviceItem
+    private lateinit var connectedOtherDeviceItem: DeviceItem
+    @Mock private lateinit var dialog: SystemUIDialog
+    @Mock private lateinit var profileManager: LocalBluetoothProfileManager
+    @Mock private lateinit var leAudioProfile: LeAudioProfile
+    @Mock private lateinit var assistantProfile: LocalBluetoothLeBroadcastAssistant
+    @Mock private lateinit var bluetoothDevice: BluetoothDevice
+    @Mock private lateinit var bluetoothDeviceGroupId2: BluetoothDevice
+    @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
+
+    @Before
+    fun setUp() {
+        mockitoSession =
+            mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
+        activeMediaDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        notConnectedDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        connectedMediaDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        connectedOtherDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        actionInteractorImpl = kosmos.deviceItemActionInteractor
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
+    @Test
+    fun testOnClick_connectedMedia_setActive() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(cachedBluetoothDevice).setActive()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_activeMedia_disconnect() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(activeMediaDeviceItem, dialog)
+                verify(cachedBluetoothDevice).disconnect()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_connectedOtherDevice_disconnect() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(connectedOtherDeviceItem, dialog)
+                verify(cachedBluetoothDevice).disconnect()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.CONNECTED_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_saved_connect() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(cachedBluetoothDevice).connect()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.SAVED_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.connectableProfiles)
+                        .thenReturn(listOf(leAudioProfile))
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+                whenever(
+                        BluetoothUtils.hasConnectedBroadcastSource(
+                            ArgumentMatchers.any(),
+                            ArgumentMatchers.any()
+                        )
+                    )
+                    .thenReturn(true)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                whenever(cachedBluetoothDevice.connectableProfiles)
+                        .thenReturn(listOf(leAudioProfile))
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+                whenever(
+                    BluetoothUtils.hasConnectedBroadcastSource(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.any()
+                    )
+                )
+                        .thenReturn(false)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_noConnectedLeDevice_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasOneConnectedLeDevice_clickedNonLe_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice))
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasOneConnectedLeDevice_clickedLe_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
+                whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice))
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasOneConnectedLeDevice_clickedConnectedLe_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice))
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasTwoConnectedLeDevice_clickedNotConnectedLe_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
+                whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
+                    val device = it.arguments.first() as BluetoothDevice
+                    if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
+                }
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
+                whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
+                whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
+                    val device = it.arguments.first() as BluetoothDevice
+                    if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
+                }
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    private companion object {
+        const val DEVICE_NAME = "device"
+        const val DEVICE_CONNECTION_SUMMARY = "active"
+        const val DEVICE_ADDRESS = "address"
+        const val GROUP_ID_1 = 1
+        const val GROUP_ID_2 = 2
+    }
+}