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,
getSharedPreferences("settings", MODE_PRIVATE).getString("name", "AirPods Pro") ?: "AirPods"
)
if (isConnectedLocally) return
val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0
val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0
val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0
@@ -243,24 +244,23 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
}
override fun onBatteryChanged(device: BLEManager.AirPodsStatus) {
// if (!isConnectedLocally) {
// val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0
// val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0
// val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0
// val leftCharging = bleManager.getMostRecentStatus()?.isLeftCharging
// val rightCharging = bleManager.getMostRecentStatus()?.isRightCharging
// val caseCharging = bleManager.getMostRecentStatus()?.isCaseCharging
if (isConnectedLocally) return
val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0
val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0
val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0
val leftCharging = bleManager.getMostRecentStatus()?.isLeftCharging
val rightCharging = bleManager.getMostRecentStatus()?.isRightCharging
val caseCharging = bleManager.getMostRecentStatus()?.isCaseCharging
// batteryNotification.setBatteryDirect(
// leftLevel = leftLevel,
// leftCharging = leftCharging == true,
// rightLevel = rightLevel,
// rightCharging = rightCharging == true,
// caseLevel = caseLevel,
// caseCharging = caseCharging == true
// )
// updateBattery()
// }
batteryNotification.setBatteryDirect(
leftLevel = leftLevel,
leftCharging = leftCharging == true,
rightLevel = rightLevel,
rightCharging = rightCharging == true,
caseLevel = caseLevel,
caseCharging = caseCharging == true
)
updateBattery()
Log.d("AirPodsBLEService", "Battery changed")
}
@@ -413,6 +413,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
connectAudio(this@AirPodsService, device)
justEnabledA2dp = true
registerA2dpConnectionReceiver()
if (MediaController.getMusicActive()) {
MediaController.userPlayedTheMedia = true
}
} else if (newInEarData == listOf(false, false)) {
MediaController.sendPause(force = true)
if (config.disconnectWhenNotWearing) {
@@ -434,7 +437,6 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
if (newInEarData.sorted() != inEarData.sorted()) {
inEarData = newInEarData
if (inEar == true) {
if (!justEnabledA2dp) {
justEnabledA2dp = false
@@ -1674,8 +1676,19 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
@RequiresApi(Build.VERSION_CODES.R)
@SuppressLint("MissingPermission")
fun takeOver(takingOverFor: String) {
if (isConnectedLocally || !CrossDevice.isAvailable || bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true) {
Log.d("AirPodsService", "Already connected or not available for takeover")
if (isConnectedLocally) {
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
}
@@ -1684,6 +1697,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
"call" -> config.takeoverWhenRingingCall
else -> false
}
if (!shouldTakeOverPState) {
Log.d("AirPodsService", "Not taking over audio, phone state takeover disabled")
return
@@ -1704,6 +1718,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
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")
CrossDevice.sendRemotePacket(CrossDevicePackets.REQUEST_DISCONNECT.packet)
Log.d("AirPodsService", macAddress)
@@ -1810,7 +1830,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
aacpManager.sendSetFeatureFlagsPacket()
aacpManager.sendNotificationRequest()
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 {
aacpManager.sendPacket(aacpManager.createHandshakePacket())
delay(200)
@@ -1818,7 +1838,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
delay(200)
aacpManager.sendNotificationRequest()
delay(200)
aacpManager.sendRequestProximityKeys(AACPManager.Companion.ProximityKeyType.IRK.value)
aacpManager.sendRequestProximityKeys((AACPManager.Companion.ProximityKeyType.IRK.value+AACPManager.Companion.ProximityKeyType.ENC_KEY.value).toByte())
startHeadTracking()
Handler(Looper.getMainLooper()).postDelayed({
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 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 rightBatteryNibble = if (xorFactor) podsBattery and 0x0F else (podsBattery shr 4) and 0x0F
val isFlipped = !primaryLeft
val leftBatteryNibble = if (isFlipped) (podsBattery shr 4) and 0x0F else podsBattery and 0x0F
val rightBatteryNibble = if (isFlipped) podsBattery and 0x0F else (podsBattery shr 4) and 0x0F
val caseBattery = flagsCase and 0x0F
val flags = (flagsCase shr 4) and 0x0F
val caseBattery = (flagsCase shr 4) and 0x0F
val flags = flagsCase and 0x0F
val isRightCharging = if (xorFactor) (flags and 0x02) != 0 else (flags and 0x01) != 0
val isLeftCharging = if (xorFactor) (flags and 0x01) != 0 else (flags and 0x02) != 0
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 lidOpen = ((lid shr 3) and 0x01) == 0

View File

@@ -88,11 +88,8 @@ object MediaController {
userPlayedTheMedia = audioManager.isMusicActive
}, 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) {
Log.d("MediaController", "Pausing for cross device and taking over.")
sendPause(true)
pausedForCrossDevice = true
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