11package com.juul.kable
22
3- import android.bluetooth.BluetoothAdapter.ERROR
4- import android.bluetooth.BluetoothAdapter.EXTRA_STATE
5- import android.bluetooth.BluetoothAdapter.STATE_OFF
6- import android.bluetooth.BluetoothAdapter.STATE_ON
7- import android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF
8- import android.bluetooth.BluetoothAdapter.STATE_TURNING_ON
93import android.bluetooth.BluetoothManager
10- import android.content.Context
11- import android.content.IntentFilter
12- import android.location.LocationManager
13- import android.location.LocationManager.EXTRA_PROVIDER_ENABLED
14- import android.location.LocationManager.PROVIDERS_CHANGED_ACTION
15- import android.os.Build.VERSION.SDK_INT
16- import android.os.Build.VERSION_CODES.R
17- import androidx.core.content.ContextCompat
18- import androidx.core.location.LocationManagerCompat
19- import com.juul.kable.Bluetooth.Availability.Available
20- import com.juul.kable.Bluetooth.Availability.Unavailable
21- import com.juul.kable.Reason.AdapterNotAvailable
22- import com.juul.kable.Reason.LocationServicesDisabled
23- import com.juul.kable.Reason.Off
24- import com.juul.kable.Reason.TurningOff
25- import com.juul.kable.Reason.TurningOn
26- import com.juul.tuulbox.coroutines.flow.broadcastReceiverFlow
27- import kotlinx.coroutines.flow.Flow
28- import kotlinx.coroutines.flow.combine
29- import kotlinx.coroutines.flow.distinctUntilChanged
30- import kotlinx.coroutines.flow.emitAll
31- import kotlinx.coroutines.flow.flow
32- import kotlinx.coroutines.flow.flowOf
33- import kotlinx.coroutines.flow.map
34- import kotlinx.coroutines.flow.onStart
35- import android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED as BLUETOOTH_STATE_CHANGED
364
375@Deprecated(
386 message = " `Bluetooth.availability` has inconsistent behavior across platforms. " +
397 " Will be removed in a future release. " +
408 " See https://github.com/JuulLabs/kable/issues/737 for more details." ,
9+ level = DeprecationLevel .ERROR ,
4110)
4211public actual enum class Reason {
4312 @Deprecated(
4413 message = " `Bluetooth.availability` has inconsistent behavior across platforms. " +
4514 " Will be removed in a future release. " +
4615 " See https://github.com/JuulLabs/kable/issues/737 for more details." ,
16+ level = DeprecationLevel .ERROR ,
4717 )
4818 Off , // BluetoothAdapter.STATE_OFF
4919
5020 @Deprecated(
5121 message = " `Bluetooth.availability` has inconsistent behavior across platforms. " +
5222 " Will be removed in a future release. " +
5323 " See https://github.com/JuulLabs/kable/issues/737 for more details." ,
24+ level = DeprecationLevel .ERROR ,
5425 )
5526 TurningOff , // BluetoothAdapter.STATE_TURNING_OFF or BluetoothAdapter.STATE_BLE_TURNING_OFF
5627
5728 @Deprecated(
5829 message = " `Bluetooth.availability` has inconsistent behavior across platforms. " +
5930 " Will be removed in a future release. " +
6031 " See https://github.com/JuulLabs/kable/issues/737 for more details." ,
32+ level = DeprecationLevel .ERROR ,
6133 )
6234 TurningOn , // BluetoothAdapter.STATE_TURNING_ON or BluetoothAdapter.STATE_BLE_TURNING_ON
6335
@@ -69,6 +41,7 @@ public actual enum class Reason {
6941 message = " `Bluetooth.availability` has inconsistent behavior across platforms. " +
7042 " Will be removed in a future release. " +
7143 " See https://github.com/JuulLabs/kable/issues/737 for more details." ,
44+ level = DeprecationLevel .ERROR ,
7245 )
7346 AdapterNotAvailable ,
7447
@@ -77,60 +50,7 @@ public actual enum class Reason {
7750 message = " `Bluetooth.availability` has inconsistent behavior across platforms. " +
7851 " Will be removed in a future release. " +
7952 " See https://github.com/JuulLabs/kable/issues/737 for more details." ,
53+ level = DeprecationLevel .ERROR ,
8054 )
8155 LocationServicesDisabled ,
8256}
83-
84- private fun Context.getLocationManagerOrNull () =
85- ContextCompat .getSystemService(this , LocationManager ::class .java)
86-
87- private fun Context.isLocationEnabledOrNull (): Boolean? =
88- getLocationManagerOrNull()?.let (LocationManagerCompat ::isLocationEnabled)
89-
90- private val locationEnabledOrNullFlow = when {
91- SDK_INT > R -> flowOf(true )
92- else -> broadcastReceiverFlow(IntentFilter (PROVIDERS_CHANGED_ACTION ))
93- .map { intent ->
94- if (SDK_INT == R ) {
95- intent.getBooleanExtra(EXTRA_PROVIDER_ENABLED , false )
96- } else {
97- applicationContext.isLocationEnabledOrNull()
98- }
99- }
100- .onStart { emit(applicationContext.isLocationEnabledOrNull()) }
101- .distinctUntilChanged()
102- }
103-
104- private val bluetoothStateFlow = flow {
105- when (val adapter = getBluetoothAdapterOrNull()) {
106- null -> emit(Unavailable (reason = AdapterNotAvailable ))
107- else -> emitAll(
108- broadcastReceiverFlow(IntentFilter (BLUETOOTH_STATE_CHANGED ))
109- .map { intent -> intent.getIntExtra(EXTRA_STATE , ERROR ) }
110- .onStart {
111- emit(if (adapter.isEnabled) STATE_ON else STATE_OFF )
112- }
113- .map { state ->
114- when (state) {
115- STATE_ON -> Available
116- STATE_OFF -> Unavailable (reason = Off )
117- STATE_TURNING_OFF -> Unavailable (reason = TurningOff )
118- STATE_TURNING_ON -> Unavailable (reason = TurningOn )
119- else -> error(" Unexpected bluetooth state: $state " )
120- }
121- },
122- )
123- }
124- }
125-
126- internal actual val bluetoothAvailability: Flow <Bluetooth .Availability > =
127- combine(
128- locationEnabledOrNullFlow,
129- bluetoothStateFlow,
130- ) { locationEnabled, bluetoothState ->
131- when (locationEnabled) {
132- true -> bluetoothState
133- false -> Unavailable (reason = LocationServicesDisabled )
134- null -> Unavailable (reason = AdapterNotAvailable )
135- }
136- }
0 commit comments