@@ -3,6 +3,7 @@ package org.akanework.gramophone.logic.utils
3
3
import android.Manifest
4
4
import android.annotation.SuppressLint
5
5
import android.bluetooth.BluetoothA2dp
6
+ import android.bluetooth.BluetoothAdapter
6
7
import android.bluetooth.BluetoothCodecConfig
7
8
import android.bluetooth.BluetoothCodecStatus
8
9
import android.bluetooth.BluetoothDevice
@@ -25,7 +26,6 @@ data class BtCodecInfo(val codec: String?, val sampleRateHz: Int?, val channelCo
25
26
private const val TAG = " BtCodecInfo"
26
27
27
28
@RequiresApi(Build .VERSION_CODES .O )
28
- // TODO test stability
29
29
fun fromCodecConfig (codecConfig : BluetoothCodecConfig ? ): BtCodecInfo ? {
30
30
if (codecConfig == null ) return null
31
31
try {
@@ -48,9 +48,9 @@ data class BtCodecInfo(val codec: String?, val sampleRateHz: Int?, val channelCo
48
48
}
49
49
when (name) {
50
50
// P returns without space, some newer versions added space
51
- " LDHCV2 " -> " LHDC V2"
52
- " LDHCV3 " -> " LHDC V3"
53
- " LDHCV5 " -> " LHDC V5"
51
+ " LHDCV2 " -> " LHDC V2"
52
+ " LHDCV3 " -> " LHDC V3"
53
+ " LHDCV5 " -> " LHDC V5"
54
54
// other known ones: aptX Adaptive, aptX TWS+
55
55
else -> name
56
56
}
@@ -114,46 +114,53 @@ data class BtCodecInfo(val codec: String?, val sampleRateHz: Int?, val channelCo
114
114
115
115
// TODO test stability
116
116
@RequiresApi(Build .VERSION_CODES .O )
117
- fun getCodec (context : Context , callback : (BtCodecInfo ? ) -> Unit ) {
117
+ fun getCodec (context : Context , callback : (BtCodecInfo ? ) -> Unit ): Proxy ? {
118
118
val adapter = ContextCompat .getSystemService(context, BluetoothManager ::class .java)?.adapter
119
- // TODO stop leaking
120
- if (adapter?.getProfileProxy(context, object : BluetoothProfile .ServiceListener {
121
- override fun onServiceConnected (profile : Int , proxy : BluetoothProfile ) {
122
- val a2dp = proxy as BluetoothA2dp
123
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S && ContextCompat .checkSelfPermission(
124
- context,
125
- Manifest .permission.BLUETOOTH_CONNECT
126
- ) != PackageManager .PERMISSION_GRANTED ) {
127
- Log .w(TAG , " missing bluetooth permission" )
128
- callback(null )
129
- return
130
- }
131
- val cd = a2dp.connectedDevices
132
- val device = if (cd.size <= 1 ) cd.firstOrNull() else try {
133
- BluetoothA2dp ::class .java.getMethod(" getActiveDevice" ).invoke(a2dp) as BluetoothDevice ?
134
- } catch (t: Throwable ) {
135
- Log .e(TAG , Log .getStackTraceString(t))
136
- callback(null )
137
- return
138
- }
139
- if (device == null ) return
140
- @SuppressLint(" NewApi" )
141
- val codec = try {
142
- BluetoothA2dp ::class .java.getMethod(" getCodecStatus" , BluetoothDevice ::class .java)
143
- .invoke(a2dp, device) as BluetoothCodecStatus ?
144
- } catch (t: Throwable ) {
145
- Log .e(TAG , Log .getStackTraceString(t))
146
- null
147
- }?.codecConfig
148
- callback(fromCodecConfig(codec))
149
- }
150
-
151
- override fun onServiceDisconnected (profile : Int ) {
152
- // do nothing
153
- }
154
- }, BluetoothProfile .A2DP ) != true ) {
119
+ ? : return null
120
+ val sl = Proxy (adapter, callback, context)
121
+ if (adapter.getProfileProxy(context, sl, BluetoothProfile .A2DP ) != true ) {
155
122
Log .e(TAG , " getProfileProxy error" )
156
123
callback(null )
124
+ return null
125
+ }
126
+ return sl
127
+ }
128
+
129
+ class Proxy (val adapter : BluetoothAdapter , private val callback : (BtCodecInfo ? ) -> Unit ,
130
+ private val context : Context ) : BluetoothProfile.ServiceListener {
131
+ var a2dp: BluetoothA2dp ? = null
132
+ override fun onServiceConnected (profile : Int , proxy : BluetoothProfile ) {
133
+ a2dp = proxy as BluetoothA2dp
134
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S && ContextCompat .checkSelfPermission(
135
+ context,
136
+ Manifest .permission.BLUETOOTH_CONNECT
137
+ ) != PackageManager .PERMISSION_GRANTED ) {
138
+ Log .w(TAG , " missing bluetooth permission" )
139
+ callback(null )
140
+ return
141
+ }
142
+ val cd = a2dp!! .connectedDevices
143
+ val device = if (cd.size <= 1 ) cd.firstOrNull() else try {
144
+ BluetoothA2dp ::class .java.getMethod(" getActiveDevice" ).invoke(a2dp) as BluetoothDevice ?
145
+ } catch (t: Throwable ) {
146
+ Log .e(TAG , Log .getStackTraceString(t))
147
+ callback(null )
148
+ return
149
+ }
150
+ if (device == null ) return
151
+ @SuppressLint(" NewApi" )
152
+ val codec = try {
153
+ BluetoothA2dp ::class .java.getMethod(" getCodecStatus" , BluetoothDevice ::class .java)
154
+ .invoke(a2dp, device) as BluetoothCodecStatus ?
155
+ } catch (t: Throwable ) {
156
+ Log .e(TAG , Log .getStackTraceString(t))
157
+ null
158
+ }?.codecConfig
159
+ callback(fromCodecConfig(codec))
160
+ }
161
+
162
+ override fun onServiceDisconnected (profile : Int ) {
163
+ a2dp = null
157
164
}
158
165
}
159
166
}
0 commit comments