From 5aa36b148bac67bacd2de48daa73b1048c74d0dc Mon Sep 17 00:00:00 2001 From: darken Date: Sun, 8 Feb 2026 04:20:34 +0100 Subject: [PATCH] fix: Harden battery display against native crashes Remove thread-unsafe shared RelativeDateTimeFormatter instance and cache battery percentage values to local variables at all call sites. --- .../ui/overview/cards/pods/DualPodsCardVH.kt | 19 ++--- .../overview/cards/pods/SinglePodsCardVH.kt | 7 +- .../capod/main/ui/widget/WidgetProvider.kt | 19 ++--- .../ui/MonitorNotificationViewFactory.kt | 78 ++++++++++--------- .../capod/monitor/ui/MonitorNotifications.kt | 15 ++-- .../capod/pods/core/PodDeviceExtensions.kt | 26 ++----- .../reaction/ui/popup/PopUpPodViewFactory.kt | 66 +++++++++------- 7 files changed, 116 insertions(+), 114 deletions(-) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualPodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualPodsCardVH.kt index 7fa75d16..48e0a119 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualPodsCardVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualPodsCardVH.kt @@ -19,9 +19,7 @@ import eu.darken.capod.pods.core.apple.DualApplePods import eu.darken.capod.pods.core.apple.DualApplePods.LidState import eu.darken.capod.pods.core.firstSeenFormatted import eu.darken.capod.pods.core.getBatteryDrawable -import eu.darken.capod.pods.core.getBatteryLevelCase -import eu.darken.capod.pods.core.getBatteryLevelLeftPod -import eu.darken.capod.pods.core.getBatteryLevelRightPod +import eu.darken.capod.pods.core.formatBatteryPercent import eu.darken.capod.pods.core.lastSeenFormatted import java.time.Duration import java.time.Instant @@ -76,11 +74,13 @@ class DualPodsCardVH(parent: ViewGroup) : // Pods battery state device.apply { - podLeftBatteryIcon.setImageResource(getBatteryDrawable(batteryLeftPodPercent)) - podLeftBatteryLabel.text = getBatteryLevelLeftPod(context) + val leftPercent = batteryLeftPodPercent + podLeftBatteryIcon.setImageResource(getBatteryDrawable(leftPercent)) + podLeftBatteryLabel.text = formatBatteryPercent(context, leftPercent) - podRightBatteryIcon.setImageResource(getBatteryDrawable(batteryRightPodPercent)) - podRightBatteryLabel.text = getBatteryLevelRightPod(context) + val rightPercent = batteryRightPodPercent + podRightBatteryIcon.setImageResource(getBatteryDrawable(rightPercent)) + podRightBatteryLabel.text = formatBatteryPercent(context, rightPercent) } // Pods charging state @@ -139,8 +139,9 @@ class DualPodsCardVH(parent: ViewGroup) : if (this is HasCase) { podCaseIcon.setImageResource(caseIcon) podCaseBatteryIcon.isGone = false - podCaseBatteryIcon.setImageResource(getBatteryDrawable(batteryCasePercent)) - podCaseBatteryLabel.text = getBatteryLevelCase(context) + val casePercent = batteryCasePercent + podCaseBatteryIcon.setImageResource(getBatteryDrawable(casePercent)) + podCaseBatteryLabel.text = formatBatteryPercent(context, casePercent) podCaseChargingIcon.isInvisible = !isCaseCharging podCaseChargingLabel.isInvisible = !isCaseCharging diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SinglePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SinglePodsCardVH.kt index dacf180f..10b8682b 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SinglePodsCardVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SinglePodsCardVH.kt @@ -13,7 +13,7 @@ import eu.darken.capod.pods.core.SinglePodDevice import eu.darken.capod.pods.core.apple.ApplePods import eu.darken.capod.pods.core.firstSeenFormatted import eu.darken.capod.pods.core.getBatteryDrawable -import eu.darken.capod.pods.core.getBatteryLevelHeadset +import eu.darken.capod.pods.core.formatBatteryPercent import eu.darken.capod.pods.core.lastSeenFormatted import java.time.Duration import java.time.Instant @@ -55,8 +55,9 @@ class SinglePodsCardVH(parent: ViewGroup) : // Battery level device.apply { - batteryLabel.text = getBatteryLevelHeadset(context) - batteryIcon.setImageResource(getBatteryDrawable(batteryHeadsetPercent)) + val headsetPercent = batteryHeadsetPercent + batteryIcon.setImageResource(getBatteryDrawable(headsetPercent)) + batteryLabel.text = formatBatteryPercent(context, headsetPercent) } // Charge state diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt index 20f7495d..d290bc23 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt @@ -30,11 +30,8 @@ import eu.darken.capod.pods.core.HasEarDetectionDual import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.PodFactory import eu.darken.capod.pods.core.SinglePodDevice +import eu.darken.capod.pods.core.formatBatteryPercent import eu.darken.capod.pods.core.getBatteryDrawable -import eu.darken.capod.pods.core.getBatteryLevelCase -import eu.darken.capod.pods.core.getBatteryLevelHeadset -import eu.darken.capod.pods.core.getBatteryLevelLeftPod -import eu.darken.capod.pods.core.getBatteryLevelRightPod import eu.darken.capod.profiles.core.ProfileId import finish2 import kotlinx.coroutines.CoroutineScope @@ -246,8 +243,9 @@ class WidgetProvider : AppWidgetProvider() { setTextViewText(R.id.headphones_label, podDevice.getLabel(context)) // Left + val leftPercent = podDevice.batteryLeftPodPercent setImageViewResource(R.id.pod_left_icon, podDevice.leftPodIcon) - setTextViewText(R.id.pod_left_label, podDevice.getBatteryLevelLeftPod(context)) + setTextViewText(R.id.pod_left_label, formatBatteryPercent(context, leftPercent)) setViewVisibility( R.id.pod_left_charging, if (podDevice is HasChargeDetectionDual && podDevice.isLeftPodCharging) View.VISIBLE else View.GONE @@ -259,15 +257,17 @@ class WidgetProvider : AppWidgetProvider() { // Case (podDevice as? HasCase)?.let { setImageViewResource(R.id.pod_case_icon, it.caseIcon) } - setTextViewText(R.id.pod_case_label, (podDevice as? HasCase)?.getBatteryLevelCase(context)) + val casePercent = (podDevice as? HasCase)?.batteryCasePercent + setTextViewText(R.id.pod_case_label, formatBatteryPercent(context, casePercent)) setViewVisibility( R.id.pod_case_charging, if (podDevice is HasCase && podDevice.isCaseCharging) View.VISIBLE else View.GONE ) // Right + val rightPercent = podDevice.batteryRightPodPercent setImageViewResource(R.id.pod_right_icon, podDevice.rightPodIcon) - setTextViewText(R.id.pod_right_label, podDevice.getBatteryLevelRightPod(context)) + setTextViewText(R.id.pod_right_label, formatBatteryPercent(context, rightPercent)) setViewVisibility( R.id.pod_right_charging, if (podDevice is HasChargeDetectionDual && podDevice.isRightPodCharging) View.VISIBLE else View.GONE @@ -293,10 +293,11 @@ class WidgetProvider : AppWidgetProvider() { setOnClickPendingIntent(R.id.widget_root, pendingIntent) + val headsetPercent = podDevice.batteryHeadsetPercent setTextViewText(R.id.headphones_label, podDevice.getLabel(context)) setImageViewResource(R.id.headphones_icon, podDevice.iconRes) - setImageViewResource(R.id.headphones_battery_icon, getBatteryDrawable(podDevice.batteryHeadsetPercent)) - setTextViewText(R.id.headphones_battery_label, podDevice.getBatteryLevelHeadset(context)) + setImageViewResource(R.id.headphones_battery_icon, getBatteryDrawable(headsetPercent)) + setTextViewText(R.id.headphones_battery_label, formatBatteryPercent(context, headsetPercent)) setViewVisibility( R.id.headphones_worn, diff --git a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt index a2be77cf..d0439a94 100644 --- a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt +++ b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt @@ -5,7 +5,15 @@ import android.view.View import android.widget.RemoteViews import dagger.hilt.android.qualifiers.ApplicationContext import eu.darken.capod.R -import eu.darken.capod.pods.core.* +import eu.darken.capod.pods.core.DualPodDevice +import eu.darken.capod.pods.core.HasCase +import eu.darken.capod.pods.core.HasChargeDetectionDual +import eu.darken.capod.pods.core.HasEarDetection +import eu.darken.capod.pods.core.HasEarDetectionDual +import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.SinglePodDevice +import eu.darken.capod.pods.core.formatBatteryPercent +import eu.darken.capod.pods.core.getBatteryDrawable import javax.inject.Inject @@ -23,48 +31,48 @@ class MonitorNotificationViewFactory @Inject constructor( context.packageName, R.layout.monitor_notification_dual_pods_small ).apply { - device.apply { - // Left - setImageViewResource(R.id.pod_left_icon, device.leftPodIcon) - setTextViewText(R.id.pod_left_label, getBatteryLevelLeftPod(context)) - val isLeftPodCharging = (device as? HasChargeDetectionDual)?.isLeftPodCharging ?: false - setViewVisibility(R.id.pod_left_charging, if (isLeftPodCharging) View.VISIBLE else View.GONE) - val isLeftPodInEar = (device as? HasEarDetectionDual)?.isLeftPodInEar ?: false - setViewVisibility(R.id.pod_left_ear, if (isLeftPodInEar) View.VISIBLE else View.GONE) + // Left + val leftPercent = device.batteryLeftPodPercent + setImageViewResource(R.id.pod_left_icon, device.leftPodIcon) + setTextViewText(R.id.pod_left_label, formatBatteryPercent(context, leftPercent)) + val isLeftPodCharging = (device as? HasChargeDetectionDual)?.isLeftPodCharging ?: false + setViewVisibility(R.id.pod_left_charging, if (isLeftPodCharging) View.VISIBLE else View.GONE) + val isLeftPodInEar = (device as? HasEarDetectionDual)?.isLeftPodInEar ?: false + setViewVisibility(R.id.pod_left_ear, if (isLeftPodInEar) View.VISIBLE else View.GONE) - // Case - setViewVisibility(R.id.pod_case_charging, if (device is HasCase) View.VISIBLE else View.GONE) - (device as? HasCase)?.let { case -> - setImageViewResource(R.id.pod_case_icon, device.caseIcon) - setTextViewText(R.id.pod_case_label, case.getBatteryLevelCase(context)) - setViewVisibility(R.id.pod_case_charging, if (case.isCaseCharging) View.VISIBLE else View.GONE) - } - - // Right - setImageViewResource(R.id.pod_right_icon, device.rightPodIcon) - setTextViewText(R.id.pod_right_label, getBatteryLevelRightPod(context)) - val isRightPodCharging = (device as? HasChargeDetectionDual)?.isRightPodCharging ?: false - setViewVisibility(R.id.pod_right_charging, if (isRightPodCharging) View.VISIBLE else View.GONE) - val isRightPodInEar = (device as? HasEarDetectionDual)?.isRightPodInEar ?: false - setViewVisibility(R.id.pod_right_ear, if (isRightPodInEar) View.VISIBLE else View.GONE) + // Case + setViewVisibility(R.id.pod_case_charging, if (device is HasCase) View.VISIBLE else View.GONE) + (device as? HasCase)?.let { case -> + setImageViewResource(R.id.pod_case_icon, device.caseIcon) + val casePercent = case.batteryCasePercent + setTextViewText(R.id.pod_case_label, formatBatteryPercent(context, casePercent)) + setViewVisibility(R.id.pod_case_charging, if (case.isCaseCharging) View.VISIBLE else View.GONE) } + + // Right + val rightPercent = device.batteryRightPodPercent + setImageViewResource(R.id.pod_right_icon, device.rightPodIcon) + setTextViewText(R.id.pod_right_label, formatBatteryPercent(context, rightPercent)) + val isRightPodCharging = (device as? HasChargeDetectionDual)?.isRightPodCharging ?: false + setViewVisibility(R.id.pod_right_charging, if (isRightPodCharging) View.VISIBLE else View.GONE) + val isRightPodInEar = (device as? HasEarDetectionDual)?.isRightPodInEar ?: false + setViewVisibility(R.id.pod_right_ear, if (isRightPodInEar) View.VISIBLE else View.GONE) } private fun createSinglePod(device: SinglePodDevice): RemoteViews = RemoteViews( context.packageName, R.layout.monitor_notification_single_pods_small ).apply { - device.apply { - setTextViewText(R.id.headphones_label, getLabel(context)) - setImageViewResource(R.id.headphones_icon, device.iconRes) - setImageViewResource(R.id.headphones_battery_icon, getBatteryDrawable(batteryHeadsetPercent)) - setTextViewText(R.id.headphones_battery_label, getBatteryLevelHeadset(context)) - if (this is HasEarDetection) { - setViewVisibility(R.id.headphones_worn, if (isBeingWorn) View.VISIBLE else View.GONE) - } - if (this is HasChargeDetectionDual) { - setViewVisibility(R.id.headphones_charging, if (isHeadsetBeingCharged) View.VISIBLE else View.GONE) - } + val headsetPercent = device.batteryHeadsetPercent + setTextViewText(R.id.headphones_label, device.getLabel(context)) + setImageViewResource(R.id.headphones_icon, device.iconRes) + setImageViewResource(R.id.headphones_battery_icon, getBatteryDrawable(headsetPercent)) + setTextViewText(R.id.headphones_battery_label, formatBatteryPercent(context, headsetPercent)) + if (device is HasEarDetection) { + setViewVisibility(R.id.headphones_worn, if (device.isBeingWorn) View.VISIBLE else View.GONE) + } + if (device is HasChargeDetectionDual) { + setViewVisibility(R.id.headphones_charging, if (device.isHeadsetBeingCharged) View.VISIBLE else View.GONE) } } diff --git a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt index 7c8c3aa1..dcd6b9ad 100644 --- a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt +++ b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt @@ -25,10 +25,7 @@ import eu.darken.capod.pods.core.HasChargeDetection import eu.darken.capod.pods.core.HasEarDetection import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.SinglePodDevice -import eu.darken.capod.pods.core.getBatteryLevelCase -import eu.darken.capod.pods.core.getBatteryLevelHeadset -import eu.darken.capod.pods.core.getBatteryLevelLeftPod -import eu.darken.capod.pods.core.getBatteryLevelRightPod +import eu.darken.capod.pods.core.formatBatteryPercent import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import javax.inject.Inject @@ -112,11 +109,11 @@ class MonitorNotifications @Inject constructor( val batteryText = when (device) { is DualPodDevice -> { - val left = device.getBatteryLevelLeftPod(context) - val right = device.getBatteryLevelRightPod(context) + val left = formatBatteryPercent(context, device.batteryLeftPodPercent) + val right = formatBatteryPercent(context, device.batteryRightPodPercent) when { device is HasCase -> { - val case = device.getBatteryLevelCase(context) + val case = formatBatteryPercent(context, device.batteryCasePercent) "$left $case $right" } @@ -125,10 +122,10 @@ class MonitorNotifications @Inject constructor( } is SinglePodDevice -> { - val headset = device.getBatteryLevelHeadset(context) + val headset = formatBatteryPercent(context, device.batteryHeadsetPercent) when { device is HasCase -> { - val case = device.getBatteryLevelCase(context) + val case = formatBatteryPercent(context, device.batteryCasePercent) "$headset $case" } diff --git a/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt b/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt index d62d9563..c7336c63 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt @@ -8,20 +8,8 @@ import java.time.Duration import java.time.Instant import kotlin.math.roundToInt -fun DualPodDevice.getBatteryLevelLeftPod(context: Context): String = - batteryLeftPodPercent?.let { "${(it * 100).roundToInt()}%" } - ?: context.getString(R.string.general_value_not_available_label) - -fun DualPodDevice.getBatteryLevelRightPod(context: Context): String = - batteryRightPodPercent?.let { "${(it * 100).roundToInt()}%" } - ?: context.getString(R.string.general_value_not_available_label) - -fun HasCase.getBatteryLevelCase(context: Context): String = - batteryCasePercent?.let { "${(it * 100).roundToInt()}%" } - ?: context.getString(R.string.general_value_not_available_label) - -fun SinglePodDevice.getBatteryLevelHeadset(context: Context): String = - batteryHeadsetPercent?.let { "${(it * 100).roundToInt()}%" } +fun formatBatteryPercent(context: Context, percent: Float?): String = + percent?.let { "${(it * 100).roundToInt()}%" } ?: context.getString(R.string.general_value_not_available_label) fun PodDevice.getSignalQuality(context: Context): String { @@ -42,18 +30,17 @@ fun getBatteryDrawable(percent: Float?): Int = when { else -> R.drawable.ic_baseline_battery_0_bar_24 } -private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance() - fun PodDevice.lastSeenFormatted(now: Instant): String { + val formatter = RelativeDateTimeFormatter.getInstance() val duration = Duration.between(seenLastAt, now) return if (duration > Duration.ofMinutes(1)) { - lastSeenFormatter.format( + formatter.format( duration.toMinutes().toDouble(), RelativeDateTimeFormatter.Direction.LAST, RelativeDateTimeFormatter.RelativeUnit.MINUTES ) } else { - lastSeenFormatter.format( + formatter.format( duration.seconds.toDouble(), RelativeDateTimeFormatter.Direction.LAST, RelativeDateTimeFormatter.RelativeUnit.SECONDS @@ -62,8 +49,9 @@ fun PodDevice.lastSeenFormatted(now: Instant): String { } fun PodDevice.firstSeenFormatted(now: Instant): String { + val formatter = RelativeDateTimeFormatter.getInstance() val duration = Duration.between(seenFirstAt, now) - return lastSeenFormatter.format( + return formatter.format( duration.toMinutes().toDouble(), RelativeDateTimeFormatter.Direction.LAST, RelativeDateTimeFormatter.RelativeUnit.MINUTES diff --git a/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpPodViewFactory.kt b/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpPodViewFactory.kt index c503877e..a1beb5dd 100644 --- a/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpPodViewFactory.kt +++ b/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpPodViewFactory.kt @@ -12,7 +12,13 @@ import eu.darken.capod.R import eu.darken.capod.common.debug.DebugSettings import eu.darken.capod.databinding.PopupNotificationDualPodsBinding import eu.darken.capod.databinding.PopupNotificationSinglePodsBinding -import eu.darken.capod.pods.core.* +import eu.darken.capod.pods.core.DualPodDevice +import eu.darken.capod.pods.core.HasCase +import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.SinglePodDevice +import eu.darken.capod.pods.core.formatBatteryPercent +import eu.darken.capod.pods.core.getBatteryDrawable +import eu.darken.capod.pods.core.getSignalQuality import javax.inject.Inject @@ -33,43 +39,43 @@ class PopUpPodViewFactory @Inject constructor( private fun createDualPods(parent: ViewGroup, device: DualPodDevice): View = PopupNotificationDualPodsBinding.inflate(layoutInflater, parent, false).apply { - device.apply { - podIcon.setImageResource(iconRes) - podLabel.text = getLabel(context) - signal.text = getSignalQuality(context) - signal.isInvisible = debugSettings.isDebugModeEnabled.value + podIcon.setImageResource(device.iconRes) + podLabel.text = device.getLabel(context) + signal.text = device.getSignalQuality(context) + signal.isInvisible = debugSettings.isDebugModeEnabled.value - // Left - podLeftIcon.setImageResource(device.leftPodIcon) - podLeftBatteryIcon.setImageResource(getBatteryDrawable(batteryLeftPodPercent)) - podLeftBatteryLabel.text = getBatteryLevelLeftPod(context) + // Left + val leftPercent = device.batteryLeftPodPercent + podLeftIcon.setImageResource(device.leftPodIcon) + podLeftBatteryIcon.setImageResource(getBatteryDrawable(leftPercent)) + podLeftBatteryLabel.text = formatBatteryPercent(context, leftPercent) - // Case - podCaseContainer.isVisible = device is HasCase - (device as? HasCase)?.let { case -> - podCaseIcon.setImageResource(case.caseIcon) - podCaseBatteryIcon.setImageResource(getBatteryDrawable(case.batteryCasePercent)) - podCaseBatteryLabel.text = case.getBatteryLevelCase(context) - } - - // Right - podRightIcon.setImageResource(device.rightPodIcon) - podRightBatteryIcon.setImageResource(getBatteryDrawable(batteryRightPodPercent)) - podRightBatteryLabel.text = getBatteryLevelRightPod(context) + // Case + podCaseContainer.isVisible = device is HasCase + (device as? HasCase)?.let { case -> + val casePercent = case.batteryCasePercent + podCaseIcon.setImageResource(case.caseIcon) + podCaseBatteryIcon.setImageResource(getBatteryDrawable(casePercent)) + podCaseBatteryLabel.text = formatBatteryPercent(context, casePercent) } + + // Right + val rightPercent = device.batteryRightPodPercent + podRightIcon.setImageResource(device.rightPodIcon) + podRightBatteryIcon.setImageResource(getBatteryDrawable(rightPercent)) + podRightBatteryLabel.text = formatBatteryPercent(context, rightPercent) }.root private fun createSinglePod(parent: ViewGroup, device: SinglePodDevice): View = PopupNotificationSinglePodsBinding.inflate(layoutInflater, parent, false).apply { - device.apply { - headphonesIcon.setImageResource(iconRes) - headphonesLabel.text = getLabel(context) - signal.text = getSignalQuality(context) - signal.isInvisible = debugSettings.isDebugModeEnabled.value + headphonesIcon.setImageResource(device.iconRes) + headphonesLabel.text = device.getLabel(context) + signal.text = device.getSignalQuality(context) + signal.isInvisible = debugSettings.isDebugModeEnabled.value - headphonesBatteryIcon.setImageResource(getBatteryDrawable(batteryHeadsetPercent)) - headphonesBatteryLabel.text = getBatteryLevelHeadset(context) - } + val headsetPercent = device.batteryHeadsetPercent + headphonesBatteryIcon.setImageResource(getBatteryDrawable(headsetPercent)) + headphonesBatteryLabel.text = formatBatteryPercent(context, headsetPercent) }.root } \ No newline at end of file