try to fix service, again

This commit is contained in:
Kavish Devar
2024-12-06 14:09:28 +05:30
parent 4374a81915
commit db8458be59
3 changed files with 240 additions and 198 deletions

View File

@@ -258,220 +258,249 @@ class AirPodsService: Service() {
HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;") HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;")
val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
try { if (isConnected != true) {
socket.close()
} catch (e: Exception) {
e.printStackTrace()
}
try {
socket = HiddenApiBypass.newInstance(
BluetoothSocket::class.java,
3,
true,
true,
device,
0x1001,
uuid
) as BluetoothSocket
}
catch (
e: Exception
) {
e.printStackTrace()
try { try {
socket = HiddenApiBypass.newInstance( socket = HiddenApiBypass.newInstance(
BluetoothSocket::class.java, BluetoothSocket::class.java,
3, 3,
1,
true, true,
true, true,
device, device,
0x1001, 0x1001,
uuid uuid
) as BluetoothSocket ) as BluetoothSocket
} } catch (
catch (
e: Exception e: Exception
) { ) {
e.printStackTrace() e.printStackTrace()
try {
socket = HiddenApiBypass.newInstance(
BluetoothSocket::class.java,
3,
1,
true,
true,
device,
0x1001,
uuid
) as BluetoothSocket
} catch (
e: Exception
) {
e.printStackTrace()
}
} }
}
try { try {
socket.connect() socket.connect()
this@AirPodsService.device = device this@AirPodsService.device = device
isConnected = true isConnected = true
socket.let { it -> socket.let { it ->
it.outputStream.write(Enums.HANDSHAKE.value) it.outputStream.write(Enums.HANDSHAKE.value)
it.outputStream.flush() it.outputStream.flush()
it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value) it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value)
it.outputStream.flush() it.outputStream.flush()
it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value) it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value)
it.outputStream.flush() it.outputStream.flush()
sendBroadcast( sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_CONNECTED) Intent(AirPodsNotifications.AIRPODS_CONNECTED)
.putExtra("device", device) .putExtra("device", device)
) )
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
while (socket.isConnected == true) { while (socket.isConnected == true) {
socket.let { socket.let {
val audioManager = this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager val audioManager =
MediaController.initialize(audioManager) this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager
val buffer = ByteArray(1024) MediaController.initialize(audioManager)
val bytesRead = it.inputStream.read(buffer) val buffer = ByteArray(1024)
var data: ByteArray = byteArrayOf() val bytesRead = it.inputStream.read(buffer)
if (bytesRead > 0) { var data: ByteArray = byteArrayOf()
data = buffer.copyOfRange(0, bytesRead) if (bytesRead > 0) {
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply { data = buffer.copyOfRange(0, bytesRead)
putExtra("data", buffer.copyOfRange(0, bytesRead)) sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply {
}) putExtra("data", buffer.copyOfRange(0, bytesRead))
val bytes = buffer.copyOfRange(0, bytesRead) })
val formattedHex = bytes.joinToString(" ") { "%02X".format(it) } val bytes = buffer.copyOfRange(0, bytesRead)
Log.d("AirPods Data", "Data received: $formattedHex") val formattedHex = bytes.joinToString(" ") { "%02X".format(it) }
} Log.d("AirPods Data", "Data received: $formattedHex")
else if (bytesRead == -1) { } else if (bytesRead == -1) {
Log.d("AirPods Service", "Socket closed (bytesRead = -1)") Log.d("AirPods Service", "Socket closed (bytesRead = -1)")
// socket.close() // socket.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
return@launch return@launch
} }
var inEar = false var inEar = false
var inEarData = listOf<Boolean>() var inEarData = listOf<Boolean>()
if (earDetectionNotification.isEarDetectionData(data)) { if (earDetectionNotification.isEarDetectionData(data)) {
earDetectionNotification.setStatus(data) earDetectionNotification.setStatus(data)
sendBroadcast(Intent(AirPodsNotifications.EAR_DETECTION_DATA).apply { sendBroadcast(Intent(AirPodsNotifications.EAR_DETECTION_DATA).apply {
val list = earDetectionNotification.status val list = earDetectionNotification.status
val bytes = ByteArray(2) val bytes = ByteArray(2)
bytes[0] = list[0] bytes[0] = list[0]
bytes[1] = list[1] bytes[1] = list[1]
putExtra("data", bytes) putExtra("data", bytes)
}) })
Log.d("AirPods Parser", "Ear Detection: ${earDetectionNotification.status[0]} ${earDetectionNotification.status[1]}") Log.d(
var justEnabledA2dp = false "AirPods Parser",
earReceiver = object : BroadcastReceiver() { "Ear Detection: ${earDetectionNotification.status[0]} ${earDetectionNotification.status[1]}"
override fun onReceive(context: Context, intent: Intent) { )
val data = intent.getByteArrayExtra("data") var justEnabledA2dp = false
if (data != null && earDetectionEnabled) { earReceiver = object : BroadcastReceiver() {
inEar = if (data.find { it == 0x02.toByte() } != null || data.find { it == 0x03.toByte() } != null) { override fun onReceive(context: Context, intent: Intent) {
data[0] == 0x00.toByte() || data[1] == 0x00.toByte() val data = intent.getByteArrayExtra("data")
} else { if (data != null && earDetectionEnabled) {
data[0] == 0x00.toByte() && data[1] == 0x00.toByte() inEar =
} if (data.find { it == 0x02.toByte() } != null || data.find { it == 0x03.toByte() } != null) {
data[0] == 0x00.toByte() || data[1] == 0x00.toByte()
val newInEarData = listOf(data[0] == 0x00.toByte(), data[1] == 0x00.toByte()) } else {
if (newInEarData.contains(true) && inEarData == listOf(false, false)) { data[0] == 0x00.toByte() && data[1] == 0x00.toByte()
connectAudio(this@AirPodsService, device)
justEnabledA2dp = true
val bluetoothAdapter = this@AirPodsService.getSystemService(BluetoothManager::class.java).adapter
bluetoothAdapter.getProfileProxy(
this@AirPodsService, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(
profile: Int,
proxy: BluetoothProfile
) {
if (profile == BluetoothProfile.A2DP) {
val connectedDevices =
proxy.connectedDevices
if (connectedDevices.isNotEmpty()) {
MediaController.sendPlay()
}
}
bluetoothAdapter.closeProfileProxy(
profile,
proxy
)
}
override fun onServiceDisconnected(
profile: Int
) {
}
} }
,BluetoothProfile.A2DP
val newInEarData = listOf(
data[0] == 0x00.toByte(),
data[1] == 0x00.toByte()
) )
if (newInEarData.contains(true) && inEarData == listOf(
false,
false
)
) {
connectAudio(this@AirPodsService, device)
justEnabledA2dp = true
val bluetoothAdapter =
this@AirPodsService.getSystemService(
BluetoothManager::class.java
).adapter
bluetoothAdapter.getProfileProxy(
this@AirPodsService,
object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(
profile: Int,
proxy: BluetoothProfile
) {
if (profile == BluetoothProfile.A2DP) {
val connectedDevices =
proxy.connectedDevices
if (connectedDevices.isNotEmpty()) {
MediaController.sendPlay()
}
}
bluetoothAdapter.closeProfileProxy(
profile,
proxy
)
}
} override fun onServiceDisconnected(
else if (newInEarData == listOf(false, false)){ profile: Int
disconnectAudio(this@AirPodsService, device) ) {
} }
},
BluetoothProfile.A2DP
)
inEarData = newInEarData } else if (newInEarData == listOf(false, false)) {
disconnectAudio(this@AirPodsService, device)
if (inEar == true) { }
if (!justEnabledA2dp) {
justEnabledA2dp = false inEarData = newInEarData
MediaController.sendPlay()
if (inEar == true) {
if (!justEnabledA2dp) {
justEnabledA2dp = false
MediaController.sendPlay()
}
} else {
MediaController.sendPause()
} }
} else {
MediaController.sendPause()
} }
} }
} }
}
val earIntentFilter = IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA) val earIntentFilter =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA)
this@AirPodsService.registerReceiver(earReceiver, earIntentFilter, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
RECEIVER_EXPORTED this@AirPodsService.registerReceiver(
earReceiver, earIntentFilter,
RECEIVER_EXPORTED
)
} else {
this@AirPodsService.registerReceiver(
earReceiver,
earIntentFilter
)
}
} else if (ancNotification.isANCData(data)) {
ancNotification.setStatus(data)
sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply {
putExtra("data", ancNotification.status)
})
Log.d("AirPods Parser", "ANC: ${ancNotification.status}")
} else if (batteryNotification.isBatteryData(data)) {
batteryNotification.setBattery(data)
sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply {
putParcelableArrayListExtra(
"data",
ArrayList(batteryNotification.getBattery())
)
})
updateNotificationContent(
true,
this@AirPodsService.getSharedPreferences(
"settings",
MODE_PRIVATE
).getString("name", device.name),
batteryNotification.getBattery()
)
for (battery in batteryNotification.getBattery()) {
Log.d(
"AirPods Parser",
"${battery.getComponentName()}: ${battery.getStatusName()} at ${battery.level}% "
)
}
if (batteryNotification.getBattery()[0].status == 1 && batteryNotification.getBattery()[1].status == 1) {
disconnectAudio(this@AirPodsService, device)
} else {
connectAudio(this@AirPodsService, device)
}
} else if (conversationAwarenessNotification.isConversationalAwarenessData(
data
)
) {
conversationAwarenessNotification.setData(data)
sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply {
putExtra("data", conversationAwarenessNotification.status)
})
if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) {
MediaController.startSpeaking()
} else if (conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) {
MediaController.stopSpeaking()
}
Log.d(
"AirPods Parser",
"Conversation Awareness: ${conversationAwarenessNotification.status}"
) )
} else { } else {
this@AirPodsService.registerReceiver(earReceiver, earIntentFilter)
} }
} }
else if (ancNotification.isANCData(data)) {
ancNotification.setStatus(data)
sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply {
putExtra("data", ancNotification.status)
})
Log.d("AirPods Parser", "ANC: ${ancNotification.status}")
}
else if (batteryNotification.isBatteryData(data)) {
batteryNotification.setBattery(data)
sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply {
putParcelableArrayListExtra("data", ArrayList(batteryNotification.getBattery()))
})
updateNotificationContent(true, this@AirPodsService.getSharedPreferences("settings", MODE_PRIVATE).getString("name", device.name), batteryNotification.getBattery())
for (battery in batteryNotification.getBattery()) {
Log.d("AirPods Parser", "${battery.getComponentName()}: ${battery.getStatusName()} at ${battery.level}% ")
}
if (batteryNotification.getBattery()[0].status == 1 && batteryNotification.getBattery()[1].status == 1) {
disconnectAudio(this@AirPodsService, device)
}
else {
connectAudio(this@AirPodsService, device)
}
}
else if (conversationAwarenessNotification.isConversationalAwarenessData(data)) {
conversationAwarenessNotification.setData(data)
sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply {
putExtra("data", conversationAwarenessNotification.status)
})
if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) {
MediaController.startSpeaking()
} else if (conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) {
MediaController.stopSpeaking()
}
Log.d("AirPods Parser", "Conversation Awareness: ${conversationAwarenessNotification.status}")
}
else { }
} }
Log.d("AirPods Service", "Socket closed")
isConnected = false
socket.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
} }
Log.d("AirPods Service", "Socket closed")
isConnected = false
socket.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
} }
} catch (e: Exception) {
e.printStackTrace()
Log.d("AirPodsService", "Failed to connect to socket")
} }
} }
catch (e: Exception) {
e.printStackTrace()
Log.d("AirPodsService", "Failed to connect to socket")
}
} }

View File

@@ -520,7 +520,7 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
), ),
topBar = { topBar = {
val darkMode = MaterialTheme.colorScheme.surface.luminance() < 0.5 val darkMode = MaterialTheme.colorScheme.surface.luminance() < 0.5
val mdensity = remember { mutableFloatStateOf(1f) } val mDensity = remember { mutableFloatStateOf(1f) }
CenterAlignedTopAppBar( CenterAlignedTopAppBar(
title = { title = {
Text( Text(
@@ -530,14 +530,14 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
modifier = Modifier modifier = Modifier
.hazeChild( .hazeChild(
state = hazeState, state = hazeState,
style = CupertinoMaterials.thin(), style = CupertinoMaterials.regular(),
block = { block = {
// make the background transparent when not scrolled yet // make the background transparent when not scrolled yet
alpha = if (verticalScrollState.value > 55.dp.value * mdensity.floatValue) 1f else 0f alpha = if (verticalScrollState.value > 55.dp.value * mDensity.floatValue) 1f else 0f
} }
) )
.drawBehind { .drawBehind {
mdensity.value = density mDensity.floatValue = density
val strokeWidth = 0.7.dp.value * density val strokeWidth = 0.7.dp.value * density
val y = size.height - strokeWidth / 2 val y = size.height - strokeWidth / 2
if (verticalScrollState.value > 55.dp.value * density) { if (verticalScrollState.value > 55.dp.value * density) {

View File

@@ -68,6 +68,10 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
@Composable @Composable
fun DebugScreen(navController: NavController) { fun DebugScreen(navController: NavController) {
val hazeState = remember { HazeState() } val hazeState = remember { HazeState() }
val text = remember { mutableStateListOf<String>("Log Start") }
val context = LocalContext.current
val listState = rememberLazyListState()
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
@@ -82,7 +86,14 @@ fun DebugScreen(navController: NavController) {
modifier = Modifier modifier = Modifier
.hazeChild( .hazeChild(
state = hazeState, state = hazeState,
style = CupertinoMaterials.thin() style = CupertinoMaterials.thin(),
block = {
alpha = if (listState.firstVisibleItemIndex > 0) {
1f
} else {
0f
}
}
), ),
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent containerColor = Color.Transparent
@@ -92,16 +103,11 @@ fun DebugScreen(navController: NavController) {
containerColor = if (MaterialTheme.colorScheme.surface.luminance() < 0.5) Color(0xFF000000) containerColor = if (MaterialTheme.colorScheme.surface.luminance() < 0.5) Color(0xFF000000)
else Color(0xFFF2F2F7), else Color(0xFFF2F2F7),
) { paddingValues -> ) { paddingValues ->
val text = remember { mutableStateListOf<String>("Log Start") }
val context = LocalContext.current
val listState = rememberLazyListState()
val receiver = object : BroadcastReceiver() { val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val data = intent.getByteArrayExtra("data") val data = intent.getByteArrayExtra("data")
data?.let { data?.let {
text.add(">" + it.joinToString(" ") { byte -> "%02X".format(byte) }) // Use ">" for received packets text.add(">" + it.joinToString(" ") { byte -> "%02X".format(byte) })
} }
} }
} }
@@ -121,12 +127,10 @@ fun DebugScreen(navController: NavController) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
// .padding(paddingValues)
.imePadding() .imePadding()
.haze(hazeState) .haze(hazeState)
.padding(top = 0.dp) .padding(top = 0.dp)
) { ) {
Spacer(modifier = Modifier.height(55.dp))
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier modifier = Modifier
@@ -136,7 +140,12 @@ fun DebugScreen(navController: NavController) {
items(text.size) { index -> items(text.size) { index ->
val message = text[index] val message = text[index]
val isSent = message.startsWith(">") val isSent = message.startsWith(">")
val backgroundColor = if (isSent) Color(0xFFE1FFC7) else Color(0xFFD1D1D1) val backgroundColor =
if (isSent) Color(0xFFE1FFC7) else Color(0xFFD1D1D1)
if (message == "Log Start") {
Spacer(modifier = Modifier.height(115.dp))
}
Box( Box(
modifier = Modifier modifier = Modifier
@@ -155,11 +164,13 @@ fun DebugScreen(navController: NavController) {
} }
Text( Text(
text = if (isSent) message.substring(1) else message, // Remove the ">" from sent packets text = if (isSent) message.substring(1) else message,
fontFamily = FontFamily(Font(R.font.hack)), fontFamily = FontFamily(Font(R.font.hack)),
color = if (MaterialTheme.colorScheme.surface.luminance() < 0.5) Color(0xFF000000) color = if (MaterialTheme.colorScheme.surface.luminance() < 0.5) Color(
0xFF000000
)
else Color(0xFF000000), else Color(0xFF000000),
modifier = Modifier.weight(1f) // Allows text to take available space modifier = Modifier.weight(1f)
) )
if (isSent) { if (isSent) {
@@ -170,6 +181,7 @@ fun DebugScreen(navController: NavController) {
} }
} }
) )
Spacer(modifier = Modifier.height(8.dp))
val airPodsService = remember { mutableStateOf<AirPodsService?>(null) } val airPodsService = remember { mutableStateOf<AirPodsService?>(null) }
val serviceConnection = object : ServiceConnection { val serviceConnection = object : ServiceConnection {
@@ -200,13 +212,14 @@ fun DebugScreen(navController: NavController) {
label = { Text("Packet") }, label = { Text("Packet") },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 8.dp), // Padding for the input field .padding(horizontal = 8.dp)
.padding(bottom = 5.dp),
trailingIcon = { trailingIcon = {
IconButton( IconButton(
onClick = { onClick = {
airPodsService.value?.sendPacket(packet.value.text) airPodsService.value?.sendPacket(packet.value.text)
text.add(packet.value.text) // Add sent message directly without prefix text.add(packet.value.text)
packet.value = TextFieldValue("") // Clear input field after sending packet.value = TextFieldValue("")
} }
) { ) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")