1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.bluetooth.qsdialog 18 19 import android.bluetooth.BluetoothAdapter 20 import android.util.Log 21 import com.android.settingslib.bluetooth.BluetoothCallback 22 import com.android.settingslib.bluetooth.LocalBluetoothManager 23 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 24 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 25 import com.android.systemui.dagger.SysUISingleton 26 import com.android.systemui.dagger.qualifiers.Application 27 import com.android.systemui.dagger.qualifiers.Background 28 import javax.inject.Inject 29 import kotlinx.coroutines.CoroutineDispatcher 30 import kotlinx.coroutines.CoroutineScope 31 import kotlinx.coroutines.channels.awaitClose 32 import kotlinx.coroutines.flow.Flow 33 import kotlinx.coroutines.flow.SharingStarted 34 import kotlinx.coroutines.flow.flowOf 35 import kotlinx.coroutines.flow.flowOn 36 import kotlinx.coroutines.flow.onStart 37 import kotlinx.coroutines.flow.stateIn 38 import kotlinx.coroutines.withContext 39 40 /** 41 * Repository class responsible for managing the Bluetooth Auto-On feature settings for the current 42 * user. 43 */ 44 @SysUISingleton 45 class BluetoothAutoOnRepository 46 @Inject 47 constructor( 48 localBluetoothManager: LocalBluetoothManager?, 49 private val bluetoothAdapter: BluetoothAdapter?, 50 @Application private val coroutineScope: CoroutineScope, 51 @Background private val backgroundDispatcher: CoroutineDispatcher, 52 ) { 53 54 // Flow representing the auto on state for the current user 55 internal val isAutoOn: Flow<Boolean> = 56 localBluetoothManager?.eventManager?.let { eventManager -> 57 conflatedCallbackFlow { 58 val listener = 59 object : BluetoothCallback { 60 override fun onAutoOnStateChanged(autoOnState: Int) { 61 super.onAutoOnStateChanged(autoOnState) 62 if ( 63 autoOnState == BluetoothAdapter.AUTO_ON_STATE_ENABLED || 64 autoOnState == BluetoothAdapter.AUTO_ON_STATE_DISABLED 65 ) { 66 trySendWithFailureLogging( 67 autoOnState == BluetoothAdapter.AUTO_ON_STATE_ENABLED, 68 TAG, 69 "onAutoOnStateChanged" 70 ) 71 } 72 } 73 } 74 eventManager.registerCallback(listener) 75 awaitClose { eventManager.unregisterCallback(listener) } 76 } 77 .onStart { emit(isAutoOnEnabled()) } 78 .flowOn(backgroundDispatcher) 79 .stateIn( 80 coroutineScope, 81 SharingStarted.WhileSubscribed(replayExpirationMillis = 0), 82 initialValue = false 83 ) 84 } 85 ?: flowOf(false) 86 87 /** 88 * Checks if the auto on feature is supported for the current user. 89 * 90 * @throws Exception if an error occurs while checking auto-on support. 91 */ 92 suspend fun isAutoOnSupported(): Boolean = 93 withContext(backgroundDispatcher) { 94 try { 95 bluetoothAdapter?.isAutoOnSupported ?: false 96 } catch (e: Exception) { 97 // Server could throw TimeoutException, InterruptedException or ExecutionException 98 Log.e(TAG, "Error calling isAutoOnSupported", e) 99 false 100 } catch (e: NoSuchMethodError) { 101 // TODO(b/346716614): Remove this when the flag is cleaned up. 102 Log.e(TAG, "Non-existed api isAutoOnSupported", e) 103 false 104 } 105 } 106 107 /** Sets the Bluetooth Auto-On for the current user. */ 108 suspend fun setAutoOn(value: Boolean) { 109 withContext(backgroundDispatcher) { 110 try { 111 bluetoothAdapter?.setAutoOnEnabled(value) 112 } catch (e: Exception) { 113 // Server could throw IllegalStateException, TimeoutException, InterruptedException 114 // or ExecutionException 115 Log.e(TAG, "Error calling setAutoOnEnabled", e) 116 } catch (e: NoSuchMethodError) { 117 // TODO(b/346716614): Remove this when the flag is cleaned up. 118 Log.e(TAG, "Non-existed api setAutoOn", e) 119 } 120 } 121 } 122 123 private suspend fun isAutoOnEnabled() = 124 withContext(backgroundDispatcher) { 125 try { 126 bluetoothAdapter?.isAutoOnEnabled ?: false 127 } catch (e: Exception) { 128 // Server could throw IllegalStateException, TimeoutException, InterruptedException 129 // or ExecutionException 130 Log.e(TAG, "Error calling isAutoOnEnabled", e) 131 false 132 } catch (e: NoSuchMethodError) { 133 // TODO(b/346716614): Remove this when the flag is cleaned up. 134 Log.e(TAG, "Non-existed api isAutoOnEnabled", e) 135 false 136 } 137 } 138 139 private companion object { 140 const val TAG = "BluetoothAutoOnRepository" 141 } 142 } 143