From 2b1fb5b71ef2a889137b619a4ab612b53d8e8c56 Mon Sep 17 00:00:00 2001 From: Kavish Devar Date: Mon, 19 May 2025 18:05:06 +0530 Subject: [PATCH] android: use broadcasted battery data if not connected via l2cap for popup --- .../librepods/services/AirPodsService.kt | 119 ++++++++++++------ .../me/kavishdevar/librepods/utils/Packets.kt | 13 ++ 2 files changed, 96 insertions(+), 36 deletions(-) diff --git a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt index 7dd8ab3..7520c3e 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt @@ -213,6 +213,22 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList this@AirPodsService, getSharedPreferences("settings", MODE_PRIVATE).getString("name", "AirPods Pro") ?: "AirPods" ) + 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 + ) + sendBatteryBroadcast() } else { Log.d("AirPodsBLEService", "Lid closed") } @@ -227,6 +243,24 @@ 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 + + // batteryNotification.setBatteryDirect( + // leftLevel = leftLevel, + // leftCharging = leftCharging == true, + // rightLevel = rightLevel, + // rightCharging = rightCharging == true, + // caseLevel = caseLevel, + // caseCharging = caseCharging == true + // ) + // updateBattery() + // } Log.d("AirPodsBLEService", "Battery changed") } @@ -482,7 +516,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList config.showPhoneBatteryInWidget = preferences.getBoolean(key, true) widgetMobileBatteryEnabled = config.showPhoneBatteryInWidget updateBattery() - } "relative_conversational_awareness_volume" -> config.relativeConversationalAwarenessVolume = preferences.getBoolean(key, true) + } + "relative_conversational_awareness_volume" -> config.relativeConversationalAwarenessVolume = preferences.getBoolean(key, true) "head_gestures" -> config.headGestures = preferences.getBoolean(key, true) "disconnect_when_not_wearing" -> config.disconnectWhenNotWearing = preferences.getBoolean(key, false) "conversational_awareness_volume" -> config.conversationalAwarenessVolume = preferences.getInt(key, 43) @@ -735,9 +770,43 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList ) } + fun setBatteryMetadata() { + device?.let { + SystemApisUtils.setMetadata( + it, + it.METADATA_UNTETHERED_CASE_BATTERY, + batteryNotification.getBattery().find { it.component == BatteryComponent.CASE }?.level.toString().toByteArray() + ) + SystemApisUtils.setMetadata( + it, + it.METADATA_UNTETHERED_CASE_CHARGING, + (if (batteryNotification.getBattery().find { it.component == BatteryComponent.CASE}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) + ) + SystemApisUtils.setMetadata( + it, + it.METADATA_UNTETHERED_LEFT_BATTERY, + batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT }?.level.toString().toByteArray() + ) + SystemApisUtils.setMetadata( + it, + it.METADATA_UNTETHERED_LEFT_CHARGING, + (if (batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) + ) + SystemApisUtils.setMetadata( + it, + it.METADATA_UNTETHERED_RIGHT_BATTERY, + batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT }?.level.toString().toByteArray() + ) + SystemApisUtils.setMetadata( + it, + it.METADATA_UNTETHERED_RIGHT_CHARGING, + (if (batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) + ) + } + } + @OptIn(ExperimentalMaterial3Api::class) - fun updateBattery() { - // Update widget + fun updateBatteryWidget() { val appWidgetManager = AppWidgetManager.getInstance(this) val componentName = ComponentName(this, BatteryWidget::class.java) val widgetIds = appWidgetManager.getAppWidgetIds(componentName) @@ -831,40 +900,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } appWidgetManager.updateAppWidget(widgetIds, remoteViews) + } - // set metadata - device?.let { - SystemApisUtils.setMetadata( - it, - it.METADATA_UNTETHERED_CASE_BATTERY, - batteryNotification.getBattery().find { it.component == BatteryComponent.CASE }?.level.toString().toByteArray() - ) - SystemApisUtils.setMetadata( - it, - it.METADATA_UNTETHERED_CASE_CHARGING, - (if (batteryNotification.getBattery().find { it.component == BatteryComponent.CASE}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) - ) - SystemApisUtils.setMetadata( - it, - it.METADATA_UNTETHERED_LEFT_BATTERY, - batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT }?.level.toString().toByteArray() - ) - SystemApisUtils.setMetadata( - it, - it.METADATA_UNTETHERED_LEFT_CHARGING, - (if (batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) - ) - SystemApisUtils.setMetadata( - it, - it.METADATA_UNTETHERED_RIGHT_BATTERY, - batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT }?.level.toString().toByteArray() - ) - SystemApisUtils.setMetadata( - it, - it.METADATA_UNTETHERED_RIGHT_CHARGING, - (if (batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) - ) - } + @SuppressLint("MissingPermission") + @OptIn(ExperimentalMaterial3Api::class) + fun updateBattery() { + setBatteryMetadata() + updateBatteryWidget() + sendBatteryBroadcast() + sendBatteryNotification() } fun updateNoiseControlWidget() { @@ -937,6 +981,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) + if (!::socket.isInitialized) { + return + } if (connected && socket.isConnected) { updatedNotification = NotificationCompat.Builder(this, "airpods_connection_status") .setSmallIcon(R.drawable.airpods) diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/Packets.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/Packets.kt index 752d00e..78f240a 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/utils/Packets.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/Packets.kt @@ -185,6 +185,19 @@ class AirPodsNotifications { return data.joinToString("") { "%02x".format(it) }.startsWith("040004000400") } + fun setBatteryDirect( + leftLevel: Int, + leftCharging: Boolean, + rightLevel: Int, + rightCharging: Boolean, + caseLevel: Int, + caseCharging: Boolean + ) { + first = Battery(BatteryComponent.LEFT, leftLevel, if (leftCharging) BatteryStatus.CHARGING else BatteryStatus.NOT_CHARGING) + second = Battery(BatteryComponent.RIGHT, rightLevel, if (rightCharging) BatteryStatus.CHARGING else BatteryStatus.NOT_CHARGING) + case = Battery(BatteryComponent.CASE, caseLevel, if (caseCharging) BatteryStatus.CHARGING else BatteryStatus.NOT_CHARGING) + } + fun setBattery(data: ByteArray) { if (data.size != 22) { return