Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03f75258 authored by Ze Li's avatar Ze Li Committed by Android (Google) Code Review
Browse files

Merge "Use the callback of CachedBluetoothDevice instead of metadata change...

Merge "Use the callback of CachedBluetoothDevice instead of metadata change listener to update summary in qs tile dialog." into main
parents 14e64aec d7406f71
Loading
Loading
Loading
Loading
+110 −0
Original line number Diff line number Diff line
@@ -19,11 +19,14 @@ package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.graphics.drawable.Drawable
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import android.util.Pair
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.flags.Flags.FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY
import com.android.systemui.SysuiTestCase
import com.android.systemui.bluetooth.bluetoothAdapter
import com.android.systemui.coroutines.collectLastValue
@@ -31,6 +34,7 @@ import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
@@ -67,6 +71,7 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
    @Mock private lateinit var drawable: Drawable
    @Captor
    private lateinit var argumentCaptor: ArgumentCaptor<BluetoothAdapter.OnMetadataChangedListener>
    @Captor private lateinit var callbackCaptor: ArgumentCaptor<CachedBluetoothDevice.Callback>
    private lateinit var interactor: BluetoothDeviceMetadataInteractor

    @Before
@@ -93,6 +98,7 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdateEmpty_doNothing() {
        with(kosmos) {
            testScope.runTest {
@@ -108,6 +114,7 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdate_registerListener() {
        with(kosmos) {
            testScope.runTest {
@@ -125,6 +132,7 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdate_sameDeviceItems_registerListenerOnce() {
        with(kosmos) {
            testScope.runTest {
@@ -143,6 +151,7 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdate_differentDeviceItems_unregisterOldAndRegisterNew() {
        with(kosmos) {
            testScope.runTest {
@@ -165,6 +174,7 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun metadataUpdate_triggerCallback_emit() {
        with(kosmos) {
            testScope.runTest {
@@ -193,6 +203,7 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun metadataUpdate_triggerCallbackNonBatteryKey_doNothing() {
        with(kosmos) {
            testScope.runTest {
@@ -221,6 +232,105 @@ class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
        }
    }

    @Test
    @OptIn(ExperimentalCoroutinesApi::class)
    @EnableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdateEmpty_doNotRegisterCallback() {
        with(kosmos) {
            testScope.runTest {
                val update by collectLastValue(interactor.metadataUpdate)
                deviceItemUpdate.emit(emptyList())
                runCurrent()

                assertThat(update).isNull()
                verify(cachedDevice1, never()).registerCallback(any(), any())
                verify(cachedDevice1, never()).unregisterCallback(any())
                verify(cachedDevice2, never()).registerCallback(any(), any())
                verify(cachedDevice2, never()).unregisterCallback(any())
            }
        }
    }

    @Test
    @OptIn(ExperimentalCoroutinesApi::class)
    @EnableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdate_registerCallback() {
        with(kosmos) {
            testScope.runTest {
                val deviceItem = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
                val update by collectLastValue(interactor.metadataUpdate)
                deviceItemUpdate.emit(listOf(deviceItem))
                runCurrent()

                assertThat(update).isNull()
                verify(cachedDevice1).registerCallback(any(), any())
                verify(cachedDevice1, never()).unregisterCallback(any())
            }
        }
    }

    @Test
    @OptIn(ExperimentalCoroutinesApi::class)
    @EnableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdate_sameDeviceItems_registerCallbackOnce() {
        with(kosmos) {
            testScope.runTest {
                val deviceItem = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
                val update by collectLastValue(interactor.metadataUpdate)
                deviceItemUpdate.emit(listOf(deviceItem))
                deviceItemUpdate.emit(listOf(deviceItem))
                runCurrent()

                assertThat(update).isNull()
                verify(cachedDevice1, times(1)).registerCallback(any(), any())
                verify(cachedDevice1, never()).unregisterCallback(any())
            }
        }
    }

    @Test
    @OptIn(ExperimentalCoroutinesApi::class)
    @EnableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun deviceItemUpdate_differentDeviceItems_unregisterOldAndRegisterNewCallback() {
        with(kosmos) {
            testScope.runTest {
                val deviceItem1 = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
                val deviceItem2 = AvailableMediaDeviceItemFactory().create(context, cachedDevice2)
                val update by collectLastValue(interactor.metadataUpdate)
                deviceItemUpdate.emit(listOf(deviceItem1))
                deviceItemUpdate.emit(listOf(deviceItem1, deviceItem2))
                runCurrent()

                assertThat(update).isNull()
                verify(cachedDevice1, times(2)).registerCallback(any(), any())
                verify(cachedDevice2, times(1)).registerCallback(any(), any())
                verify(cachedDevice1, times(1)).unregisterCallback(any())
            }
        }
    }

    @Test
    @OptIn(ExperimentalCoroutinesApi::class)
    @EnableFlags(FLAG_REFACTOR_BATTERY_LEVEL_DISPLAY)
    fun callbackTriggered_emit() {
        with(kosmos) {
            testScope.runTest {
                val deviceItem = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
                val update by collectLastValue(interactor.metadataUpdate)
                deviceItemUpdate.emit(listOf(deviceItem))
                runCurrent()

                assertThat(update).isNull()
                verify(cachedDevice1).registerCallback(any(), callbackCaptor.capture())

                val callback = callbackCaptor.value
                callback.onDeviceAttributesChanged()

                assertThat(update).isEqualTo(Unit)
            }
        }
    }

    companion object {
        private const val DEVICE_NAME = "DeviceName"
        private const val CONNECTION_SUMMARY = "ConnectionSummary"
+58 −24
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.bluetooth.qsdialog

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.flags.Flags.refactorBatteryLevelDisplay
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -59,23 +61,49 @@ constructor(
                bluetoothAdapter?.addOnMetadataChangedListener(
                    bluetoothDevice,
                    executor,
                metadataChangedListener
                    metadataChangedListener,
                )
                awaitClose {
                    bluetoothAdapter?.removeOnMetadataChangedListener(
                        bluetoothDevice,
                    metadataChangedListener
                        metadataChangedListener,
                    )
                }
            }
            .flowOn(backgroundDispatcher)

    private fun callbackUpdateForCachedBluetoothDevice(
        cachedBluetoothDevice: CachedBluetoothDevice
    ): Flow<Unit> =
        conflatedCallbackFlow {
                val attributesChangedCallback =
                    CachedBluetoothDevice.Callback {
                        trySendWithFailureLogging(Unit, TAG, "onAttributesChanged")
                        logger.logAttributesChanged(cachedBluetoothDevice.address)
                    }
                cachedBluetoothDevice.registerCallback(executor, attributesChangedCallback)
                awaitClose { cachedBluetoothDevice.unregisterCallback(attributesChangedCallback) }
            }
            .flowOn(backgroundDispatcher)

    val metadataUpdate: Flow<Unit> =
        if (refactorBatteryLevelDisplay()) {
            deviceItemInteractor.deviceItemUpdate
                .distinctUntilChangedBy { it.cachedBluetoothDevices }
                .flatMapLatest { items ->
                    items.cachedBluetoothDevices
                        .map { cachedBluetoothDevice ->
                            callbackUpdateForCachedBluetoothDevice(cachedBluetoothDevice)
                        }
                        .merge()
                }
        } else {
            deviceItemInteractor.deviceItemUpdate
                .distinctUntilChangedBy { it.bluetoothDevices }
                .flatMapLatest { items ->
                    items.bluetoothDevices.map { device -> metadataUpdateForDevice(device) }.merge()
                }
            .flowOn(backgroundDispatcher)
        }

    private companion object {
        private const val TAG = "BluetoothDeviceMetadataInteractor"
@@ -85,5 +113,11 @@ constructor(
                    listOf(item.cachedBluetoothDevice.device) +
                        item.cachedBluetoothDevice.memberDevice.map { it.device }
                }

        private val List<DeviceItem>.cachedBluetoothDevices: Set<CachedBluetoothDevice>
            get() =
                flatMapTo(mutableSetOf()) { item ->
                    listOf(item.cachedBluetoothDevice) + item.cachedBluetoothDevice.memberDevice
                }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -113,6 +113,9 @@ constructor(@BluetoothTileDialogLog private val logBuffer: LogBuffer) {
            { "BatteryChanged. address=$str1 key=$int1 value=$str2" },
        )

    fun logAttributesChanged(address: String) =
        logBuffer.log(TAG, DEBUG, { str1 = address }, { "AttributesChanged. address=$str1" })

    fun logDeviceFetch(status: JobStatus, trigger: DeviceFetchTrigger, duration: Long) =
        logBuffer.log(
            TAG,