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

Commit bb3194ff authored by Chelsea Hao's avatar Chelsea Hao Committed by Android (Google) Code Review
Browse files

Merge "Listen to metadata changed when battery info updated." into main

parents a586bb3e 57069d3c
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
/*
 * 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.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.merge

@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class BluetoothDeviceMetadataInteractor
@Inject
constructor(
    deviceItemInteractor: DeviceItemInteractor,
    private val bluetoothAdapter: BluetoothAdapter?,
    private val logger: BluetoothTileDialogLogger,
    @Background private val executor: Executor,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
    private fun metadataUpdateForDevice(bluetoothDevice: BluetoothDevice): Flow<Unit> =
        conflatedCallbackFlow {
            val metadataChangedListener =
                BluetoothAdapter.OnMetadataChangedListener { device, key, value ->
                    when (key) {
                        BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY,
                        BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY,
                        BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY,
                        BluetoothDevice.METADATA_MAIN_BATTERY -> {
                            trySendWithFailureLogging(Unit, TAG, "onMetadataChanged")
                            logger.logBatteryChanged(device.address, key, value)
                        }
                    }
                }
            bluetoothAdapter?.addOnMetadataChangedListener(
                bluetoothDevice,
                executor,
                metadataChangedListener
            )
            awaitClose {
                bluetoothAdapter?.removeOnMetadataChangedListener(
                    bluetoothDevice,
                    metadataChangedListener
                )
            }
        }

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

    private companion object {
        private const val TAG = "BluetoothDeviceMetadataInteractor"
        private val List<DeviceItem>.bluetoothDevices: Set<BluetoothDevice>
            get() =
                flatMapTo(mutableSetOf()) { item ->
                    listOf(item.cachedBluetoothDevice.device) +
                        item.cachedBluetoothDevice.memberDevice.map { it.device }
                }
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -445,7 +445,6 @@ internal constructor(

    internal companion object {
        const val MIN_HEIGHT_CHANGE_INTERVAL_MS = 800L
        const val MAX_DEVICE_ITEM_ENTRY = 3
        const val ACTION_BLUETOOTH_DEVICE_DETAILS =
            "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"
        const val ACTION_PREVIOUSLY_CONNECTED_DEVICE =
+12 −0
Original line number Diff line number Diff line
@@ -90,6 +90,18 @@ constructor(@BluetoothTileDialogLog private val logBuffer: LogBuffer) {
            { "ProfileConnectionStateChanged. address=$str1 state=$str2 profileId=$int1" }
        )

    fun logBatteryChanged(address: String, key: Int, value: ByteArray?) =
        logBuffer.log(
            TAG,
            DEBUG,
            {
                str1 = address
                int1 = key
                str2 = value?.toString() ?: ""
            },
            { "BatteryChanged. address=$str1 key=$int1 value=$str2" }
        )

    fun logDeviceFetch(status: JobStatus, trigger: DeviceFetchTrigger, duration: Long) =
        logBuffer.log(
            TAG,
+1 −1
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ import javax.inject.Inject

/** Repository to get CachedBluetoothDevices for the Bluetooth Dialog. */
@SysUISingleton
internal class BluetoothTileDialogRepository
class BluetoothTileDialogRepository
@Inject
constructor(
    private val localBluetoothManager: LocalBluetoothManager?,
+14 −7
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Compa
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PAIR_NEW_DEVICE
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.MAX_DEVICE_ITEM_ENTRY
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -50,8 +49,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -66,6 +67,7 @@ constructor(
    private val bluetoothStateInteractor: BluetoothStateInteractor,
    private val bluetoothAutoOnInteractor: BluetoothAutoOnInteractor,
    private val audioSharingInteractor: AudioSharingInteractor,
    private val bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor,
    private val dialogTransitionAnimator: DialogTransitionAnimator,
    private val activityStarter: ActivityStarter,
    private val uiEventLogger: UiEventLogger,
@@ -112,15 +114,17 @@ constructor(

                // deviceItemUpdate is emitted when device item list is done fetching, update UI and
                // stop the progress bar.
                deviceItemInteractor.deviceItemUpdate
                    .onEach {
                combine(
                        deviceItemInteractor.deviceItemUpdate,
                        deviceItemInteractor.showSeeAllUpdate
                    ) { deviceItem, showSeeAll ->
                        updateDialogUiJob?.cancel()
                        updateDialogUiJob = launch {
                            dialogDelegate.apply {
                                onDeviceItemUpdated(
                                    dialog,
                                    it.take(MAX_DEVICE_ITEM_ENTRY),
                                    showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
                                    deviceItem,
                                    showSeeAll,
                                    showPairNewDevice =
                                        bluetoothStateInteractor.isBluetoothEnabled()
                                )
@@ -131,8 +135,11 @@ constructor(
                    .launchIn(this)

                // deviceItemUpdateRequest is emitted when a bluetooth callback is called, re-fetch
                // the device item list and animiate the progress bar.
                deviceItemInteractor.deviceItemUpdateRequest
                // the device item list and animate the progress bar.
                merge(
                        deviceItemInteractor.deviceItemUpdateRequest,
                        bluetoothDeviceMetadataInteractor.metadataUpdate
                    )
                    .onEach {
                        dialogDelegate.animateProgressBar(dialog, true)
                        updateDeviceItemJob?.cancel()
Loading