android: improve ble-based autoconnection

This commit is contained in:
Kavish Devar
2025-05-20 09:54:18 +05:30
parent 2b1fb5b71e
commit 5eb13ace0c
3 changed files with 60 additions and 33 deletions

View File

@@ -213,6 +213,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
this@AirPodsService, this@AirPodsService,
getSharedPreferences("settings", MODE_PRIVATE).getString("name", "AirPods Pro") ?: "AirPods" getSharedPreferences("settings", MODE_PRIVATE).getString("name", "AirPods Pro") ?: "AirPods"
) )
if (isConnectedLocally) return
val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0 val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0
val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0 val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0
val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0 val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0
@@ -243,24 +244,23 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
} }
override fun onBatteryChanged(device: BLEManager.AirPodsStatus) { override fun onBatteryChanged(device: BLEManager.AirPodsStatus) {
// if (!isConnectedLocally) { if (isConnectedLocally) return
// val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0 val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0
// val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0 val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0
// val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0 val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0
// val leftCharging = bleManager.getMostRecentStatus()?.isLeftCharging val leftCharging = bleManager.getMostRecentStatus()?.isLeftCharging
// val rightCharging = bleManager.getMostRecentStatus()?.isRightCharging val rightCharging = bleManager.getMostRecentStatus()?.isRightCharging
// val caseCharging = bleManager.getMostRecentStatus()?.isCaseCharging val caseCharging = bleManager.getMostRecentStatus()?.isCaseCharging
// batteryNotification.setBatteryDirect( batteryNotification.setBatteryDirect(
// leftLevel = leftLevel, leftLevel = leftLevel,
// leftCharging = leftCharging == true, leftCharging = leftCharging == true,
// rightLevel = rightLevel, rightLevel = rightLevel,
// rightCharging = rightCharging == true, rightCharging = rightCharging == true,
// caseLevel = caseLevel, caseLevel = caseLevel,
// caseCharging = caseCharging == true caseCharging = caseCharging == true
// ) )
// updateBattery() updateBattery()
// }
Log.d("AirPodsBLEService", "Battery changed") Log.d("AirPodsBLEService", "Battery changed")
} }
@@ -413,6 +413,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
connectAudio(this@AirPodsService, device) connectAudio(this@AirPodsService, device)
justEnabledA2dp = true justEnabledA2dp = true
registerA2dpConnectionReceiver() registerA2dpConnectionReceiver()
if (MediaController.getMusicActive()) {
MediaController.userPlayedTheMedia = true
}
} else if (newInEarData == listOf(false, false)) { } else if (newInEarData == listOf(false, false)) {
MediaController.sendPause(force = true) MediaController.sendPause(force = true)
if (config.disconnectWhenNotWearing) { if (config.disconnectWhenNotWearing) {
@@ -434,7 +437,6 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
if (newInEarData.sorted() != inEarData.sorted()) { if (newInEarData.sorted() != inEarData.sorted()) {
inEarData = newInEarData inEarData = newInEarData
if (inEar == true) { if (inEar == true) {
if (!justEnabledA2dp) { if (!justEnabledA2dp) {
justEnabledA2dp = false justEnabledA2dp = false
@@ -1674,8 +1676,19 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
fun takeOver(takingOverFor: String) { fun takeOver(takingOverFor: String) {
if (isConnectedLocally || !CrossDevice.isAvailable || bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true) { if (isConnectedLocally) {
Log.d("AirPodsService", "Already connected or not available for takeover") Log.d("AirPodsService", "Already connected locally, skipping")
return
}
if (CrossDevice.isAvailable) {
Log.d("AirPodsService", "CrossDevice is available, continuing")
}
else if (bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true) {
Log.d("AirPodsService", "At least one AirPod is in ear, continuing")
}
else {
Log.d("AirPodsService", "CrossDevice not available and AirPods not in ear, skipping")
return return
} }
@@ -1684,6 +1697,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
"call" -> config.takeoverWhenRingingCall "call" -> config.takeoverWhenRingingCall
else -> false else -> false
} }
if (!shouldTakeOverPState) { if (!shouldTakeOverPState) {
Log.d("AirPodsService", "Not taking over audio, phone state takeover disabled") Log.d("AirPodsService", "Not taking over audio, phone state takeover disabled")
return return
@@ -1704,6 +1718,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
return return
} }
if (takingOverFor == "music") {
Log.d("AirPodsService", "Pausing music so that it doesn't play through speakers")
MediaController.pausedForCrossDevice = true
MediaController.sendPause(true)
}
Log.d("AirPodsService", "Taking over audio") Log.d("AirPodsService", "Taking over audio")
CrossDevice.sendRemotePacket(CrossDevicePackets.REQUEST_DISCONNECT.packet) CrossDevice.sendRemotePacket(CrossDevicePackets.REQUEST_DISCONNECT.packet)
Log.d("AirPodsService", macAddress) Log.d("AirPodsService", macAddress)
@@ -1810,7 +1830,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
aacpManager.sendSetFeatureFlagsPacket() aacpManager.sendSetFeatureFlagsPacket()
aacpManager.sendNotificationRequest() aacpManager.sendNotificationRequest()
Log.d("AirPodsService", "Requesting proximity keys") Log.d("AirPodsService", "Requesting proximity keys")
aacpManager.sendRequestProximityKeys(AACPManager.Companion.ProximityKeyType.IRK.value) aacpManager.sendRequestProximityKeys((AACPManager.Companion.ProximityKeyType.IRK.value + AACPManager.Companion.ProximityKeyType.ENC_KEY.value).toByte())
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
aacpManager.sendPacket(aacpManager.createHandshakePacket()) aacpManager.sendPacket(aacpManager.createHandshakePacket())
delay(200) delay(200)
@@ -1818,7 +1838,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
delay(200) delay(200)
aacpManager.sendNotificationRequest() aacpManager.sendNotificationRequest()
delay(200) delay(200)
aacpManager.sendRequestProximityKeys(AACPManager.Companion.ProximityKeyType.IRK.value) aacpManager.sendRequestProximityKeys((AACPManager.Companion.ProximityKeyType.IRK.value+AACPManager.Companion.ProximityKeyType.ENC_KEY.value).toByte())
startHeadTracking() startHeadTracking()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
aacpManager.sendPacket(aacpManager.createHandshakePacket()) aacpManager.sendPacket(aacpManager.createHandshakePacket())

View File

@@ -336,14 +336,16 @@ class BLEManager(private val context: Context) {
val isLeftInEar = if (xorFactor) (status and 0x08) != 0 else (status and 0x02) != 0 val isLeftInEar = if (xorFactor) (status and 0x08) != 0 else (status and 0x02) != 0
val isRightInEar = if (xorFactor) (status and 0x02) != 0 else (status and 0x08) != 0 val isRightInEar = if (xorFactor) (status and 0x02) != 0 else (status and 0x08) != 0
val leftBatteryNibble = if (xorFactor) (podsBattery shr 4) and 0x0F else podsBattery and 0x0F val isFlipped = !primaryLeft
val rightBatteryNibble = if (xorFactor) podsBattery and 0x0F else (podsBattery shr 4) and 0x0F
val caseBattery = (flagsCase shr 4) and 0x0F val leftBatteryNibble = if (isFlipped) (podsBattery shr 4) and 0x0F else podsBattery and 0x0F
val flags = flagsCase and 0x0F val rightBatteryNibble = if (isFlipped) podsBattery and 0x0F else (podsBattery shr 4) and 0x0F
val isRightCharging = if (xorFactor) (flags and 0x02) != 0 else (flags and 0x01) != 0 val caseBattery = flagsCase and 0x0F
val isLeftCharging = if (xorFactor) (flags and 0x01) != 0 else (flags and 0x02) != 0 val flags = (flagsCase shr 4) and 0x0F
val isLeftCharging = if (isFlipped) (flags and 0x02) != 0 else (flags and 0x01) != 0
val isRightCharging = if (isFlipped) (flags and 0x01) != 0 else (flags and 0x02) != 0
val isCaseCharging = (flags and 0x04) != 0 val isCaseCharging = (flags and 0x04) != 0
val lidOpen = ((lid shr 3) and 0x01) == 0 val lidOpen = ((lid shr 3) and 0x01) == 0

View File

@@ -88,11 +88,8 @@ object MediaController {
userPlayedTheMedia = audioManager.isMusicActive userPlayedTheMedia = audioManager.isMusicActive
}, 7) // i have no idea why android sends an event a hundred times after the user does something. }, 7) // i have no idea why android sends an event a hundred times after the user does something.
} }
Log.d("MediaController", "pausedforcrossdevice: $pausedForCrossDevice Ear detection status: ${ServiceManager.getService()?.earDetectionNotification?.status}, music active: ${audioManager.isMusicActive} and cross device available: ${CrossDevice.isAvailable}") Log.d("MediaController", "pausedforcrossdevice: $pausedForCrossDevice")
if (!pausedForCrossDevice && audioManager.isMusicActive) { if (!pausedForCrossDevice && audioManager.isMusicActive) {
Log.d("MediaController", "Pausing for cross device and taking over.")
sendPause(true)
pausedForCrossDevice = true
ServiceManager.getService()?.takeOver("music") ServiceManager.getService()?.takeOver("music")
} }
} }
@@ -143,6 +140,14 @@ object MediaController {
) )
) )
} }
if (!audioManager.isMusicActive) {
Log.d("MediaController", "Setting iPausedTheMedia to false")
iPausedTheMedia = false
}
if (pausedForCrossDevice) {
Log.d("MediaController", "Setting pausedForCrossDevice to false")
pausedForCrossDevice = false
}
} }
@Synchronized @Synchronized