Compare commits

...
24 Commits
Author SHA1 Message Date
d4rken-org-releaser[bot] df06a49949 Release: 5.2.0-rc0 2026-07-03 10:53:28 +00:00
darken b6dcc74383 fix: Hide charging indicator for disconnected devices 2026-07-03 12:43:33 +02:00
darken fead8381bc General: Update app translations from Crowdin 2026-07-03 11:25:05 +02:00
darken 2ab9f8ace6 chore(screenshots): Show battery estimates in dashboard screenshots 2026-07-02 17:32:29 +02:00
darken 81effcb349 chore(screenshots): Regenerate smoke locale screenshots 2026-07-02 17:32:29 +02:00
darken 3dd1f8151a fix(widget): Correct typo in the configuration resize hint 2026-07-02 17:32:29 +02:00
darken 272a7121a9 fix(screenshots): Use resource-qualifier locales for region-coded languages 2026-07-02 17:32:29 +02:00
darken 52a7850c1d feat(battery): Show battery estimates in widget and expanded notification 2026-07-02 17:32:29 +02:00
darken b297ad8564 ui(battery): Simplify the health placeholder text 2026-07-02 17:32:29 +02:00
darken a3810353f7 Revert "feat(battery): Add experimental case charge ETA and case battery health"
This reverts commit 87a9881024.
2026-07-02 17:32:29 +02:00
darken d4dddb1f38 feat(battery): Add experimental case charge ETA and case battery health
- Show the time until the case is full inside its charging chip, learned from
  the case's own rising level with the existing charge-band model; no rating
  exists for case charging, so the first charge learns before it shows
- Derive a case battery health from observed transfer efficiency: pod percent
  gained per case percent spent while docked and unplugged, corrected by each
  pod's own health, compared against Apple's "with charging case" totals
- Case data is only genuine while a pod is docked — both transports silently
  freeze the last value otherwise. Battery updates now flag whether the case
  entry is live, and BLE gains strict same-frame case accessors, so estimates
  never learn from frozen echoes
- The case deliberately gets no runtime estimate: idle-then-burst drain has no
  meaningful hourly rate
- Mark the case metrics as experimental in the Battery settings card
2026-07-02 17:32:29 +02:00
darken 21560a1f3a ui(battery): Tighten estimate setting titles and descriptions 2026-07-02 17:32:29 +02:00
darken bdfb6bb073 ui(battery): Match the divider style of the sibling settings cards 2026-07-02 17:32:29 +02:00
darken aac0590017 ui(battery): Surface health before data exists, reorder the Battery card
- Show a "Still determining — check back after a few more listening sessions"
  placeholder under Battery Health in the device info sheet while the estimate
  is still accumulating data, so the feature is discoverable from day one; no
  placeholder for profiles without a paired device, where it could never resolve
- Reorder the Battery card: time remaining & health settings first, then a
  divider, then the charging-side settings (charge limit, charged notification)
- Mention the battery-health component in the estimate toggle and reset texts
- Ground the charge-band boundaries in Apple's documented fast-to-80%/trickle
  charging behavior and standard lithium CC/CV charging references
2026-07-02 17:32:29 +02:00
darken afba33dd83 feat(battery): Model charge taper per band, base health on listening-only drain
- Replace the single linear charge rate with a three-band model (bulk / taper /
  trickle) matching lithium CC/CV charging: each band learns its own rate, the
  ETA walks the remaining bands, and the spec seed gets a taper haircut for the
  slow bands — no more over-promising above 80%
- Base the battery-health figure exclusively on drain observed while the pod is
  worn, audio is playing, AND this device is the system's audio sink; idle wear
  previously diluted health upward against Apple's listening ratings
- Listening segments are flushed for persistence the moment their gate breaks
  (playback stop, docking, transport flip) instead of being discarded with the
  cleared window
- The time-remaining estimate keeps learning from all usage — actual current
  drain, idle included, is the right basis for "how long will they last"
2026-07-02 17:32:29 +02:00
darken 5de5dee52f ui(battery): Show charge ETA in the charging chip, make health per-pod
- The gauge line under the percentage now always shows the runtime estimate
  ("if used now") even while charging; the time-until-charged moved into the
  charging chip itself ("Charging · 25m"), so the two can't be confused
- Battery health is now computed and shown per pod (Left/Right paired row in
  the info sheet, mirroring the serial rows) — single-pod listening habits or
  a replaced earbud make the sides genuinely diverge, and a combined figure
  would mask a failing pod
2026-07-02 17:32:29 +02:00
darken ba3e253569 fix(battery): Seed charge ETA from quick-charge specs, hide projection while charging
- While charging, the gauge showed the bare runtime projection when no charge
  rate existed yet — "1% · 4m" next to a charging chip reads as a four-minute
  charge. The line now shows the "until charged" ETA or nothing
- Seed the charge rate from Apple's published quick-charge claims ("5 minutes
  in the case provides around 1 hour of listening"), normalized against the
  rated listening hours, so the ETA is present from the very first charge; a
  live fit still takes over within minutes
2026-07-02 17:32:29 +02:00
darken 4488af0ab7 feat(battery): Add time-until-charged and derived battery health
- While a pod charges, fit its rising level and show the time until full in the
  gauge instead of the runtime estimate; learned charge rates are persisted per
  slot so the ETA appears immediately on later charges
- Suppress the charge ETA during Optimized Battery Charging holds, the final
  trickle phase, and whenever the level stalls longer than one visible step
  should take (granularity-aware: 1% AAP steps vs 10% BLE steps)
- Clear a slot's fit window when its readings switch between AAP and BLE — the
  granularity jump would otherwise read as a fake level step
- Derive a battery-health percentage (median of accumulated drain rates vs the
  model's rated life) and show it in the device info sheet; the info button now
  also appears for BLE-only devices once health data exists
- Tag learned rates with the model they came from so re-pointing a profile at
  different hardware starts learning fresh instead of inheriting foreign rates
- Track how many sessions blended into each learned rate and require three
  before a health figure is shown
2026-07-02 17:32:29 +02:00
darken aae8ad62bd feat(battery): Make estimate per-device and seed from model specs
- Replace the global estimate toggle with a per-device toggle stored on the profile
- Seed the estimate from each model's rated battery life and show it immediately, using
  the rating as a hard upper bound on displayed life while the measured rate converges
- When the ANC mode is unknown, seed from the shorter of a model's ANC-on/off ratings
- Show a projection while charging ("if used now") without ever learning from a rising battery
- Consolidate charge limit, "notify when charged", the estimate toggle and reset into one
  Battery card; the charge notification now works for any live device, not only classic
  audio connections
- Smooth the displayed time asymmetrically (drop fast, rise slow) so a faster-than-rated
  drain stops over-promising within a couple of updates
2026-07-02 17:32:29 +02:00
darken 9606a33f15 feat: Add earbud battery time-remaining estimate 2026-07-01 17:58:12 +02:00
darken f626d70538 fix(monitor): Show worn device in notification and popup 2026-06-30 23:29:34 +02:00
d4rken-org-releaser[bot] a91ef44686 Release: 5.1.10-rc0 2026-06-22 19:00:07 +00:00
darken e3bef4e74b General: Update app translations from Crowdin 2026-06-22 20:42:35 +02:00
darken 358bf5e244 fix(monitor): Drive monitor mode from first addressed profile 2026-06-22 20:16:37 +02:00
169 changed files with 5078 additions and 397 deletions
+1 -1
View File
@@ -1 +1 @@
5.1.9-rc0 50109000
5.2.0-rc0 50200000
@@ -21,11 +21,14 @@ import eu.darken.capod.common.compose.preview.MockPodDataProvider
import eu.darken.capod.common.theming.CapodTheme
import eu.darken.capod.main.ui.overview.OverviewScreen
import eu.darken.capod.main.ui.overview.OverviewViewModel
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import androidx.compose.ui.platform.LocalContext
import eu.darken.capod.main.ui.widget.ComposeWidgetPreview
import eu.darken.capod.main.ui.widget.WidgetConfigurationScreen
import eu.darken.capod.main.ui.widget.WidgetConfigurationViewModel
import eu.darken.capod.main.ui.widget.WidgetRenderState
import eu.darken.capod.main.ui.widget.WidgetTheme
import eu.darken.capod.main.ui.widget.withLocalizedPreviewEstimates
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.profiles.ui.DeviceManagerScreen
import eu.darken.capod.profiles.ui.DeviceManagerViewModel
@@ -52,6 +55,29 @@ internal fun DashboardContent(showAap: Boolean = false) = PreviewWrapper {
MockPodDataProvider.unknownMonitored(),
)
}
// Plausible estimates for the mock battery levels: runtime under each gauge, charge ETA in
// the charging chip of whichever mock pod is charging.
val estimates = if (showAap) {
mapOf(
"preview-dual-aap" to BatteryEstimate(
left = BatteryEstimate.Pod(285, 0.17f, BatteryEstimate.Source.LIVE),
right = BatteryEstimate.Pod(160, 0.17f, BatteryEstimate.Source.LIVE),
),
"preview-single-aap" to BatteryEstimate(
headset = BatteryEstimate.Pod(480, 0.05f, BatteryEstimate.Source.LIVE, minutesUntilCharged = 45),
),
)
} else {
mapOf(
"preview-dual-mixed" to BatteryEstimate(
left = BatteryEstimate.Pod(285, 0.17f, BatteryEstimate.Source.LIVE, minutesUntilCharged = 20),
right = BatteryEstimate.Pod(160, 0.17f, BatteryEstimate.Source.LIVE),
),
"preview-single" to BatteryEstimate(
headset = BatteryEstimate.Pod(1020, 0.05f, BatteryEstimate.Source.LIVE),
),
)
}
OverviewScreen(
state = OverviewViewModel.State(
now = MOCK_NOW,
@@ -65,6 +91,7 @@ internal fun DashboardContent(showAap: Boolean = false) = PreviewWrapper {
),
upgradeInfo = MockPodDataProvider.gplayInfo(isPro = true),
showUnmatchedDevices = false,
batteryEstimates = estimates,
),
onRequestPermission = {},
onBluetoothSettings = {},
@@ -229,7 +256,7 @@ internal fun HomescreenWidgetContent() {
bgColor = MaterialTheme.colorScheme.surface.toArgb(),
textColor = MaterialTheme.colorScheme.onSurface.toArgb(),
iconColor = MaterialTheme.colorScheme.onSurface.toArgb(),
),
).withLocalizedPreviewEstimates(LocalContext.current),
)
}
}
+8 -8
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="foss_upgrade_donate_label">تبرع</string>
<string name="foss_upgrade_alreadydonated_label">أنا متبرّع فعلًا</string>
<string name="foss_upgrade_no_money_label">أنفق كل أموالي على AirPods</string>
<string name="upgrade_foss_preamble">CAPod FOSS مجاني ومفتوح المصدر. إذا وجدته مفيدًا، ففكّر في دعم تطويره للمساعدة في استمرار المشروع.</string>
<string name="upgrade_foss_sponsor_action">دعم المشروع</string>
<string name="upgrade_foss_sponsor_subtitle">لا إعلانات. لا تتبع. لا قيود من Google Play.</string>
<string name="upgrade_foss_sponsor_returned_early">هل عدت فعلًا؟ دعمكم يُبقي CAPod حيًا.</string>
<string name="upgrade_badge_label">البرمجيات الحرة (FOSS)</string>
<string name="foss_upgrade_donate_label">تبرُّع</string>
<string name="foss_upgrade_alreadydonated_label">لقد تبرّعتُ بالفعل</string>
<string name="foss_upgrade_no_money_label">أنفق كل أموالي على سمّاعات AirPods</string>
<string name="upgrade_foss_preamble">برنامج CAPod FOSS مجاني ومفتوح المصدر. إذا وجدتهُ مفيدًا، ففكّر في دعم تطويره للمساعدة في استمرار المشروع.</string>
<string name="upgrade_foss_sponsor_action">دعم التطوير</string>
<string name="upgrade_foss_sponsor_subtitle">لا إعلانات. لا تتبُّع. لا قيود من جوجل بلاي.</string>
<string name="upgrade_foss_sponsor_returned_early">هل عدت بالفعل؟ دعمك يُبقي CAPod حيًّا.</string>
<string name="upgrade_badge_label">البرمجيات الحرة والمفتوحة المصدر</string>
</resources>
+2 -2
View File
@@ -5,7 +5,7 @@
<string name="foss_upgrade_no_money_label">Es iztērēju visu savu naudu AirPods austiņām</string>
<string name="upgrade_foss_preamble">CAPod FOSS ir bezmaksas un atkļējkoda. Ja tā ir noderba, apsveriet attīstības sponsēšanu, lai palīdzētu turpināt projektu.</string>
<string name="upgrade_foss_sponsor_action">Sponsorēt attīstību</string>
<string name="upgrade_foss_sponsor_subtitle">Bez reklamām. Bez sekšanas. Bez Google Play saistībām.</string>
<string name="upgrade_foss_sponsor_returned_early">Jau atpakaļ? Jūsu atbalsts uztur CAPod pie dzīvības.</string>
<string name="upgrade_foss_sponsor_subtitle">Bez reklamām. Bez izsekošanas. Bez Google Play saistībām.</string>
<string name="upgrade_foss_sponsor_returned_early">Jau atpakaļ? Tavs atbalsts uztur CAPod esamību.</string>
<string name="upgrade_badge_label">FOSS</string>
</resources>
+7 -7
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="upgrades_gplay_unavailable_error">خدمات Google Play غير متوفّرة.</string>
<string name="upgrades_no_purchases_found_check_account">لم يتم العثور على أي مشتريات. هل تستخدم الحساب الصحيح؟</string>
<string name="upgrades_gplay_billing_error_label">خطأ في Google Play</string>
<string name="upgrades_gplay_billing_error_description">حدث خطأ في Google Play. يُرجى المحاولة لاحقًا أو إعادة تشغيل هاتفك.\n\nخطأ: %s</string>
<string name="upgrades_gplay_billing_result_error_label">خطأ في فوترة Google Play</string>
<string name="upgrades_gplay_billing_result_error_description">حدث خطأ أثناء طلب تفاصيل عملية الشراء من Google Play. يُرجى مسح ذاكرة التخزين المؤقّت لـ Google Play وإعادة تشغيل هاتفك.\n\nخطأ %s</string>
<string name="upgrade_restore_action">استعادة المشتريات</string>
<string name="upgrades_gplay_unavailable_error">خدمات جوجل بلاي غير متوفّرة.</string>
<string name="upgrades_no_purchases_found_check_account">لم يُعثَر على أيّ مشتريات. هل تستخدم الحساب الصحيح؟</string>
<string name="upgrades_gplay_billing_error_label">خطأ في جوجل بلاي</string>
<string name="upgrades_gplay_billing_error_description">حدث خطأ في سوق جوجل بلاي. يُرجى المحاولة لاحقًا أو إعادة تشغيل هاتفك.\n\nالخطأ: %s</string>
<string name="upgrades_gplay_billing_result_error_label">خطأ في فوترة جوجل بلاي</string>
<string name="upgrades_gplay_billing_result_error_description">حدث خطأ أثناء طلب تفاصيل عملية الشراء من سوق جوجل بلاي. يُرجى مسح ذاكرة التخزين المؤقّت لسوق جوجل بلاي وإعادة تشغيل هاتفك.\n\nالخطأ %s</string>
<string name="upgrade_restore_action">استعادة عملية الشراء</string>
<string name="upgrade_badge_label">النسخة الاحترافية</string>
</resources>
+3 -3
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="upgrades_gplay_unavailable_error">Google Play pakalpojumi nav pieejami.</string>
<string name="upgrades_no_purchases_found_check_account">Nav atrasts neviens pirkums. Vai izmantojat pareizo kontu?</string>
<string name="upgrades_no_purchases_found_check_account">Nav atrasts neviens pirkums. Vai izmanto pareizo kontu?</string>
<string name="upgrades_gplay_billing_error_label">Google Play kļūda</string>
<string name="upgrades_gplay_billing_error_description">Google Play radās kļūda. Lūdzu, mēģiniet vēlreiz vēlāk vai pārstartējiet tālruni.\n\nKļūda: %s</string>
<string name="upgrades_gplay_billing_error_description">Google Play radās kļūda. Lūgums mēģināt vēlreiz vēlāk vai pārsāknēt tālruni.\n\nKļūda: %s</string>
<string name="upgrades_gplay_billing_result_error_label">Google Play norēķinu kļūda</string>
<string name="upgrades_gplay_billing_result_error_description">Radās kļūda, pieprasot no Google Play jūsu pirkuma informāciju. Notīriet Google Play kešatmiņu un pārstartējiet tālruni.\n\nKļūda %s</string>
<string name="upgrades_gplay_billing_result_error_description">Radās kļūda, pieprasot no Google Play pirkuma informāciju. Jāiztīra Google Play kešatmiņa un pārsāknē tālrunis.\n\nKļūda %s</string>
<string name="upgrade_restore_action">Atjaunot pirkumu</string>
<string name="upgrade_badge_label">Pro</string>
</resources>
+14 -2
View File
@@ -15,12 +15,16 @@ import eu.darken.capod.common.upgrade.UpgradeRepo
import eu.darken.capod.main.ui.widget.WidgetManager
import eu.darken.capod.main.ui.widget.toWidgetKey
import eu.darken.capod.monitor.core.DeviceMonitor
import eu.darken.capod.monitor.core.battery.BatteryEstimator
import eu.darken.capod.monitor.core.battery.displayKey
import eu.darken.capod.monitor.core.battery.estimateFor
import eu.darken.capod.monitor.core.devicesWithProfiles
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@@ -36,6 +40,7 @@ open class App : Application() {
@Inject lateinit var deviceMonitor: DeviceMonitor
@Inject lateinit var widgetManager: WidgetManager
@Inject lateinit var upgradeRepo: UpgradeRepo
@Inject lateinit var batteryEstimator: BatteryEstimator
@Inject @AppScope lateinit var appScope: CoroutineScope
override fun onCreate() {
@@ -62,8 +67,15 @@ open class App : Application() {
appScope.launch { widgetManager.refreshWidgets() }
deviceMonitor.devicesWithProfiles()
.distinctUntilChangedBy { devices -> devices.map { it.toWidgetKey() } }
combine(
deviceMonitor.devicesWithProfiles(),
batteryEstimator.estimates,
) { devices, estimates -> devices to estimates }
// Estimate display values can change while the device key is stable (e.g. a charge
// ETA gets suppressed after a stall) — refresh on those too, but only on visible ones.
.distinctUntilChangedBy { (devices, estimates) ->
devices.map { device -> device.toWidgetKey() to estimates.estimateFor(device)?.displayKey(device) }
}
.throttleLatest(1000)
.onEach {
log(TAG, VERBOSE) { "Devices changed, refreshing widgets." }
@@ -212,6 +212,37 @@ object MockPodDataProvider {
),
)
/**
* Dual pods with a fully-populated AAP session: ANC on, both pods in-ear, left pod as the
* primary (microphone) pod. Used to preview the card with all status flags AND a battery estimate.
*/
fun dualPodFullyLoaded(): PodDevice = PodDevice(
profileId = "preview-dual-full",
label = "My AirPods Pro",
ble = airPodsProWithKeys(),
aap = AapPodState(
connectionState = AapPodState.ConnectionState.READY,
settings = mapOf(
AapSetting.AncMode::class to AapSetting.AncMode(
current = AapSetting.AncMode.Value.ON,
supported = listOf(
AapSetting.AncMode.Value.OFF,
AapSetting.AncMode.Value.ON,
AapSetting.AncMode.Value.TRANSPARENCY,
AapSetting.AncMode.Value.ADAPTIVE,
),
),
AapSetting.EarDetection::class to AapSetting.EarDetection(
primaryPod = AapSetting.EarDetection.PodPlacement.IN_EAR,
secondaryPod = AapSetting.EarDetection.PodPlacement.IN_EAR,
),
AapSetting.PrimaryPod::class to AapSetting.PrimaryPod(
pod = AapSetting.PrimaryPod.Pod.LEFT,
),
),
),
)
/** Dual pods with a profile that has reaction toggles set — used for the Reactions screenshot. */
fun dualPodMonitoredWithReactions(): PodDevice = PodDevice(
profileId = "preview-dual-reactions",
@@ -459,7 +490,6 @@ private class MockSingleBlePodSnapshot(
override val isBeingWorn: Boolean = _isBeingWorn
}
@Suppress("PropertyName")
private class MockSingleAppleBlePodSnapshot(
private val _model: PodModel,
private val _label: String,
@@ -498,7 +528,6 @@ private class MockSingleAppleBlePodSnapshot(
override val isBeingWorn: Boolean = _isBeingWorn
}
@Suppress("PropertyName")
private class MockDualAppleBlePodSnapshot(
private val _model: PodModel,
private val _label: String,
@@ -43,6 +43,7 @@ import eu.darken.capod.common.settings.SettingsInfoBox
import eu.darken.capod.common.settings.SettingsSection
import eu.darken.capod.main.ui.devicesettings.cards.AapUnavailableCard
import eu.darken.capod.main.ui.devicesettings.cards.BatteryCard
import eu.darken.capod.main.ui.devicesettings.cards.BatteryHealthTexts
import eu.darken.capod.main.ui.devicesettings.cards.ControlsCard
import eu.darken.capod.main.ui.devicesettings.cards.DeviceInfoCard
import eu.darken.capod.main.ui.devicesettings.cards.NoiseControlCard
@@ -181,6 +182,8 @@ fun DeviceSettingsScreenHost(
onChargedSlotScopeChange = { vm.setChargedSlotScope(it) },
onOpenIssueTracker = { vm.openIssueTracker() },
onOpenAapTracker = { vm.openAapCompatibilityTracker() },
onBatteryEstimateEnabledChange = { vm.setBatteryEstimateEnabled(it) },
onResetBatteryEstimate = { vm.resetBatteryEstimate() },
)
}
@@ -224,6 +227,8 @@ fun DeviceSettingsScreen(
onChargedSlotScopeChange: (ChargedSlotScope) -> Unit = {},
onOpenIssueTracker: () -> Unit = {},
onOpenAapTracker: () -> Unit = {},
onBatteryEstimateEnabledChange: (Boolean) -> Unit = {},
onResetBatteryEstimate: () -> Unit = {},
) {
val device = state.device
val features = device?.model?.features
@@ -291,6 +296,23 @@ fun DeviceSettingsScreen(
info = info,
labels = rememberDeviceInfoDetailLabels(),
formatDate = { instant -> dateFormatter.format(instant) },
batteryHealth = when {
state.batteryHealth != null -> BatteryHealthTexts(
left = state.batteryHealth.left?.let {
stringResource(R.string.device_settings_info_battery_health_value, it)
},
right = state.batteryHealth.right?.let {
stringResource(R.string.device_settings_info_battery_health_value, it)
},
headset = state.batteryHealth.headset?.let {
stringResource(R.string.device_settings_info_battery_health_value, it)
},
)
state.batteryHealthPending -> BatteryHealthTexts(
pending = stringResource(R.string.device_settings_info_battery_health_pending),
)
else -> null
},
)
DeviceInfoCard(
deviceInfo = device.deviceInfo,
@@ -363,10 +385,26 @@ fun DeviceSettingsScreen(
onAutoConnectConditionChange = onAutoConnectConditionChange,
onShowPopUpOnCaseOpenChange = onShowPopUpOnCaseOpenChange,
onShowPopUpOnConnectionChange = onShowPopUpOnConnectionChange,
onOpenIssueTracker = onOpenIssueTracker,
)
}
}
// ── Battery (time-remaining estimate for any live device; charge limit needs AAP) ──
if (device != null && features != null && device.isLive) {
item("battery_section") {
BatteryCard(
device = device,
features = features,
isPro = isPro,
chargeCapControlEnabled = enabled,
estimateEnabled = state.batteryEstimateEnabled,
onDynamicEndOfChargeChange = onDynamicEndOfChargeChange,
onNotifyWhenChargedChange = onNotifyWhenChargedChange,
onChargedThresholdChange = onChargedThresholdChange,
onChargedSlotScopeChange = onChargedSlotScopeChange,
onOpenIssueTracker = onOpenIssueTracker,
onEstimateEnabledChange = onBatteryEstimateEnabledChange,
onResetEstimate = onResetBatteryEstimate,
)
}
}
@@ -439,18 +477,6 @@ fun DeviceSettingsScreen(
}
}
// ── Battery ──────────────────────────────────
if (features.hasDynamicEndOfCharge && device.dynamicEndOfCharge != null) {
item("battery_section") {
BatteryCard(
device = device,
features = features,
enabled = enabled,
onDynamicEndOfChargeChange = onDynamicEndOfChargeChange,
)
}
}
// ── Connections ───────────────────────────────
val connectedDevices = device.connectedDevices
if (connectedDevices != null && connectedDevices.devices.isNotEmpty()) {
@@ -22,6 +22,10 @@ import eu.darken.capod.main.core.MonitorMode
import eu.darken.capod.monitor.core.DeviceMonitor
import eu.darken.capod.monitor.core.MonitorModeResolver
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryDrainStore
import eu.darken.capod.monitor.core.battery.BatteryEstimator
import eu.darken.capod.monitor.core.battery.BatteryHealth
import eu.darken.capod.monitor.core.battery.DrainProfile
import eu.darken.capod.monitor.core.resolvedAncCycleMask
import eu.darken.capod.pods.core.apple.aap.AapConnectionManager
import eu.darken.capod.pods.core.apple.aap.protocol.AapCommand
@@ -55,6 +59,8 @@ class DeviceSettingsViewModel @Inject constructor(
private val upgradeRepo: UpgradeRepo,
private val bluetoothManager: BluetoothManager2,
private val profilesRepo: DeviceProfilesRepo,
private val batteryEstimator: BatteryEstimator,
private val drainStore: BatteryDrainStore,
private val monitorModeResolver: MonitorModeResolver,
private val nudgeCapabilityStore: NudgeCapabilityStore,
private val timeSource: TimeSource,
@@ -122,6 +128,7 @@ class DeviceSettingsViewModel @Inject constructor(
monitorModeResolver.effectiveMode,
profilesRepo.profiles,
nudgeCapabilityStore.availability,
drainStore.profiles,
) { args ->
val device = args[1] as PodDevice?
val upgrade = args[2] as UpgradeRepo.Info
@@ -134,9 +141,12 @@ class DeviceSettingsViewModel @Inject constructor(
@Suppress("UNCHECKED_CAST")
val profiles = args[6] as List<eu.darken.capod.profiles.core.DeviceProfile>
val nudgeAvailability = args[7] as NudgeAvailability
val stemActions = profiles.filterIsInstance<AppleDeviceProfile>()
@Suppress("UNCHECKED_CAST")
val drainProfiles = args[8] as Map<ProfileId, DrainProfile>
val appleProfile = profiles.filterIsInstance<AppleDeviceProfile>()
.firstOrNull { it.id == profileId }
?.stemActions
val stemActions = appleProfile?.stemActions
val connectedAddresses = connectedDevices.map { it.address }.toSet()
val systemBtName = device?.address?.let { addr ->
try {
@@ -158,6 +168,19 @@ class DeviceSettingsViewModel @Inject constructor(
(it.leftLong !is StemAction.None && it.leftLong !is StemAction.CycleAnc) ||
(it.rightLong !is StemAction.None && it.rightLong !is StemAction.CycleAnc)
} == true,
batteryEstimateEnabled = appleProfile?.batteryEstimateEnabled ?: true,
// Health rides on the same learned data as the estimate — the per-device toggle
// governs both.
batteryHealth = device
?.takeIf { appleProfile?.batteryEstimateEnabled ?: true }
?.let { BatteryHealth.estimate(drainProfiles[profileId], it.model) },
// A model with a rating WILL eventually produce a health figure — surface the
// feature as "still determining" until the listening sessions accumulate. Without
// a paired device the listening gate can never open, so no promise is made.
batteryHealthPending = device != null &&
(appleProfile?.batteryEstimateEnabled ?: true) &&
device.model.batterySpec != null &&
device.hasSelectedPairedDevice,
)
}
}.asLiveState()
@@ -183,6 +206,12 @@ class DeviceSettingsViewModel @Inject constructor(
val monitorMode: MonitorMode = MonitorMode.AUTOMATIC,
val systemBluetoothName: String? = null,
val hasCustomLongPressStemAction: Boolean = false,
val batteryEstimateEnabled: Boolean = true,
/** Derived per-pod battery health (1..100 each), or null when there isn't enough learned data. */
val batteryHealth: BatteryHealth.PerPod? = null,
/** True when health CAN be derived for this device (rated model, feature on) — shows the
* "still determining" placeholder while [batteryHealth] is null. */
val batteryHealthPending: Boolean = false,
) {
val reactions: ReactionConfig get() = device?.reactions ?: ReactionConfig()
}
@@ -473,6 +502,19 @@ class DeviceSettingsViewModel @Inject constructor(
updateProfileNow { it.copy(conversationVolumeReduction = percent) }
}
// ── Battery estimate (per-profile) ───────────────────────────────────────
fun setBatteryEstimateEnabled(enabled: Boolean) = launch {
log(TAG, INFO) { "setBatteryEstimateEnabled($enabled)" }
updateProfileNow { it.copy(batteryEstimateEnabled = enabled) }
}
fun resetBatteryEstimate() = launch {
val profileId = targetProfileId.value ?: return@launch
log(TAG, INFO) { "resetBatteryEstimate($profileId)" }
batteryEstimator.reset(profileId)
}
fun navToPressControls() = launch {
log(TAG, INFO) { "navToPressControls()" }
@@ -2,41 +2,176 @@ package eu.darken.capod.main.ui.devicesettings.cards
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.BatteryChargingFull
import androidx.compose.material.icons.twotone.NotificationsActive
import androidx.compose.material.icons.twotone.RestartAlt
import androidx.compose.material.icons.twotone.Schedule
import androidx.compose.material.icons.twotone.Workspaces
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.darken.capod.R
import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.common.settings.SettingsBaseItem
import eu.darken.capod.common.settings.SettingsSection
import eu.darken.capod.common.settings.SettingsSliderItem
import eu.darken.capod.common.settings.SettingsSwitchItem
import eu.darken.capod.main.ui.devicesettings.dialogs.ChargedSlotScopeDialog
import eu.darken.capod.main.ui.devicesettings.previewFullState
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
import eu.darken.capod.profiles.core.ReactionConfig
import eu.darken.capod.reaction.core.charged.ChargedSlotScope
/**
* Apple's "Optimized Charge Limit" toggle (AAP setting 0x3B), shown for models that advertise
* [PodModel.Features.hasDynamicEndOfCharge]. The wire format follows the Apple-bool convention
* used by every other boolean setting.
* The device's "Battery" settings, grouping everything charge/battery-related. Time remaining &
* battery health lead, then a divider, then the charging-side settings:
* - The time-remaining/health estimate: a per-device toggle (disabling pauses/hides without
* discarding learned data) and a reset that wipes the learned drain, charge and health data so
* it starts over from the model's rated battery life.
* - Apple's "Optimized Charge Limit" (AAP setting 0x3B) — only for models that advertise
* [PodModel.Features.hasDynamicEndOfCharge] over an active AAP session.
* - "Notify when charged" (a per-device reaction) — fires purely off observed charging state, so it
* works for any live device (BLE or AAP), not just when the phone is the audio source.
*
* Each row is gated independently; the whole card hides when nothing applies.
*/
@Composable
internal fun BatteryCard(
device: PodDevice,
features: PodModel.Features,
enabled: Boolean,
isPro: Boolean,
chargeCapControlEnabled: Boolean,
estimateEnabled: Boolean,
onDynamicEndOfChargeChange: (Boolean) -> Unit = {},
onNotifyWhenChargedChange: (Boolean) -> Unit = {},
onChargedThresholdChange: (Int) -> Unit = {},
onChargedSlotScopeChange: (ChargedSlotScope) -> Unit = {},
onEstimateEnabledChange: (Boolean) -> Unit = {},
onResetEstimate: () -> Unit = {},
) {
if (!features.hasDynamicEndOfCharge) return
val cap = device.dynamicEndOfCharge ?: return
val chargeCap = device.dynamicEndOfCharge?.takeIf { features.hasDynamicEndOfCharge }
// Per-profile controls (charge notification + estimate) apply to any live, profile-matched device.
val showLiveControls = device.isLive && device.profileId != null
if (chargeCap == null && !showLiveControls) return
val reactions = device.reactions
var showResetConfirm by remember { mutableStateOf(false) }
var showChargedScopeDialog by remember { mutableStateOf(false) }
SettingsSection(title = stringResource(R.string.device_settings_category_battery_label)) {
SettingsSwitchItem(
icon = Icons.TwoTone.BatteryChargingFull,
title = stringResource(R.string.device_settings_charge_cap_label),
subtitle = stringResource(R.string.device_settings_charge_cap_description),
checked = cap.enabled,
onCheckedChange = onDynamicEndOfChargeChange,
enabled = enabled,
// Time remaining & battery health lead — the headline feature. Charging-side settings
// (Apple's charge limit, the charged notification) follow below the divider.
if (showLiveControls) {
SettingsSwitchItem(
icon = Icons.TwoTone.Schedule,
title = stringResource(R.string.device_battery_estimate_toggle_label),
subtitle = stringResource(R.string.device_battery_estimate_card_desc),
checked = estimateEnabled,
onCheckedChange = onEstimateEnabledChange,
)
SettingsBaseItem(
icon = Icons.TwoTone.RestartAlt,
title = stringResource(R.string.device_battery_estimate_reset_action),
subtitle = stringResource(R.string.device_battery_estimate_reset_desc),
onClick = { showResetConfirm = true },
)
// Same divider style the sibling device-settings cards use (see ReactionsCard).
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
color = MaterialTheme.colorScheme.outlineVariant,
)
}
if (chargeCap != null) {
SettingsSwitchItem(
icon = Icons.TwoTone.BatteryChargingFull,
title = stringResource(R.string.device_settings_charge_cap_label),
subtitle = stringResource(R.string.device_settings_charge_cap_description),
checked = chargeCap.enabled,
onCheckedChange = onDynamicEndOfChargeChange,
enabled = chargeCapControlEnabled,
)
}
if (showLiveControls) {
SettingsSwitchItem(
icon = Icons.TwoTone.NotificationsActive,
title = stringResource(R.string.settings_charged_notification_label),
subtitle = stringResource(R.string.settings_charged_notification_description),
checked = reactions.notifyWhenCharged,
onCheckedChange = onNotifyWhenChargedChange,
requiresUpgrade = !isPro,
)
if (reactions.notifyWhenCharged) {
var thresholdValue by remember(reactions.chargedThreshold) {
mutableIntStateOf(reactions.chargedThreshold)
}
SettingsSliderItem(
icon = Icons.TwoTone.BatteryChargingFull,
title = stringResource(R.string.settings_charged_threshold_label),
value = thresholdValue.toFloat(),
onValueChange = { thresholdValue = it.toInt() },
onValueChangeFinished = { onChargedThresholdChange(thresholdValue) },
valueRange = ReactionConfig.MIN_CHARGED_THRESHOLD.toFloat()..
ReactionConfig.MAX_CHARGED_THRESHOLD.toFloat(),
steps = (ReactionConfig.MAX_CHARGED_THRESHOLD - ReactionConfig.MIN_CHARGED_THRESHOLD) /
ReactionConfig.CHARGED_THRESHOLD_STEP - 1,
valueLabel = { "${it.toInt()}%" },
)
if (features.hasCase) {
SettingsBaseItem(
icon = Icons.TwoTone.Workspaces,
title = stringResource(R.string.settings_charged_scope_label),
subtitle = stringResource(reactions.chargedSlotScope.labelRes),
onClick = { showChargedScopeDialog = true },
)
}
}
}
}
if (showChargedScopeDialog) {
ChargedSlotScopeDialog(
current = reactions.chargedSlotScope,
onSelect = {
onChargedSlotScopeChange(it)
showChargedScopeDialog = false
},
onDismiss = { showChargedScopeDialog = false },
)
}
if (showResetConfirm) {
AlertDialog(
onDismissRequest = { showResetConfirm = false },
title = { Text(text = stringResource(R.string.device_battery_estimate_reset_confirm_title)) },
text = { Text(text = stringResource(R.string.device_battery_estimate_reset_confirm_message)) },
confirmButton = {
TextButton(
onClick = {
showResetConfirm = false
onResetEstimate()
},
) {
Text(text = stringResource(R.string.device_battery_estimate_reset_action))
}
},
dismissButton = {
TextButton(onClick = { showResetConfirm = false }) {
Text(text = stringResource(R.string.general_cancel_action))
}
},
)
}
}
@@ -48,8 +183,10 @@ private fun BatteryCardEnabledPreview() = PreviewWrapper {
val device = state.device!!
BatteryCard(
device = device,
features = PodModel.Features(hasDynamicEndOfCharge = true),
enabled = true,
features = PodModel.Features(hasDynamicEndOfCharge = true, hasCase = true),
isPro = true,
chargeCapControlEnabled = true,
estimateEnabled = true,
)
}
@@ -60,7 +197,9 @@ private fun BatteryCardDisabledPreview() = PreviewWrapper {
val device = state.device!!
BatteryCard(
device = device,
features = PodModel.Features(hasDynamicEndOfCharge = true),
enabled = false,
features = PodModel.Features(hasDynamicEndOfCharge = true, hasCase = true),
isPro = false,
chargeCapControlEnabled = false,
estimateEnabled = false,
)
}
@@ -18,6 +18,9 @@ internal fun rememberDeviceInfoDetailLabels() = DeviceInfoDetailLabels(
rightSerial = stringResource(R.string.device_settings_info_right_serial_label),
leftBonded = stringResource(R.string.device_settings_info_left_bonded_label),
rightBonded = stringResource(R.string.device_settings_info_right_bonded_label),
batteryHealth = stringResource(R.string.device_settings_info_battery_health_label),
leftBatteryHealth = stringResource(R.string.device_settings_info_battery_health_left_label),
rightBatteryHealth = stringResource(R.string.device_settings_info_battery_health_right_label),
)
internal data class DeviceInfoDetailLabels(
@@ -31,14 +34,52 @@ internal data class DeviceInfoDetailLabels(
val rightSerial: String,
val leftBonded: String,
val rightBonded: String,
val batteryHealth: String,
val leftBatteryHealth: String,
val rightBatteryHealth: String,
)
/**
* Pre-formatted per-pod health values ("~85%") for the info sheet; null slots are omitted.
* [pending] is a placeholder shown under the health label while NO value exists yet — the feature
* stays discoverable while the estimator is still accumulating listening sessions.
*/
internal data class BatteryHealthTexts(
val left: String? = null,
val right: String? = null,
val headset: String? = null,
val pending: String? = null,
)
private fun healthItems(health: BatteryHealthTexts?, labels: DeviceInfoDetailLabels): List<DeviceDetailItem> {
if (health == null) return emptyList()
return buildList {
when {
health.left != null && health.right != null -> add(
DeviceDetailItem.Paired(
start = DeviceDetailItem.Single(labels.leftBatteryHealth, health.left),
end = DeviceDetailItem.Single(labels.rightBatteryHealth, health.right),
)
)
health.left != null -> add(DeviceDetailItem.Single(labels.leftBatteryHealth, health.left))
health.right != null -> add(DeviceDetailItem.Single(labels.rightBatteryHealth, health.right))
}
health.headset?.let { add(DeviceDetailItem.Single(labels.batteryHealth, it)) }
if (isEmpty() && health.pending != null) {
add(DeviceDetailItem.Single(labels.batteryHealth, health.pending))
}
}
}
internal fun buildDeviceInfoDetailItems(
info: AapDeviceInfo?,
labels: DeviceInfoDetailLabels,
batteryHealth: BatteryHealthTexts? = null,
formatDate: (Instant) -> String,
): List<DeviceDetailItem> {
if (info == null) return emptyList()
// Battery health is derived locally, so it's available (and shows the info button) even for
// BLE-only devices that never produce an AAP device-info response.
if (info == null) return healthItems(batteryHealth, labels)
return buildList {
info.manufacturer.takeIf { it.isNotBlank() }?.let {
add(DeviceDetailItem.Single(labels.manufacturer, it))
@@ -82,5 +123,6 @@ internal fun buildDeviceInfoDetailItems(
leftBonded != null -> add(DeviceDetailItem.Single(labels.leftBonded, leftBonded))
rightBonded != null -> add(DeviceDetailItem.Single(labels.rightBonded, rightBonded))
}
addAll(healthItems(batteryHealth, labels))
}
}
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.twotone.Message
import androidx.compose.material.icons.automirrored.twotone.VolumeDown
import androidx.compose.material.icons.twotone.BatteryChargingFull
import androidx.compose.material.icons.twotone.BluetoothConnected
import androidx.compose.material.icons.twotone.Hearing
import androidx.compose.material.icons.twotone.LooksOne
@@ -39,7 +38,6 @@ import eu.darken.capod.common.settings.SettingsSection
import eu.darken.capod.common.settings.SettingsSliderItem
import eu.darken.capod.common.settings.SettingsSwitchItem
import eu.darken.capod.main.ui.devicesettings.dialogs.AutoConnectConditionDialog
import eu.darken.capod.main.ui.devicesettings.dialogs.ChargedSlotScopeDialog
import eu.darken.capod.main.ui.devicesettings.dialogs.ConversationActionDialog
import eu.darken.capod.main.ui.devicesettings.previewFullState
import eu.darken.capod.monitor.core.PodDevice
@@ -47,7 +45,6 @@ import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
import eu.darken.capod.profiles.core.ReactionConfig
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
import eu.darken.capod.reaction.core.charged.ChargedSlotScope
import eu.darken.capod.reaction.core.conversation.ConversationAction
@Composable
@@ -67,9 +64,6 @@ internal fun ReactionsCard(
onAutoConnectConditionChange: (AutoConnectCondition) -> Unit = {},
onShowPopUpOnCaseOpenChange: (Boolean) -> Unit = {},
onShowPopUpOnConnectionChange: (Boolean) -> Unit = {},
onNotifyWhenChargedChange: (Boolean) -> Unit = {},
onChargedThresholdChange: (Int) -> Unit = {},
onChargedSlotScopeChange: (ChargedSlotScope) -> Unit = {},
onOpenIssueTracker: () -> Unit = {},
) {
val reactions = device.reactions
@@ -77,7 +71,6 @@ internal fun ReactionsCard(
var showAutoConnectConditionDialog by remember { mutableStateOf(false) }
var showConversationActionDialog by remember { mutableStateOf(false) }
var showChargedScopeDialog by remember { mutableStateOf(false) }
SettingsSection(title = stringResource(R.string.settings_reaction_label)) {
if (features.hasEarDetection) {
@@ -258,40 +251,6 @@ internal fun ReactionsCard(
text = stringResource(R.string.settings_popup_info_not_in_app),
)
}
ReactionsDivider()
SettingsSwitchItem(
icon = Icons.TwoTone.BatteryChargingFull,
title = stringResource(R.string.settings_charged_notification_label),
subtitle = stringResource(R.string.settings_charged_notification_description),
checked = reactions.notifyWhenCharged,
onCheckedChange = onNotifyWhenChargedChange,
requiresUpgrade = !isPro,
)
if (reactions.notifyWhenCharged) {
var thresholdValue by remember(reactions.chargedThreshold) {
mutableIntStateOf(reactions.chargedThreshold)
}
SettingsSliderItem(
icon = Icons.TwoTone.BatteryChargingFull,
title = stringResource(R.string.settings_charged_threshold_label),
value = thresholdValue.toFloat(),
onValueChange = { thresholdValue = it.toInt() },
onValueChangeFinished = { onChargedThresholdChange(thresholdValue) },
valueRange = ReactionConfig.MIN_CHARGED_THRESHOLD.toFloat()..
ReactionConfig.MAX_CHARGED_THRESHOLD.toFloat(),
steps = (ReactionConfig.MAX_CHARGED_THRESHOLD - ReactionConfig.MIN_CHARGED_THRESHOLD) /
ReactionConfig.CHARGED_THRESHOLD_STEP - 1,
valueLabel = { "${it.toInt()}%" },
)
if (features.hasCase) {
SettingsBaseItem(
icon = Icons.TwoTone.Workspaces,
title = stringResource(R.string.settings_charged_scope_label),
subtitle = stringResource(reactions.chargedSlotScope.labelRes),
onClick = { showChargedScopeDialog = true },
)
}
}
}
if (showConversationActionDialog) {
@@ -305,17 +264,6 @@ internal fun ReactionsCard(
)
}
if (showChargedScopeDialog) {
ChargedSlotScopeDialog(
current = reactions.chargedSlotScope,
onSelect = {
onChargedSlotScopeChange(it)
showChargedScopeDialog = false
},
onDismiss = { showChargedScopeDialog = false },
)
}
if (showAutoConnectConditionDialog) {
AutoConnectConditionDialog(
current = reactions.autoConnectCondition,
@@ -65,6 +65,7 @@ import eu.darken.capod.main.ui.overview.cards.TroubleshootSuggestionCard
import eu.darken.capod.main.ui.overview.cards.UnknownPodDeviceCard
import eu.darken.capod.main.ui.overview.cards.UnmatchedDevicesCard
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
import java.time.Instant
@@ -333,6 +334,7 @@ fun OverviewScreen(
showDebug = state.isDebug,
now = state.now,
isCollapsed = isCollapsed,
batteryEstimate = state.estimateFor(device),
onToggleCollapse = if (isToggleable) {
{ onToggleDeviceExpansion(device) }
} else null,
@@ -407,6 +409,7 @@ private fun PodDeviceCard(
showDebug: Boolean,
now: Instant,
isCollapsed: Boolean = false,
batteryEstimate: BatteryEstimate? = null,
onToggleCollapse: (() -> Unit)? = null,
onAncModeChange: (AapSetting.AncMode.Value) -> Unit,
onUpgrade: () -> Unit,
@@ -417,6 +420,7 @@ private fun PodDeviceCard(
device.hasDualPods -> DualPodsCard(
device = device, isPro = isPro, showDebug = showDebug, now = now,
isCollapsed = isCollapsed,
batteryEstimate = batteryEstimate,
onToggleCollapse = onToggleCollapse,
onAncModeChange = onAncModeChange,
onUpgrade = onUpgrade,
@@ -426,6 +430,7 @@ private fun PodDeviceCard(
device.model != PodModel.UNKNOWN -> SinglePodsCard(
device = device, isPro = isPro, showDebug = showDebug, now = now,
isCollapsed = isCollapsed,
batteryEstimate = batteryEstimate,
onToggleCollapse = onToggleCollapse,
onAncModeChange = onAncModeChange,
onUpgrade = onUpgrade,
@@ -444,7 +449,7 @@ private fun OverviewScreenWithDevicesPreview() = PreviewWrapper {
now = SystemTimeSource.now(),
permissions = emptySet(),
devices = listOf(
MockPodDataProvider.dualPodMonitoredMixed(),
MockPodDataProvider.dualPodFullyLoaded(),
MockPodDataProvider.singlePodMonitored(),
MockPodDataProvider.unknownMonitored(),
),
@@ -457,6 +462,16 @@ private fun OverviewScreenWithDevicesPreview() = PreviewWrapper {
upgradeInfo = MockPodDataProvider.fossInfo(),
showUnmatchedDevices = false,
showReactionsHint = true,
userExpandedIds = setOf("preview-single"),
batteryEstimates = mapOf(
"preview-dual-full" to BatteryEstimate(
left = BatteryEstimate.Pod(minutesRemaining = 135, fractionPerHour = 0.18f, source = BatteryEstimate.Source.LIVE),
right = BatteryEstimate.Pod(minutesRemaining = 122, fractionPerHour = 0.20f, source = BatteryEstimate.Source.LIVE),
),
"preview-single" to BatteryEstimate(
headset = BatteryEstimate.Pod(minutesRemaining = 320, fractionPerHour = 0.09f, source = BatteryEstimate.Source.LEARNED),
),
),
),
onRequestPermission = {},
onBluetoothSettings = {},
@@ -23,6 +23,9 @@ import eu.darken.capod.main.core.PermissionTool
import eu.darken.capod.monitor.core.DeviceMonitor
import eu.darken.capod.monitor.core.MonitorModeResolver
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.monitor.core.battery.BatteryEstimator
import eu.darken.capod.monitor.core.battery.estimateFor
import eu.darken.capod.monitor.core.tierRank
import eu.darken.capod.monitor.core.worker.MonitorControl
import eu.darken.capod.pods.core.apple.aap.AapConnectionManager
@@ -59,6 +62,7 @@ class OverviewViewModel @Inject constructor(
private val profilesRepo: DeviceProfilesRepo,
private val aapManager: AapConnectionManager,
private val monitorModeResolver: MonitorModeResolver,
private val batteryEstimator: BatteryEstimator,
private val timeSource: TimeSource,
) : ViewModel4(dispatcherProvider) {
@@ -85,6 +89,7 @@ class OverviewViewModel @Inject constructor(
val reactionsHintDismissed: Boolean,
val hideUnmatchedDevices: Boolean,
val showTroubleshootSuggestion: Boolean,
val batteryEstimates: Map<String, BatteryEstimate>,
)
/**
@@ -120,8 +125,14 @@ class OverviewViewModel @Inject constructor(
generalSettings.reactionsHintDismissed.flow,
generalSettings.hideUnmatchedDevices.flow,
troubleshootSuggestion,
) { reactionsHintDismissed, hideUnmatched, showTroubleshootSuggestion ->
OverviewUiSettings(reactionsHintDismissed, hideUnmatched, showTroubleshootSuggestion)
batteryEstimator.estimates,
) { reactionsHintDismissed, hideUnmatched, showTroubleshootSuggestion, batteryEstimates ->
OverviewUiSettings(
reactionsHintDismissed = reactionsHintDismissed,
hideUnmatchedDevices = hideUnmatched,
showTroubleshootSuggestion = showTroubleshootSuggestion,
batteryEstimates = batteryEstimates,
)
}
init {
@@ -210,6 +221,7 @@ class OverviewViewModel @Inject constructor(
showReactionsHint = hadLegacyReactionData && !uiSettings.reactionsHintDismissed,
hideUnmatchedDevices = uiSettings.hideUnmatchedDevices,
showTroubleshootSuggestion = uiSettings.showTroubleshootSuggestion,
batteryEstimates = uiSettings.batteryEstimates,
)
}.asLiveState()
@@ -228,9 +240,16 @@ class OverviewViewModel @Inject constructor(
val showReactionsHint: Boolean = false,
val hideUnmatchedDevices: Boolean = false,
val showTroubleshootSuggestion: Boolean = false,
val batteryEstimates: Map<String, BatteryEstimate> = emptyMap(),
) {
val isScanBlocked: Boolean get() = permissions.any { it.isScanBlocking }
/**
* Time-remaining estimate to show for [device], or null when the device has the estimate
* disabled, isn't live (no estimate for cached/offline cards), or no rate is available yet.
*/
fun estimateFor(device: PodDevice): BatteryEstimate? = batteryEstimates.estimateFor(device)
/** Profile list order used as tiebreaker within each connection tier. */
private val profileOrder: Map<String, Int> by lazy {
profiles.mapIndexed { index, profile -> profile.id to index }.toMap()
@@ -60,12 +60,14 @@ import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.common.compose.preview.MockPodDataProvider
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.monitor.core.cachedBatteryFormatted
import eu.darken.capod.pods.core.apple.aap.AapPodState
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
import eu.darken.capod.pods.core.apple.ble.devices.DualApplePods
import eu.darken.capod.pods.core.apple.ble.devices.DualApplePods.LidState
import eu.darken.capod.pods.core.apple.ble.devices.HasPodStyle
import eu.darken.capod.pods.core.apple.ble.formatBatteryDurationShort
import eu.darken.capod.pods.core.apple.ble.formatBatteryPercent
import java.time.Instant
@@ -76,6 +78,7 @@ fun DualPodsCard(
showDebug: Boolean,
now: Instant,
isCollapsed: Boolean = false,
batteryEstimate: BatteryEstimate? = null,
onToggleCollapse: (() -> Unit)? = null,
onAncModeChange: ((AapSetting.AncMode.Value) -> Unit)? = null,
onUpgrade: (() -> Unit)? = null,
@@ -192,6 +195,7 @@ fun DualPodsCard(
device = device,
showDebug = showDebug,
now = now,
batteryEstimate = batteryEstimate,
onAncModeChange = onAncModeChange,
)
}
@@ -204,8 +208,10 @@ private fun ColumnScope.DualPodsCardExpanded(
device: PodDevice,
showDebug: Boolean,
now: Instant,
batteryEstimate: BatteryEstimate?,
onAncModeChange: ((AapSetting.AncMode.Value) -> Unit)?,
) {
val context = LocalContext.current
Spacer(modifier = Modifier.height(16.dp))
// Circular battery gauges side by side
@@ -233,6 +239,8 @@ private fun ColumnScope.DualPodsCardExpanded(
isMicrophone = device.isLeftPodMicrophone ?: false,
showMicrophone = device.hasDualMicrophone,
modifier = Modifier.weight(1f),
timeRemaining = batteryEstimate?.left?.let { formatBatteryDurationShort(context, it.minutesRemaining) },
untilCharged = batteryEstimate?.left?.minutesUntilCharged?.let { formatBatteryDurationShort(context, it) },
)
PodGauge(
@@ -245,6 +253,8 @@ private fun ColumnScope.DualPodsCardExpanded(
isMicrophone = device.isRightPodMicrophone ?: false,
showMicrophone = device.hasDualMicrophone,
modifier = Modifier.weight(1f),
timeRemaining = batteryEstimate?.right?.let { formatBatteryDurationShort(context, it.minutesRemaining) },
untilCharged = batteryEstimate?.right?.minutesUntilCharged?.let { formatBatteryDurationShort(context, it) },
)
}
@@ -299,6 +309,8 @@ private fun PodGauge(
isMicrophone: Boolean,
showMicrophone: Boolean,
modifier: Modifier = Modifier,
timeRemaining: String? = null,
untilCharged: String? = null,
) {
val context = LocalContext.current
val clamped = if (batteryPercent >= 0f) batteryPercent.coerceIn(0f, 1f) else -1f
@@ -356,16 +368,27 @@ private fun PodGauge(
Spacer(modifier = Modifier.height(6.dp))
// Battery percentage
Text(
text = formatBatteryPercent(context, batteryPercent),
style = MaterialTheme.typography.titleMedium,
color = if (batteryPercent >= 0f) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
// Battery percentage, with the time-remaining estimate inline next to it
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
Text(
text = formatBatteryPercent(context, batteryPercent),
style = MaterialTheme.typography.titleMedium,
color = if (batteryPercent >= 0f) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
modifier = Modifier.alignByBaseline(),
)
if (timeRemaining != null) {
Text(
text = "· $timeRemaining",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.alignByBaseline(),
)
}
}
Spacer(modifier = Modifier.height(4.dp))
@@ -380,6 +403,7 @@ private fun PodGauge(
chargingOptimizedLabel = stringResource(R.string.pods_charging_optimized_label),
inEarLabel = stringResource(R.string.pods_inear_label),
microphoneLabel = stringResource(R.string.pods_microphone_label),
chargingDetail = untilCharged,
)
}
}
@@ -454,10 +478,49 @@ private fun CaseRow(
@Composable
private fun DualPodsCardFullPreview() = PreviewWrapper {
DualPodsCard(
device = MockPodDataProvider.dualPodMonitoredWithAap(),
device = MockPodDataProvider.dualPodFullyLoaded(),
showDebug = false,
now = SystemTimeSource.now(),
isPro = false,
batteryEstimate = BatteryEstimate(
left = BatteryEstimate.Pod(minutesRemaining = 135, fractionPerHour = 0.18f, source = BatteryEstimate.Source.LIVE),
right = BatteryEstimate.Pod(minutesRemaining = 122, fractionPerHour = 0.20f, source = BatteryEstimate.Source.LIVE),
),
onDeviceSettings = {},
)
}
@Preview2
@Composable
private fun DualPodsCardEstimateLearnedPreview() = PreviewWrapper {
// LEARNED: rate seeded from persisted history on reconnect, before this session has gathered
// enough live samples.
DualPodsCard(
device = MockPodDataProvider.dualPodFullyLoaded(),
showDebug = false,
now = SystemTimeSource.now(),
isPro = false,
batteryEstimate = BatteryEstimate(
left = BatteryEstimate.Pod(minutesRemaining = 92, fractionPerHour = 0.27f, source = BatteryEstimate.Source.LEARNED),
right = BatteryEstimate.Pod(minutesRemaining = 100, fractionPerHour = 0.25f, source = BatteryEstimate.Source.LEARNED),
),
onDeviceSettings = {},
)
}
@Preview2
@Composable
private fun DualPodsCardEstimateProvisionalPreview() = PreviewWrapper {
// SPEC: rated estimate shown immediately on connect, before any drain has been measured.
DualPodsCard(
device = MockPodDataProvider.dualPodFullyLoaded(),
showDebug = false,
now = SystemTimeSource.now(),
isPro = false,
batteryEstimate = BatteryEstimate(
left = BatteryEstimate.Pod(minutesRemaining = 360, fractionPerHour = 0.167f, source = BatteryEstimate.Source.SPEC),
right = BatteryEstimate.Pod(minutesRemaining = 360, fractionPerHour = 0.167f, source = BatteryEstimate.Source.SPEC),
),
onDeviceSettings = {},
)
}
@@ -57,10 +57,12 @@ import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.common.compose.preview.MockPodDataProvider
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.monitor.core.cachedBatteryFormatted
import eu.darken.capod.pods.core.apple.aap.AapPodState
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
import eu.darken.capod.pods.core.apple.ble.batteryProgress
import eu.darken.capod.pods.core.apple.ble.formatBatteryDurationShort
import eu.darken.capod.pods.core.apple.ble.formatBatteryPercent
import eu.darken.capod.pods.core.apple.ble.isKnownBattery
import java.time.Instant
@@ -73,6 +75,7 @@ fun SinglePodsCard(
showDebug: Boolean,
now: Instant,
isCollapsed: Boolean = false,
batteryEstimate: BatteryEstimate? = null,
onToggleCollapse: (() -> Unit)? = null,
onAncModeChange: ((AapSetting.AncMode.Value) -> Unit)? = null,
onUpgrade: (() -> Unit)? = null,
@@ -178,6 +181,7 @@ fun SinglePodsCard(
device = device,
showDebug = showDebug,
now = now,
batteryEstimate = batteryEstimate,
onAncModeChange = onAncModeChange,
)
}
@@ -190,6 +194,7 @@ private fun ColumnScope.SinglePodsCardExpanded(
device: PodDevice,
showDebug: Boolean,
now: Instant,
batteryEstimate: BatteryEstimate?,
onAncModeChange: ((AapSetting.AncMode.Value) -> Unit)?,
) {
val context = LocalContext.current
@@ -251,16 +256,28 @@ private fun ColumnScope.SinglePodsCardExpanded(
)
}
// Battery text inside ring
Text(
text = formatBatteryPercent(context, percent),
style = MaterialTheme.typography.headlineSmall,
color = if (isKnown) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
// Battery percentage + time remaining, stacked inside the ring
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = formatBatteryPercent(context, percent),
style = MaterialTheme.typography.headlineSmall,
color = if (isKnown) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
val headsetEstimate = batteryEstimate?.headset
if (headsetEstimate != null) {
Text(
text = formatBatteryDurationShort(context, headsetEstimate.minutesRemaining),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
softWrap = false,
)
}
}
}
Spacer(modifier = Modifier.height(8.dp))
@@ -277,10 +294,18 @@ private fun ColumnScope.SinglePodsCardExpanded(
icon = Icons.TwoTone.BatteryChargingFull,
label = stringResource(R.string.pods_charging_optimized_label),
)
AapPodState.ChargingState.CHARGING -> StatusChip(
icon = Icons.TwoTone.BatteryChargingFull,
label = stringResource(R.string.pods_charging_label),
)
AapPodState.ChargingState.CHARGING -> {
// Time-until-charged lives in the charging chip; the in-ring estimate
// always means runtime, so the two can't be confused.
val untilCharged = batteryEstimate?.headset?.minutesUntilCharged
?.let { formatBatteryDurationShort(context, it) }
StatusChip(
icon = Icons.TwoTone.BatteryChargingFull,
label = untilCharged
?.let { "${stringResource(R.string.pods_charging_label)} · $it" }
?: stringResource(R.string.pods_charging_label),
)
}
else -> Unit
}
if (device.isBeingWorn == true) {
@@ -333,6 +358,21 @@ private fun SinglePodsCardFullPreview() = PreviewWrapper {
)
}
@Preview2
@Composable
private fun SinglePodsCardEstimatePreview() = PreviewWrapper {
// Worn (not charging) so the "in ear" chip shows alongside the estimate.
SinglePodsCard(
device = MockPodDataProvider.singlePodMonitored(),
showDebug = false,
now = SystemTimeSource.now(),
batteryEstimate = BatteryEstimate(
headset = BatteryEstimate.Pod(minutesRemaining = 320, fractionPerHour = 0.09f, source = BatteryEstimate.Source.LEARNED),
),
onDeviceSettings = {},
)
}
@Preview2
@Composable
private fun SinglePodsCardMinimalPreview() = PreviewWrapper {
@@ -71,6 +71,7 @@ fun StatusChipRow(
inEarLabel: String,
microphoneLabel: String,
modifier: Modifier = Modifier,
chargingDetail: String? = null,
) {
FlowRow(
modifier = modifier.animateContentSize(),
@@ -82,9 +83,11 @@ fun StatusChipRow(
icon = Icons.TwoTone.BatteryChargingFull,
label = chargingOptimizedLabel,
)
// The time-until-charged lives INSIDE the charging chip ("Charging · 25m") — the
// estimate line under the percentage always means runtime, so the two can't be confused.
AapPodState.ChargingState.CHARGING -> StatusChip(
icon = Icons.TwoTone.BatteryChargingFull,
label = chargingLabel,
label = chargingDetail?.let { "$chargingLabel · $it" } ?: chargingLabel,
)
else -> Unit
}
@@ -122,6 +125,7 @@ private fun StatusChipRowAllPreview() = PreviewWrapper {
chargingOptimizedLabel = "Optimized",
inEarLabel = "In Ear",
microphoneLabel = "Mic",
chargingDetail = "25m",
)
}
@@ -24,8 +24,13 @@ import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.upgrade.UpgradeRepo
import eu.darken.capod.common.upgrade.isPro
import eu.darken.capod.monitor.core.DeviceMonitor
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.monitor.core.battery.BatteryEstimator
import eu.darken.capod.monitor.core.battery.takeFor
import eu.darken.capod.profiles.core.DeviceProfilesRepo
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
class BatteryGlanceWidget : GlanceAppWidget() {
@@ -40,6 +45,7 @@ class BatteryGlanceWidget : GlanceAppWidget() {
fun upgradeRepo(): UpgradeRepo
fun widgetSettings(): WidgetSettings
fun deviceProfilesRepo(): DeviceProfilesRepo
fun batteryEstimator(): BatteryEstimator
}
override suspend fun provideGlance(context: Context, id: GlanceId) {
@@ -99,6 +105,13 @@ class BatteryGlanceWidget : GlanceAppWidget() {
?.let { pid -> remember(pid) { ep.deviceMonitor().widgetDeviceFlow(pid) } }
?.collectAsState(initial = initialDevice)
?: remember(initialDevice) { androidx.compose.runtime.mutableStateOf(initialDevice) }
// Empty while the monitor service isn't running — estimates simply stay hidden.
val rawEstimate by config.profileId
?.let { pid ->
remember(pid) { ep.batteryEstimator().estimates.map { it[pid] }.distinctUntilChanged() }
}
?.collectAsState(initial = null)
?: remember { androidx.compose.runtime.mutableStateOf<BatteryEstimate?>(null) }
val profileLabel = config.profileId?.let { pid ->
runCatching {
runBlocking { ep.deviceProfilesRepo().profiles.first().firstOrNull { it.id == pid }?.label }
@@ -117,6 +130,7 @@ class BatteryGlanceWidget : GlanceAppWidget() {
hasConfiguredProfile = config.profileId != null,
profileLabel = profileLabel,
layout = layout,
estimate = device?.let { d -> rawEstimate?.takeFor(d) },
)
} else {
WidgetRenderState.Message(
@@ -138,7 +152,7 @@ class BatteryGlanceWidget : GlanceAppWidget() {
bgColor = WidgetRenderStateMapper.resolvedBgColor(context, WidgetTheme.DEFAULT),
textColor = WidgetRenderStateMapper.resolvedTextColor(context, WidgetTheme.DEFAULT),
iconColor = WidgetRenderStateMapper.resolvedIconColor(context, WidgetTheme.DEFAULT),
)
).withLocalizedPreviewEstimates(context)
provideContent {
GlanceWidgetContent(state = previewState, context = context)
@@ -96,9 +96,9 @@ private fun DualPodPreview(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
PodItemRow(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textColor, iconTint, iconSize = 40, modifier = Modifier.padding(end = 12.dp))
PodItemRow(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textColor, iconTint, iconSize = 40, estimate = state.leftEstimate, estimateBelow = true, modifier = Modifier.padding(end = 12.dp))
PodItemRow(state.caseIcon, state.casePercent, state.caseCharging, false, textColor, iconTint, iconSize = 40, modifier = Modifier.padding(end = 12.dp))
PodItemRow(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textColor, iconTint, iconSize = 40)
PodItemRow(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textColor, iconTint, iconSize = 40, estimate = state.rightEstimate, estimateBelow = true)
}
DeviceLabel(
label = state.deviceLabel,
@@ -111,8 +111,8 @@ private fun DualPodPreview(
BatteryLayout.NARROW -> {
WidgetContainer(bgColor = bgColor, modifier = modifier) {
PodItemRow(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textColor, iconTint)
PodItemRow(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textColor, iconTint)
PodItemRow(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textColor, iconTint, estimate = state.leftEstimate)
PodItemRow(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textColor, iconTint, estimate = state.rightEstimate)
PodItemRow(state.caseIcon, state.casePercent, state.caseCharging, false, textColor, iconTint)
DeviceLabel(
label = state.deviceLabel,
@@ -160,9 +160,10 @@ private fun SinglePodPreview(
colorFilter = iconTint,
)
Text(
text = formatPercent(state.percent),
text = formatPercent(state.percent) + estimateSuffix(state.estimate),
fontSize = 12.sp,
color = textColor,
maxLines = 1,
modifier = Modifier.padding(horizontal = 8.dp),
)
if (state.charging) {
@@ -297,39 +298,54 @@ private fun PodItemRow(
textColor: Color,
iconTint: ColorFilter,
iconSize: Int = 20,
estimate: String? = null,
estimateBelow: Boolean = false,
modifier: Modifier = Modifier,
) {
Row(
Column(
modifier = modifier,
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(icon),
contentDescription = null,
modifier = Modifier.size(iconSize.dp),
colorFilter = iconTint,
)
Text(
text = formatPercent(percent),
fontSize = 12.sp,
color = textColor,
modifier = Modifier.padding(horizontal = 4.dp),
)
if (charging) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painter = painterResource(R.drawable.ic_baseline_power_24),
painter = painterResource(icon),
contentDescription = null,
modifier = Modifier.size(20.dp),
modifier = Modifier.size(iconSize.dp),
colorFilter = iconTint,
)
Text(
text = formatPercent(percent) + if (estimateBelow) "" else estimateSuffix(estimate),
fontSize = 12.sp,
color = textColor,
maxLines = 1,
modifier = Modifier.padding(horizontal = 4.dp),
)
if (charging) {
Image(
painter = painterResource(R.drawable.ic_baseline_power_24),
contentDescription = null,
modifier = Modifier.size(20.dp),
colorFilter = iconTint,
)
}
if (inEar) {
Image(
painter = painterResource(R.drawable.ic_baseline_hearing_24),
contentDescription = null,
modifier = Modifier.size(20.dp),
colorFilter = iconTint,
)
}
}
if (inEar) {
Image(
painter = painterResource(R.drawable.ic_baseline_hearing_24),
contentDescription = null,
modifier = Modifier.size(20.dp),
colorFilter = iconTint,
if (estimateBelow && estimate != null) {
Text(
text = estimate,
fontSize = 12.sp,
color = textColor,
maxLines = 1,
)
}
}
@@ -385,6 +401,8 @@ private fun DeviceLabel(
private fun formatPercent(percent: Float): String =
if (isKnownBattery(percent)) "${(percent * 100).roundToInt()}%" else ""
private fun estimateSuffix(estimate: String?): String = estimate?.let { " · $it" } ?: ""
@Preview2
@Composable
private fun PreviewDualTiny11() = PreviewWrapper {
@@ -65,9 +65,9 @@ private fun GlanceDualPod(
horizontalAlignment = Alignment.CenterHorizontally,
verticalAlignment = Alignment.CenterVertically,
) {
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint, iconSize = 40, modifier = GlanceModifier.padding(end = 12.dp))
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint, iconSize = 40, estimate = state.leftEstimate, estimateBelow = true, modifier = GlanceModifier.padding(end = 12.dp))
GlancePodItem(state.caseIcon, state.casePercent, state.caseCharging, false, textStyle, iconTint, iconSize = 40, modifier = GlanceModifier.padding(end = 12.dp))
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint, iconSize = 40)
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint, iconSize = 40, estimate = state.rightEstimate, estimateBelow = true)
}
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
}
@@ -76,8 +76,8 @@ private fun GlanceDualPod(
BatteryLayout.NARROW -> {
val textStyle = TextStyle(color = fixedColor(state.resolvedTextColor), fontSize = 12.sp)
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint)
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint)
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint, estimate = state.leftEstimate)
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint, estimate = state.rightEstimate)
GlancePodItem(state.caseIcon, state.casePercent, state.caseCharging, false, textStyle, iconTint)
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
}
@@ -122,8 +122,9 @@ private fun GlanceSinglePod(
colorFilter = iconTint,
)
Text(
text = formatGlancePercent(state.percent),
text = formatGlancePercent(state.percent) + estimateSuffix(state.estimate),
style = textStyle,
maxLines = 1,
modifier = GlanceModifier.padding(horizontal = 8.dp),
)
if (state.charging) {
@@ -257,37 +258,49 @@ private fun GlancePodItem(
textStyle: TextStyle,
iconTint: ColorFilter,
iconSize: Int = 20,
estimate: String? = null,
estimateBelow: Boolean = false,
modifier: GlanceModifier = GlanceModifier,
) {
Row(
Column(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
provider = ImageProvider(icon),
contentDescription = null,
modifier = GlanceModifier.size(iconSize.dp),
colorFilter = iconTint,
)
Text(
text = formatGlancePercent(percent),
style = textStyle,
modifier = GlanceModifier.padding(horizontal = 4.dp),
)
if (charging) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
provider = ImageProvider(R.drawable.ic_baseline_power_24),
provider = ImageProvider(icon),
contentDescription = null,
modifier = GlanceModifier.size(20.dp),
modifier = GlanceModifier.size(iconSize.dp),
colorFilter = iconTint,
)
Text(
text = formatGlancePercent(percent) + if (estimateBelow) "" else estimateSuffix(estimate),
style = textStyle,
maxLines = 1,
modifier = GlanceModifier.padding(horizontal = 4.dp),
)
if (charging) {
Image(
provider = ImageProvider(R.drawable.ic_baseline_power_24),
contentDescription = null,
modifier = GlanceModifier.size(20.dp),
colorFilter = iconTint,
)
}
if (inEar) {
Image(
provider = ImageProvider(R.drawable.ic_baseline_hearing_24),
contentDescription = null,
modifier = GlanceModifier.size(20.dp),
colorFilter = iconTint,
)
}
}
if (inEar) {
Image(
provider = ImageProvider(R.drawable.ic_baseline_hearing_24),
contentDescription = null,
modifier = GlanceModifier.size(20.dp),
colorFilter = iconTint,
if (estimateBelow && estimate != null) {
Text(
text = estimate,
style = textStyle,
maxLines = 1,
)
}
}
@@ -343,3 +356,5 @@ private fun fixedColor(argb: Int): ColorProvider = ColorProvider(Color(argb))
private fun formatGlancePercent(percent: Float): String =
if (isKnownBattery(percent)) "${(percent * 100).roundToInt()}%" else ""
private fun estimateSuffix(estimate: String?): String = estimate?.let { " · $it" } ?: ""
@@ -507,7 +507,7 @@ private fun WidgetConfigPreview(
bgColor = WidgetRenderStateMapper.resolvedBgColor(context, theme),
textColor = WidgetRenderStateMapper.resolvedTextColor(context, theme),
iconColor = WidgetRenderStateMapper.resolvedIconColor(context, theme),
).copy(deviceLabel = deviceLabel)
).withLocalizedPreviewEstimates(context).copy(deviceLabel = deviceLabel)
}
ComposeWidgetPreview(state = batteryState, modifier = innerModifier)
}
@@ -25,6 +25,7 @@ internal data class WidgetDeviceKey(
val isLeftInEar: Boolean?,
val isRightInEar: Boolean?,
val isBeingWorn: Boolean?,
val batteryEstimateEnabled: Boolean,
val isAapConnected: Boolean,
val isAapReady: Boolean,
val hasBleAdvertisement: Boolean,
@@ -48,6 +49,7 @@ internal fun PodDevice.toWidgetKey(): WidgetDeviceKey = WidgetDeviceKey(
isLeftInEar = isLeftInEar,
isRightInEar = isRightInEar,
isBeingWorn = isBeingWorn,
batteryEstimateEnabled = batteryEstimateEnabled,
isAapConnected = isAapConnected,
isAapReady = isAapReady,
hasBleAdvertisement = ble != null,
@@ -1,8 +1,10 @@
package eu.darken.capod.main.ui.widget
import android.content.Context
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import eu.darken.capod.R
import eu.darken.capod.pods.core.apple.ble.formatBatteryDurationShort
enum class BatteryLayout {
TINY_COLUMN,
@@ -36,10 +38,12 @@ sealed class WidgetRenderState {
val leftPercent: Float,
val leftCharging: Boolean,
val leftInEar: Boolean,
val leftEstimate: String?,
@DrawableRes val rightIcon: Int,
val rightPercent: Float,
val rightCharging: Boolean,
val rightInEar: Boolean,
val rightEstimate: String?,
@DrawableRes val caseIcon: Int,
val casePercent: Float,
val caseCharging: Boolean,
@@ -57,6 +61,7 @@ sealed class WidgetRenderState {
@DrawableRes val batteryIcon: Int,
val charging: Boolean,
val worn: Boolean,
val estimate: String?,
) : WidgetRenderState()
data class Message(
@@ -95,10 +100,12 @@ sealed class WidgetRenderState {
leftPercent = 0.85f,
leftCharging = false,
leftInEar = true,
leftEstimate = "4h 55m",
rightIcon = R.drawable.device_airpods_pro2_right,
rightPercent = 0.92f,
rightCharging = true,
rightInEar = false,
rightEstimate = "25m",
caseIcon = R.drawable.device_airpods_pro2_case,
casePercent = 1.0f,
caseCharging = false,
@@ -122,6 +129,16 @@ sealed class WidgetRenderState {
batteryIcon = R.drawable.ic_baseline_battery_3_bar_24,
charging = false,
worn = true,
estimate = "14h 30m",
)
}
}
/**
* Replaces the preview factory's hardcoded sample estimates with properly localized ones for
* user-facing preview surfaces (widget picker, configuration screen, store screenshots).
*/
fun WidgetRenderState.DualPod.withLocalizedPreviewEstimates(context: Context): WidgetRenderState.DualPod = copy(
leftEstimate = formatBatteryDurationShort(context, 295),
rightEstimate = formatBatteryDurationShort(context, 25),
)
@@ -5,7 +5,10 @@ import androidx.annotation.ColorInt
import androidx.appcompat.view.ContextThemeWrapper
import eu.darken.capod.R
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.monitor.core.battery.displayMinutes
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.ble.formatBatteryDurationShort
import eu.darken.capod.pods.core.apple.ble.getBatteryDrawable
object WidgetRenderStateMapper {
@@ -18,6 +21,7 @@ object WidgetRenderStateMapper {
hasConfiguredProfile: Boolean,
profileLabel: String?,
layout: BatteryLayout = BatteryLayout.NARROW,
estimate: BatteryEstimate? = null,
): WidgetRenderState {
val bgColor = resolvedBgColor(context, theme)
val textColor = resolvedTextColor(context, theme)
@@ -45,10 +49,12 @@ object WidgetRenderStateMapper {
leftPercent = device.batteryLeft,
leftCharging = device.isLeftPodCharging == true,
leftInEar = device.isLeftInEar == true,
leftEstimate = estimate?.left.estimateText(context, device.isLeftPodCharging == true),
rightIcon = device.rightPodIcon,
rightPercent = device.batteryRight,
rightCharging = device.isRightPodCharging == true,
rightInEar = device.isRightInEar == true,
rightEstimate = estimate?.right.estimateText(context, device.isRightPodCharging == true),
caseIcon = device.caseIcon,
casePercent = device.batteryCase,
caseCharging = device.isCaseCharging == true,
@@ -66,6 +72,7 @@ object WidgetRenderStateMapper {
batteryIcon = getBatteryDrawable(device.batteryHeadset),
charging = device.isHeadsetBeingCharged == true,
worn = device.isBeingWorn == true,
estimate = estimate?.headset.estimateText(context, device.isHeadsetBeingCharged == true),
)
device != null -> WidgetRenderState.Message(
@@ -95,6 +102,9 @@ object WidgetRenderStateMapper {
}
}
private fun BatteryEstimate.Pod?.estimateText(context: Context, charging: Boolean): String? =
this?.displayMinutes(charging)?.let { formatBatteryDurationShort(context, it) }
@ColorInt
fun resolvedBgColor(context: Context, theme: WidgetTheme): Int {
val bgColor = theme.backgroundColor
@@ -79,6 +79,7 @@ class DeviceMonitor @Inject constructor(
profileLearnedAllowOffEnabled = (profile as? AppleDeviceProfile)?.learnedAllowOffEnabled,
profileLastRequestedListeningModeCycleMask = (profile as? AppleDeviceProfile)?.lastRequestedListeningModeCycleMask,
reactions = profile.toReactionConfig(),
batteryEstimateEnabled = (profile as? AppleDeviceProfile)?.batteryEstimateEnabled ?: true,
isSystemConnected = profile?.address in connectedAddresses,
)
}
@@ -115,6 +116,7 @@ class DeviceMonitor @Inject constructor(
profileLearnedAllowOffEnabled = (profile as? AppleDeviceProfile)?.learnedAllowOffEnabled,
profileLastRequestedListeningModeCycleMask = (profile as? AppleDeviceProfile)?.lastRequestedListeningModeCycleMask,
reactions = profile.toReactionConfig(),
batteryEstimateEnabled = (profile as? AppleDeviceProfile)?.batteryEstimateEnabled ?: true,
isSystemConnected = profile.address in state.connectedAddresses,
)
}
@@ -181,6 +183,7 @@ class DeviceMonitor @Inject constructor(
profileLearnedAllowOffEnabled = (profile as? AppleDeviceProfile)?.learnedAllowOffEnabled,
profileLastRequestedListeningModeCycleMask = (profile as? AppleDeviceProfile)?.lastRequestedListeningModeCycleMask,
reactions = profile.toReactionConfig(),
batteryEstimateEnabled = (profile as? AppleDeviceProfile)?.batteryEstimateEnabled ?: true,
isSystemConnected = profile.address in connectedAddresses,
)
}
@@ -212,6 +215,21 @@ class DeviceMonitor @Inject constructor(
dedupedLiveDevices + nonLiveDevices
}.replayingShare(appScope)
/**
* Connection-aware "primary" device for display surfaces (ongoing notification, case popup):
* ranks by [PodDevice.tierRank] (system-connected > live > offline) with the user's
* profile-list order as the tiebreaker. Unlike [primaryDevice], this does NOT blindly pick the
* first profile, so a worn/connected pod is surfaced even when a different profile sits higher
* in the list. Shared so multiple display collectors don't each re-run the combine.
*/
val primaryDeviceByTier: Flow<PodDevice?> = combine(
devices,
profilesRepo.profiles,
) { devices, profiles ->
val profileOrder = profiles.mapIndexed { index, profile -> profile.id to index }.toMap()
devices.primaryByTier(profileOrder)
}.replayingShare(appScope)
private val reportedPersistFailures = mutableSetOf<String>()
private suspend fun persistLiveDevices(devices: List<PodDevice>) {
@@ -273,6 +291,7 @@ class DeviceMonitor @Inject constructor(
profileLearnedAllowOffEnabled = (profile as? AppleDeviceProfile)?.learnedAllowOffEnabled,
profileLastRequestedListeningModeCycleMask = (profile as? AppleDeviceProfile)?.lastRequestedListeningModeCycleMask,
reactions = profile.toReactionConfig(),
batteryEstimateEnabled = (profile as? AppleDeviceProfile)?.batteryEstimateEnabled ?: true,
)
}
@@ -26,8 +26,14 @@ class MonitorModeResolver @Inject constructor(
profilesRepo.profiles,
nudgeCapabilityStore.availability,
) { profiles, nudge ->
val primary = profiles.firstOrNull() ?: return@combine MonitorMode.MANUAL
primary.requiredMode(nudge)
// The mode is driven by the first *addressed* profile, not just the first profile. An
// address-less profile can neither host an AAP session (AapAutoConnect requires an address)
// nor auto-connect, so it can only ever resolve to MANUAL. Letting such a profile sit at the
// top of the list and force MANUAL would silently stop the foreground service that keeps the
// process — and thus background AAP/stem controls — alive for an addressed profile below it.
// No addressed profile at all => genuinely nothing to monitor => MANUAL.
profiles.firstOrNull { !it.address.isNullOrBlank() }?.requiredMode(nudge)
?: MonitorMode.MANUAL
}
.distinctUntilChanged()
.onEach { log(TAG, VERBOSE) { "effectiveMode = $it" } }
@@ -60,6 +60,8 @@ data class PodDevice(
internal val profileLastRequestedListeningModeCycleMask: Int? = null,
/** Reaction toggle snapshot from the profile. Defaults to all-off when no profile is matched. */
val reactions: ReactionConfig = ReactionConfig(),
/** Whether the dashboard battery time-remaining estimate is enabled for this device (per-profile). */
val batteryEstimateEnabled: Boolean = true,
/** True when the profile's BR/EDR address is in the system's connected Bluetooth devices. */
val isSystemConnected: Boolean = false,
) {
@@ -181,18 +183,23 @@ data class PodDevice(
).minOrNull()
}
// Charging — AAP preferred, BLE fallback, then cached
// Charging — AAP preferred, BLE fallback, then cached. The cached fallback only applies while
// the device is live (connected): charging is a real-time claim ("power is flowing now") that
// we can't stand behind once the device is out of range, so we suppress it after full
// disconnect rather than showing a frozen charging state on the dimmed "cached N ago" card.
// The raw charging bits are still persisted in `cached` for callers that want the last-known
// value directly (device.cached?.is…Charging).
val isLeftPodCharging: Boolean?
get() = aap?.isLeftCharging ?: (ble as? HasChargeDetectionDual)?.isLeftPodCharging ?: cached?.isLeftCharging
get() = aap?.isLeftCharging ?: (ble as? HasChargeDetectionDual)?.isLeftPodCharging ?: cached?.isLeftCharging?.takeIf { isLive }
val isRightPodCharging: Boolean?
get() = aap?.isRightCharging ?: (ble as? HasChargeDetectionDual)?.isRightPodCharging ?: cached?.isRightCharging
get() = aap?.isRightCharging ?: (ble as? HasChargeDetectionDual)?.isRightPodCharging ?: cached?.isRightCharging?.takeIf { isLive }
val isCaseCharging: Boolean?
get() = aap?.isCaseCharging ?: (ble as? HasCase)?.isCaseCharging ?: cached?.isCaseCharging
get() = aap?.isCaseCharging ?: (ble as? HasCase)?.isCaseCharging ?: cached?.isCaseCharging?.takeIf { isLive }
val isHeadsetBeingCharged: Boolean?
get() = aap?.isHeadsetCharging ?: (ble as? HasChargeDetection)?.isHeadsetBeingCharged ?: cached?.isHeadsetCharging
get() = aap?.isHeadsetCharging ?: (ble as? HasChargeDetection)?.isHeadsetBeingCharged ?: cached?.isHeadsetCharging?.takeIf { isLive }
// Full per-slot charging state — AAP only (BLE + cache don't carry CHARGING_OPTIMIZED).
// Null means "no live AAP reading", which lets callers avoid showing a stale Optimized chip
@@ -5,8 +5,13 @@ package eu.darken.capod.monitor.core
* lower rank = higher priority. System-connected devices come first, then
* any live device (BLE or AAP), then profiled-but-offline.
*
* Distinct from [DeviceMonitor.primaryDevice] which is intentionally
* non-tier-ranked for reaction flows that want "any profiled device".
* Backs [DeviceMonitor.primaryDeviceByTier], used by display surfaces (ongoing notification,
* case popup) that should follow the worn/connected pod.
*
* Distinct from [DeviceMonitor.primaryDevice], which is intentionally non-tier-ranked: address-
* and eligibility-gated reaction flows (auto-connect targets a not-yet-connected device; sleep and
* conversation match by the event's source address) must keep "first profiled device" semantics,
* which tier ranking would break.
*/
fun PodDevice.tierRank(): Int = when {
isSystemConnected -> 0
@@ -0,0 +1,120 @@
package eu.darken.capod.monitor.core.battery
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.coroutine.AppScope
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.logging.Logging
import eu.darken.capod.common.debug.logging.asLog
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.serialization.SerializationCapod
import eu.darken.capod.profiles.core.ProfileId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
/**
* Persists learned battery drain rates per profile so the time-remaining estimate survives app
* restarts and is available immediately on reconnect. Mirrors [eu.darken.capod.monitor.core.cache.DeviceStateCache]:
* one small JSON file per profile, guarded by a [Mutex], read/written on IO, broadcast via a
* [StateFlow]. Per-profile files mean one corrupt write can only lose a single device's history.
*/
@Singleton
class BatteryDrainStore @Inject constructor(
@ApplicationContext private val context: Context,
@AppScope private val appScope: CoroutineScope,
private val dispatcherProvider: DispatcherProvider,
@SerializationCapod private val json: Json,
) {
private val storeDir by lazy {
File(context.filesDir, "battery_drain_rates").apply { mkdirs() }
}
private val lock = Mutex()
private val _profiles = MutableStateFlow<Map<ProfileId, DrainProfile>>(emptyMap())
val profiles: StateFlow<Map<ProfileId, DrainProfile>> = _profiles
init {
appScope.launch { loadAll() }
}
private suspend fun loadAll() = withContext(dispatcherProvider.IO) {
lock.withLock {
val dir = storeDir
if (!dir.exists()) return@withLock
val loaded = mutableMapOf<ProfileId, DrainProfile>()
dir.listFiles()
?.filter { it.name.startsWith(PREFIX) && it.name.endsWith(SUFFIX) }
?.forEach { file ->
val profileId = file.name.removePrefix(PREFIX).removeSuffix(SUFFIX)
try {
loaded[profileId] = json.decodeFromString<DrainProfile>(file.readText())
} catch (e: Exception) {
log(TAG, Logging.Priority.ERROR) { "Failed to load ${file.name}: ${e.asLog()}, deleting" }
file.delete()
}
}
log(TAG, Logging.Priority.VERBOSE) { "loadAll(): loaded ${loaded.size} entries" }
_profiles.value = loaded
}
}
private fun ProfileId.toFile(): File = File(storeDir, "$PREFIX$this$SUFFIX")
suspend fun save(id: ProfileId, profile: DrainProfile) = withContext(dispatcherProvider.IO) {
lock.withLock {
log(TAG, Logging.Priority.VERBOSE) { "save(id=$id, rates=${profile.rates.keys})" }
val file = id.toFile()
val tmp = File(file.parentFile, "${file.name}.tmp")
try {
tmp.writeText(json.encodeToString(DrainProfile.serializer(), profile))
if (!tmp.renameTo(file)) {
// renameTo can fail across some filesystems; fall back to a direct write.
file.writeText(tmp.readText())
tmp.delete()
}
_profiles.value += (id to profile)
} catch (e: Exception) {
log(TAG, Logging.Priority.ERROR) { "Failed to save $id: ${e.asLog()}" }
tmp.delete()
}
}
}
suspend fun delete(id: ProfileId) = withContext(dispatcherProvider.IO) {
lock.withLock {
log(TAG, Logging.Priority.VERBOSE) { "delete(id=$id)" }
val file = id.toFile()
if (file.exists() && !file.delete()) {
// In-memory is cleared regardless; warn so a failed delete (which would resurrect the
// rate on the next loadAll()) is at least visible rather than silently undoing a reset.
log(TAG, Logging.Priority.ERROR) { "delete($id): failed to remove $file" }
}
_profiles.value -= id
}
}
suspend fun deleteAll() = withContext(dispatcherProvider.IO) {
lock.withLock {
log(TAG, Logging.Priority.VERBOSE) { "deleteAll()" }
storeDir.listFiles()?.forEach { it.delete() }
_profiles.value = emptyMap()
}
}
companion object {
private val TAG = logTag("Monitor", "BatteryDrainStore")
private const val PREFIX = "drainrate_"
private const val SUFFIX = ".json"
}
}
@@ -0,0 +1,47 @@
package eu.darken.capod.monitor.core.battery
/**
* Per-pod time-remaining estimates for one device. Each earbud is measured and shown independently
* (the mic pod, for example, drains faster). [headset] is used for single-headset devices; [left] /
* [right] for dual-pod devices. Slots with no usable estimate are null.
*/
data class BatteryEstimate(
val left: Pod? = null,
val right: Pod? = null,
val headset: Pod? = null,
) {
/**
* @property minutesRemaining smoothed estimate of minutes until this pod empties
* @property fractionPerHour the drain rate it was derived from (fraction/hour)
* @property source how the drain was determined (see [Source]) — reflects measurement provenance,
* NOT whether the model rating capped the shown value; a LIVE/LEARNED estimate can still be
* bounded by the model's rated life
* @property minutesUntilCharged minutes until this pod is full — non-null only while it is
* actively charging with a usable charge rate (not during an Optimized Battery Charging hold
* or the final trickle phase). Shown inside the charging chip; [minutesRemaining] stays on
* the gauge line as the runtime projection.
*/
data class Pod(
val minutesRemaining: Int,
val fractionPerHour: Float,
val source: Source,
val minutesUntilCharged: Int? = null,
) {
/** True while the estimate rests on the model's rated spec, before any drain has been measured. */
val isProvisional: Boolean get() = source == Source.SPEC
}
/** Where a pod's drain rate was derived from, in order of increasing confidence. */
enum class Source {
/** Apple's published rating — shown immediately on connect, before any drain is observed. */
SPEC,
/** Persisted rate from earlier sessions with this device. */
LEARNED,
/** Measured from the current session's observed drain. */
LIVE,
}
val hasAny: Boolean get() = left != null || right != null || headset != null
}
@@ -0,0 +1,40 @@
package eu.darken.capod.monitor.core.battery
import eu.darken.capod.monitor.core.PodDevice
/**
* Returns this estimate only if [device] should display it: the per-device toggle is on, the
* device is live (no estimates for cached/offline cards), and it maps to a profile.
*/
fun BatteryEstimate.takeFor(device: PodDevice): BatteryEstimate? = takeIf {
device.batteryEstimateEnabled && device.isLive && device.profileId != null
}
/**
* Looks up and gates the estimate to show for [device]. Shared by all surfaces (dashboard,
* widget, notification) so the display rules can't drift apart.
*/
fun Map<String, BatteryEstimate>.estimateFor(device: PodDevice): BatteryEstimate? =
device.profileId?.let { this[it] }?.takeFor(device)
/**
* Minutes to display for one pod: the time-until-full while [charging] (null when no usable
* charge ETA exists, e.g. during an Optimized Battery Charging hold), otherwise the runtime
* projection. [charging] should be the same flag that drives the visible charging indicator,
* so the shown duration never contradicts the bolt icon next to it.
*/
fun BatteryEstimate.Pod.displayMinutes(charging: Boolean): Int? = when {
charging -> minutesUntilCharged
else -> minutesRemaining
}
/**
* The per-slot minutes a surface would actually render for [device], or null when no slot shows
* anything. Everything else on the estimate (rates, source) churns without visible effect —
* dedupe render triggers on this key so invisible changes don't refresh widgets/notifications.
*/
fun BatteryEstimate.displayKey(device: PodDevice): List<Int?>? = listOf(
left?.displayMinutes(device.isLeftPodCharging == true),
right?.displayMinutes(device.isRightPodCharging == true),
headset?.displayMinutes(device.isHeadsetBeingCharged == true),
).takeUnless { key -> key.all { it == null } }
@@ -0,0 +1,672 @@
package eu.darken.capod.monitor.core.battery
import android.media.AudioManager
import eu.darken.capod.common.TimeSource
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.flow.setupCommonEventHandlers
import eu.darken.capod.monitor.core.DeviceMonitor
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.pods.core.apple.aap.AapPodState
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot
import eu.darken.capod.pods.core.apple.ble.SingleBlePodSnapshot
import eu.darken.capod.pods.core.apple.ble.devices.HasChargeDetection
import eu.darken.capod.pods.core.apple.ble.devices.HasChargeDetectionDual
import eu.darken.capod.pods.core.apple.ble.isKnownBattery
import eu.darken.capod.profiles.core.ProfileId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
/**
* Learns each device's battery drain rate from observed levels over time and turns it into a
* time-remaining estimate for the earbuds. Rates are learned per ANC mode (drain differs sharply
* between OFF / ON / Transparency / Adaptive) and persisted via [BatteryDrainStore] so an estimate
* is available immediately on reconnect.
*
* Lifecycle: [monitor] is launched by the foreground monitor service (see
* `MonitorService.doMonitor`), NOT eagerly in `init`. `DeviceMonitor.devices` starts BLE scanning
* while it has a subscriber, so a permanent subscription would keep scanning alive forever; gating
* on the service keeps sampling tied to active monitoring. All mutable state below is touched only
* from the single `monitor()` collector; [estimates] is the read-only output other components observe.
*/
@Singleton
class BatteryEstimator @Inject constructor(
private val deviceMonitor: DeviceMonitor,
private val drainStore: BatteryDrainStore,
private val timeSource: TimeSource,
private val audioManager: AudioManager,
) {
private val _estimates = MutableStateFlow<Map<ProfileId, BatteryEstimate>>(emptyMap())
val estimates: StateFlow<Map<ProfileId, BatteryEstimate>> = _estimates
private enum class Slot { LEFT, RIGHT, HEADSET }
/** Which transport a battery reading came from — AAP is 1% granularity, BLE 10%. */
private enum class DataSource { AAP, BLE }
private class SlotHistory {
enum class Direction { DRAIN, CHARGE }
var direction: Direction = Direction.DRAIN
private set
private var source: DataSource? = null
private val samples = ArrayDeque<DrainSample>()
val lastFraction: Float? get() = samples.lastOrNull()?.fraction
val size: Int get() = samples.size
/**
* Keeps the window only while it still describes the same thing: a direction flip
* (drain <-> charge) obviously invalidates it, and so does an AAP <-> BLE source change —
* the granularity jump (1% vs 10%) between transports would read as a fake level step.
*/
fun matches(direction: Direction, source: DataSource): Boolean =
this.direction == direction && this.source == source
fun realign(direction: Direction, source: DataSource) {
if (!matches(direction, source)) samples.clear()
this.direction = direction
this.source = source
}
fun record(sample: DrainSample) {
samples.addLast(sample)
while (samples.size > RING_SIZE) samples.removeFirst()
}
fun clear() = samples.clear()
fun toList(): List<DrainSample> = samples.toList()
}
private class DeviceTracker {
var modeBucket: String = MODE_UNKNOWN
val slots: Map<Slot, SlotHistory> = Slot.entries.associateWith { SlotHistory() }
/**
* Parallel drain windows fed ONLY while the pod is worn, audio is playing, and this device
* is the system's audio sink — pure listening segments, the basis for battery health.
*/
val listeningSlots: Map<Slot, SlotHistory> = Slot.entries.associateWith { SlotHistory() }
/** Fit + sample count of a just-closed listening segment, persisted on the next pass. */
val pendingListeningFits: MutableMap<Slot, Pair<Float, Int>> = mutableMapOf()
/** Smoothed displayed minutes, per pod. */
val lastMinutes: MutableMap<Slot, Int> = mutableMapOf()
var lastUpdateMs: Long? = null
/** When each slot's level last visibly ROSE while charging — drives stall suppression. */
val lastRiseMs: MutableMap<Slot, Long> = mutableMapOf()
// Keyed by "<bucket>/<slot>" for drain rates, "CHARGE/<slot>" for charge rates.
val lastPersistAtMs: MutableMap<String, Long> = mutableMapOf()
/**
* Pre-session stored rate captured once per (bucket, slot), so repeated periodic persists
* during a single session blend against a fixed baseline instead of runaway-converging.
* [sessionBaselineCounts] captures the matching pre-session updateCount, so repeated persists
* within one session count as ONE update, not many.
*/
val sessionBaseline: MutableMap<String, Float?> = mutableMapOf()
val sessionBaselineCounts: MutableMap<String, Int> = mutableMapOf()
fun clearSlots() = slots.values.forEach { it.clear() }
fun resetWindow() {
clearSlots()
listeningSlots.values.forEach { it.clear() }
pendingListeningFits.clear()
lastMinutes.clear()
lastRiseMs.clear()
sessionBaseline.clear()
sessionBaselineCounts.clear()
}
}
private val trackers = mutableMapOf<ProfileId, DeviceTracker>()
// Serialises process() against reset() so an in-flight persist can't resurrect a just-wiped rate.
private val mutex = Mutex()
/**
* Wipes ALL learned state for [profileId] — the in-memory tracker, the current estimate, and the
* persisted rates — under the same lock as [process], so it's atomic w.r.t. sampling/persisting.
* The next live emission re-seeds the device from its model rating. Safe to call when the monitor
* isn't running (trackers/estimates are already empty; only the store delete has effect).
*/
suspend fun reset(profileId: ProfileId) = withContext(NonCancellable) {
// NonCancellable so a cancelled caller (e.g. the settings screen closing) can't leave the
// wipe half-applied — memory cleared but persisted rates surviving to resurrect later.
mutex.withLock {
log(TAG) { "reset($profileId)" }
trackers.remove(profileId)
_estimates.value = _estimates.value - profileId
drainStore.delete(profileId)
}
}
fun monitor(): Flow<Unit> = deviceMonitor.devices
.onEach { devices -> process(devices) }
.onCompletion {
// The collector stops with the monitor service; drop session state so the UI can't
// keep showing a stale estimate and the next session re-seeds from persistence.
trackers.clear()
_estimates.value = emptyMap()
}
.map { }
.setupCommonEventHandlers(TAG) { "batteryEstimator" }
private suspend fun process(devices: List<PodDevice>) = mutex.withLock {
// Only profiles with a single, unambiguous live candidate that has the estimate enabled.
// DeviceMonitor keeps multiple same-profile devices when there's no IRK-verified match, and
// blending two physical devices' levels would be garbage — skip those. A device with the
// feature disabled is skipped entirely (not sampled, not persisted).
val unambiguous = devices
.filter { it.profileId != null && it.isLive && it.batteryEstimateEnabled }
.groupBy { it.profileId!! }
.filter { (_, group) -> group.size == 1 }
.mapValues { (_, group) -> group.single() }
val next = _estimates.value.toMutableMap()
// Drop estimates for profiles no longer live/unambiguous/enabled this emission (offline gating).
next.keys.retainAll(unambiguous.keys)
// Sampled once per emission — the gate for health-grade "listening" segments.
val musicActive = audioManager.isMusicActive
for ((profileId, device) in unambiguous) {
val estimate = updateTracker(profileId, device, musicActive)
if (estimate != null) next[profileId] = estimate else next.remove(profileId)
}
_estimates.value = next
}
private suspend fun updateTracker(profileId: ProfileId, device: PodDevice, musicActive: Boolean): BatteryEstimate? {
val tracker = trackers.getOrPut(profileId) { DeviceTracker() }
val nowMs = timeSource.elapsedRealtime()
val bucket = device.modeBucket()
// A long gap since the last update means the device was out of range / reconnected — the
// prior window is stale and must not be extended across the absence.
val gap = tracker.lastUpdateMs?.let { nowMs - it }
if (gap != null && gap > STALE_GAP_MS) tracker.resetWindow()
tracker.lastUpdateMs = nowMs
// A mode change invalidates the current window — flush what we learned to the OLD bucket,
// then start fresh so OFF-rate samples never blend into ON-rate.
if (tracker.modeBucket != bucket) {
persistFromWindow(profileId, tracker, device, tracker.modeBucket, nowMs, force = true)
tracker.resetWindow()
tracker.modeBucket = bucket
}
for (slot in Slot.entries) {
val history = tracker.slots.getValue(slot)
val charging = device.liveCharging(slot)
val reading = device.liveReading(slot)
when {
reading == null -> { // unavailable reading → just drop this slot's window
history.clear()
tracker.lastRiseMs.remove(slot)
// A charging jump breaks discharge continuity even when the level is unreadable.
if (charging == true) tracker.lastMinutes.remove(slot)
}
charging == true -> { // battery rising → sample the CHARGE, never a drain
val (fraction, source) = reading
tracker.lastMinutes.remove(slot) // jump breaks continuity → drop discharge smoothing
if (device.liveChargingOptimized(slot)) {
// Optimized Battery Charging parks the level below full for hours while
// still flagged charging — fitting that plateau would learn garbage.
history.clear()
tracker.lastRiseMs.remove(slot)
} else {
history.realign(SlotHistory.Direction.CHARGE, source)
val last = history.lastFraction
when {
last == null -> { // charge session starts (or resumes) for this slot
history.record(DrainSample(nowMs, fraction))
tracker.lastRiseMs[slot] = nowMs
}
fraction > last + EPSILON -> {
history.record(DrainSample(nowMs, fraction))
tracker.lastRiseMs[slot] = nowMs
}
fraction < last - EPSILON -> { // level DROPPED while charging → reseat/swap
history.clear()
history.record(DrainSample(nowMs, fraction))
tracker.lastRiseMs[slot] = nowMs
}
else -> Unit // ~unchanged; stall detection judges the silence
}
}
}
else -> { // draining (or unknown charging state — treated as draining, as before)
val (fraction, source) = reading
tracker.lastRiseMs.remove(slot)
history.realign(SlotHistory.Direction.DRAIN, source)
val last = history.lastFraction
when {
last == null -> history.record(DrainSample(nowMs, fraction))
fraction > last + EPSILON -> { // level went UP (reseat/swap) → reset
history.clear()
history.record(DrainSample(nowMs, fraction))
tracker.lastMinutes.remove(slot)
}
fraction < last - EPSILON -> history.record(DrainSample(nowMs, fraction)) // a drop
else -> Unit // ~unchanged, no new information
}
}
}
// Health-grade listening window: only pure segments count — the pod worn, audio playing,
// and this device the audio sink (isMusicActive alone would count phone-speaker
// playback). The moment the gate breaks, the finished segment's fit is captured for
// persistence and the window cleared; mixed idle/listening samples would flatten the
// slope and re-dilute health.
val listening = charging != true && reading != null &&
musicActive && device.isSystemConnected && device.wornForSlot(slot)
val listeningHistory = tracker.listeningSlots.getValue(slot)
if (listening) {
val (fraction, source) = reading!!
// An AAP<->BLE flip mid-listening still ends a PURE segment — flush it rather
// than letting realign silently discard it.
if (!listeningHistory.matches(SlotHistory.Direction.DRAIN, source)) {
captureListeningSegment(tracker, slot)
}
listeningHistory.realign(SlotHistory.Direction.DRAIN, source)
val last = listeningHistory.lastFraction
when {
last == null -> listeningHistory.record(DrainSample(nowMs, fraction))
fraction > last + EPSILON -> { // reseat mid-listening → fresh segment
listeningHistory.clear()
listeningHistory.record(DrainSample(nowMs, fraction))
}
fraction < last - EPSILON -> listeningHistory.record(DrainSample(nowMs, fraction))
else -> Unit
}
} else {
captureListeningSegment(tracker, slot)
}
}
persistFromWindow(profileId, tracker, device, bucket, nowMs, force = false)
return computeEstimate(profileId, tracker, device, bucket, nowMs)
}
/** Computes an independent estimate for each pod (left / right / headset). */
private fun computeEstimate(
profileId: ProfileId,
tracker: DeviceTracker,
device: PodDevice,
bucket: String,
nowMs: Long,
): BatteryEstimate? {
val estimate = BatteryEstimate(
left = slotEstimate(profileId, tracker, device, bucket, Slot.LEFT, nowMs),
right = slotEstimate(profileId, tracker, device, bucket, Slot.RIGHT, nowMs),
headset = slotEstimate(profileId, tracker, device, bucket, Slot.HEADSET, nowMs),
)
return estimate.takeIf { it.hasAny }
}
private fun slotEstimate(
profileId: ProfileId,
tracker: DeviceTracker,
device: PodDevice,
bucket: String,
slot: Slot,
nowMs: Long,
): BatteryEstimate.Pod? {
val fraction = device.liveFraction(slot) ?: return null
val spec = device.specRate(bucket)
// While charging the battery is rising, so there's no live drain to fit — project the runtime
// "if used now" from the learned rate or the model rating instead. Live sampling is skipped
// here (and updateTracker already clears the window while charging), so a rising level is
// never learned as a drain — we only surface a projection so the estimate stays visible in
// the case, climbing as the pod charges.
val charging = device.liveCharging(slot) == true
val live = if (charging) {
null
} else {
DrainModel.slopeFractionPerHour(tracker.slots.getValue(slot).toList())
?.takeIf { plausibleForModel(it, spec) }
}
val learned = learnedRate(profileId, device, bucket, slot)
val displayRate = live ?: learned ?: spec ?: return null
// Apple's rating is a hard ceiling on remaining life (a floor on the drain rate) for every
// source: a degraded battery only ever drains FASTER than new-condition spec, so a measured
// rate normally wins — spec only bites when a source implausibly implies MORE life than Apple.
val effectiveRate = spec?.let { maxOf(displayRate, it) } ?: displayRate
val minutes = DrainModel.minutesRemaining(fraction, effectiveRate) ?: return null
// Smooth against the last displayed value — but while charging neither read nor write that
// state: the in-case projection is shown raw and must not pollute the discharge history, else
// the first estimate after undocking would blend against a stale charging projection.
var smoothed = DrainModel.blendMinutes(if (charging) null else tracker.lastMinutes[slot], minutes)
// Smoothing lags, so just after a drop it can sit above the ceiling — clamp the shown value to
// the spec cap too, so we never DISPLAY more life than Apple rates even mid-transition.
spec?.let { DrainModel.minutesRemaining(fraction, it) }?.let { smoothed = minOf(smoothed, it) }
if (!charging) tracker.lastMinutes[slot] = smoothed
val source = when {
live != null -> BatteryEstimate.Source.LIVE
learned != null -> BatteryEstimate.Source.LEARNED
else -> BatteryEstimate.Source.SPEC
}
return BatteryEstimate.Pod(
minutesRemaining = smoothed,
fractionPerHour = effectiveRate,
source = source,
minutesUntilCharged = if (charging) chargeEstimate(profileId, tracker, device, slot, fraction, nowMs) else null,
)
}
/**
* Minutes until [slot] is full, or null when no usable charge rate exists, the pod is in an
* Optimized Battery Charging hold, or the level has sat still longer than one visible step
* should take (stall — trickle phase or an unreported hold; a linear ETA would just freeze).
*/
private fun chargeEstimate(
profileId: ProfileId,
tracker: DeviceTracker,
device: PodDevice,
slot: Slot,
fraction: Float,
nowMs: Long,
): Int? {
if (device.liveChargingOptimized(slot)) return null // held below full — an ETA would mislead
val history = tracker.slots.getValue(slot)
val ring = if (history.direction == SlotHistory.Direction.CHARGE) history.toList() else emptyList()
val liveScalar = if (ring.isNotEmpty()) DrainModel.chargeSlopeFractionPerHour(ring) else null
val learnedScalar = learnedChargeRate(profileId, device, slot)
val specRate = device.model.batterySpec?.chargeFractionPerHour
// Per band: this session's in-band fit, then the learned band rate, then the scalar
// fallbacks, then Apple's quick-charge claim with the band's taper haircut (the claim
// measures the bulk phase; scalars already average what was actually observed).
fun rateFor(band: DrainModel.ChargeBand): Float? =
(if (ring.isNotEmpty()) DrainModel.chargeBandSlopeFractionPerHour(ring, band) else null)
?: learnedChargeBand(profileId, device, slot, band)
?: liveScalar
?: learnedScalar
?: specRate?.let { it * band.specMultiplier }
val currentBand = DrainModel.ChargeBand.entries.firstOrNull { fraction < it.to }
?: DrainModel.ChargeBand.TRICKLE
val stallRate = rateFor(currentBand) ?: return null
val lastRise = tracker.lastRiseMs[slot] ?: return null
val step = if (device.liveReading(slot)?.second == DataSource.AAP) STEP_AAP else STEP_BLE
if (nowMs - lastRise > DrainModel.chargeStallThresholdMs(stallRate, step)) return null
return DrainModel.minutesUntilFull(fraction, ::rateFor)
}
private fun learnedChargeBand(
profileId: ProfileId,
device: PodDevice,
slot: Slot,
band: DrainModel.ChargeBand,
): Float? = storedProfileFor(profileId, device)?.chargeBands[slot.name]?.get(band.name)?.fractionPerHour
/**
* Closes [slot]'s current listening segment: a valid fit is queued for persistence (the next
* persist pass writes it, bypassing cadence) and the window cleared either way.
*/
private fun captureListeningSegment(tracker: DeviceTracker, slot: Slot) {
val history = tracker.listeningSlots.getValue(slot)
if (history.size == 0) return
DrainModel.slopeFractionPerHour(history.toList())?.let {
tracker.pendingListeningFits[slot] = it to history.size
}
history.clear()
}
/** Whether the pod in [slot] is being worn — per-pod for buds, whole-device for headsets. */
private fun PodDevice.wornForSlot(slot: Slot): Boolean = when (slot) {
Slot.LEFT -> isLeftInEar
Slot.RIGHT -> isRightInEar
Slot.HEADSET -> isBeingWorn
} == true
/**
* Persists each pod's live drain or charge rate (whichever direction its window currently
* tracks), at most once per [PERSIST_INTERVAL_MS] (mirrors the cache's periodic-save cadence)
* unless [force]d (mode change). Drain rates are keyed per (bucket, slot); charge rates per slot
* only — the ANC mode doesn't apply inside the case.
*/
private suspend fun persistFromWindow(
profileId: ProfileId,
tracker: DeviceTracker,
device: PodDevice,
bucket: String,
nowMs: Long,
force: Boolean,
) {
// A profile tagged with DIFFERENT hardware means the user re-pointed it — its rates don't
// describe this device, so learning starts over instead of blending into foreign history.
val existing = drainStore.profiles.value[profileId]?.takeIf { it.matchesModel(device.model) }
?: DrainProfile()
val spec = device.specRate(bucket)
var rates = existing.rates
var chargeRates = existing.chargeRates
var changed = false
var chargeBands = existing.chargeBands
var listeningRates = existing.listeningRates
// Blend against the rate stored when this session began, captured once per key, so a single
// long session's repeated writes can't dominate prior history by re-blending their own
// output. The captured updateCount keeps a whole session counting as ONE accumulated update.
fun blended(cadenceKey: String, stored: DrainProfile.LearnedRate?, fit: Float, samples: Int): DrainProfile.LearnedRate {
if (!tracker.sessionBaseline.containsKey(cadenceKey)) {
tracker.sessionBaseline[cadenceKey] = stored?.fractionPerHour
tracker.sessionBaselineCounts[cadenceKey] = stored?.updateCount ?: 0
}
return DrainProfile.LearnedRate(
fractionPerHour = DrainModel.blendRate(tracker.sessionBaseline[cadenceKey], fit),
sampleCount = samples,
updateCount = (tracker.sessionBaselineCounts[cadenceKey] ?: 0) + 1,
updatedAt = timeSource.now(),
)
}
// True at most once per PERSIST_INTERVAL_MS per key (bypassed on [force] or [always]).
fun cadenceOk(cadenceKey: String, always: Boolean = false): Boolean {
val last = tracker.lastPersistAtMs[cadenceKey]
if (!always && !force && last != null && nowMs - last < PERSIST_INTERVAL_MS) return false
tracker.lastPersistAtMs[cadenceKey] = nowMs
return true
}
for (slot in Slot.entries) {
val history = tracker.slots.getValue(slot)
if (history.direction == SlotHistory.Direction.CHARGE) {
// Whole-session scalar — the fallback basis and the stall-threshold reference.
DrainModel.chargeSlopeFractionPerHour(history.toList())?.let { fit ->
val key = chargeRateKey(slot)
if (cadenceOk(key)) {
chargeRates = chargeRates + (slot.name to blended(key, chargeRates[slot.name], fit, history.size))
changed = true
log(TAG, VERBOSE) { "Persisting charge rate for $profileId [$key]: ${"%.3f".format(fit)}/hr" }
}
}
// Per-band rates — charging is CC/CV, each regime learns its own speed.
for (band in DrainModel.ChargeBand.entries) {
val fit = DrainModel.chargeBandSlopeFractionPerHour(history.toList(), band) ?: continue
val key = "${chargeRateKey(slot)}/${band.name}"
if (!cadenceOk(key)) continue
val stored = chargeBands[slot.name]?.get(band.name)
val slotBands = chargeBands[slot.name].orEmpty() + (band.name to blended(key, stored, fit, history.size))
chargeBands = chargeBands + (slot.name to slotBands)
changed = true
log(TAG, VERBOSE) { "Persisting charge band for $profileId [$key]: ${"%.3f".format(fit)}/hr" }
}
} else {
// Same model-aware plausibility gate as display, so an implausibly fast fit isn't learned.
DrainModel.slopeFractionPerHour(history.toList())
?.takeIf { plausibleForModel(it, spec) }
?.let { fit ->
val key = rateKey(bucket, slot)
if (cadenceOk(key)) {
rates = rates + (key to blended(key, rates[key], fit, history.size))
changed = true
log(TAG, VERBOSE) { "Persisting learned rate for $profileId [$key]: ${"%.3f".format(fit)}/hr" }
}
}
}
// Listening rates (health basis): a just-closed segment persists immediately — it would
// be lost otherwise, the window is already cleared. An ongoing window follows cadence.
val pending = tracker.pendingListeningFits.remove(slot)
val (fit, samples) = when {
pending != null -> pending
else -> DrainModel.slopeFractionPerHour(tracker.listeningSlots.getValue(slot).toList())
?.let { it to tracker.listeningSlots.getValue(slot).size } ?: (null to 0)
}
if (fit != null && plausibleForModel(fit, spec)) {
val key = rateKey(bucket, slot)
val cadenceKey = "LISTEN/$key"
if (cadenceOk(cadenceKey, always = pending != null)) {
listeningRates = listeningRates + (key to blended(cadenceKey, listeningRates[key], fit, samples))
changed = true
log(TAG, VERBOSE) { "Persisting listening rate for $profileId [$key]: ${"%.3f".format(fit)}/hr" }
}
}
}
if (changed) {
drainStore.save(
profileId,
existing.copy(
model = device.model.name,
rates = rates,
chargeRates = chargeRates,
chargeBands = chargeBands,
listeningRates = listeningRates,
),
)
}
}
private fun learnedRate(profileId: ProfileId, device: PodDevice, bucket: String, slot: Slot): Float? {
val profile = storedProfileFor(profileId, device) ?: return null
return (profile.rates[rateKey(bucket, slot)] ?: profile.rates[rateKey(MODE_UNKNOWN, slot)])?.fractionPerHour
}
private fun learnedChargeRate(profileId: ProfileId, device: PodDevice, slot: Slot): Float? =
storedProfileFor(profileId, device)?.chargeRates[slot.name]?.fractionPerHour
/** The stored profile, ignored entirely when its rates were learned on different hardware. */
private fun storedProfileFor(profileId: ProfileId, device: PodDevice): DrainProfile? =
drainStore.profiles.value[profileId]?.takeIf { it.matchesModel(device.model) }
private fun rateKey(bucket: String, slot: Slot): String = "$bucket/${slot.name}"
/** Session-state key for charge windows — namespaced so it can't collide with an ANC bucket. */
private fun chargeRateKey(slot: Slot): String = "CHARGE/${slot.name}"
private fun PodDevice.modeBucket(): String = ancMode?.current?.name ?: MODE_UNKNOWN
/**
* Apple's rated drain (fraction/hour) for this model in the given ANC bucket, or null when the
* model has no published rating (Beats / unknown). Used to seed an estimate before any drain is
* observed and as the upper bound on remaining life. When the mode isn't known yet (BLE-only, or
* a just-connected AAP session) the shorter of the two ratings is used, so an unknown mode can't
* over-promise.
*/
private fun PodDevice.specRate(bucket: String): Float? {
val spec = model.batterySpec ?: return null
val on = spec.listeningHoursAncOn
val off = spec.listeningHoursAncOff
val hours = when (bucket) {
AapSetting.AncMode.Value.OFF.name -> off ?: on
MODE_UNKNOWN -> listOfNotNull(on, off).minOrNull()
else -> on ?: off // ON / TRANSPARENCY / ADAPTIVE
} ?: return null
return if (hours.isFinite() && hours > 0f) 1f / hours else null
}
/**
* A measured rate is trusted only when it isn't absurdly faster than the model's rated drain. The
* slow side is intentionally NOT bounded: a genuinely gentle drain is real data worth learning,
* and the spec ceiling already stops a slow rate from over-reporting on screen.
*/
private fun plausibleForModel(rate: Float, spec: Float?): Boolean =
spec == null || rate <= spec * SPEC_BAND_MAX
// RAW LIVE extraction — must NOT use device.batteryLeft/isLeftPodCharging (which fall back to
// cache); learning from a re-stamped stale reading would poison the rate.
private fun PodDevice.liveFraction(slot: Slot): Float? = liveReading(slot)?.first
/**
* The slot's live battery fraction plus which transport reported it. The source matters because
* the two granularities (AAP 1%, BLE 10%) can't share a fit window — see [SlotHistory.realign].
*/
private fun PodDevice.liveReading(slot: Slot): Pair<Float, DataSource>? {
val aapValue = when (slot) {
Slot.LEFT -> aap?.batteryLeft
Slot.RIGHT -> aap?.batteryRight
Slot.HEADSET -> aap?.batteryHeadset
}
val (value, source) = when {
aapValue != null -> aapValue to DataSource.AAP
else -> when (slot) {
Slot.LEFT -> (ble as? DualBlePodSnapshot)?.batteryLeftPodPercent
Slot.RIGHT -> (ble as? DualBlePodSnapshot)?.batteryRightPodPercent
Slot.HEADSET -> (ble as? SingleBlePodSnapshot)?.batteryHeadsetPercent
}?.let { it to DataSource.BLE } ?: return null
}
// coerce defends against a malformed >1 reading, which would otherwise beat the full-charge spec.
return value.takeIf { isKnownBattery(it) }?.coerceIn(0f, 1f)?.let { it to source }
}
private fun PodDevice.liveCharging(slot: Slot): Boolean? = when (slot) {
Slot.LEFT -> aap?.isLeftCharging ?: (ble as? HasChargeDetectionDual)?.isLeftPodCharging
Slot.RIGHT -> aap?.isRightCharging ?: (ble as? HasChargeDetectionDual)?.isRightPodCharging
Slot.HEADSET -> aap?.isHeadsetCharging ?: (ble as? HasChargeDetection)?.isHeadsetBeingCharged
}
/** Only AAP reports the Optimized Battery Charging hold; BLE can't distinguish it. */
private fun PodDevice.liveChargingOptimized(slot: Slot): Boolean = when (slot) {
Slot.LEFT -> aap?.leftChargingState
Slot.RIGHT -> aap?.rightChargingState
Slot.HEADSET -> aap?.headsetChargingState
} == AapPodState.ChargingState.CHARGING_OPTIMIZED
companion object {
private val TAG = logTag("Monitor", "BatteryEstimator")
private const val RING_SIZE = 32
private const val EPSILON = 0.001f
private const val MODE_UNKNOWN = DrainProfile.BUCKET_UNKNOWN
private const val PERSIST_INTERVAL_MS = 5 * 60_000L
/** Visible battery step per transport — feeds the granularity-aware stall threshold. */
private const val STEP_AAP = 0.01f
private const val STEP_BLE = 0.10f
/** A measured rate above this multiple of the model's rated drain is rejected as implausible. */
private const val SPEC_BAND_MAX = 4f
/** A gap longer than this between updates means the device was away — reset its window. */
private const val STALE_GAP_MS = 15 * 60_000L
}
}
@@ -0,0 +1,86 @@
package eu.darken.capod.monitor.core.battery
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
import kotlin.math.roundToInt
/**
* Derives a rough per-pod battery-health percentage from learned drain rates vs the model's rated
* battery life. Nothing on the wire exposes Apple's real health/cycle data, so this is a usage-based
* proxy: a pod that only lasts 4.5h of a rated 6h reads as ~75%.
*
* Health is computed PER POD — single-pod listening habits or a replaced earbud make the two sides
* genuinely diverge, and a combined figure would mask a failing pod. Only [DrainProfile.listeningRates]
* feed it (segments where the pod was worn AND audio was playing on this device), because Apple's
* ratings are listening figures — general rates include idle wear and would flatter health. Within a
* pod, the MEDIAN of its qualifying rates is used rather than the best or worst, damping remaining
* confounds (volume, calls, cold) in either direction. It remains an estimate — label it as such in
* the UI.
*/
object BatteryHealth {
/** A learned rate must have accumulated this many separate sessions before it counts. */
const val MIN_UPDATE_COUNT = 3
data class PerPod(
val left: Int? = null,
val right: Int? = null,
val headset: Int? = null,
) {
val hasAny: Boolean get() = left != null || right != null || headset != null
}
fun estimate(profile: DrainProfile?, model: PodModel): PerPod? {
if (profile == null) return null
val spec = model.batterySpec ?: return null
if (!profile.matchesModel(model)) return null
return PerPod(
left = slotPercent(profile, spec, "LEFT"),
right = slotPercent(profile, spec, "RIGHT"),
headset = slotPercent(profile, spec, "HEADSET"),
).takeIf { it.hasAny }
}
private fun slotPercent(profile: DrainProfile, spec: PodModel.BatterySpec, slot: String): Int? {
val ratios = profile.listeningRates.mapNotNull { (key, rate) ->
// Keys must be exactly "<bucket>/<slot>" — anything else is corrupted or
// future-format data and must not feed a health figure.
val parts = key.split('/')
if (parts.size != 2 || parts[1] != slot) return@mapNotNull null
val specHours = specHoursFor(spec, parts[0]) ?: return@mapNotNull null
if (rate.updateCount < MIN_UPDATE_COUNT) return@mapNotNull null
if (!rate.fractionPerHour.isFinite() || rate.fractionPerHour <= 0f) return@mapNotNull null
(1f / specHours) / rate.fractionPerHour
}
if (ratios.isEmpty()) return null
val sorted = ratios.sorted()
val median = if (sorted.size % 2 == 1) {
sorted[sorted.size / 2]
} else {
(sorted[sorted.size / 2 - 1] + sorted[sorted.size / 2]) / 2f
}
return (median * 100f).roundToInt().coerceIn(1, 100)
}
/**
* The rated hours a rate learned in [bucket] should be judged against. UNKNOWN-bucket usage
* can't be matched to a specific mode, so it's compared to the middle of the two ratings —
* the shorter one would systematically flatter health, the longer one would slander it.
* Malformed or unrecognized bucket keys yield null (entry is skipped).
*/
private fun specHoursFor(spec: PodModel.BatterySpec, bucket: String): Float? {
val on = spec.listeningHoursAncOn
val off = spec.listeningHoursAncOff
return when (bucket) {
AapSetting.AncMode.Value.OFF.name -> off ?: on
AapSetting.AncMode.Value.ON.name,
AapSetting.AncMode.Value.TRANSPARENCY.name,
AapSetting.AncMode.Value.ADAPTIVE.name,
-> on ?: off
DrainProfile.BUCKET_UNKNOWN -> listOfNotNull(on, off).takeIf { it.isNotEmpty() }?.average()?.toFloat()
else -> null
}?.takeIf { it.isFinite() && it > 0f }
}
}
@@ -0,0 +1,262 @@
package eu.darken.capod.monitor.core.battery
import kotlin.math.abs
import kotlin.math.roundToInt
import kotlin.math.roundToLong
/**
* A single battery observation for one slot (left / right / headset).
*
* [atElapsedMs] is a monotonic timestamp ([eu.darken.capod.common.TimeSource.elapsedRealtime]) so
* wall-clock jumps can't corrupt the regression. [fraction] is a battery level in `0.0..1.0`
* (the same unit used everywhere else in the app), NOT a 0..100 percentage.
*/
data class DrainSample(
val atElapsedMs: Long,
val fraction: Float,
)
/**
* Pure, side-effect-free drain-rate math. Kept separate from [BatteryEstimator] so the numerics can
* be unit-tested without Android, coroutines, or persistence.
*
* All rates are **fraction per hour** (e.g. `0.169` = 16.9 %/hr) to match the `0.0..1.0` battery unit.
* Mixing this up with a 0..100 percentage would produce a 100× error, so the unit is in the name.
*/
object DrainModel {
/** Minimum samples before a live regression is trusted. */
const val MIN_SAMPLES = 4
/** Minimum span between oldest and newest sample. Rejects bursts of rapid 1% ticks. */
const val MIN_SPAN_MS = 3 * 60_000L
/** Minimum total drop across the window. Rejects noise that isn't a real discharge. */
const val MIN_TOTAL_DROP = 0.03f
/**
* Only samples within this window of the newest one feed the regression. Drops stale points
* from before a long gap (out of range / overnight) so a reconnect can't fit a line across
* hours of absence.
*/
const val MAX_SAMPLE_AGE_MS = 2 * 60 * 60_000L
/** Plausible drain-rate band (fraction/hour). Outside this, the estimate is rejected. */
const val RATE_MIN = 0.02f
const val RATE_MAX = 0.80f
/**
* Charge fits get by with fewer samples than drain fits: a charge session is short (well under
* an hour) and BLE's 10% steps would otherwise need most of the session before a fit exists.
*/
const val MIN_SAMPLES_CHARGE = 3
/** Minimum total rise across the charge window — rejects jitter that isn't a real charge. */
const val MIN_TOTAL_RISE = 0.05f
/** Plausible charge-rate band (fraction/hour): a full charge in 15 minutes .. 4 hours. */
const val CHARGE_RATE_MIN = 0.25f
const val CHARGE_RATE_MAX = 4.0f
/**
* Above this level the "until charged" estimate is suppressed. The trickle band models the slow
* tail, so suppression only covers the last sliver where the firmware is about to flip the
* charging flag off at 100% anyway.
*/
const val NEAR_FULL_SUPPRESS = 0.99f
/** Narrow bands (10% wide) accept a two-point fit — a full BLE band is exactly two ticks. */
const val MIN_SAMPLES_CHARGE_NARROW = 2
/** Minimum rise WITHIN a band before its fit is trusted (half a narrow band). */
const val MIN_BAND_RISE = 0.05f
/** [chargeStallThresholdMs] never goes below this, however fast the rate claims to be. */
const val CHARGE_STALL_FLOOR_MS = 10 * 60_000L
/** Estimates above this are implausible and suppressed. */
const val MAX_MINUTES = 24 * 60
/**
* Smoothing for the displayed minutes. Asymmetric on purpose: react quickly when the estimate
* DROPS (less time left — e.g. a degraded battery measured draining faster than its rating, or a
* first live fit undercutting the spec seed) so we don't keep showing more life than the latest
* reading supports, but ease UP slowly to swallow upward noise and stay conservative.
*/
const val MINUTES_ALPHA_DOWN = 0.6f
const val MINUTES_ALPHA_UP = 0.25f
/** Smoothing for the persisted per-mode learned rate. */
const val LEARN_ALPHA = 0.3f
/**
* Least-squares slope of [samples] (fraction vs. hours) as a positive drain rate in
* fraction/hour, or null if there aren't enough samples, the window is too short/small, the
* battery isn't actually draining, or the result is outside [RATE_MIN]..[RATE_MAX].
*/
fun slopeFractionPerHour(samples: List<DrainSample>): Float? {
val recent = recentWindow(samples, MIN_SAMPLES) ?: return null
if (recent.first().fraction - recent.last().fraction < MIN_TOTAL_DROP) return null
// Negative slope == draining; flip to a positive drain rate.
val rate = regressionSlopePerHour(recent)?.let { -it } ?: return null
if (!rate.isFinite() || rate < RATE_MIN || rate > RATE_MAX) return null
return rate
}
/**
* Least-squares slope of RISING [samples] (a charging pod) as a positive charge rate in
* fraction/hour, with the same window guards as the drain fit but charge-tuned thresholds.
*/
fun chargeSlopeFractionPerHour(samples: List<DrainSample>): Float? {
val recent = recentWindow(samples, MIN_SAMPLES_CHARGE) ?: return null
if (recent.last().fraction - recent.first().fraction < MIN_TOTAL_RISE) return null
val rate = regressionSlopePerHour(recent) ?: return null
if (!rate.isFinite() || rate < CHARGE_RATE_MIN || rate > CHARGE_RATE_MAX) return null
return rate
}
/**
* The regimes of a lithium CC/CV charge. Constant-current bulk is fast and roughly linear;
* once the cell hits its voltage ceiling the charger switches to constant-voltage and the
* current decays, ending in a slow trickle. One linear rate over-promises badly above ~80%,
* so each band learns its own rate.
*
* Band boundaries and seeds are grounded rather than guessed:
* - The 80% bulk boundary is Apple's own documented behavior for their devices ("uses fast
* charging to quickly reach 80% ... then switches to slower trickle charging",
* apple.com/batteries/why-lithium-ion, mirrored in the iPhone user guide).
* - Battery-charging literature places the CC->CV transition at ~75-85% SoC (lower at high
* charge currents) and describes the CV current as decaying until cutoff, with the
* saturation stage taking a large share of total charge time (Battery University BU-409,
* "Charging Lithium-ion").
* - Two CV bands, not more: the CV current decays continuously, so finer bands would model
* noise while multiplying the state to learn. With the 0.5/0.3 seeds below, the last 20%
* takes ~40% of the seeded total charge time — inside BU-409's reported envelope.
*
* [specMultiplier] scales the spec-derived seed for the band: Apple's quick-charge claims
* ("5 minutes = ~1 hour of listening") measure the bulk phase, so seeding the taper/trickle
* bands from them needs the CV haircut. Applied ONLY to the spec seed — measured or learned
* rates already reflect where they were observed.
*/
enum class ChargeBand(val from: Float, val to: Float, val specMultiplier: Float) {
BULK(0.0f, 0.8f, 1.0f),
TAPER(0.8f, 0.9f, 0.5f),
TRICKLE(0.9f, 1.0f, 0.3f),
}
/**
* Least-squares charge rate fitted ONLY to the recent samples inside [band], or null when the
* band lacks coverage. Narrow bands accept two points (a full BLE band is exactly two ticks);
* the wide bulk band keeps the regular sample requirement.
*/
fun chargeBandSlopeFractionPerHour(samples: List<DrainSample>, band: ChargeBand): Float? {
val newestMs = samples.lastOrNull()?.atElapsedMs ?: return null
val recent = samples.filter {
newestMs - it.atElapsedMs <= MAX_SAMPLE_AGE_MS && it.fraction in band.from..band.to
}
val minSamples = if (band == ChargeBand.BULK) MIN_SAMPLES_CHARGE else MIN_SAMPLES_CHARGE_NARROW
if (recent.size < minSamples) return null
if (recent.last().atElapsedMs - recent.first().atElapsedMs < MIN_SPAN_MS) return null
if (recent.last().fraction - recent.first().fraction < MIN_BAND_RISE) return null
val rate = regressionSlopePerHour(recent) ?: return null
// The taper/trickle bands are legitimately slower than any plausible bulk rate.
val floor = CHARGE_RATE_MIN * band.specMultiplier
return rate.takeIf { it.isFinite() && it >= floor && it <= CHARGE_RATE_MAX }
}
/**
* Minutes until [levelFraction] reaches full, walking the remaining [ChargeBand]s at
* [rateForBand]'s per-band rates (partial current band + all bands above it). Null when the
* level is already in the suppression sliver ([NEAR_FULL_SUPPRESS]), any needed band has no
* usable rate, or the result is implausible.
*/
fun minutesUntilFull(levelFraction: Float, rateForBand: (ChargeBand) -> Float?): Int? {
if (!levelFraction.isFinite() || levelFraction < 0f) return null
if (levelFraction >= NEAR_FULL_SUPPRESS) return null
var totalMinutes = 0.0
for (band in ChargeBand.entries) {
val start = maxOf(levelFraction, band.from)
val missing = band.to - start
if (missing <= 0f) continue
val rate = rateForBand(band) ?: return null
if (rate <= 0f) return null
totalMinutes += missing / rate * 60.0
}
return totalMinutes.roundToInt().takeIf { it in 1..MAX_MINUTES }
}
/**
* How long the level may sit unchanged while charging before the "until charged" estimate is
* considered stalled (Optimized Battery Charging hold, trickle phase, or a dead reading) and
* suppressed. Granularity-aware: at [chargeFractionPerHour] a single visible step of
* [stepFraction] (1% on AAP, 10% on BLE) takes `step/rate` hours — a slow BLE charge legitimately
* shows no change for ~20 minutes, so a fixed timeout would falsely suppress it.
*/
fun chargeStallThresholdMs(chargeFractionPerHour: Float, stepFraction: Float): Long {
if (chargeFractionPerHour <= 0f) return CHARGE_STALL_FLOOR_MS
val stepMs = (stepFraction.toDouble() / chargeFractionPerHour * 3_600_000).roundToLong()
return maxOf(CHARGE_STALL_FLOOR_MS, stepMs * 3 / 2)
}
/** Samples within [MAX_SAMPLE_AGE_MS] of the newest, or null when count/span guards fail. */
private fun recentWindow(samples: List<DrainSample>, minSamples: Int): List<DrainSample>? {
if (samples.size < minSamples) return null
// Restrict to the recent window so a gap before the newest sample can't span the fit.
val newestMs = samples.last().atElapsedMs
val recent = samples.filter { newestMs - it.atElapsedMs <= MAX_SAMPLE_AGE_MS }
if (recent.size < minSamples) return null
if (recent.last().atElapsedMs - recent.first().atElapsedMs < MIN_SPAN_MS) return null
return recent
}
/** Signed least-squares slope (fraction per hour) over [recent]; negative when draining. */
private fun regressionSlopePerHour(recent: List<DrainSample>): Float? {
val first = recent.first()
val n = recent.size.toDouble()
var sumX = 0.0
var sumY = 0.0
var sumXY = 0.0
var sumXX = 0.0
for (s in recent) {
val x = (s.atElapsedMs - first.atElapsedMs) / 3_600_000.0 // hours since first
val y = s.fraction.toDouble()
sumX += x
sumY += y
sumXY += x * y
sumXX += x * x
}
val denominator = n * sumXX - sumX * sumX
if (abs(denominator) < 1e-9) return null
return ((n * sumXY - sumX * sumY) / denominator).toFloat()
}
/**
* Minutes until [levelFraction] reaches empty at [fractionPerHour], or null if the rate is
* non-positive or the result is implausible (<=0 or above [MAX_MINUTES]).
*/
fun minutesRemaining(levelFraction: Float, fractionPerHour: Float): Int? {
if (fractionPerHour <= 0f || !levelFraction.isFinite() || levelFraction <= 0f) return null
val minutes = (levelFraction / fractionPerHour * 60.0).roundToInt()
return minutes.takeIf { it in 1..MAX_MINUTES }
}
/**
* Exponential moving average over the displayed minutes, to avoid a jumpy number. Uses the
* asymmetric [MINUTES_ALPHA_DOWN]/[MINUTES_ALPHA_UP] factors by default; pass an explicit [alpha]
* to force a symmetric factor (used by tests).
*/
fun blendMinutes(previous: Int?, next: Int, alpha: Float? = null): Int {
if (previous == null) return next
val a = alpha ?: if (next < previous) MINUTES_ALPHA_DOWN else MINUTES_ALPHA_UP
return (next * a + previous * (1f - a)).roundToInt()
}
/** Exponential moving average over the persisted learned rate across sessions. */
fun blendRate(previous: Float?, next: Float, alpha: Float = LEARN_ALPHA): Float =
if (previous == null) next else next * alpha + previous * (1f - alpha)
}
@@ -0,0 +1,70 @@
package eu.darken.capod.monitor.core.battery
import eu.darken.capod.common.serialization.InstantEpochMillisSerializer
import eu.darken.capod.pods.core.apple.PodModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.Instant
/**
* Persisted drain-rate knowledge for a single device profile, keyed per ANC-mode bucket AND per pod
* — map key is `"<bucket>/<slot>"`, e.g. `"ON/LEFT"`, `"UNKNOWN/HEADSET"`. Drain differs sharply by
* both mode and pod (the mic pod drains faster), so each is learned independently. Seeds the
* time-remaining estimate immediately on reconnect, before the live session has enough samples.
*
* [chargeRates] is the charging counterpart, keyed per slot only (`"LEFT"` / `"RIGHT"` /
* `"HEADSET"`) — the ANC mode is irrelevant while a pod sits in the case.
*
* [model] tags which [PodModel] the rates were learned on, so a profile re-assigned to different
* hardware doesn't inherit the old device's rates (see [matchesModel]).
*/
@Serializable
data class DrainProfile(
@SerialName("model") val model: String? = null,
@SerialName("rates") val rates: Map<String, LearnedRate> = emptyMap(),
@SerialName("chargeRates") val chargeRates: Map<String, LearnedRate> = emptyMap(),
/**
* Per-band charge rates, `"<slot>" -> "<band>" -> rate` with band names from
* [DrainModel.ChargeBand]. Charging is nonlinear (fast bulk, slow taper/trickle), so each
* regime learns its own rate; [chargeRates] stays as the whole-session scalar fallback.
*/
@SerialName("chargeBands") val chargeBands: Map<String, Map<String, LearnedRate>> = emptyMap(),
/**
* Drain rates learned ONLY while the pod was worn and audio was actually playing on this
* device — same `"<bucket>/<slot>"` keys as [rates]. Apple's battery ratings are listening
* figures, so the battery-health estimate compares against these; the general [rates]
* (which include idle wear) keep powering the time-remaining estimate.
*/
@SerialName("listeningRates") val listeningRates: Map<String, LearnedRate> = emptyMap(),
) {
@Serializable
data class LearnedRate(
/** Drain (or charge) rate in fraction/hour (e.g. 0.169 = 16.9 %/hr). */
@SerialName("fractionPerHour") val fractionPerHour: Float,
@SerialName("sampleCount") val sampleCount: Int,
/**
* How many distinct sessions have blended into this rate. [sampleCount] is only the window
* size at the LAST save, so this is the actual accumulated-evidence signal (used e.g. to
* gate the derived battery-health figure).
*/
@SerialName("updateCount") val updateCount: Int = 1,
@Serializable(with = InstantEpochMillisSerializer::class)
@SerialName("updatedAt") val updatedAt: Instant,
)
/**
* Whether these learned rates apply to [model]. An untagged profile or an UNKNOWN model on
* either side is treated as matching — only a definite known-A vs known-B mismatch (the user
* re-pointed the profile at different hardware) disqualifies the data.
*/
fun matchesModel(model: PodModel): Boolean =
this.model == null ||
model == PodModel.UNKNOWN ||
this.model == PodModel.UNKNOWN.name ||
this.model == model.name
companion object {
/** Bucket key for rates learned while the ANC mode wasn't known (BLE-only sessions). */
const val BUCKET_UNKNOWN = "UNKNOWN"
}
}
@@ -29,10 +29,13 @@ import eu.darken.capod.main.core.MonitorMode
import eu.darken.capod.main.core.PermissionTool
import eu.darken.capod.monitor.core.DeviceMonitor
import eu.darken.capod.monitor.core.MonitorCoroutineScope
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.monitor.core.battery.BatteryEstimator
import eu.darken.capod.monitor.core.battery.displayKey
import eu.darken.capod.monitor.core.battery.estimateFor
import eu.darken.capod.monitor.core.MonitorModeResolver
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.ble.BlePodMonitor
import eu.darken.capod.monitor.core.primaryDevice
import eu.darken.capod.monitor.ui.MonitorNotifications
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.aap.AapConnectionManager
@@ -88,6 +91,7 @@ class MonitorService : Service() {
@Inject lateinit var profilesRepo: DeviceProfilesRepo
@Inject lateinit var aapConnectionManager: AapConnectionManager
@Inject lateinit var monitorModeResolver: MonitorModeResolver
@Inject lateinit var batteryEstimator: BatteryEstimator
private val monitorScope = MonitorCoroutineScope()
private var monitoringJob: Job? = null
@@ -211,7 +215,7 @@ class MonitorService : Service() {
return
}
val deviceFlow = deviceMonitor.primaryDevice()
val deviceFlow = deviceMonitor.primaryDeviceByTier
.setupCommonEventHandlers(TAG) { "BlePodMonitor" }
.distinctUntilChangedBy { it?.toNotificationKey() }
.throttleLatest(1000)
@@ -223,16 +227,34 @@ class MonitorService : Service() {
NotificationSettings(useExtraNotification = useExtra, keepAfterDisconnect = keepAfter)
}
val monitorJob = combine(deviceFlow, notificationSettingsFlow) { currentDevice, settings ->
currentDevice to settings
val monitorJob = combine(
deviceFlow,
notificationSettingsFlow,
batteryEstimator.estimates,
) { currentDevice, settings, estimates ->
NotificationInput(
device = currentDevice,
settings = settings,
estimate = currentDevice?.let { estimates.estimateFor(it) },
)
}
.onEach { (currentDevice, settings) ->
// The estimates map churns on fields the notification doesn't display (and for other
// profiles' devices) — only re-notify when something visible changed.
.distinctUntilChangedBy { input ->
Triple(
input.device?.toNotificationKey(),
input.settings,
input.device?.let { input.estimate?.displayKey(it) },
)
}
.onEach { (currentDevice, settings, estimate) ->
latestNotificationSettings = settings
notificationManager.notify(
MonitorNotifications.NOTIFICATION_ID,
notifications.getNotification(
currentDevice,
estimate = estimate,
showHint = settings.useExtraNotification,
),
)
@@ -240,7 +262,7 @@ class MonitorService : Service() {
when (val action = decideExtraNotificationAction(currentDevice, settings)) {
is ExtraNotificationAction.Post -> notificationManager.notify(
MonitorNotifications.NOTIFICATION_ID_CONNECTED,
notifications.getNotificationConnected(action.device),
notifications.getNotificationConnected(action.device, estimate),
)
ExtraNotificationAction.Cancel -> notificationManager.cancel(
MonitorNotifications.NOTIFICATION_ID_CONNECTED
@@ -359,6 +381,11 @@ class MonitorService : Service() {
.catch { log(TAG, WARN) { "conversationReaction failed:\n${it.asLog()}" } }
.launchIn(monitorScope)
batteryEstimator.monitor()
.setupCommonEventHandlers(TAG) { "batteryEstimator" }
.catch { log(TAG, WARN) { "batteryEstimator failed:\n${it.asLog()}" } }
.launchIn(monitorScope)
log(TAG, VERBOSE) { "Monitor job is active" }
monitorJob.join()
log(TAG, VERBOSE) { "Monitor job quit" }
@@ -424,6 +451,12 @@ internal fun buildMonitorModeState(
hasAapSession = aapStates.isNotEmpty(),
)
internal data class NotificationInput(
val device: PodDevice?,
val settings: NotificationSettings,
val estimate: BatteryEstimate?,
)
private data class NotificationDeviceKey(
val profileId: String?,
val label: String?,
@@ -443,6 +476,7 @@ private data class NotificationDeviceKey(
val isLeftInEar: Boolean?,
val isRightInEar: Boolean?,
val isBeingWorn: Boolean?,
val batteryEstimateEnabled: Boolean,
val iconRes: Int,
val leftPodIcon: Int,
val rightPodIcon: Int,
@@ -468,6 +502,7 @@ private fun PodDevice.toNotificationKey(): NotificationDeviceKey = NotificationD
isLeftInEar = isLeftInEar,
isRightInEar = isRightInEar,
isBeingWorn = isBeingWorn,
batteryEstimateEnabled = batteryEstimateEnabled,
iconRes = iconRes,
leftPodIcon = leftPodIcon,
rightPodIcon = rightPodIcon,
@@ -6,7 +6,10 @@ import android.widget.RemoteViews
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.R
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.monitor.core.battery.displayMinutes
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.ble.formatBatteryDurationShort
import eu.darken.capod.pods.core.apple.ble.formatBatteryPercent
import eu.darken.capod.pods.core.apple.ble.getBatteryDrawable
import eu.darken.capod.pods.core.apple.ble.isKnownBattery
@@ -68,12 +71,10 @@ class MonitorNotificationViewFactory @Inject constructor(
if (device.hasEarDetection) {
setViewVisibility(R.id.headphones_worn, if (device.isBeingWorn == true) View.VISIBLE else View.GONE)
}
if (device.isHeadsetBeingCharged != null) {
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
private fun createUnknownDevice(device: PodDevice): RemoteViews = RemoteViews(
@@ -83,13 +84,13 @@ class MonitorNotificationViewFactory @Inject constructor(
setTextViewText(R.id.device, device.getLabel(context))
}
fun createBigContentView(device: PodDevice): RemoteViews = when {
device.hasDualPods -> createDualPodsBig(device)
device.model != PodModel.UNKNOWN -> createSinglePodBig(device)
fun createBigContentView(device: PodDevice, estimate: BatteryEstimate? = null): RemoteViews = when {
device.hasDualPods -> createDualPodsBig(device, estimate)
device.model != PodModel.UNKNOWN -> createSinglePodBig(device, estimate)
else -> createUnknownDeviceBig(device)
}
private fun createDualPodsBig(device: PodDevice): RemoteViews = RemoteViews(
private fun createDualPodsBig(device: PodDevice, estimate: BatteryEstimate?): RemoteViews = RemoteViews(
context.packageName,
R.layout.monitor_notification_dual_pods_big
).apply {
@@ -97,7 +98,11 @@ class MonitorNotificationViewFactory @Inject constructor(
val leftPercent = device.batteryLeft
setImageViewResource(R.id.pod_left_icon, device.leftPodIcon)
setProgressBar(R.id.pod_left_progress, 100, percentToInt(leftPercent), false)
setTextViewText(R.id.pod_left_label, formatBatteryPercent(context, leftPercent))
setTextViewText(
R.id.pod_left_label,
formatBatteryPercent(context, leftPercent) +
estimateSuffix(estimate?.left, device.isLeftPodCharging == true)
)
val isLeftPodCharging = device.isLeftPodCharging ?: false
setViewVisibility(R.id.pod_left_charging, if (isLeftPodCharging) View.VISIBLE else View.GONE)
val isLeftPodInEar = device.isLeftInEar ?: false
@@ -117,14 +122,18 @@ class MonitorNotificationViewFactory @Inject constructor(
val rightPercent = device.batteryRight
setImageViewResource(R.id.pod_right_icon, device.rightPodIcon)
setProgressBar(R.id.pod_right_progress, 100, percentToInt(rightPercent), false)
setTextViewText(R.id.pod_right_label, formatBatteryPercent(context, rightPercent))
setTextViewText(
R.id.pod_right_label,
formatBatteryPercent(context, rightPercent) +
estimateSuffix(estimate?.right, device.isRightPodCharging == true)
)
val isRightPodCharging = device.isRightPodCharging ?: false
setViewVisibility(R.id.pod_right_charging, if (isRightPodCharging) View.VISIBLE else View.GONE)
val isRightPodInEar = device.isRightInEar ?: false
setViewVisibility(R.id.pod_right_ear, if (isRightPodInEar) View.VISIBLE else View.GONE)
}
private fun createSinglePodBig(device: PodDevice): RemoteViews = RemoteViews(
private fun createSinglePodBig(device: PodDevice, estimate: BatteryEstimate?): RemoteViews = RemoteViews(
context.packageName,
R.layout.monitor_notification_single_pods_big
).apply {
@@ -132,16 +141,18 @@ class MonitorNotificationViewFactory @Inject constructor(
setTextViewText(R.id.headphones_label, device.getLabel(context))
setImageViewResource(R.id.headphones_icon, device.iconRes)
setProgressBar(R.id.headphones_battery_progress, 100, percentToInt(headsetPercent), false)
setTextViewText(R.id.headphones_battery_label, formatBatteryPercent(context, headsetPercent))
setTextViewText(
R.id.headphones_battery_label,
formatBatteryPercent(context, headsetPercent) +
estimateSuffix(estimate?.headset, device.isHeadsetBeingCharged == true)
)
if (device.hasEarDetection) {
setViewVisibility(R.id.headphones_worn, if (device.isBeingWorn == true) View.VISIBLE else View.GONE)
}
if (device.isHeadsetBeingCharged != null) {
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
private fun createUnknownDeviceBig(device: PodDevice): RemoteViews = RemoteViews(
@@ -154,4 +165,7 @@ class MonitorNotificationViewFactory @Inject constructor(
private fun percentToInt(percent: Float): Int =
if (isKnownBattery(percent)) (percent * 100).roundToInt().coerceIn(0, 100) else 0
private fun estimateSuffix(pod: BatteryEstimate.Pod?, charging: Boolean): String =
pod?.displayMinutes(charging)?.let { " · ${formatBatteryDurationShort(context, it)}" } ?: ""
}
@@ -16,6 +16,7 @@ import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.notifications.PendingIntentCompat
import eu.darken.capod.main.ui.MainActivity
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.battery.BatteryEstimate
import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.pods.core.apple.ble.formatBatteryPercent
import javax.inject.Inject
@@ -56,7 +57,8 @@ class MonitorNotifications @Inject constructor(
private fun getBuilder(
device: PodDevice?,
channelId: String,
showHint: Boolean = false
estimate: BatteryEstimate? = null,
showHint: Boolean = false,
): NotificationCompat.Builder {
if (device == null) {
return baseBuilder(channelId).apply {
@@ -119,18 +121,21 @@ class MonitorNotifications @Inject constructor(
setStyle(NotificationCompat.DecoratedCustomViewStyle())
setCustomContentView(notificationViewFactory.createContentView(device))
setCustomBigContentView(notificationViewFactory.createBigContentView(device))
setCustomBigContentView(notificationViewFactory.createBigContentView(device, estimate))
setContentTitle("$batteryText ~ $stateText")
setSubText(null)
log(TAG, VERBOSE) { "updatingNotification(): $device" }
}
}
fun getNotification(podDevice: PodDevice?, showHint: Boolean = false): Notification =
getBuilder(podDevice, NOTIFICATION_CHANNEL_ID, showHint).build()
fun getNotification(
podDevice: PodDevice?,
estimate: BatteryEstimate? = null,
showHint: Boolean = false,
): Notification = getBuilder(podDevice, NOTIFICATION_CHANNEL_ID, estimate, showHint).build()
fun getNotificationConnected(podDevice: PodDevice?): Notification =
getBuilder(podDevice, NOTIFICATION_CHANNEL_ID_CONNECTED).build()
fun getNotificationConnected(podDevice: PodDevice?, estimate: BatteryEstimate? = null): Notification =
getBuilder(podDevice, NOTIFICATION_CHANNEL_ID_CONNECTED, estimate).build()
fun getStartupNotification(): Notification =
getBuilder(null, NOTIFICATION_CHANNEL_ID).build()
@@ -14,6 +14,7 @@ enum class PodModel(
@DrawableRes val leftPodIconRes: Int? = null,
@DrawableRes val rightPodIconRes: Int? = null,
@DrawableRes val caseIconRes: Int? = null,
val batterySpec: BatterySpec? = null,
) {
@SerialName("airpods.gen1")
AIRPODS_GEN1(
@@ -26,6 +27,7 @@ enum class PodModel(
hasMicrophoneMode = true,
hasEarDetectionToggle = true,
),
batterySpec = BatterySpec(listeningHoursAncOff = 5f, chargeFractionPerHour = 2.4f),
modelNumbers = setOf("A1523", "A1722"), // L/R earphones
leftPodIconRes = R.drawable.device_airpods_gen1_left,
rightPodIconRes = R.drawable.device_airpods_gen1_right,
@@ -43,6 +45,7 @@ enum class PodModel(
hasMicrophoneMode = true,
hasEarDetectionToggle = true,
),
batterySpec = BatterySpec(listeningHoursAncOff = 5f, chargeFractionPerHour = 2.4f),
modelNumbers = setOf("A2031", "A2032"), // L/R earphones
leftPodIconRes = R.drawable.device_airpods_gen1_left,
rightPodIconRes = R.drawable.device_airpods_gen1_right,
@@ -64,6 +67,7 @@ enum class PodModel(
hasMicrophoneMode = true,
hasEarDetectionToggle = true,
),
batterySpec = BatterySpec(listeningHoursAncOff = 6f, chargeFractionPerHour = 2.0f),
modelNumbers = setOf("A2564", "A2565"), // L/R earphones
leftPodIconRes = R.drawable.device_airpods_gen3_left,
rightPodIconRes = R.drawable.device_airpods_gen3_right,
@@ -86,6 +90,7 @@ enum class PodModel(
hasEarDetectionToggle = true,
hasSleepDetection = true,
),
batterySpec = BatterySpec(listeningHoursAncOff = 5f, chargeFractionPerHour = 2.4f),
modelNumbers = setOf("A3050", "A3053", "A3054"), // earphones
leftPodIconRes = R.drawable.device_airpods_gen3_left,
rightPodIconRes = R.drawable.device_airpods_gen3_right,
@@ -117,6 +122,7 @@ enum class PodModel(
hasStemConfig = true,
hasSleepDetection = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 4f, listeningHoursAncOff = 5f, chargeFractionPerHour = 2.4f),
modelNumbers = setOf("A3055", "A3056", "A3057"), // earphones
leftPodIconRes = R.drawable.device_airpods_gen4anc_left,
rightPodIconRes = R.drawable.device_airpods_gen4anc_right,
@@ -142,6 +148,7 @@ enum class PodModel(
hasListeningModeCycle = true,
hasAllowOffOption = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 4.5f, listeningHoursAncOff = 5f, chargeFractionPerHour = 2.4f),
modelNumbers = setOf("A2083", "A2084"), // L/R earphones
leftPodIconRes = R.drawable.device_airpods_pro2_left,
rightPodIconRes = R.drawable.device_airpods_pro2_right,
@@ -175,6 +182,7 @@ enum class PodModel(
hasStemConfig = true,
hasSleepDetection = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 6f, chargeFractionPerHour = 2.0f),
modelNumbers = setOf("A2698", "A2699", "A2931"), // earphones
leftPodIconRes = R.drawable.device_airpods_pro2_left,
rightPodIconRes = R.drawable.device_airpods_pro2_right,
@@ -208,6 +216,7 @@ enum class PodModel(
hasStemConfig = true,
hasSleepDetection = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 6f, chargeFractionPerHour = 2.0f),
modelNumbers = setOf("A3047", "A3048", "A3049"), // earphones
leftPodIconRes = R.drawable.device_airpods_pro2_left,
rightPodIconRes = R.drawable.device_airpods_pro2_right,
@@ -242,6 +251,7 @@ enum class PodModel(
hasSleepDetection = true,
hasDynamicEndOfCharge = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 8f, chargeFractionPerHour = 1.5f),
modelNumbers = setOf("A3063", "A3064", "A3065"), // earphones
leftPodIconRes = R.drawable.device_airpods_pro2_left,
rightPodIconRes = R.drawable.device_airpods_pro2_right,
@@ -262,6 +272,7 @@ enum class PodModel(
hasListeningModeCycle = true,
hasAllowOffOption = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 20f, chargeFractionPerHour = 0.9f),
modelNumbers = setOf("A2096"), // headphones
),
@@ -279,6 +290,7 @@ enum class PodModel(
hasListeningModeCycle = true,
hasAllowOffOption = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 20f, chargeFractionPerHour = 0.9f),
modelNumbers = setOf("A3184"), // headphones
),
@@ -300,6 +312,7 @@ enum class PodModel(
hasListeningModeCycle = true,
hasAllowOffOption = true,
),
batterySpec = BatterySpec(listeningHoursAncOn = 20f, chargeFractionPerHour = 0.9f),
modelNumbers = setOf("A3454"), // headphones
),
@@ -580,4 +593,27 @@ enum class PodModel(
*/
val hasDynamicEndOfCharge: Boolean = false,
)
/**
* Apple's published single-charge listening-time ratings, in hours, used to seed the battery
* time-remaining estimate before observed drain is available and as an upper bound on it. These
* are the POD-ONLY listening figures from Apple's tech-spec pages — NOT the "with charging case"
* aggregate. Left null for models Apple doesn't publish figures for (Beats, unknown), which simply
* fall back to purely observed drain. Figures verified against apple.com tech specs (2026-07).
*/
data class BatterySpec(
/** Listening hours with Active Noise Cancellation / Transparency active. */
val listeningHoursAncOn: Float? = null,
/** Listening hours with noise control off, when Apple publishes a distinct figure; else null. */
val listeningHoursAncOff: Float? = null,
/**
* Nominal charge rate (battery fraction gained per hour of charging), derived from Apple's
* published quick-charge claim: e.g. "5 minutes in the case provides around 1 hour of
* listening time" against a 6 h rating is (1/6) / (5/60) = 2.0/hr. Where the model has two
* listening ratings it's normalized against the LONGER one, so the seeded "until charged"
* never under-promises. Seeds the charge ETA on the very first charge, before any charge
* rate has been measured; a live fit takes over within minutes.
*/
val chargeFractionPerHour: Float? = null,
)
}
@@ -30,6 +30,17 @@ fun formatBatteryPercent(context: Context, percent: Float): String =
if (isKnownBattery(percent)) "${(percent * 100).roundToInt()}%"
else context.getString(R.string.general_value_not_available_label)
/** Formats an earbud time-remaining estimate as a compact duration, e.g. "2h 15m" / "5h" / "45m". */
fun formatBatteryDurationShort(context: Context, minutes: Int): String {
val hours = minutes / 60
val mins = minutes % 60
return when {
hours > 0 && mins > 0 -> context.getString(R.string.battery_time_remaining_format_hm, hours, mins)
hours > 0 -> context.getString(R.string.battery_time_remaining_format_h, hours)
else -> context.getString(R.string.battery_time_remaining_format_m, mins)
}
}
@DrawableRes
fun getBatteryDrawable(percent: Float): Int = when {
!isKnownBattery(percent) -> R.drawable.ic_baseline_battery_unknown_24
@@ -38,6 +38,8 @@ data class AppleDeviceProfile(
@SerialName("reactionNotifyWhenCharged") val notifyWhenCharged: Boolean = false,
@SerialName("reactionChargedThreshold") val chargedThreshold: Int = ReactionConfig.DEFAULT_CHARGED_THRESHOLD,
@SerialName("reactionChargedSlotScope") val chargedSlotScope: ChargedSlotScope = ChargedSlotScope.PODS_AND_CASE,
/** Whether the dashboard battery time-remaining estimate is shown for this device. */
@SerialName("batteryEstimateEnabled") val batteryEstimateEnabled: Boolean = true,
/**
* Last-known device-side AllowOffOption (AAP setting 0x34). Persisted so the UI can honor
* the learned value across sessions — AAP state is dropped on disconnect, but whether OFF
@@ -86,6 +88,7 @@ data class AppleDeviceProfile(
"notifyWhenCharged=$notifyWhenCharged, " +
"chargedThreshold=$chargedThreshold, " +
"chargedSlotScope=$chargedSlotScope, " +
"batteryEstimateEnabled=$batteryEstimateEnabled, " +
"learnedAllowOffEnabled=$learnedAllowOffEnabled, " +
"lastRequestedListeningModeCycleMask=$lastRequestedListeningModeCycleMask, " +
"stemActions=$stemActions" +
@@ -11,6 +11,7 @@ import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.serialization.SerializationCapod
import eu.darken.capod.main.core.GeneralSettings
import eu.darken.capod.monitor.core.battery.BatteryDrainStore
import eu.darken.capod.monitor.core.cache.DeviceStateCache
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
import kotlinx.serialization.json.Json
@@ -30,6 +31,7 @@ class DeviceProfilesRepo @Inject constructor(
private val generalSettings: GeneralSettings,
private val settings: DeviceProfilesSettings,
private val deviceStateCache: DeviceStateCache,
private val batteryDrainStore: BatteryDrainStore,
@SerializationCapod private val json: Json,
) {
@@ -183,6 +185,7 @@ class DeviceProfilesRepo @Inject constructor(
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
log(VERBOSE) { "Removed device profile with ID: $profileId" }
deviceStateCache.delete(profileId)
batteryDrainStore.delete(profileId)
}
suspend fun reorderProfilesById(orderedIds: List<ProfileId>) = mutex.withLock {
@@ -199,6 +202,7 @@ class DeviceProfilesRepo @Inject constructor(
suspend fun clear() = mutex.withLock {
settings.profiles.valueBlocking = DeviceProfilesContainer(emptyList())
deviceStateCache.deleteAll()
batteryDrainStore.deleteAll()
}
private fun checkAddressUniqueness(profile: DeviceProfile, existingProfiles: List<DeviceProfile>) {
@@ -11,7 +11,6 @@ import eu.darken.capod.common.flow.setupCommonEventHandlers
import eu.darken.capod.common.flow.withPrevious
import eu.darken.capod.monitor.core.DeviceMonitor
import eu.darken.capod.monitor.core.PodDevice
import eu.darken.capod.monitor.core.primaryDevice
import eu.darken.capod.pods.core.apple.ble.devices.DualApplePods
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@@ -35,7 +34,7 @@ class PopUpReaction @Inject constructor(
private val caseCoolDowns = java.util.concurrent.ConcurrentHashMap<String, Instant>()
private fun monitorCase(): Flow<Event> = deviceMonitor.primaryDevice()
private fun monitorCase(): Flow<Event> = deviceMonitor.primaryDeviceByTier
.distinctUntilChangedBy {
// Re-emit on profile changes (eligibility), raw BLE changes (content), AND the derived
// lid state. The latter is essential: caseLidState is recovered from history, so it can
@@ -122,7 +121,7 @@ class PopUpReaction @Inject constructor(
private fun monitorConnection(): Flow<Event> = combine(
bluetoothManager.connectedDevices,
deviceMonitor.primaryDevice().distinctUntilChangedBy {
deviceMonitor.primaryDeviceByTier.distinctUntilChangedBy {
Triple(it?.profileId, it?.reactions?.showPopUpOnConnection, it?.rawDataHex)
},
) { devices, broadcast ->
@@ -208,7 +207,7 @@ class PopUpReaction @Inject constructor(
* handles the normal close; a redundant Hide here is harmless ([PopUpWindow.close] is idempotent).
*/
private fun monitorCaseStaleClose(): Flow<Event> = combine(
deviceMonitor.primaryDevice(),
deviceMonitor.primaryDeviceByTier,
staleCheckTicker(),
) { device, _ -> isCaseOpenBroadcastFresh(device) }
.distinctUntilChanged()
@@ -45,7 +45,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
tools:text="95%" />
tools:text="95% · 5h 40m" />
<LinearLayout
android:layout_width="36dp"
@@ -163,7 +163,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
tools:text="92%" />
tools:text="92% · 5h 25m" />
<LinearLayout
android:layout_width="36dp"
@@ -64,7 +64,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
tools:text="95%" />
tools:text="95% · 14h 30m" />
<ImageView
android:id="@+id/headphones_charging"
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Algemene aanpassings wat die hele toepassing beïnvloed.</string>
<string name="settings_overview_hide_unmatched_label">Versteek onooreenstemmende toestelle</string>
<string name="settings_overview_hide_unmatched_description">Moenie naby toestelle wys wat nie ooreenstem met enige van jou profiele in die oorsig nie, bv. ander mense se AirPods.</string>
<string name="device_battery_estimate_toggle_label">Batteryskattings</string>
<string name="device_battery_estimate_card_desc">Leer hoe hierdie toestel laai en ontlaai om oorblywende tyd, laadtyd en batterygesondheid in te skat.</string>
<string name="device_battery_estimate_reset_action">Geleerde gegevens herstel</string>
<string name="device_battery_estimate_reset_desc">Begin opnuut van die gegradeerde batterylewensduur.</string>
<string name="device_battery_estimate_reset_confirm_title">Geleerde gegevens herstel?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod sal hierdie toestel se gemete afvoer, laadsnelheid en batterygesondheid vergeet en opnuut met die gegradeerde batterylewensduur begin.</string>
<string name="settings_acknowledgements_label">Erkennings</string>
<string name="settings_debug_autoreports_label">Outomatiese foutverslae</string>
<string name="settings_debug_autoreports_description">Rapporteer probleme outomaties, bv. besonderhede oor \'n toepassingstorting sodat ek kan uitvind hoe om dit reg te stel.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Agtergrond-deursigtigheid</string>
<string name="widget_config_show_device_label">Wys toestelnaam</string>
<string name="widget_config_reset_label">Stel terug na standaarde</string>
<string name="widget_config_preview_resize_hint">U kan die widget vergroot of verklein nadat u dit op u tuisskerm geplaas het.</string>
<string name="widget_config_preview_resize_hint">Jy kan die widget se grootte aanpas nadat jy dit op jou tuisskerm geplaas het.</string>
<string name="widget_config_custom_label">Aangepas</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Donker</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Laas gesien: %s</string>
<string name="first_seen_x">Eerste keer gesien: %s</string>
<string name="battery_cached_label">Laas bekend · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Wys kennisgewings</string>
<string name="permission_post_notifications_description">"Laat CAPod toe om kennisgewings oor jou AirPods te wys, bv. hul huidige status terwyl hulle gekoppel is."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Regter Pod Serienommer</string>
<string name="device_settings_info_left_bonded_label">Links Gekoppel</string>
<string name="device_settings_info_right_bonded_label">Regs Gekoppel</string>
<string name="device_settings_info_battery_health_label">Batterygesondheid (beraamde)</string>
<string name="device_settings_info_battery_health_left_label">Linker Batterygesondheid (beraamde)</string>
<string name="device_settings_info_battery_health_right_label">Regter Batterygesondheid (beraamde)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Besig om te bepaal, kom later terug</string>
<string name="device_settings_info_details_label">Toestelbesonderhede</string>
<string name="device_settings_info_details_action">Wys toestelbesonderhede</string>
<string name="device_settings_info_status_label">Status</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">ሙሉውን መተግበሪያ የሚነኩ አጠቃላይ ማስተካከያዎች።</string>
<string name="settings_overview_hide_unmatched_label">ያልተዛመዱ መሳሪያዎችን ደብቅ</string>
<string name="settings_overview_hide_unmatched_description">ከፕሮፋይሎቻቸው ጋር የማይዛመዱ አቅራቢያ ያሉ መሳሪያዎችን በዝርዝሩ ውስጥ አታሳይ፣ ለምሳሌ የሌሎች ሰዎች AirPods።</string>
<string name="device_battery_estimate_toggle_label">ባትሪ ግምቶች</string>
<string name="device_battery_estimate_card_desc">ይህ መሳሪያ እንዴት ሲሞላ እና እንደሚወድ በመማር ተቀሩትን ጊዜ፣ ሞላ ሲሳ የሚቆይ ጊዜ እና ባትሪ ጤናን ግምት ማድረግ ይችላሉ።</string>
<string name="device_battery_estimate_reset_action">የተማረውን ዳታ ዳግም ያስጀምሩ</string>
<string name="device_battery_estimate_reset_desc">ተወሰነ ባትሪ ዕድሜ ሪጀምር።</string>
<string name="device_battery_estimate_reset_confirm_title">የተማረውን ዳታ ዳግም ያስጀምራሉ?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod ይህ መሳሪያ የተለካው ወደዳ፣ ሙሎት ፍጥነት እና ባትሪ ጤናን ይረሳል እና ተወሰነ ባትሪ ዕድሜ ሪጀምራል።</string>
<string name="settings_acknowledgements_label">ምስጋናዎች</string>
<string name="settings_debug_autoreports_label">ራስ-ሰር የሳንካ ሪፖርቶች</string>
<string name="settings_debug_autoreports_description">ችግሮችን በራስ-ሰር ሪፖርት ያደርጋል፣ ለምሳሌ የመተግበሪያ ብልሽት ዝርዝሮችን እንዴት ማስተካከል እንዳለብኝ እንድገነዘብ።</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">የዳራ ግልጽነት</string>
<string name="widget_config_show_device_label">የመሳሪያ ስም አሳይ</string>
<string name="widget_config_reset_label">ወደ ነባሪ መመለስ</string>
<string name="widget_config_preview_resize_hint">ጄቱን በዋናው ስክሪን ላይ ስቀመጡ በኋላ መጠን መቀየር ይችላሉ።</string>
<string name="widget_config_preview_resize_hint">ጀትን በቤት ስክሪን ላይ ከአስቀመጡ በኋላ መጠንዋን መቀየር ይችላሉ።</string>
<string name="widget_config_custom_label">ብጁ</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">ጨለማ</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">ለመጨረሻ ጊዜ የታየው: %s</string>
<string name="first_seen_x">ለመጀመሪያ ጊዜ የታየው: %s</string>
<string name="battery_cached_label">የመጨረሻ ታወቀ · %s</string>
<string name="battery_time_remaining_format_hm">%1$dሰ %2$ደ</string>
<string name="battery_time_remaining_format_h">%1$dሰ</string>
<string name="battery_time_remaining_format_m">%1$dደ</string>
<string name="permission_post_notifications_label">ማሳወቂያዎችን አሳይ</string>
<string name="permission_post_notifications_description">"CAPod ስለ AirPodsዎ ማሳወቂያዎችን እንዲያሳይ ፍቀድ፣ ለምሳሌ ሲገናኙ የአሁኑ ሁኔታቸውን።"</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">የቀኝ ፖድ ተከታታይ ቅጥር</string>
<string name="device_settings_info_left_bonded_label">ግራ ተያይዟል</string>
<string name="device_settings_info_right_bonded_label">ቀኝ ተያይዟል</string>
<string name="device_settings_info_battery_health_label">ባትሪ ጤና (ግምታዊ)</string>
<string name="device_settings_info_battery_health_left_label">ግራ ባትሪ ጤና (ግምታዊ)</string>
<string name="device_settings_info_battery_health_right_label">ቀኝ ባትሪ ጤና (ግምታዊ)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">ገና ይወሰናል፣ በኋላ ይመልከቱ</string>
<string name="device_settings_info_details_label">የመሳሪያ ዝርዝሮች</string>
<string name="device_settings_info_details_action">የመሳሪያ ዝርዝሮችን አሳይ</string>
<string name="device_settings_info_status_label">ሁኔታ</string>
+27 -13
View File
@@ -6,14 +6,14 @@
<string name="general_copy_action">نسخ</string>
<string name="general_thank_you_label">شكرًا لك</string>
<string name="general_upgrade_action">ترقية</string>
<string name="common_feature_requires_pro_msg">رقّي للفتح.</string>
<string name="common_feature_requires_pro_msg">رقِّ للفتح.</string>
<string name="common_upgrade_required_label">يتطلب الترقية</string>
<string name="general_donate_action">تبرع</string>
<string name="general_check_action">تحقق</string>
<string name="general_donate_action">تبرُّع</string>
<string name="general_check_action">فحص</string>
<string name="general_close_action">إغلاق</string>
<string name="general_edit_action">تعديل</string>
<string name="general_save_action">حفظ</string>
<string name="general_guide_action">دليل</string>
<string name="general_guide_action">الدليل</string>
<string name="general_continue_action">متابعة</string>
<string name="general_show_action">عرض</string>
<string name="general_hide_action">إخفاء</string>
@@ -22,16 +22,16 @@
<string name="upgrade_capod_description">احصل على ميزات إضافية وادعم المطور.</string>
<string name="upgrade_preamble">تم تطوير تطبيق CAPod بواسطة شخص واحد. يتيح التحديث ميزات إضافية ويساعد في استمرار عمل التطبيق.</string>
<string name="upgrade_benefit_themes">تخصيص السمة</string>
<string name="upgrade_benefit_autoplay">التشغيل التلقائي &amp; الإيقاف التلقائي</string>
<string name="upgrade_benefit_popups">نافذة منبثقة عند فتح العلبة &amp; الاتصال</string>
<string name="upgrade_benefit_autoplay">التشغيل التلقائي والإيقاف التلقائي</string>
<string name="upgrade_benefit_popups">نافذة منبثقة عند فتح العلبة والاتصال</string>
<string name="upgrade_benefit_widgets">عناصر الشاشة الرئيسة</string>
<string name="upgrade_benefit_device_settings">إعدادات الجهاز المتقدّمة</string>
<string name="upgrade_benefit_device_controls">عناصر التحكم في الجهاز &#8212; الضوضاء، &amp; السيقان، &amp; المزيد</string>
<string name="upgrade_benefit_support">دعم المطوّر</string>
<string name="upgrade_benefit_disclaimer">يعتمد توفر الميزات على سمّاعات الرأس والجهاز المستخدَم.</string>
<string name="upgrade_screen_options_description">نفس الميزات، أسعار مختلفة. يشمل الاشتراك فترة تجريبية مجانية ويمكن إلغاؤه في أي وقت.</string>
<string name="upgrade_benefit_device_settings">إعدادات الجهاز المتقدمة</string>
<string name="upgrade_benefit_device_controls">عناصر التحكم في الجهاز &#8212; الضوضاء والسيقان والمزيد</string>
<string name="upgrade_benefit_support">ادعم المطور</string>
<string name="upgrade_benefit_disclaimer">يعتمد توفر الميزات على سماعات الرأس والجهاز المُستخدَم.</string>
<string name="upgrade_screen_options_description">نفس الميزات، بتسعير مختلف. يشمل الاشتراك تجربة مجانية ويمكن إلغاؤه في أي وقت.</string>
<string name="upgrade_screen_subscription_trial_action">بدء التجربة المجانية</string>
<string name="upgrade_screen_subscription_action">الاشتراك سنويًا</string>
<string name="upgrade_screen_subscription_action">الاشتراك سنويًّا</string>
<string name="upgrade_screen_subscription_action_hint">%s/سنة</string>
<string name="upgrade_screen_iap_action">الشراء مرة واحدة</string>
<string name="upgrade_screen_iap_action_hint">الشراء لمرة واحدة: %s</string>
@@ -122,6 +122,12 @@
<string name="settings_general_description">تعديلات عامة تؤثر على التطبيق بأكمله.</string>
<string name="settings_overview_hide_unmatched_label">إخفاء الأجهزة غير المتطابقة</string>
<string name="settings_overview_hide_unmatched_description">لا تعرض الأجهزة القريبة التي لا تتطابق مع أي من ملفات تعريفك في النظرة العامة، على سبيل المثال سمّاعات AirPods الخاصة بالآخرين.</string>
<string name="device_battery_estimate_toggle_label">تقديرات البطارية</string>
<string name="device_battery_estimate_card_desc">تعرّف على كيفية شحن هذا الجهاز وتفريغه لتقدير الوقت المتبقي، والوقت اللازم للشحن، وحالة البطارية.</string>
<string name="device_battery_estimate_reset_action">إعادة ضبط البيانات المُكتسَبة</string>
<string name="device_battery_estimate_reset_desc">البدء من جديد بناءً على عمر البطارية المُقدَّر.</string>
<string name="device_battery_estimate_reset_confirm_title">هل ترغب في إعادة ضبط البيانات المُكتسَبة؟</string>
<string name="device_battery_estimate_reset_confirm_message">سيتجاهل تطبيق CAPod معدل استهلاك البطارية المقاس لهذا الجهاز، وسرعة الشحن، وحالة البطارية، وسيبدأ من جديد بناءً على عمر البطارية المُقدَّر.</string>
<string name="settings_acknowledgements_label">شكر وتقدير</string>
<string name="settings_debug_autoreports_label">تقارير الأخطاء التلقائية</string>
<string name="settings_debug_autoreports_description">الإبلاغ تلقائيًا عن المشكلات، على سبيل المثال تفاصيل حول تعطل التطبيق حتى أتمكن من معرفة كيفية إصلاحه.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">شفافية الخلفية</string>
<string name="widget_config_show_device_label">إظهار اسم الجهاز</string>
<string name="widget_config_reset_label">إعادة تعيين إلى الإعدادات الافتراضية</string>
<string name="widget_config_preview_resize_hint">يمكنك تغيير حجم الأداة بعد وضعها على شاشتك الرئيسية.</string>
<string name="widget_config_preview_resize_hint">يمكنك تغيير حجم الأداة بعد وضعها على شاشتك الرئيسة.</string>
<string name="widget_config_custom_label">مخصص</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">مظلم</string>
@@ -286,6 +292,9 @@
<string name="last_seen_x">آخر ظهور: %s</string>
<string name="first_seen_x">أول ظهور: %s</string>
<string name="battery_cached_label">آخر معروف · %s</string>
<string name="battery_time_remaining_format_hm">%1$dس و%2$dد</string>
<string name="battery_time_remaining_format_h">%1$dس</string>
<string name="battery_time_remaining_format_m">%1$dد</string>
<string name="permission_post_notifications_label">إظهار الإشعارات</string>
<string name="permission_post_notifications_description">"السماح لـ CAPod بعرض إشعارات حول AirPods، مثل حالتها الحالية أثناء الاتصال."</string>
<!-- Device profiles -->
@@ -443,6 +452,11 @@
<string name="device_settings_info_right_serial_label">الرقم التسلسلي للسماعة اليمنى</string>
<string name="device_settings_info_left_bonded_label">اليسار مقترن</string>
<string name="device_settings_info_right_bonded_label">اليمين مقترن</string>
<string name="device_settings_info_battery_health_label">صحة البطارية (مُقدَّرة)</string>
<string name="device_settings_info_battery_health_left_label">صحة البطارية اليسرى (مُقدَّرة)</string>
<string name="device_settings_info_battery_health_right_label">صحة البطارية اليمنى (مُقدَّرة)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">لا يزال الأمر قيد التحديد، راجع لاحقًا</string>
<string name="device_settings_info_details_label">تفاصيل الجهاز</string>
<string name="device_settings_info_details_action">عرض تفاصيل الجهاز</string>
<string name="device_settings_info_status_label">الحالة</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Bütün tətbiqə təsir edən ümumi düzəlişlər.</string>
<string name="settings_overview_hide_unmatched_label">Uyğun olmayan cihazları gizlət</string>
<string name="settings_overview_hide_unmatched_description">İcmalda profillərinizə uyğun gəlməyən yaxın cihazları göstərməyin, məs. başqalarının AirPods-ları.</string>
<string name="device_battery_estimate_toggle_label">Batareya qiymətləndirmələri</string>
<string name="device_battery_estimate_card_desc">Bu cihazın necə doldurulduğunu və enerji istifadə etdiyini öyrənərək, qalan vaxtı, dolğunlaşma vaxtını və batareya sağlamlığını qiymətləndirin.</string>
<string name="device_battery_estimate_reset_action">Öyrənilmiş məlumatları sıfırla</string>
<string name="device_battery_estimate_reset_desc">Qiymətləndirilmiş batareya ömrünə qayıdıb yenidən başlayın.</string>
<string name="device_battery_estimate_reset_confirm_title">Öyrənilmiş məlumatları sıfırla?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod bu cihazın ölçülmüş enerji istifadəsini, dolğunlaşma sürətini və batareya sağlamlığını unudacaq, qiymətləndirilmiş batareya ömrünə qayıdıb yenidən başlayacaq.</string>
<string name="settings_acknowledgements_label">Təşəkkürlər</string>
<string name="settings_debug_autoreports_label">Avtomatik səhv hesabatları</string>
<string name="settings_debug_autoreports_description">Problemləri avtomatik olaraq bildirir, məsələn, tətbiqin çökməsi ilə bağlı təfərrüatları, beləliklə onu necə düzəldəcəyimi anlaya bilim.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Fon şəffaflığı</string>
<string name="widget_config_show_device_label">Cihaz adını göstər</string>
<string name="widget_config_reset_label">Varsayılan ayarlara sıfırla</string>
<string name="widget_config_preview_resize_hint">Vidceti ana ekranınıza yerləşdirdikdən sonra ölçüsünü dəyişə bilərsiniz.</string>
<string name="widget_config_preview_resize_hint">Vidcetin ana ekrana qoyduqdan sonra ölçüsünü dəyişə bilərsiniz.</string>
<string name="widget_config_custom_label">Fərdi</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Tünd</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Son görülmə: %s</string>
<string name="first_seen_x">İlk görülmə: %s</string>
<string name="battery_cached_label">Son məlum · %s</string>
<string name="battery_time_remaining_format_hm">%1$dsa %2$ddəq</string>
<string name="battery_time_remaining_format_h">%1$dsa</string>
<string name="battery_time_remaining_format_m">%1$ddəq</string>
<string name="permission_post_notifications_label">Bildirişlər göstər</string>
<string name="permission_post_notifications_description">"CAPod-a AirPods haqqında bildirişlər göstərməyə icazə verin, məsələn, qoşulduqda onların cari statusu."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Sağ Qulaqcıq Seriya Nömrəsi</string>
<string name="device_settings_info_left_bonded_label">Sol cütləşdirilmiş</string>
<string name="device_settings_info_right_bonded_label">Sağ cütləşdirilmiş</string>
<string name="device_settings_info_battery_health_label">Batareya Sağlığı (təxmini)</string>
<string name="device_settings_info_battery_health_left_label">Sol Batareya Sağlığı (təx.)</string>
<string name="device_settings_info_battery_health_right_label">Sağ Batareya Sağlığı (təx.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Hələ müəyyən edilir, sonra yoxlayın</string>
<string name="device_settings_info_details_label">Cihaz Ǝtraflı Məlumatları</string>
<string name="device_settings_info_details_action">Cihaz təfsilatlarını göstər</string>
<string name="device_settings_info_status_label">Vəziyyət</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Агульныя налады, якія ўплываюць на ўсю праграму.</string>
<string name="settings_overview_hide_unmatched_label">Хаваць непрызнаныя прылады</string>
<string name="settings_overview_hide_unmatched_description">Не паказваць у агляднічку блізкія прылады, якія не адпавядаюць ніводнаму з вашых профіляў, напр. AirPods іншых людзей.</string>
<string name="device_battery_estimate_toggle_label">Ацэнкі стану батарэі</string>
<string name="device_battery_estimate_card_desc">Даведайцеся, як гэтая прылада зараджаецца і разраджаецца, каб ацаніць час, які застаўся, час да поўнай зарадкі і здароўе батарэі.</string>
<string name="device_battery_estimate_reset_action">Скінуць вывучаныя даныя</string>
<string name="device_battery_estimate_reset_desc">Пачаць спачатку з намінальнага рэсурсу батарэі.</string>
<string name="device_battery_estimate_reset_confirm_title">Скінуць вывучаныя даныя?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod забудзе вымераныя для гэтай прылады хуткасць разраджэння, хуткасць зарадкі і здароўе батарэі, і пачне нанова з намінальнага рэсурсу батарэі.</string>
<string name="settings_acknowledgements_label">Падзякі</string>
<string name="settings_debug_autoreports_label">Аўтаматычныя справаздачы пра памылкі</string>
<string name="settings_debug_autoreports_description">Аўтаматычна паведамляе пра праблемы, напрыклад, звесткі пра збой праграмы, каб я мог зразумець, як гэта выправіць.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Прозрачнасць фона</string>
<string name="widget_config_show_device_label">Паказаць назву прылады</string>
<string name="widget_config_reset_label">Скінуць да змаўчання</string>
<string name="widget_config_preview_resize_hint">Вы можаце змяніць памер віджэта пасля размяшчэння яго на галоўным экране.</string>
<string name="widget_config_preview_resize_hint">Пасля размяшчэння віджэта на галоўным экране вы можаце змяніць яго памер.</string>
<string name="widget_config_custom_label">Адвольны</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Цёмны</string>
@@ -282,6 +288,9 @@
<string name="last_seen_x">Апошні раз бачана: %s</string>
<string name="first_seen_x">Упершыню бачана: %s</string>
<string name="battery_cached_label">Апошняе вядомае · %s</string>
<string name="battery_time_remaining_format_hm">%1$dг %2$dхв</string>
<string name="battery_time_remaining_format_h">%1$dг</string>
<string name="battery_time_remaining_format_m">%1$dхв</string>
<string name="permission_post_notifications_label">Паказваць апавяшчэнні</string>
<string name="permission_post_notifications_description">"Дазвольце CAPod паказваць апавяшчэнні пра вашы AirPods, напрыклад іх бягучы стан пры падключэнні."</string>
<!-- Device profiles -->
@@ -433,6 +442,11 @@
<string name="device_settings_info_right_serial_label">Сэрыйны нумар правага навушніка</string>
<string name="device_settings_info_left_bonded_label">Левы спалучаны</string>
<string name="device_settings_info_right_bonded_label">Правы спалучаны</string>
<string name="device_settings_info_battery_health_label">Здароўе батарэі (ацэнка)</string>
<string name="device_settings_info_battery_health_left_label">Здароўе левай батарэі (ацэнка)</string>
<string name="device_settings_info_battery_health_right_label">Здароўе правай батарэі (ацэнка)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Яшчэ вызначаецца, зайдзіце пазней</string>
<string name="device_settings_info_details_label">Дэталі прылады</string>
<string name="device_settings_info_details_action">Паказаць дэталі прылады</string>
<string name="device_settings_info_status_label">Стан</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Общи настройки, които засягат цялото приложение.</string>
<string name="settings_overview_hide_unmatched_label">Скриване на несъответстващи устройства</string>
<string name="settings_overview_hide_unmatched_description">Не показвай намиращи се наблизо устройства, които не съответстват на нито един от профилите ти в прегледа, напр. AirPods на други хора.</string>
<string name="device_battery_estimate_toggle_label">Оценки на батерията</string>
<string name="device_battery_estimate_card_desc">Научете как това устройство се зарежда и разходва, за да оцените оставащото време, времето до зареждане и състоянието на батерията.</string>
<string name="device_battery_estimate_reset_action">Нулирай научените данни</string>
<string name="device_battery_estimate_reset_desc">Начало отново от номиналния живот на батерията.</string>
<string name="device_battery_estimate_reset_confirm_title">Нулирай научените данни?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod ще забрави измереното потребление, скоростта на зареждане и състоянието на батерията на това устройство и ще начне отново от номиналния живот на батерията.</string>
<string name="settings_acknowledgements_label">Благодарности</string>
<string name="settings_debug_autoreports_label">Автоматични доклади за грешки</string>
<string name="settings_debug_autoreports_description">Автоматично докладва проблеми, напр. подробности за срив на приложението, за да мога да разбера как да го поправя.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Прозрачност на фона</string>
<string name="widget_config_show_device_label">Показване на името на устройството</string>
<string name="widget_config_reset_label">Възстановяване на стандартните настройки</string>
<string name="widget_config_preview_resize_hint">Можете да преоразмерите джаджата, след като я поставите на началния си екран.</string>
<string name="widget_config_preview_resize_hint">Можете да преоразмеряте виджета след поставянето му на начален екран.</string>
<string name="widget_config_custom_label">Персонализирано</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Тъмно</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Последно видяно: %s</string>
<string name="first_seen_x">Първо видяно: %s</string>
<string name="battery_cached_label">Последно известно · %s</string>
<string name="battery_time_remaining_format_hm">%1$dч %2$dм</string>
<string name="battery_time_remaining_format_h">%1$dч</string>
<string name="battery_time_remaining_format_m">%1$dм</string>
<string name="permission_post_notifications_label">Показване на известия</string>
<string name="permission_post_notifications_description">"Позволете на CAPod да показва известия за вашите AirPods, напр. текущото им състояние, докато са свързани."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Сериен номер на десния под</string>
<string name="device_settings_info_left_bonded_label">Ляво сдвоено</string>
<string name="device_settings_info_right_bonded_label">Дясно сдвоено</string>
<string name="device_settings_info_battery_health_label">Здравословно състояние на батерията (прогноза)</string>
<string name="device_settings_info_battery_health_left_label">Здравословно състояние на лявата батерия (прогноза)</string>
<string name="device_settings_info_battery_health_right_label">Здравословно състояние на дясната батерия (прогноза)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Все още се определя, проверете отново по-късно</string>
<string name="device_settings_info_details_label">Подробности за устройството</string>
<string name="device_settings_info_details_action">Покажи подробности за устройството</string>
<string name="device_settings_info_status_label">Статус</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">পুরো অ্যাপকে প্রভাবিত করে এমন সাধারণ পরিবর্তন।</string>
<string name="settings_overview_hide_unmatched_label">অমিল ডিভাইস লুকান</string>
<string name="settings_overview_hide_unmatched_description">ওভারভিউতে কাছের এমন ডিভাইস দেখাবে না যেগুলো আপনার কোনো প্রোফাইলের সাথে মেলে না, যেমন অন্যদের AirPods।</string>
<string name="device_battery_estimate_toggle_label">ব্যাটারি অনুমান</string>
<string name="device_battery_estimate_card_desc">এই ডিভাইসটি কীভাবে চার্জ হয় এবং কীভাবে খরচ হয় তা শিখুন যাতে অবশিষ্ট সময়, চার্জ হওয়ার সময় এবং ব্যাটারি স্বাস্থ্য অনুমান করতে পারেন।</string>
<string name="device_battery_estimate_reset_action">শেখা ডেটা রিসেট করুন</string>
<string name="device_battery_estimate_reset_desc">রেটেড ব্যাটারি আয়ু থেকে শুরু করুন।</string>
<string name="device_battery_estimate_reset_confirm_title">শেখা ডেটা রিসেট করুন?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod এই ডিভাইসের পরিমাপ করা ড্রেইন, চার্জিং গতি এবং ব্যাটারি স্বাস্থ্য ভুলে যাবে এবং রেটেড ব্যাটারি আয়ু থেকে শুরু করবে।</string>
<string name="settings_acknowledgements_label">স্বীকৃতি</string>
<string name="settings_debug_autoreports_label">স্বয়ংক্রিয় বাগ রিপোর্ট</string>
<string name="settings_debug_autoreports_description">স্বয়ংক্রিয়ভাবে সমস্যাগুলি রিপোর্ট করে, যেমন অ্যাপ ক্র্যাশের বিবরণ যাতে আমি এটি কীভাবে ঠিক করতে পারি তা বের করতে পারি।</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">পটভূমি স্বচ্ছতা</string>
<string name="widget_config_show_device_label">ডিভাইসের নাম দেখান</string>
<string name="widget_config_reset_label">ডিফল্টে পুনরায় সেট করুন</string>
<string name="widget_config_preview_resize_hint">আপনি হোম স্ক্রিনে রাখার পরে উইজেটটি পুনরায় আকার দিতে পারেন।</string>
<string name="widget_config_preview_resize_hint">আপনার হোম স্ক্রিনে উইজেটটি রাখার পরে আপনি এটির আকার পরিবর্তন করতে পারেন।</string>
<string name="widget_config_custom_label">কাস্টম</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">গাঢ়</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">সর্বশেষ দেখা: %s</string>
<string name="first_seen_x">প্রথম দেখা: %s</string>
<string name="battery_cached_label">শেষ জানা · %s</string>
<string name="battery_time_remaining_format_hm">%1$dঘ %2$dমি</string>
<string name="battery_time_remaining_format_h">%1$dঘ</string>
<string name="battery_time_remaining_format_m">%1$dমি</string>
<string name="permission_post_notifications_label">বিজ্ঞপ্তি দেখান</string>
<string name="permission_post_notifications_description">"CAPod-কে আপনার AirPods সম্পর্কে বিজ্ঞপ্তি দেখানোর অনুমতি দিন, যেমন সংযুক্ত থাকাকালীন তাদের বর্তমান অবস্থা।"</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">ডান পড সিরিয়াল</string>
<string name="device_settings_info_left_bonded_label">বাম পেয়ার করা</string>
<string name="device_settings_info_right_bonded_label">ডান পেয়ার করা</string>
<string name="device_settings_info_battery_health_label">ব্যাটারি স্বাস্থ্য (অনুমানিত)</string>
<string name="device_settings_info_battery_health_left_label">বাম ব্যাটারি স্বাস্থ্য (অনু.)</string>
<string name="device_settings_info_battery_health_right_label">ডান ব্যাটারি স্বাস্থ্য (অনু.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">এখনও নির্ধারণ করছি, পরে আবার চেক করুন</string>
<string name="device_settings_info_details_label">ডিভাইস বিস্তারিত</string>
<string name="device_settings_info_details_action">ডিভাইস বিস্তারিত দেখুন</string>
<string name="device_settings_info_status_label">স্ট্যাটাস</string>
+14
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Configuracions generals que afecten a tota l\'aplicació.</string>
<string name="settings_overview_hide_unmatched_label">Amaga els dispositius no coincidents</string>
<string name="settings_overview_hide_unmatched_description">No mostris els dispositius propers que no coincideixen amb cap dels perfils a la vista general. P. ex. els AirPods daltres persones.</string>
<string name="device_battery_estimate_toggle_label">Estimació de la bateria</string>
<string name="device_battery_estimate_card_desc">Obteniu informació sobre com es carrega i s\'esgota aquest dispositiu per estimar el temps restant, el temps fins que es carrega completament i l\'estat de la bateria.</string>
<string name="device_battery_estimate_reset_action">Restableix les dades apreses</string>
<string name="device_battery_estimate_reset_desc">Comença de nou des de la durada nominal de la bateria.</string>
<string name="device_battery_estimate_reset_confirm_title">Voleu restablir les dades apreses?</string>
<string name="device_battery_estimate_reset_confirm_message">El CAPod oblidarà el consum mesurat d\'aquest dispositiu, la velocitat de càrrega i la salut de la bateria, i tornarà a començar des de la durada nominal de la bateria.</string>
<string name="settings_acknowledgements_label">Agraïments</string>
<string name="settings_debug_autoreports_label">Informes automàtics d\'errors</string>
<string name="settings_debug_autoreports_description">Informa automàticament de problemes. Per exemple: detalls sobre un bloqueig de l\'aplicació perquè pugui esbrinar com solucionar-ho.</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Últim cop vist: %s</string>
<string name="first_seen_x">Primera vegada vist: %s</string>
<string name="battery_cached_label">Última vegada vist · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostra notificacions</string>
<string name="permission_post_notifications_description">"Permet que el CAPod mostri notificacions sobre els vostres AirPods, p. ex. el seu estat actual mentre estan connectats."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Número de sèrie de l\'auricular dret</string>
<string name="device_settings_info_left_bonded_label">Esquerre vinculat</string>
<string name="device_settings_info_right_bonded_label">Dret vinculat</string>
<string name="device_settings_info_battery_health_label">Salut de la bateria (estimat)</string>
<string name="device_settings_info_battery_health_left_label">Salut de la bateria esquerra (est.)</string>
<string name="device_settings_info_battery_health_right_label">Salut de la bateria dreta (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Encara s\'està determinant, torneu més tard</string>
<string name="device_settings_info_details_label">Detalls del dispositiu</string>
<string name="device_settings_info_details_action">Mostra els detalls del dispositiu</string>
<string name="device_settings_info_status_label">Estat</string>
+16 -2
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Obecná vylepšení, která ovlivňují celou aplikaci.</string>
<string name="settings_overview_hide_unmatched_label">Skrýt nepřiřazená zařízení</string>
<string name="settings_overview_hide_unmatched_description">V přehledu nezobrazovat blízká zařízení, která neodpovídají žádnému z vašich profilů, např. AirPods jiných lidí.</string>
<string name="device_battery_estimate_toggle_label">Odhady baterie</string>
<string name="device_battery_estimate_card_desc">Zjistěte, jak se toto zařízení nabíjí a vybíjí, abyste mohli odhadnout zbývající čas, čas do nabití a stav baterie.</string>
<string name="device_battery_estimate_reset_action">Obnovit naučená data</string>
<string name="device_battery_estimate_reset_desc">Začněte znovu od deklarované výdrže baterie.</string>
<string name="device_battery_estimate_reset_confirm_title">Obnovit naučená data?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod zapomene naměřený úbytek, rychlost nabíjení a stav baterie tohoto zařízení a začne znovu od deklarované výdrže baterie.</string>
<string name="settings_acknowledgements_label">Poděkování</string>
<string name="settings_debug_autoreports_label">Automatické hlášení chyb</string>
<string name="settings_debug_autoreports_description">Automatické hlášení chyb např. podrobnosti o pádech aplikace.</string>
@@ -282,6 +288,9 @@
<string name="last_seen_x">Naposledy spatřeno: %s</string>
<string name="first_seen_x">Poprvé spatřeno: %s</string>
<string name="battery_cached_label">Poslední známé \u00B7 %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Zobrazit oznámení</string>
<string name="permission_post_notifications_description">"Povolte aplikaci CAPod zobrazovat oznámení o sluchátkách AirPods, např. o jejich aktuálním stavu během připojení."</string>
<!-- Device profiles -->
@@ -433,6 +442,11 @@
<string name="device_settings_info_right_serial_label">Sériové číslo pravého sluchátka</string>
<string name="device_settings_info_left_bonded_label">Levé spárováno</string>
<string name="device_settings_info_right_bonded_label">Pravé spárováno</string>
<string name="device_settings_info_battery_health_label">Stav baterie (odhadovaný)</string>
<string name="device_settings_info_battery_health_left_label">Stav levé baterie (odh.)</string>
<string name="device_settings_info_battery_health_right_label">Stav pravé baterie (odh.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Stále se určuje, zkontrolujte později</string>
<string name="device_settings_info_details_label">Detaily zařízení</string>
<string name="device_settings_info_details_action">Zobrazit detaily zařízení</string>
<string name="device_settings_info_status_label">Stav</string>
@@ -514,8 +528,8 @@
<string name="reaction_sleep_notification_text">%1$s oznámil, že jste usnuli, takže vaše hudba byla pozastavena. Toto můžete vypnout v nastavení zařízení.</string>
<string name="reaction_charged_channel_label">Oznámení o nabití</string>
<string name="reaction_charged_notification_title">Nabíjení dokončeno</string>
<string name="reaction_charged_notification_text_full">%1$s je plně nabitý.</string>
<string name="reaction_charged_notification_text_partial">%1$s byl nabit na %2$d%%.</string>
<string name="reaction_charged_notification_text_full">%1$s je plně nabito.</string>
<string name="reaction_charged_notification_text_partial">%1$s nabito na %2$d%%.</string>
<string name="device_settings_rename_label">Přejmenovat</string>
<string name="device_settings_rename_hint">Název zařízení</string>
<string name="device_settings_rename_confirm">Přejmenovat</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Generelle justeringer, der påvirker hele appen.</string>
<string name="settings_overview_hide_unmatched_label">Skjul ikke-matchende enheder</string>
<string name="settings_overview_hide_unmatched_description">Vis ikke nærliggende enheder, der ikke matcher nogen af dine profiler i overblikkket, f.eks. andres AirPods.</string>
<string name="device_battery_estimate_toggle_label">Batteriestimater</string>
<string name="device_battery_estimate_card_desc">Lær, hvordan denne enhed oplades og aflades, for at estimere resterende tid, tid til opladning, og batteritilstand.</string>
<string name="device_battery_estimate_reset_action">Nulstil indlærte data</string>
<string name="device_battery_estimate_reset_desc">Start forfra fra den nominelle batterilevetid.</string>
<string name="device_battery_estimate_reset_confirm_title">Nulstil indlærte data?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod vil glemme denne enheds målte forbrug, opladningshastighed og batteritilstand, og starte forfra fra den nominelle batterilevetid.</string>
<string name="settings_acknowledgements_label">Anerkendelser</string>
<string name="settings_debug_autoreports_label">Automatiske fejlrapporter</string>
<string name="settings_debug_autoreports_description">Rapporterer automatisk problemer, f.eks. detaljer om et app-nedbrud, så jeg kan finde ud af, hvordan jeg løser det.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Baggrundsgennemsigtighed</string>
<string name="widget_config_show_device_label">Vis enhedsnavn</string>
<string name="widget_config_reset_label">Nulstil til standard</string>
<string name="widget_config_preview_resize_hint">Du kan ændre størrelsen på widgetten, efter du har placeret den på din startskærm.</string>
<string name="widget_config_preview_resize_hint">Du kan ændre størrelse på widgetten efter at have placeret den på din hjemmeskærm.</string>
<string name="widget_config_custom_label">Brugerdefineret</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Mørk</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Sidst set: %s</string>
<string name="first_seen_x">Først set: %s</string>
<string name="battery_cached_label">Sidst kendte · %s</string>
<string name="battery_time_remaining_format_hm">%1$dt %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dt</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Vis notifikationer</string>
<string name="permission_post_notifications_description">"Tillad CAPod at vise notifikationer om dine AirPods, f.eks. deres aktuelle status mens de er tilsluttet."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Højre pod-serienummer</string>
<string name="device_settings_info_left_bonded_label">Venstre parret</string>
<string name="device_settings_info_right_bonded_label">Højre parret</string>
<string name="device_settings_info_battery_health_label">Batteritilstand (estimeret)</string>
<string name="device_settings_info_battery_health_left_label">Venstre batteritilstand (est.)</string>
<string name="device_settings_info_battery_health_right_label">Højre batteritilstand (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Bestemmes stadig, kom tilbage senere</string>
<string name="device_settings_info_details_label">Enhedsdetaljer</string>
<string name="device_settings_info_details_action">Vis enhedsdetaljer</string>
<string name="device_settings_info_status_label">Status</string>
+16 -2
View File
@@ -49,7 +49,7 @@
<string name="settings_autopplay_label">Automatisches abspielen</string>
<string name="settings_autoplay_description">Musik fortsetzen, wenn du deine Pods wieder trägst aber nur, wenn Auto-Pause sie gestoppt hat. Pausen, die du selbst ausgelöst hast (Telefon, AirPods-Stiel, Schlaf), bleiben aktiv.</string>
<string name="settings_start_music_on_wear_label">Musik beim Anlegen starten</string>
<string name="settings_start_music_on_wear_description">Versucht immer, Musik abzuspielen, wenn du deine Pods aufsetzt auch nach einer manuellen Pause.</string>
<string name="settings_start_music_on_wear_description">Versucht immer, Musik abzuspielen, wenn du deine Pods aufsetzt.</string>
<string name="settings_eardetection_info_label">Hinweis zur Ohr-Erkennung</string>
<string name="settings_eardetection_info_description">Wenn die Ohr-Erkennung nur für einen Pod funktioniert, ist das leider Apple-Einschränkung. Es wird dann nur der \"primäre Pod\" (Mikrofon) erkannt. Weitere Konfiguration auf Apple-Geräten: Einstellungen → Bluetooth → AirPods → Mikrofon.</string>
<string name="settings_signal_minimum_label">Minimale Signalqualität</string>
@@ -122,6 +122,12 @@
<string name="settings_general_description">Allgemeine Optimierungen, die die gesamte App betreffen.</string>
<string name="settings_overview_hide_unmatched_label">Nicht zugeordnete Geräte ausblenden</string>
<string name="settings_overview_hide_unmatched_description">Geräte in der Nähe, die keinem deiner Profile entsprechen, werden in der Übersicht nicht angezeigt, z. B. AirPods anderer Personen.</string>
<string name="device_battery_estimate_toggle_label">Batterieschätzungen</string>
<string name="device_battery_estimate_card_desc">Erfahre, wie dieses Gerät geladen und entladen wird, um die verbleibende Zeit, die Zeit bis zur Aufladung und die Batteriegesundheit zu schätzen.</string>
<string name="device_battery_estimate_reset_action">Gelernte Daten zurücksetzen</string>
<string name="device_battery_estimate_reset_desc">Nochmal von vorne mit der nominalen Batterielaufzeit beginnen.</string>
<string name="device_battery_estimate_reset_confirm_title">Gelernte Daten zurücksetzen?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod wird die gemessene Entladung, die Ladegeschwindigkeit und die Batteriegesundheit dieses Geräts vergessen und nochmal von vorne mit der nominalen Batterielaufzeit beginnen.</string>
<string name="settings_acknowledgements_label">Danksagungen</string>
<string name="settings_debug_autoreports_label">Automatische Fehlerberichte</string>
<string name="settings_debug_autoreports_description">Meldet Probleme automatisch, z.B. Details zu einem App-Absturz, damit ich herausfinden kann, wie ich ihn beheben kann.</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Zuletzt gesehen: %s</string>
<string name="first_seen_x">Zum ersten Mal gesehen: %s</string>
<string name="battery_cached_label">Zuletzt bekannt · %s</string>
<string name="battery_time_remaining_format_hm">%1$d h %2$d Min</string>
<string name="battery_time_remaining_format_h">%1$d h</string>
<string name="battery_time_remaining_format_m">%1$d Min</string>
<string name="permission_post_notifications_label">Zeige Benachrichtigungen</string>
<string name="permission_post_notifications_description">"Erlaube CAPod, Benachrichtigungen über deine AirPods anzuzeigen, z. B. deren aktuellen Status, während die Verbindung besteht."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Seriennummer rechter Pod</string>
<string name="device_settings_info_left_bonded_label">Links gekoppelt</string>
<string name="device_settings_info_right_bonded_label">Rechts gekoppelt</string>
<string name="device_settings_info_battery_health_label">Batteriezustand (geschätzt)</string>
<string name="device_settings_info_battery_health_left_label">Linker Batteriezustand (gesch.)</string>
<string name="device_settings_info_battery_health_right_label">Rechter Batteriezustand (gesch.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Wird noch ermittelt, schau später nochmal</string>
<string name="device_settings_info_details_label">Gerätedetails</string>
<string name="device_settings_info_details_action">Gerätedetails anzeigen</string>
<string name="device_settings_info_status_label">Status</string>
@@ -480,7 +494,7 @@
<!-- New device settings -->
<string name="device_settings_category_general_label">Allgemein</string>
<string name="device_settings_microphone_mode_label">Mikrofon</string>
<string name="device_settings_microphone_mode_description">Welches Ohrstöpsel als Mikrofon verwendet wird</string>
<string name="device_settings_microphone_mode_description">Welcher Ohrstöpsel als Mikrofon verwendet wird</string>
<string name="device_settings_microphone_mode_auto">Automatisch</string>
<string name="device_settings_microphone_mode_right">Rechts</string>
<string name="device_settings_microphone_mode_left">Links</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Γενικές τροποποιήσεις που επηρεάζουν ολόκληρη την εφαρμογή.</string>
<string name="settings_overview_hide_unmatched_label">Απόκρυψη μη αντιστοιχισμένων συσκευών</string>
<string name="settings_overview_hide_unmatched_description">Να μην εμφανίζονται κοντινές συσκευές που δεν αντιστοιχούν σε κανένα από τα προφίλ σου στην επισκόπηση, π.χ. τα AirPods άλλων ανθρώπων.</string>
<string name="device_battery_estimate_toggle_label">Εκτιμήσεις μπαταρίας</string>
<string name="device_battery_estimate_card_desc">Μάθετε πώς φορτίζεται και εκφορτίζεται αυτή η συσκευή για να εκτιμήσετε τον υπόλοιπο χρόνο, τον χρόνο έως τη φόρτιση και την υγεία της μπαταρίας.</string>
<string name="device_battery_estimate_reset_action">Επαναφορά μαθημένων δεδομένων</string>
<string name="device_battery_estimate_reset_desc">Ξεκινήστε ξανά από τη χρήση μπαταρίας που αναφέρεται.</string>
<string name="device_battery_estimate_reset_confirm_title">Επαναφορά μαθημένων δεδομένων;</string>
<string name="device_battery_estimate_reset_confirm_message">Το CAPod θα ξεχάσει την μετρημένη κατανάλωση, την ταχύτητα φόρτισης και την υγεία της μπαταρίας αυτής της συσκευής, και θα ξεκινήσει ξανά από τη χρήση μπαταρίας που αναφέρεται.</string>
<string name="settings_acknowledgements_label">Ευχαριστίες</string>
<string name="settings_debug_autoreports_label">Αυτόματες αναφορές σφαλμάτων</string>
<string name="settings_debug_autoreports_description">Αναφέρει αυτόματα προβλήματα, π.χ. λεπτομέρειες σχετικά με μια κατάρρευση της εφαρμογής, ώστε να μπορώ να καταλάβω πώς να το διορθώσω.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Διαφάνεια φόντου</string>
<string name="widget_config_show_device_label">Εμφάνιση ονόματος συσκευής</string>
<string name="widget_config_reset_label">Επαναφορά στις προεπιλογές</string>
<string name="widget_config_preview_resize_hint">Μπορείτε να αλλάξετε το μέγεθος του widget αφού το τοποθετήσετε στην αρχική οθόνη σας.</string>
<string name="widget_config_preview_resize_hint">Μπορείτε να αλλάξετε το μέγεθος του widget μετά την τοποθέτησή του στην αρχική οθόνη σας.</string>
<string name="widget_config_custom_label">Προσαρμοσμένη</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Σκοτεινό</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Τελευταία εμφάνιση: %s</string>
<string name="first_seen_x">Πρώτη εμφάνιση: %s</string>
<string name="battery_cached_label">Τελευταία γνωστή · %s</string>
<string name="battery_time_remaining_format_hm">%1$dώ %2$dλ</string>
<string name="battery_time_remaining_format_h">%1$dώ</string>
<string name="battery_time_remaining_format_m">%1$dλ</string>
<string name="permission_post_notifications_label">Εμφάνιση ειδοποιήσεων</string>
<string name="permission_post_notifications_description">"Επιτρέψτε στο CAPod να εμφανίζει ειδοποιήσεις σχετικά με τα AirPods σας, π.χ. την τρέχουσα κατάστασή τους ενώ είναι συνδεδεμένα."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Σειριακός Αριθμός Δεξιού Pod</string>
<string name="device_settings_info_left_bonded_label">Αριστερό Συζευγμένο</string>
<string name="device_settings_info_right_bonded_label">Δεξί Συζευγμένο</string>
<string name="device_settings_info_battery_health_label">Υγεία Μπαταρίας (εκτιμώμενη)</string>
<string name="device_settings_info_battery_health_left_label">Υγεία Αριστερής Μπαταρίας (εκτ.)</string>
<string name="device_settings_info_battery_health_right_label">Υγεία Δεξιάς Μπαταρίας (εκτ.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Εξακολουθεί να προσδιορίζεται, ελέγξτε αργότερα</string>
<string name="device_settings_info_details_label">Λεπτομέρειες Συσκευής</string>
<string name="device_settings_info_details_action">Εμφάνιση λεπτομερειών συσκευής</string>
<string name="device_settings_info_status_label">Κατάσταση</string>
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ajustes generales que afectan a toda la app.</string>
<string name="settings_overview_hide_unmatched_label">Ocultar dispositivos no emparejados</string>
<string name="settings_overview_hide_unmatched_description">No mostrar en el resumen los dispositivos cercanos que no coincidan con ninguno de tus perfiles, p. ej., los AirPods de otras personas.</string>
<string name="device_battery_estimate_toggle_label">Estimaciones de batería</string>
<string name="device_battery_estimate_card_desc">Aprendé cómo este dispositivo se carga y se descarga para estimar el tiempo restante, el tiempo hasta que se cargue y la salud de la batería.</string>
<string name="device_battery_estimate_reset_action">Reiniciar datos aprendidos</string>
<string name="device_battery_estimate_reset_desc">Comenzá desde cero con la vida útil nominal de la batería.</string>
<string name="device_battery_estimate_reset_confirm_title">¿Reiniciar datos aprendidos?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod olvidará el consumo medido, la velocidad de carga y la salud de la batería de este dispositivo, y comenzará de nuevo con la vida útil nominal de la batería.</string>
<string name="settings_acknowledgements_label">Agradecimientos</string>
<string name="settings_debug_autoreports_label">Reportes automáticos de errores</string>
<string name="settings_debug_autoreports_description">Reporta problemas automáticamente, ej. detalles sobre un cierre inesperado de la app para que pueda averiguar cómo solucionarlo.</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Ultima vez conectado: %s</string>
<string name="first_seen_x">Primera vez conectados: %s</string>
<string name="battery_cached_label">Último conocido · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostrar las notificaciones</string>
<string name="permission_post_notifications_description">"Permitir que CAPod muestre notificaciones de tus AirPods, por ejemplo, su estado actual mientras están conectados."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Número de serie del auricular derecho</string>
<string name="device_settings_info_left_bonded_label">Izquierdo vinculado</string>
<string name="device_settings_info_right_bonded_label">Derecho vinculado</string>
<string name="device_settings_info_battery_health_label">Salud de la batería (estimada)</string>
<string name="device_settings_info_battery_health_left_label">Salud batería izq. (est.)</string>
<string name="device_settings_info_battery_health_right_label">Salud batería der. (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Aún determinando, volvé más tarde</string>
<string name="device_settings_info_details_label">Detalles del dispositivo</string>
<string name="device_settings_info_details_action">Mostrar detalles del dispositivo</string>
<string name="device_settings_info_status_label">Estado</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ajustes generales que afectan a toda la aplicación.</string>
<string name="settings_overview_hide_unmatched_label">Ocultar dispositivos no emparejados</string>
<string name="settings_overview_hide_unmatched_description">No mostrar dispositivos cercanos que no coincidan con ninguno de tus perfiles en el resumen, p. ej. los AirPods de otras personas.</string>
<string name="device_battery_estimate_toggle_label">Estimaciones de batería</string>
<string name="device_battery_estimate_card_desc">Aprende cómo se carga y descarga este dispositivo para estimar el tiempo restante, el tiempo hasta cargarse y la salud de la batería.</string>
<string name="device_battery_estimate_reset_action">Restablecer datos aprendidos</string>
<string name="device_battery_estimate_reset_desc">Comienza desde la vida útil nominal de la batería.</string>
<string name="device_battery_estimate_reset_confirm_title">¿Restablecer datos aprendidos?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod olvidará el consumo medido, la velocidad de carga y la salud de la batería de este dispositivo, y comenzará desde la vida útil nominal de la batería.</string>
<string name="settings_acknowledgements_label">Agradecimientos</string>
<string name="settings_debug_autoreports_label">Reportes automáticos de errores</string>
<string name="settings_debug_autoreports_description">Reporta problemas automáticamente, ej. detalles sobre un cierre inesperado de la aplicación para que pueda averiguar cómo solucionarlo.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparencia de fondo</string>
<string name="widget_config_show_device_label">Mostrar nombre del dispositivo</string>
<string name="widget_config_reset_label">Restablecer predeterminados</string>
<string name="widget_config_preview_resize_hint">Puedes cambiar el tamaño del widget después de colocarlo en tu pantalla de inicio.</string>
<string name="widget_config_preview_resize_hint">Puedes redimensionar el widget después de colocarlo en tu pantalla de inicio.</string>
<string name="widget_config_custom_label">Personalizado</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Oscuro</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Ultima vez conectado: %s</string>
<string name="first_seen_x">Primera vez conectados: %s</string>
<string name="battery_cached_label">Último conocido · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostrar las notificaciones</string>
<string name="permission_post_notifications_description">"Permitir que CAPod muestre notificaciones de tus AirPods, por ejemplo, su estado actual mientras están conectados."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Número de serie del auricular derecho</string>
<string name="device_settings_info_left_bonded_label">Izquierdo vinculado</string>
<string name="device_settings_info_right_bonded_label">Derecho vinculado</string>
<string name="device_settings_info_battery_health_label">Salud de la batería (estimada)</string>
<string name="device_settings_info_battery_health_left_label">Salud de la batería izquierda (est.)</string>
<string name="device_settings_info_battery_health_right_label">Salud de la batería derecha (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Aún se está determinando, vuelve más tarde</string>
<string name="device_settings_info_details_label">Detalles del dispositivo</string>
<string name="device_settings_info_details_action">Mostrar detalles del dispositivo</string>
<string name="device_settings_info_status_label">Estado</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ajustes generales que afectan a toda la aplicación.</string>
<string name="settings_overview_hide_unmatched_label">Ocultar dispositivos no reconocidos</string>
<string name="settings_overview_hide_unmatched_description">No mostrar en el resumen los dispositivos cercanos que no coincidan con ninguno de tus perfiles, p. ej. los AirPods de otras personas.</string>
<string name="device_battery_estimate_toggle_label">Estimaciones de batería</string>
<string name="device_battery_estimate_card_desc">Aprende cómo se carga y descarga este dispositivo para estimar el tiempo restante, el tiempo hasta que se cargue completamente y la salud de la batería.</string>
<string name="device_battery_estimate_reset_action">Restablecer datos aprendidos</string>
<string name="device_battery_estimate_reset_desc">Comienza de nuevo desde la duración nominal de la batería.</string>
<string name="device_battery_estimate_reset_confirm_title">¿Restablecer datos aprendidos?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod olvidará el consumo medido de este dispositivo, la velocidad de carga y la salud de la batería, y comenzará de nuevo desde la duración nominal de la batería.</string>
<string name="settings_acknowledgements_label">Agradecimientos</string>
<string name="settings_debug_autoreports_label">Informes de error automáticos</string>
<string name="settings_debug_autoreports_description">Informa automáticamente de los problemas, p. ej., detalles sobre un fallo de la aplicación para que pueda averiguar cómo solucionarlo.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparencia del fondo</string>
<string name="widget_config_show_device_label">Mostrar el nombre del dispositivo</string>
<string name="widget_config_reset_label">Restaurar los valores predeterminados</string>
<string name="widget_config_preview_resize_hint">Puedes cambiar el tamaño del widget después de colocarlo en la pantalla de inicio.</string>
<string name="widget_config_preview_resize_hint">Puedes cambiar el tamaño del widget después de colocarlo en tu pantalla de inicio.</string>
<string name="widget_config_custom_label">Personalizado</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Oscuro</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Ultima vez conectado: %s</string>
<string name="first_seen_x">Primera vez conectados: %s</string>
<string name="battery_cached_label">Último conocido · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostrar las notificaciones</string>
<string name="permission_post_notifications_description">"Permitir que CAPod muestre notificaciones de tus AirPods, por ejemplo, su estado actual mientras están conectados."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Número de serie del auricular derecho</string>
<string name="device_settings_info_left_bonded_label">Izquierdo vinculado</string>
<string name="device_settings_info_right_bonded_label">Derecho vinculado</string>
<string name="device_settings_info_battery_health_label">Salud de la batería (estimado)</string>
<string name="device_settings_info_battery_health_left_label">Salud de la batería izquierda (est.)</string>
<string name="device_settings_info_battery_health_right_label">Salud de la batería derecha (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Aún se está determinando, comprueba más tarde</string>
<string name="device_settings_info_details_label">Detalles del dispositivo</string>
<string name="device_settings_info_details_action">Mostrar detalles del dispositivo</string>
<string name="device_settings_info_status_label">Estado</string>
@@ -122,6 +122,12 @@
<string name="settings_general_description">Kogu rakendust mõjutavad üldseaded.</string>
<string name="settings_overview_hide_unmatched_label">Peida mittevastavad seadmed</string>
<string name="settings_overview_hide_unmatched_description">Ära näita ülevaates lähedalasuvaid seadmeid, mis ei vasta ühelegi sinu profiilile, nt teiste inimeste AirPods.</string>
<string name="device_battery_estimate_toggle_label">Aku hinnangud</string>
<string name="device_battery_estimate_card_desc">Saate teada, kuidas see seade laadib ja tühjeneb, et hinnata ülejäänud aega, laadimiseni kuluva aega ja aku tervist.</string>
<string name="device_battery_estimate_reset_action">Lähtesta õpitud andmed</string>
<string name="device_battery_estimate_reset_desc">Alusta uuesti nimiväärtustest.</string>
<string name="device_battery_estimate_reset_confirm_title">Lähtesta õpitud andmed?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod unustab selle seadme mõõdetud kulu, laadimiskiiruse ja aku tervise ning alustab uuesti nimiväärtustest.</string>
<string name="settings_acknowledgements_label">Tunnustused</string>
<string name="settings_debug_autoreports_label">Automaatsed veateated</string>
<string name="settings_debug_autoreports_description">Teavitab vigadest ise, nt andmed rakenduse hangumise kohta, et saaksin mõista, kuidas olukorda lahendada.</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Viimati kasutusel: %s</string>
<string name="first_seen_x">Esimesena kasutusel: %s</string>
<string name="battery_cached_label">Viimati teadaolev · %s</string>
<string name="battery_time_remaining_format_hm">%1$dt %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dt</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Näita teateid</string>
<string name="permission_post_notifications_description">"Luba CAPodil näidata AirPodsi teateid, nt praegust olekut ühendatud olles."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Parema kõrvaklapiosa seerianumber</string>
<string name="device_settings_info_left_bonded_label">Vasak seotud</string>
<string name="device_settings_info_right_bonded_label">Parem seotud</string>
<string name="device_settings_info_battery_health_label">Aku tervis (hinnanguline)</string>
<string name="device_settings_info_battery_health_left_label">Vasaku aku tervis (hinnanguline)</string>
<string name="device_settings_info_battery_health_right_label">Parema aku tervis (hinnanguline)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Määratakse, vaata hiljem uuesti</string>
<string name="device_settings_info_details_label">Seadme andmed</string>
<string name="device_settings_info_details_action">Kuva seadme andmed</string>
<string name="device_settings_info_status_label">Olek</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Aplikazio osoari eragiten dioten doikuntza orokorrak.</string>
<string name="settings_overview_hide_unmatched_label">Ezkutatu bat ez datozen gailuak</string>
<string name="settings_overview_hide_unmatched_description">Ez erakutsi ikuspegiaren inguruan dauden eta zure profilekin bat ez datozen gailuak, esaterako, beste pertsonen AirPods-ak.</string>
<string name="device_battery_estimate_toggle_label">Bateriaren estimazioak</string>
<string name="device_battery_estimate_card_desc">Ikasi nola kargatzen eta deskargatzen den gailu hau, geratzen den denbora, kargatzeko denbora eta bateriaren osasuna estimatzeko.</string>
<string name="device_battery_estimate_reset_action">Ikasitako datuak berrezarri</string>
<string name="device_battery_estimate_reset_desc">Kalifikatutako bateriaren bizitzatik berriro hasi.</string>
<string name="device_battery_estimate_reset_confirm_title">Ikasitako datuak berrezarri?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod-ek gailu honetako neurtutako kontsumoa, kargatzeko abiadura eta bateriaren osasuna ahaztuko ditu, eta kalifikatutako bateriaren bizitzatik berriro hasiko da.</string>
<string name="settings_acknowledgements_label">Eskertzak</string>
<string name="settings_debug_autoreports_label">Erroreen txosten automatikoak</string>
<string name="settings_debug_autoreports_description">Automatikoki arazoak salatzen ditu, adib. aplikazioaren kraskaduraren xehetasunak, konpontzen jakin dezadan.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Atzeko planoaren gardentasuna</string>
<string name="widget_config_show_device_label">Gailuaren izena erakutsi</string>
<string name="widget_config_reset_label">Berriro lehenespenera berrezarri</string>
<string name="widget_config_preview_resize_hint">Widgeta tamainaz alda dezakezu hasierako pantailan jarri ondoren.</string>
<string name="widget_config_preview_resize_hint">Widgeta tamaina aldatu dezakezu, hasierako pantailan jarri ondoren.</string>
<string name="widget_config_custom_label">Pertsonalizatua</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Iluna</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Azkenekoz ikusia: %s</string>
<string name="first_seen_x">Lehen aldiz ikusia: %s</string>
<string name="battery_cached_label">Azken ezaguna · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Erakutsi jakinarazpenak</string>
<string name="permission_post_notifications_description">"Baimendu CAPod-i zure AirPods-ei buruzko jakinarazpenak erakustea, adib. konektatuta dauden bitartean euren egungo egoera."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Eskuineko pod-aren seriea</string>
<string name="device_settings_info_left_bonded_label">Ezkerra Lotuta</string>
<string name="device_settings_info_right_bonded_label">Eskuina Lotuta</string>
<string name="device_settings_info_battery_health_label">Bateriaren osasuna (kalkulatua)</string>
<string name="device_settings_info_battery_health_left_label">Ezkerreko bateriaren osasuna (est.)</string>
<string name="device_settings_info_battery_health_right_label">Eskuineko bateriaren osasuna (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Oraindik kalkulatzen, beranduago itzuli</string>
<string name="device_settings_info_details_label">Gailuaren xehetasunak</string>
<string name="device_settings_info_details_action">Erakutsi gailuaren xehetasunak</string>
<string name="device_settings_info_status_label">Egoera</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">تنظیمات کلی که بر کل برنامه تأثیر می‌گذارند.</string>
<string name="settings_overview_hide_unmatched_label">پنهان کردن دستگاه‌های نامتناسب</string>
<string name="settings_overview_hide_unmatched_description">دستگاه‌های نزدیکی که با هیچ‌کدام از پروفایل‌های شما تطابق ندارند در نمای کلی نمایش داده نمی‌شوند، مثلاً AirPods دیگران.</string>
<string name="device_battery_estimate_toggle_label">برآورد زمان باتری</string>
<string name="device_battery_estimate_card_desc">یاد بگیرید که این دستگاه چگونه شارژ و تخلیه می‌شود تا زمان باقی‌مانده، زمان تا پر شدن و سلامت باتری را تخمین بزنید.</string>
<string name="device_battery_estimate_reset_action">بازنشانی داده‌های یادگیری</string>
<string name="device_battery_estimate_reset_desc">شروع مجدد از عمر اسمی باتری.</string>
<string name="device_battery_estimate_reset_confirm_title">بازنشانی داده‌های یادگیری‌شده؟</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod داده‌های اندازه‌گیری‌شده این دستگاه (تخلیه، سرعت شارژ و سلامت باتری) را فراموش خواهد کرد و دوباره از عمر اسمی باتری شروع خواهد کرد.</string>
<string name="settings_acknowledgements_label">قدردانی‌ها</string>
<string name="settings_debug_autoreports_label">گزارش‌های خطای خودکار</string>
<string name="settings_debug_autoreports_description">مشکلات را به‌طور خودکار گزارش می‌دهد، مثلاً جزئیات مربوط به خرابی برنامه تا بتوانم نحوه رفع آن را بفهمم.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">شفافیت پس‌زمینه</string>
<string name="widget_config_show_device_label">نمایش نام دستگاه</string>
<string name="widget_config_reset_label">بازنشانی به مقادیر پیش‌فرض</string>
<string name="widget_config_preview_resize_hint">می‌توانید پس از قرار دادن ویجت روی صفحه اصلی، اندازه آن را تغییر دهید.</string>
<string name="widget_config_preview_resize_hint">شما می‌توانید ابزارک را پس از قرار دادن آن در صفحه اصلی تغییر اندازه دهید.</string>
<string name="widget_config_custom_label">سفارشی</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">تیره</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">آخرین مشاهده: %s</string>
<string name="first_seen_x">اولین مشاهده: %s</string>
<string name="battery_cached_label">آخرین · %s</string>
<string name="battery_time_remaining_format_hm">%1$d س %2$d د</string>
<string name="battery_time_remaining_format_h">%1$d س</string>
<string name="battery_time_remaining_format_m">%1$d د</string>
<string name="permission_post_notifications_label">نمایش اعلان‌ها</string>
<string name="permission_post_notifications_description">"به CAPod اجازه دهید اعلان‌هایی درباره AirPods شما نشان دهد، مثلاً وضعیت فعلی آن‌ها هنگام اتصال."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">شماره سریال پاد راست</string>
<string name="device_settings_info_left_bonded_label">چپ متصل</string>
<string name="device_settings_info_right_bonded_label">راست متصل</string>
<string name="device_settings_info_battery_health_label">سلامت باتری (تخمینی)</string>
<string name="device_settings_info_battery_health_left_label">سلامت باتری سمت چپ (تخمینی)</string>
<string name="device_settings_info_battery_health_right_label">سلامت باتری سمت راست (تخمینی)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">هنوز درحال تعیین است، بعدا دوباره مراجعه کنید</string>
<string name="device_settings_info_details_label">جزئیات دستگاه</string>
<string name="device_settings_info_details_action">نمایش جزئیات دستگاه</string>
<string name="device_settings_info_status_label">وضعیت</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Yleiset säädöt, jotka vaikuttavat koko sovellukseen.</string>
<string name="settings_overview_hide_unmatched_label">Piilota yhteensopimattomat laitteet</string>
<string name="settings_overview_hide_unmatched_description">Älä näytä lähistöllä olevia laitteita, jotka eivät vastaa mitään profiileistasi yleiskatsauksessa, esim. muiden ihmisten AirPodseja.</string>
<string name="device_battery_estimate_toggle_label">Akun arviot</string>
<string name="device_battery_estimate_card_desc">Opi kuinka tämä laite latautuu ja kulutetaan, jotta voit arvioida jäljellä olevaa aikaa, latautumiseen kuluvaa aikaa ja akun kuntoa.</string>
<string name="device_battery_estimate_reset_action">Nollaa opitut tiedot</string>
<string name="device_battery_estimate_reset_desc">Aloita alusta nimellisakun perusteella.</string>
<string name="device_battery_estimate_reset_confirm_title">Nollataan opitut tiedot?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod unohtaa tämän laitteen mitatun kulutuksen, latausnopeuden ja akun kunnon, ja aloittaa alusta nimellisakun perusteella.</string>
<string name="settings_acknowledgements_label">Kiitokset</string>
<string name="settings_debug_autoreports_label">Automaattiset virheraportit</string>
<string name="settings_debug_autoreports_description">Raportoi ongelmat automaattisesti, esim. tiedot sovelluksen kaatumisesta, jotta voin selvittää, miten se korjataan.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Taustan läpinäkyvyys</string>
<string name="widget_config_show_device_label">Näytä laitteen nimi</string>
<string name="widget_config_reset_label">Palauta oletusasetukset</string>
<string name="widget_config_preview_resize_hint">Voit muuttaa widgetin kokoa sen asettamisen jälkeen aloitusnäytöllä.</string>
<string name="widget_config_preview_resize_hint">Voit muuttaa widgetin kokoa sen jälkeen, kun olet asettanut sen kotinäytöllesi.</string>
<string name="widget_config_custom_label">Mukautettu</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Tumma</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Viimeksi nähty: %s</string>
<string name="first_seen_x">Ensimmäisen kerran nähty: %s</string>
<string name="battery_cached_label">Viimeksi nähty · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Näytä ilmoitukset</string>
<string name="permission_post_notifications_description">"Salli CAPod-sovelluksen näyttää ilmoituksia AirPodeistasi, esim. niiden nykyinen tila yhdistettynä."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Oikean kuulokkeen sarjanumero</string>
<string name="device_settings_info_left_bonded_label">Vasen yhdistetty</string>
<string name="device_settings_info_right_bonded_label">Oikea yhdistetty</string>
<string name="device_settings_info_battery_health_label">Akun kunto (arvioitu)</string>
<string name="device_settings_info_battery_health_left_label">Vasemman akun kunto (arvioitu)</string>
<string name="device_settings_info_battery_health_right_label">Oikean akun kunto (arvioitu)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Vielä määritteillään, tarkista myöhemmin</string>
<string name="device_settings_info_details_label">Laitteen tiedot</string>
<string name="device_settings_info_details_action">Näytä laitteen tiedot</string>
<string name="device_settings_info_status_label">Tila</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Mga pangkalahatang pag-aayos na nakakaapekto sa buong app.</string>
<string name="settings_overview_hide_unmatched_label">Itago ang mga hindi tugmang device</string>
<string name="settings_overview_hide_unmatched_description">Huwag ipakita ang mga kalapit na device na hindi tugma sa alinman sa iyong mga profile sa pangkalahatang-tanaw, hal. ang mga AirPods ng ibang tao.</string>
<string name="device_battery_estimate_toggle_label">Mga Pagtataya ng Baterya</string>
<string name="device_battery_estimate_card_desc">Alamin kung paano nag-charge at gumagamit ng kuryente ang device na ito upang matantya ang natitirang oras, oras hanggang sa puno, at kalusugan ng baterya.</string>
<string name="device_battery_estimate_reset_action">Burahin ang natutunan na datos</string>
<string name="device_battery_estimate_reset_desc">Magsimula muli mula sa rated na buhay ng baterya.</string>
<string name="device_battery_estimate_reset_confirm_title">Burahin ang natutunan na datos?</string>
<string name="device_battery_estimate_reset_confirm_message">Makakalimutan ng CAPod ang sinusukat na paggamit ng kuryente, bilis ng pag-charge, at kalusugan ng baterya ng device na ito, at magsisimula muli mula sa rated na buhay ng baterya.</string>
<string name="settings_acknowledgements_label">Mga Pasasalamat</string>
<string name="settings_debug_autoreports_label">Mga awtomatikong ulat ng bug</string>
<string name="settings_debug_autoreports_description">Awtomatikong nag-uulat ng mga isyu, hal. mga detalye sa isang pag-crash ng app para malaman ko kung paano ito ayusin.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparency ng background</string>
<string name="widget_config_show_device_label">Ipakita ang pangalan ng device</string>
<string name="widget_config_reset_label">I-reset sa mga default</string>
<string name="widget_config_preview_resize_hint">Maaari mong baguhin ang laki ng widget pagkatapos ilagay ito sa iyong home screen.</string>
<string name="widget_config_preview_resize_hint">Maaari mong baguhin ang laki ng widget pagkatapos itong ilagay sa iyong home screen.</string>
<string name="widget_config_custom_label">Pasadyang</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Madilim</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Huling nakita: %s</string>
<string name="first_seen_x">Unang nakita: %s</string>
<string name="battery_cached_label">Huling kilala · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$d h</string>
<string name="battery_time_remaining_format_m">%1$d m</string>
<string name="permission_post_notifications_label">Ipakita ang mga notipikasyon</string>
<string name="permission_post_notifications_description">"Payagan ang CAPod na magpakita ng mga notipikasyon tungkol sa iyong AirPods, hal. ang kanilang kasalukuyang status habang konektado."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Serial ng Kanang Pod</string>
<string name="device_settings_info_left_bonded_label">Kaliwang Naka-bond</string>
<string name="device_settings_info_right_bonded_label">Kanang Naka-bond</string>
<string name="device_settings_info_battery_health_label">Kalusugan ng Baterya (tinantya)</string>
<string name="device_settings_info_battery_health_left_label">Kalusugan ng Kaliwang Baterya (tinantya.)</string>
<string name="device_settings_info_battery_health_right_label">Kalusugan ng Kananang Baterya (tinantya.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Patuloy na tinutukoy, bumalik mamaya</string>
<string name="device_settings_info_details_label">Mga Detalye ng Device</string>
<string name="device_settings_info_details_action">Ipakita ang mga detalye ng device</string>
<string name="device_settings_info_status_label">Katayuan</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Réglage généraux qui affectent l\'appli entière.</string>
<string name="settings_overview_hide_unmatched_label">Masquer les appareils sans correspondance</string>
<string name="settings_overview_hide_unmatched_description">Ne pas afficher dans laperçu les appareils à proximité qui ne correspondent à aucun de vos profils, p. ex. les AirPods dautres personnes.</string>
<string name="device_battery_estimate_toggle_label">Estimations batterie</string>
<string name="device_battery_estimate_card_desc">Découvrez comment cet appareil se charge et se décharge pour estimer le temps restant, le temps jusqu’à la charge complète et l’état de la batterie.</string>
<string name="device_battery_estimate_reset_action">Réinitialiser les données apprises</string>
<string name="device_battery_estimate_reset_desc">Recommencer à partir de lautonomie nominale de la batterie.</string>
<string name="device_battery_estimate_reset_confirm_title">Réinitialiser les données apprises ?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod oubliera la consommation mesurée de cet appareil, sa vitesse de charge et l’état de sa batterie, et recommencera à partir de lautonomie nominale de la batterie.</string>
<string name="settings_acknowledgements_label">Remerciements</string>
<string name="settings_debug_autoreports_label">Relevés de bogue automatiques</string>
<string name="settings_debug_autoreports_description">Signale automatiquement les problèmes, p. ex., les détails dun plantage de lappli, afin que je puisse essayer de les corriger.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparence de l\'arrière-plan</string>
<string name="widget_config_show_device_label">Afficher le nom de lappareil</string>
<string name="widget_config_reset_label">Rétablir les valeurs par défaut</string>
<string name="widget_config_preview_resize_hint">Vous pouvez redimensionner le widget après l\'avoir placé sur votre écran d\'accueil.</string>
<string name="widget_config_preview_resize_hint">Vous pouvez redimensionner le widget après lavoir placé sur votre écran daccueil.</string>
<string name="widget_config_custom_label">Personnalisée</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Sombre</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Dernière connection : %s</string>
<string name="first_seen_x">Première connexion : %s</string>
<string name="battery_cached_label">Dernière fois vu · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Afficher les notifications</string>
<string name="permission_post_notifications_description">"Permettre à CAPod dafficher des notifications au sujet des AirPods, p. ex. leur état actuel quand ils sont connectés."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Numéro de série de l’écouteur droit</string>
<string name="device_settings_info_left_bonded_label">Gauche appairé</string>
<string name="device_settings_info_right_bonded_label">Droite appairée</string>
<string name="device_settings_info_battery_health_label">Santé de la batterie (estimée)</string>
<string name="device_settings_info_battery_health_left_label">Santé de la batterie gauche (est.)</string>
<string name="device_settings_info_battery_health_right_label">Santé de la batterie droite (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">En cours de détermination, revenez plus tard</string>
<string name="device_settings_info_details_label">Détails de lappareil</string>
<string name="device_settings_info_details_action">Afficher les détails de lappareil</string>
<string name="device_settings_info_status_label">État</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Axustes xerais que afectan a toda a aplicación.</string>
<string name="settings_overview_hide_unmatched_label">Ocultar dispositivos non coincidentes</string>
<string name="settings_overview_hide_unmatched_description">Non mostrar dispositivos próximos que non coincidan con ningún dos teus perfís na vista xeral, p. ex. os AirPods doutras persoas.</string>
<string name="device_battery_estimate_toggle_label">Estimacións de batería</string>
<string name="device_battery_estimate_card_desc">Aprende como este dispositivo se carga e se descarrega para estimar o tempo restante, o tempo ata estar cargado e a saúde da batería.</string>
<string name="device_battery_estimate_reset_action">Restablecer datos aprendidos</string>
<string name="device_battery_estimate_reset_desc">Comezar de novo desde a vida útil da batería indicada.</string>
<string name="device_battery_estimate_reset_confirm_title">Restablecer datos aprendidos?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod esquecerá a drenaxe medida deste dispositivo, a velocidade de carga e a saúde da batería, e comezará de novo desde a vida útil da batería indicada.</string>
<string name="settings_acknowledgements_label">Agradecementos</string>
<string name="settings_debug_autoreports_label">Informes de erros automáticos</string>
<string name="settings_debug_autoreports_description">Informa automaticamente de problemas, p. ex., detalles sobre un fallo da aplicación para que poida descubrir como solucionalo.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparencia de fondo</string>
<string name="widget_config_show_device_label">Mostrar nome do dispositivo</string>
<string name="widget_config_reset_label">Restablecer aos valores predeterminados</string>
<string name="widget_config_preview_resize_hint">Podes cambiar o tamaño do widget despois de colocalo na pantalla de inicio.</string>
<string name="widget_config_preview_resize_hint">Podes redimensionar o widget despois de colocalo na túa pantalla de inicio.</string>
<string name="widget_config_custom_label">Personalizado</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Escuro</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Visto por última vez: %s</string>
<string name="first_seen_x">Visto por primeira vez: %s</string>
<string name="battery_cached_label">Último coñecido · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostrar notificacións</string>
<string name="permission_post_notifications_description">"Permitir que CAPod mostre notificacións sobre os teus AirPods, p. ex., o seu estado actual mentres están conectados."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Número de serie do auricular dereito</string>
<string name="device_settings_info_left_bonded_label">Esquerdo vinculado</string>
<string name="device_settings_info_right_bonded_label">Dereito vinculado</string>
<string name="device_settings_info_battery_health_label">Saúde da batería (estimada)</string>
<string name="device_settings_info_battery_health_left_label">Saúde da batería esquerda (est.)</string>
<string name="device_settings_info_battery_health_right_label">Saúde da batería dereita (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Aínda se está determinando, volve máis tarde</string>
<string name="device_settings_info_details_label">Detalles do dispositivo</string>
<string name="device_settings_info_details_action">Mostrar detalles do dispositivo</string>
<string name="device_settings_info_status_label">Estado</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">सामान्य समायोजन जो पूरे ऐप को प्रभावित करते हैं।</string>
<string name="settings_overview_hide_unmatched_label">बेमेल डिवाइस छुपाएँ</string>
<string name="settings_overview_hide_unmatched_description">ओवरव्यू में उन नज़दीकी डिवाइस न दिखाएँ जो आपकी किसी प्रोफ़ाइल से मेल नहीं खाते, जैसे दूसरों के AirPods।</string>
<string name="device_battery_estimate_toggle_label">बैटरी अनुमान</string>
<string name="device_battery_estimate_card_desc">यह सीखें कि यह डिवाइस कैसे चार्ज होता है और खत्म होता है ताकि शेष समय, चार्ज होने तक का समय और बैटरी स्वास्थ्य का अनुमान लगाया जा सके।</string>
<string name="device_battery_estimate_reset_action">सीखे गए डेटा को रीसेट करें</string>
<string name="device_battery_estimate_reset_desc">रेटेड बैटरी जीवन से फिर से शुरू करें।</string>
<string name="device_battery_estimate_reset_confirm_title">सीखे गए डेटा को रीसेट करें?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod इस डिवाइस की मापी गई खपत, चार्जिंग गति और बैटरी स्वास्थ्य को भूल जाएगा, और रेटेड बैटरी जीवन से फिर से शुरू करेगा।</string>
<string name="settings_acknowledgements_label">स्वीकृतियाँ</string>
<string name="settings_debug_autoreports_label">स्वचालित बग रिपोर्ट</string>
<string name="settings_debug_autoreports_description">समस्याओं की स्वचालित रूप से रिपोर्ट करता है, जैसे ऐप क्रैश पर विवरण ताकि मैं यह पता लगा सकूं कि इसे कैसे ठीक किया जाए।</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">पृष्ठभूमि पारदर्शिता</string>
<string name="widget_config_show_device_label">डिवाइस का नाम दिखाएं</string>
<string name="widget_config_reset_label">डिफ़ॉल्ट पर रीसेट करें</string>
<string name="widget_config_preview_resize_hint">आप होम स्क्रीन पर रखने के बाद विजेट का आकार बदल सकते हैं।</string>
<string name="widget_config_preview_resize_hint">होम स्क्रीन पर विजेट रखने के बाद आप इसका आकार बदल सकते हैं।</string>
<string name="widget_config_custom_label">कस्टम</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">गहरा</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">अंतिम बार देखा: %s</string>
<string name="first_seen_x">पहली बार देखा: %s</string>
<string name="battery_cached_label">अंतिम ज्ञात · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">सूचनाएं दिखाएं</string>
<string name="permission_post_notifications_description">"CAPod को आपके AirPods के बारे में सूचनाएं दिखाने की अनुमति दें, जैसे कनेक्ट होने पर उनकी वर्तमान स्थिति।"</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">दाएं पॉड का सीरियल नंबर</string>
<string name="device_settings_info_left_bonded_label">बायाँ युग्मित</string>
<string name="device_settings_info_right_bonded_label">दायाँ युग्मित</string>
<string name="device_settings_info_battery_health_label">बैटरी स्वास्थ्य (अनुमानित)</string>
<string name="device_settings_info_battery_health_left_label">बाईं बैटरी स्वास्थ्य (अनुमानित)</string>
<string name="device_settings_info_battery_health_right_label">दाईं बैटरी स्वास्थ्य (अनुमानित)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">अभी निर्धारण हो रहा है, बाद में फिर से जांचें</string>
<string name="device_settings_info_details_label">डिवाइस विवरण</string>
<string name="device_settings_info_details_action">डिवाइस विवरण दिखाएं</string>
<string name="device_settings_info_status_label">स्थिति</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Opće prilagodbe koje utječu na cijelu aplikaciju.</string>
<string name="settings_overview_hide_unmatched_label">Sakrij neprepoznate uređaje</string>
<string name="settings_overview_hide_unmatched_description">Ne prikazuj obližnje uređaje koji ne odgovaraju nijednom od tvojih profila u pregledu, npr. AirPods-e drugih osoba.</string>
<string name="device_battery_estimate_toggle_label">Procjene baterije</string>
<string name="device_battery_estimate_card_desc">Saznajte kako se ovaj uređaj puni i prazni kako bi se procijenilo preostalo vrijeme, vrijeme do punjenja i zdravlje baterije.</string>
<string name="device_battery_estimate_reset_action">Obriši naučene podatke</string>
<string name="device_battery_estimate_reset_desc">Započnite ponovno od ocijenjene životnosti baterije.</string>
<string name="device_battery_estimate_reset_confirm_title">Obriši naučene podatke?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod će zaboraviti izmjereno pražnjenje ovog uređaja, brzinu punjenja i zdravlje baterije, te početi ponovno od ocijenjene životnosti baterije.</string>
<string name="settings_acknowledgements_label">Zahvale</string>
<string name="settings_debug_autoreports_label">Automatska izvješća o pogreškama</string>
<string name="settings_debug_autoreports_description">Automatski prijavljuje probleme, npr. detalje o rušenju aplikacije kako bih mogao shvatiti kako to popraviti.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Prozirnost pozadine</string>
<string name="widget_config_show_device_label">Prikaži naziv uređaja</string>
<string name="widget_config_reset_label">Vrati na zadane vrijednosti</string>
<string name="widget_config_preview_resize_hint">Možete promijeniti veličinu widgeta nakon što ga postavite na početni zaslon.</string>
<string name="widget_config_preview_resize_hint">Možete promijeniti veličinu widgeta nakon postavljanja na početni zaslon.</string>
<string name="widget_config_custom_label">Prilagođeno</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Tamno</string>
@@ -280,6 +286,9 @@
<string name="last_seen_x">Zadnji put viđeno: %s</string>
<string name="first_seen_x">Prvi put viđeno: %s</string>
<string name="battery_cached_label">Zadnje poznato · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Prikaži obavijesti</string>
<string name="permission_post_notifications_description">"Dopustite CAPod-u prikazivanje obavijesti o vašim AirPods, npr. njihov trenutni status dok su povezane."</string>
<!-- Device profiles -->
@@ -428,6 +437,11 @@
<string name="device_settings_info_right_serial_label">Serijski broj desnog poda</string>
<string name="device_settings_info_left_bonded_label">Lijevo vezano</string>
<string name="device_settings_info_right_bonded_label">Desno vezano</string>
<string name="device_settings_info_battery_health_label">Zdravlje baterije (procijenjena)</string>
<string name="device_settings_info_battery_health_left_label">Zdravlje lijeve baterije (proc.)</string>
<string name="device_settings_info_battery_health_right_label">Zdravlje desne baterije (proc.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Još se određuje, provjeri ponovno kasnije</string>
<string name="device_settings_info_details_label">Detalji uređaja</string>
<string name="device_settings_info_details_action">Prikaži detalje uređaja</string>
<string name="device_settings_info_status_label">Stanje</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Általános módosítások, amelyek az egész alkalmazást érintik.</string>
<string name="settings_overview_hide_unmatched_label">Párosítatlan eszközök elrejtése</string>
<string name="settings_overview_hide_unmatched_description">Ne jelenjenek meg az áttekintőben azok a közeli eszközök, amelyek nem egyeznek egy profilodhoz sem, pl. mások AirPodjai.</string>
<string name="device_battery_estimate_toggle_label">Akkumulátor becslések</string>
<string name="device_battery_estimate_card_desc">Ismerje meg, hogyan töltődik és merül az eszköz, hogy megbecsülhesse a hátralévő időt, a töltésig tartó időt és az akkumulátor állapotát.</string>
<string name="device_battery_estimate_reset_action">Megtanult adatok alaphelyzetbe állítása</string>
<string name="device_battery_estimate_reset_desc">Kezdje újra a névleges akkumulátor-élettartamtól.</string>
<string name="device_battery_estimate_reset_confirm_title">Megtanult adatok alaphelyzetbe állítása?</string>
<string name="device_battery_estimate_reset_confirm_message">A CAPod elfelejti az eszköz mért fogyasztását, töltési sebességét és akkumulátor állapotát, és a névleges akkumulátor-élettartamtól kezdi újra.</string>
<string name="settings_acknowledgements_label">Köszönetnyilvánítás</string>
<string name="settings_debug_autoreports_label">Automatikus hibajelentések</string>
<string name="settings_debug_autoreports_description">Automatikusan jelenti a problémákat, például részleteket az alkalmazás összeomlásáról, hogy kitaláljam, hogyan javíthatom ki.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Háttér átlátszósága</string>
<string name="widget_config_show_device_label">Eszköznév megjelenítése</string>
<string name="widget_config_reset_label">Alapértékek visszaállítása</string>
<string name="widget_config_preview_resize_hint">A widgetet átméretezheted, miután elhelyezted a kezdőképernyőn.</string>
<string name="widget_config_preview_resize_hint">A widgetet az otthoni képernyőre helyezés után átméretezhet.</string>
<string name="widget_config_custom_label">Egyéni</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Sötét</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Utoljára látva: %s</string>
<string name="first_seen_x">Először látva: %s</string>
<string name="battery_cached_label">Utoljára ismert · %s</string>
<string name="battery_time_remaining_format_hm">%1$dó %2$dp</string>
<string name="battery_time_remaining_format_h">%1$dó</string>
<string name="battery_time_remaining_format_m">%1$dp</string>
<string name="permission_post_notifications_label">Értesítések megjelenítése</string>
<string name="permission_post_notifications_description">"Engedélyezze a CAPod-nak, hogy értesítéseket jelenítsen meg az AirPods-ról, pl. aktuális állapotuk csatlakozás közben."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Jobb fülhallgató sorozatszáma</string>
<string name="device_settings_info_left_bonded_label">Bal fülhallgató párosítva</string>
<string name="device_settings_info_right_bonded_label">Jobb fülhallgató párosítva</string>
<string name="device_settings_info_battery_health_label">Akkumulátor egészsége (becsült)</string>
<string name="device_settings_info_battery_health_left_label">Bal akkumulátor egészsége (becsült)</string>
<string name="device_settings_info_battery_health_right_label">Jobb akkumulátor egészsége (becsült)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Még megállapítás alatt, később nézzen vissza</string>
<string name="device_settings_info_details_label">Eszköz részletei</string>
<string name="device_settings_info_details_action">Eszköz részleteinek megjelenítése</string>
<string name="device_settings_info_status_label">Állapot</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ընդհանուր կարգավորումներ, որոնք ազդում են ամբողջ հավելվածի վրա։</string>
<string name="settings_overview_hide_unmatched_label">Թաքցնել անհամապատասխան սարքերը</string>
<string name="settings_overview_hide_unmatched_description">Չցուցադրել մոտակա սարքերը, որոնք չեն համընկնում ձեր պրոֆիլներից որևէ մեկի հետ, օրինակ՝ ուրիշի AirPods-ները:</string>
<string name="device_battery_estimate_toggle_label">Մարտկոցի գնահատականներ</string>
<string name="device_battery_estimate_card_desc">Իմացեք, թե ինչպես է այս սարքը լիցքավորվում և լիցքազրկվում՝ մնացած ժամանակը, լիցքավորման ժամանակը և մարտկոցի առողջությունը գնահատելու համար:</string>
<string name="device_battery_estimate_reset_action">Վերականգնել սովորած տվյալները</string>
<string name="device_battery_estimate_reset_desc">Նորից սկսել գնահատված մարտկոցի կյանքից:</string>
<string name="device_battery_estimate_reset_confirm_title">Վերականգնել սովորած տվյալները?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod-ը կմոռանա այս սարքի չափված լիցքազրկումը, լիցքավորման արագությունը և մարտկոցի առողջությունը, և կսկսի նորից գնահատված մարտկոցի կյանքից:</string>
<string name="settings_acknowledgements_label">Շնորհակալագրեր</string>
<string name="settings_debug_autoreports_label">Սխալների ավտոմատ հաշվետվություններ</string>
<string name="settings_debug_autoreports_description">Ավտոմատ կերպով հաղորդում է խնդիրների մասին, օրինակ՝ հավելվածի խափանման մանրամասները, որպեսզի կարողանամ պարզել, թե ինչպես այն շտկել։</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Ֆոնի թափանցիկություն</string>
<string name="widget_config_show_device_label">Ցուցադրել սարքի անունը</string>
<string name="widget_config_reset_label">Վերականգնել լռելյալը</string>
<string name="widget_config_preview_resize_hint">Դուք կարող եք փոխել վիջեթի չափը այն ձեր գլխավոր էկրանին տեղադրելուց հետո:</string>
<string name="widget_config_preview_resize_hint">Կարող եք վիջեթի չափը փոխել այն տեղադրելուց հետո:</string>
<string name="widget_config_custom_label">Հատուկ</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Մութ</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Վերջին անգամ տեսնված՝ %s</string>
<string name="first_seen_x">Առաջին անգամ տեսնված՝ %s</string>
<string name="battery_cached_label">Վերջին հայտնի · %s</string>
<string name="battery_time_remaining_format_hm">%1$dժ %2$dր</string>
<string name="battery_time_remaining_format_h">%1$dժ</string>
<string name="battery_time_remaining_format_m">%1$dր</string>
<string name="permission_post_notifications_label">Ցուցադրել ծանուցումներ</string>
<string name="permission_post_notifications_description">"Թույլ տվեք CAPod-ին ցուցադրել ծանուցումներ ձեր AirPods-ների մասին, օրինակ՝ դրանց ընթացիկ կարգավիճակը՝ երբ միացված են։"</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Աջ ականջակալի սերիա</string>
<string name="device_settings_info_left_bonded_label">Ձախ կապակցված</string>
<string name="device_settings_info_right_bonded_label">Աջ կապակցված</string>
<string name="device_settings_info_battery_health_label">Մարտկոցի առողջություն (գնահատված)</string>
<string name="device_settings_info_battery_health_left_label">Ձախ մարտկոցի առողջություն (գն.)</string>
<string name="device_settings_info_battery_health_right_label">Աջ մարտկոցի առողջություն (գն.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Դեռ որոշվում է, ավելի ուշ կրկին ստուգեք</string>
<string name="device_settings_info_details_label">Սարքի մանրամասները</string>
<string name="device_settings_info_details_action">Ցուցադրել սարքի մանրամասները</string>
<string name="device_settings_info_status_label">Կարգավիճակ</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Penyesuaian umum yang memengaruhi seluruh aplikasi.</string>
<string name="settings_overview_hide_unmatched_label">Sembunyikan perangkat yang tidak cocok</string>
<string name="settings_overview_hide_unmatched_description">Jangan tampilkan perangkat terdekat yang tidak cocok dengan profil mana pun di ringkasan, misalnya AirPods milik orang lain.</string>
<string name="device_battery_estimate_toggle_label">Perkiraan Baterai</string>
<string name="device_battery_estimate_card_desc">Pelajari cara perangkat ini diisi daya dan dihabiskan untuk memperkirakan waktu yang tersisa, waktu hingga terisi penuh, dan kesehatan baterai.</string>
<string name="device_battery_estimate_reset_action">Setel Ulang Data Terbelajar</string>
<string name="device_battery_estimate_reset_desc">Mulai dari awal dengan daya tahan baterai yang dinilai.</string>
<string name="device_battery_estimate_reset_confirm_title">Setel Ulang Data Terbelajar?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod akan melupakan pengurasan, kecepatan pengisian, dan kesehatan baterai perangkat yang telah diukur, serta memulai dari awal dengan daya tahan baterai yang dinilai.</string>
<string name="settings_acknowledgements_label">Ucapan Terima Kasih</string>
<string name="settings_debug_autoreports_label">Laporan bug otomatis</string>
<string name="settings_debug_autoreports_description">Melaporkan masalah secara otomatis, mis. detail tentang kerusakan aplikasi sehingga saya dapat mencari tahu cara memperbaikinya.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparansi latar belakang</string>
<string name="widget_config_show_device_label">Tampilkan nama perangkat</string>
<string name="widget_config_reset_label">Atur ulang ke default</string>
<string name="widget_config_preview_resize_hint">Anda dapat mengubah ukuran widget setelah menempatkannya di layar beranda.</string>
<string name="widget_config_preview_resize_hint">Anda dapat mengubah ukuran widget setelah menempatkannya di layar utama.</string>
<string name="widget_config_custom_label">Kustom</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Gelap</string>
@@ -276,6 +282,9 @@
<string name="last_seen_x">Terakhir terlihat: %s</string>
<string name="first_seen_x">Pertama terlihat: %s</string>
<string name="battery_cached_label">Terakhir diketahui · %s</string>
<string name="battery_time_remaining_format_hm">%1$dj %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dj</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Tampilkan notifikasi</string>
<string name="permission_post_notifications_description">"Izinkan CAPod menampilkan notifikasi tentang AirPods Anda, mis. status saat ini saat terhubung."</string>
<!-- Device profiles -->
@@ -418,6 +427,11 @@
<string name="device_settings_info_right_serial_label">Nomor Seri Pod Kanan</string>
<string name="device_settings_info_left_bonded_label">Kiri Terhubung</string>
<string name="device_settings_info_right_bonded_label">Kanan Terhubung</string>
<string name="device_settings_info_battery_health_label">Kesehatan Baterai (diperkirakan)</string>
<string name="device_settings_info_battery_health_left_label">Kesehatan Baterai Kiri (perkiraan)</string>
<string name="device_settings_info_battery_health_right_label">Kesehatan Baterai Kanan (perkiraan)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Sedang ditentukan, periksa kembali nanti</string>
<string name="device_settings_info_details_label">Detail Perangkat</string>
<string name="device_settings_info_details_action">Tampilkan detail perangkat</string>
<string name="device_settings_info_status_label">Status</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Almennar stillingar sem hafa áhrif á allt forritið.</string>
<string name="settings_overview_hide_unmatched_label">Fela ópassandi tæki</string>
<string name="settings_overview_hide_unmatched_description">Sýnir ekki nálæg tæki sem passa ekki við neitt af sniðunum þínum í yfirlitinu, t.d. AirPods annarra.</string>
<string name="device_battery_estimate_toggle_label">Rafhlöðuáætlanir</string>
<string name="device_battery_estimate_card_desc">Lærðu hvernig þetta tæki hleðst og eyðir orku til að áætla eftirstandandi tíma, tíma þar til fullt er og heilsu rafhlöðu.</string>
<string name="device_battery_estimate_reset_action">Endurstilla lærð gögn</string>
<string name="device_battery_estimate_reset_desc">Byrja upp á nýtt frá tilgreindri rafhlöðuendingu.</string>
<string name="device_battery_estimate_reset_confirm_title">Endurstilla lærð gögn?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod mun gleyma mældum gangi, hleðsluhraða og heilsu rafhlöðu þessa tækis og byrja upp á nýtt frá tilgreindri rafhlöðuendingu.</string>
<string name="settings_acknowledgements_label">Þakkir</string>
<string name="settings_debug_autoreports_label">Sjálfvirkar villuskýrslur</string>
<string name="settings_debug_autoreports_description">Tilkynnir sjálfkrafa um vandamál, t.d. upplýsingar um hrun forrits svo ég geti fundið út hvernig á að laga það.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Gagnsæi bakgrunns</string>
<string name="widget_config_show_device_label">Sýna heiti tækis</string>
<string name="widget_config_reset_label">Endurstilla sjálfgildi</string>
<string name="widget_config_preview_resize_hint">Þú getur breytt stærð græjunnar eftir að þú hefur sett hana á heimaskjáinn þinn.</string>
<string name="widget_config_preview_resize_hint">Þú getur breytt stærð græjunnar eftir að hafa sett hana á heimaskjá þinn.</string>
<string name="widget_config_custom_label">Sérsniðið</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Dökkt</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Síðast séð: %s</string>
<string name="first_seen_x">Fyrst séð: %s</string>
<string name="battery_cached_label">Síðast þekkt · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Sýna tilkynningar</string>
<string name="permission_post_notifications_description">"Leyfðu CAPod að sýna tilkynningar um AirPods, t.d. núverandi stöðu þeirra á meðan þau eru tengd."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Raðnúmer hægri hlustunarpípu</string>
<string name="device_settings_info_left_bonded_label">Vinstri tengd</string>
<string name="device_settings_info_right_bonded_label">Hægri tengd</string>
<string name="device_settings_info_battery_health_label">Heilsa rafhlöðu (áætluð)</string>
<string name="device_settings_info_battery_health_left_label">Heilsa vinstrar rafhlöðu (áætl.)</string>
<string name="device_settings_info_battery_health_right_label">Heilsa hægrar rafhlöðu (áætl.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Enn er verið að reikna, athugaðu seinna</string>
<string name="device_settings_info_details_label">Tækjaupplýsingar</string>
<string name="device_settings_info_details_action">Sýna tækjaupplýsingar</string>
<string name="device_settings_info_status_label">Staða</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Modifiche generali che interessano l\'intera app.</string>
<string name="settings_overview_hide_unmatched_label">Nascondi dispositivi non abbinati</string>
<string name="settings_overview_hide_unmatched_description">Non mostrare nella panoramica i dispositivi nelle vicinanze che non corrispondono a nessuno dei tuoi profili, ad es. gli AirPods di altre persone.</string>
<string name="device_battery_estimate_toggle_label">Stime della batteria</string>
<string name="device_battery_estimate_card_desc">Scopri come questo dispositivo si carica e si scarica per stimare il tempo rimasto, il tempo fino alla carica completa e lo stato della batteria.</string>
<string name="device_battery_estimate_reset_action">Reimposta dati acquisiti</string>
<string name="device_battery_estimate_reset_desc">Ricomincia dalla durata della batteria dichiarata.</string>
<string name="device_battery_estimate_reset_confirm_title">Reimpostare i dati acquisiti?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod dimenticherà il consumo misurato del dispositivo, la velocità di ricarica e lo stato della batteria, e ricomincerà dalla durata della batteria dichiarata.</string>
<string name="settings_acknowledgements_label">Riconoscimenti</string>
<string name="settings_debug_autoreports_label">Segnalazioni automatiche di bug</string>
<string name="settings_debug_autoreports_description">Segnala automaticamente i problemi, ad es. dettagli su un arresto anomalo dell\'app in modo che io possa capire come risolverlo.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Trasparenza sfondo</string>
<string name="widget_config_show_device_label">Mostra nome dispositivo</string>
<string name="widget_config_reset_label">Ripristina predefiniti</string>
<string name="widget_config_preview_resize_hint">Puoi ridimensionare il widget dopo averlo posizionato sulla schermata Home.</string>
<string name="widget_config_preview_resize_hint">Puoi ridimensionare il widget dopo averlo posizionato sulla schermata iniziale.</string>
<string name="widget_config_custom_label">Personalizzato</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Scuro</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Visto l\'ultima volta: %s</string>
<string name="first_seen_x">Visto la prima volta: %s</string>
<string name="battery_cached_label">Ultimo noto · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostra notifiche</string>
<string name="permission_post_notifications_description">"Consenti a CAPod di mostrare notifiche sui tuoi AirPods, ad es. il loro stato attuale mentre sono connessi."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Seriale pod destro</string>
<string name="device_settings_info_left_bonded_label">Sinistra associata</string>
<string name="device_settings_info_right_bonded_label">Destra associata</string>
<string name="device_settings_info_battery_health_label">Salute della batteria (stimata)</string>
<string name="device_settings_info_battery_health_left_label">Salute della batteria sinistra (stim.)</string>
<string name="device_settings_info_battery_health_right_label">Salute della batteria destra (stim.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Determinazione in corso, controlla più tardi</string>
<string name="device_settings_info_details_label">Dettagli dispositivo</string>
<string name="device_settings_info_details_action">Mostra dettagli dispositivo</string>
<string name="device_settings_info_status_label">Stato</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">שינויים כלליים שמשפיעים על האפליקציה כולה.</string>
<string name="settings_overview_hide_unmatched_label">הסתר מכשירים שאינם תואמים</string>
<string name="settings_overview_hide_unmatched_description">אל תציג מכשירים קרובים שאינם תואמים אף אחד מהפרופילים שלך בתצוגה הכללית, למשל AirPods של אנשים אחרים.</string>
<string name="device_battery_estimate_toggle_label">הערכות סוללה</string>
<string name="device_battery_estimate_card_desc">למד כיצד הציוד הזה טוען ומתרוקן כדי להעריך את הזמן הנותר, הזמן עד לטעינה מלאה ובריאות הסוללה.</string>
<string name="device_battery_estimate_reset_action">איפוס נתונים שנלמדו</string>
<string name="device_battery_estimate_reset_desc">התחל מחדש מחיי הסוללה המדורגים.</string>
<string name="device_battery_estimate_reset_confirm_title">לאפס את הנתונים שנלמדו?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod תשכח את ניקוז הציוד שנמדד, מהירות הטעינה ובריאות הסוללה, והתחלה מחדש מחיי הסוללה המדורגים.</string>
<string name="settings_acknowledgements_label">תודות</string>
<string name="settings_debug_autoreports_label">דוחות באגים אוטומטיים</string>
<string name="settings_debug_autoreports_description">מדווח אוטומטית על בעיות, למשל, פרטים על קריסת אפליקציה כדי שאוכל להבין איך לתקן את זה.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">שקיפות רקע</string>
<string name="widget_config_show_device_label">הצג שם התקן</string>
<string name="widget_config_reset_label">אפס לברירות המחדל</string>
<string name="widget_config_preview_resize_hint">ניתן לשנות את גודל הווידג\'ט לאחר הצבתו על מסך הבית שלך.</string>
<string name="widget_config_preview_resize_hint">אתה יכול לשנות את גודל הווידג\'ט לאחר הצבתו במסך הבית שלך.</string>
<string name="widget_config_custom_label">מותאם אישית</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">כהה</string>
@@ -282,6 +288,9 @@
<string name="last_seen_x">נראה לאחרונה: %s</string>
<string name="first_seen_x">נראה לראשונה: %s</string>
<string name="battery_cached_label">אחרון ידוע · %s</string>
<string name="battery_time_remaining_format_hm">%1$dש %2$dד</string>
<string name="battery_time_remaining_format_h">%1$dש</string>
<string name="battery_time_remaining_format_m">%1$dד</string>
<string name="permission_post_notifications_label">הצגת התראות</string>
<string name="permission_post_notifications_description">"אפשר ל-CAPod להציג התראות על ה-AirPods שלך, למשל המצב הנוכחי שלהם בזמן חיבור."</string>
<!-- Device profiles -->
@@ -433,6 +442,11 @@
<string name="device_settings_info_right_serial_label">מספר סידורי ימין</string>
<string name="device_settings_info_left_bonded_label">שמאל מחובר</string>
<string name="device_settings_info_right_bonded_label">ימין מחובר</string>
<string name="device_settings_info_battery_health_label">בריאות הסוללה (משוערת)</string>
<string name="device_settings_info_battery_health_left_label">בריאות סוללה שמאלה (משוערת)</string>
<string name="device_settings_info_battery_health_right_label">בריאות סוללה ימינה (משוערת)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">עדיין בקביעה, בדוק שוב מאוחר יותר</string>
<string name="device_settings_info_details_label">פרטי התקן</string>
<string name="device_settings_info_details_action">הצג פרטי התקן</string>
<string name="device_settings_info_status_label">מצב</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">アプリ全体に影響する一般的な調整。</string>
<string name="settings_overview_hide_unmatched_label">一致しないデバイスを非表示</string>
<string name="settings_overview_hide_unmatched_description">プロフィールに一致しない近くのデバイス(他の人のAirPodsなど)を概要に表示しません。</string>
<string name="device_battery_estimate_toggle_label">バッテリー推定値</string>
<string name="device_battery_estimate_card_desc">このデバイスの充電・放電パターンを学習して、残り時間、充電完了までの時間、バッテリーの状態を推定します。</string>
<string name="device_battery_estimate_reset_action">学習データをリセット</string>
<string name="device_battery_estimate_reset_desc">定格バッテリー寿命からやり直します。</string>
<string name="device_battery_estimate_reset_confirm_title">学習データをリセットしますか?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPodはこのデバイスの測定済み放電、充電速度、バッテリーの状態を忘れて、定格バッテリー寿命からやり直します。</string>
<string name="settings_acknowledgements_label">謝辞</string>
<string name="settings_debug_autoreports_label">自動バグレポート</string>
<string name="settings_debug_autoreports_description">アプリのクラッシュに関する詳細など、問題を自動的に報告して、修正方法を把握できるようにします。</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">背景の透明度</string>
<string name="widget_config_show_device_label">デバイス名を表示</string>
<string name="widget_config_reset_label">デフォルトにリセット</string>
<string name="widget_config_preview_resize_hint">ホーム画面に配置した後、ウィジェットサイズを変更できます。</string>
<string name="widget_config_preview_resize_hint">ホーム画面に配置した後、ウィジェットをリサイズできます。</string>
<string name="widget_config_custom_label">カスタム</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">ダーク</string>
@@ -276,6 +282,9 @@
<string name="last_seen_x">最終確認: %s</string>
<string name="first_seen_x">初回確認: %s</string>
<string name="battery_cached_label">最後の既知の位置 · %s</string>
<string name="battery_time_remaining_format_hm">%1$d時間%2$d分</string>
<string name="battery_time_remaining_format_h">%1$d時間</string>
<string name="battery_time_remaining_format_m">%1$d分</string>
<string name="permission_post_notifications_label">通知を表示</string>
<string name="permission_post_notifications_description">"接続中の現在の状態など、AirPodsに関する通知をCAPodが表示できるようにします。"</string>
<!-- Device profiles -->
@@ -418,6 +427,11 @@
<string name="device_settings_info_right_serial_label">右ポッドのシリアル</string>
<string name="device_settings_info_left_bonded_label">左ボンド済み</string>
<string name="device_settings_info_right_bonded_label">右ボンド済み</string>
<string name="device_settings_info_battery_health_label">バッテリーの状態 (推定値)</string>
<string name="device_settings_info_battery_health_left_label">左のバッテリー状態 (推定値)</string>
<string name="device_settings_info_battery_health_right_label">右のバッテリー状態 (推定値)</string>
<string name="device_settings_info_battery_health_value">〜%1$d%%</string>
<string name="device_settings_info_battery_health_pending">計算中です。後ほどもう一度ご確認ください。</string>
<string name="device_settings_info_details_label">デバイス詳細</string>
<string name="device_settings_info_details_action">デバイス詳細を表示</string>
<string name="device_settings_info_status_label">状態</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">ზოგადი შესწორებები, რომლებიც გავლენას ახდენს მთელ აპლიკაციაზე.</string>
<string name="settings_overview_hide_unmatched_label">შეუსაბამო მოწყობილობების დამალვა</string>
<string name="settings_overview_hide_unmatched_description">არ აჩვენო მახლობლად მყოფი მოწყობილობები, რომლებიც არ ემთხვევა თქვენს პროფილებს მიმოხილვაში, მაგ. სხვა ადამიანების AirPods.</string>
<string name="device_battery_estimate_toggle_label">ბატარეის შეფასებები</string>
<string name="device_battery_estimate_card_desc">შეიტყვეთ, როგორ იტენდება და იხარჯება ეს მოწყობილობა, რათა შეაფასოთ დარჩენილი დრო, დრო სრული დატენებამდე და ბატარეის მდგომარეობა.</string>
<string name="device_battery_estimate_reset_action">მიღებული მონაცემების გადატვირთვა</string>
<string name="device_battery_estimate_reset_desc">დაიწყეთ ისევ შეფასებული ბატარეის სიცოცხლიდან.</string>
<string name="device_battery_estimate_reset_confirm_title">გადატვირთოთ მიღებული მონაცემები?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod დაავიწყდება ამ მოწყობილობის გაზომილი მოხმარება, დატენების სიჩქარე და ბატარეის მდგომარეობა, და დაიწყებს ისევ შეფასებული ბატარეის სიცოცხლიდან.</string>
<string name="settings_acknowledgements_label">მადლობები</string>
<string name="settings_debug_autoreports_label">შეცდომების ავტომატური ანგარიშები</string>
<string name="settings_debug_autoreports_description">ავტომატურად აცნობებს პრობლემების შესახებ, მაგ. აპლიკაციის ავარიის დეტალებს, რათა გავარკვიო, როგორ გამოვასწორო.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">ფონის გამჭვირვალობა</string>
<string name="widget_config_show_device_label">მოწყობილობის სახელის ჩვენება</string>
<string name="widget_config_reset_label">დაბრუნება ნაგულისხმებ პარამეტრებზე</string>
<string name="widget_config_preview_resize_hint">ვიჯეტის ზომის შეცვლა შესაძლებელია მთავარ ეკრანზე განთავსების შემდეგ.</string>
<string name="widget_config_preview_resize_hint">თქვენ შეგიძლიათ გადაზომოთ ვიჯეტი მას შემდეგ, რაც მოათავსებთ მას თქვენი მთავარ ეკრანზე.</string>
<string name="widget_config_custom_label">მორგებული</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">მუქი</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">ბოლოს ნანახია: %s</string>
<string name="first_seen_x">პირველად ნანახია: %s</string>
<string name="battery_cached_label">ბოლო ცნობილი · %s</string>
<string name="battery_time_remaining_format_hm">%1$dსთ %2$dწთ</string>
<string name="battery_time_remaining_format_h">%1$dსთ</string>
<string name="battery_time_remaining_format_m">%1$dწთ</string>
<string name="permission_post_notifications_label">შეტყობინებების ჩვენება</string>
<string name="permission_post_notifications_description">"დაუშვით, რომ CAPod აჩვენებდეს შეტყობინებებს თქვენი AirPods-ის შესახებ, მაგალითად მათ მიმდინარე სტატუსს დაკავშირებისას."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">მარჯვენა Pod-ის სერიალური</string>
<string name="device_settings_info_left_bonded_label">მარცხენა დაწყვილებული</string>
<string name="device_settings_info_right_bonded_label">მარჯვენა დაწყვილებული</string>
<string name="device_settings_info_battery_health_label">ბატარეის ჯანმრთელობა (შეფასებული)</string>
<string name="device_settings_info_battery_health_left_label">მარცხენა ბატარეის ჯანმრთელობა (შ.)</string>
<string name="device_settings_info_battery_health_right_label">მარჯვენა ბატარეის ჯანმრთელობა (შ.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">ჯერ კიდევ განისაზღვრება, მოგვიანებით შეამოწმეთ</string>
<string name="device_settings_info_details_label">მოწყობილობის დეტალურები</string>
<string name="device_settings_info_details_action">მოწყობილობის დეტალურების ჩვენება</string>
<string name="device_settings_info_status_label">სტატუსი</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">ការកែតម្រូវទូទៅដែលប៉ះពាល់ដល់កម្មវិធីទាំងមូល។</string>
<string name="settings_overview_hide_unmatched_label">លាក់ឧបករណ៍ដែលមិនត្រូវគ្នា</string>
<string name="settings_overview_hide_unmatched_description">មិនបង្ហាញឧបករណ៍នៅជិតដែលមិនត្រូវនឹងប្រវត្តិរូបណាមួយរបស់អ្នកនៅក្នុងទិដ្ឋភាពទូទៅ ឧ. AirPods របស់អ្នកដទៃ។</string>
<string name="device_battery_estimate_toggle_label">ការប៉ាន់ប្រមាណថាមពលថ្ម</string>
<string name="device_battery_estimate_card_desc">រៀនពីរបៀបដែលឧបករណ៍នេះសាក និងរលាយថាមពល ដើម្បីប៉ាន់ប្រមាណពេលដែលនៅសល់ រយៈពេលសាក និងសុខភាពថ្ម។</string>
<string name="device_battery_estimate_reset_action">កំដាប់ទិន្នន័យដែលរៀន</string>
<string name="device_battery_estimate_reset_desc">ចាប់ផ្តើមម្តងទៀតពីរយៈពេលថ្មដែលបានវាយតម្លៃ។</string>
<string name="device_battery_estimate_reset_confirm_title">លុបទិន្នន័យដែលរៀនឡើងវិញ?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod នឹងភ្លេចការរលាយដែលបានវាស់វែងរបស់ឧបករណ៍នេះ ល្បឿនសាក និងសុខភាពថ្ម ហើយចាប់ផ្តើមម្តងទៀតពីរយៈពេលថ្មដែលបានវាយតម្លៃ។</string>
<string name="settings_acknowledgements_label">ការទទួលស្គាល់</string>
<string name="settings_debug_autoreports_label">របាយការណ៍កំហុសដោយស្វ័យប្រវត្តិ</string>
<string name="settings_debug_autoreports_description">រាយការណ៍បញ្ហាដោយស្វ័យប្រវត្តិ ឧ. ព័ត៌មានលម្អិតអំពីការគាំងកម្មវិធី ដូច្នេះខ្ញុំអាចរកវិធីដោះស្រាយវាបាន។</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">ភាពថ្លាផ្ទៃខាងក្រោយ</string>
<string name="widget_config_show_device_label">បង្ហាញឈ្មោះឧបករណ៍</string>
<string name="widget_config_reset_label">កំណត់ឡើងវិញទៅលម្អិតដើម</string>
<string name="widget_config_preview_resize_hint">អ្នកអាចប្តូរទំហំ widget បន្ទាប់ពីដាក់វានៅលើអេក្រង់ដើមរបស់អ្នក។</string>
<string name="widget_config_preview_resize_hint">អ្នកអាចផ្លាស់ប្តូរទំហំវីដ្ជិតបន្ទាប់ពីដាក់វានៅលើអេក្រង់ដើមរបស់អ្នក។</string>
<string name="widget_config_custom_label">ផ្ទាល់ខ្លួន</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">ងងឹត</string>
@@ -276,6 +282,9 @@
<string name="last_seen_x">ឃើញចុងក្រោយ: %s</string>
<string name="first_seen_x">ឃើញដំបូង: %s</string>
<string name="battery_cached_label">ដឹងចុងក្រោយ · %s</string>
<string name="battery_time_remaining_format_hm">%1$dម %2$dន</string>
<string name="battery_time_remaining_format_h">%1$dម</string>
<string name="battery_time_remaining_format_m">%1$dន</string>
<string name="permission_post_notifications_label">បង្ហាញការជូនដំណឹង</string>
<string name="permission_post_notifications_description">"អនុញ្ញាតឱ្យ CAPod បង្ហាញការជូនដំណឹងអំពី AirPods របស់អ្នក ឧ. ស្ថានភាពបច្ចុប្បន្នរបស់ពួកគេខណៈពេលភ្ជាប់។"</string>
<!-- Device profiles -->
@@ -418,6 +427,11 @@
<string name="device_settings_info_right_serial_label">លើខស៊ើរី Pod ខាងស្តាំ</string>
<string name="device_settings_info_left_bonded_label">ខាងឆ្វេងបានភ្ជាប់</string>
<string name="device_settings_info_right_bonded_label">ខាងស្តាំបានភ្ជាប់</string>
<string name="device_settings_info_battery_health_label">សុខភាពថ្ម (ប៉ាន់ស្មាន)</string>
<string name="device_settings_info_battery_health_left_label">សុខភាពថ្ម ឆ្វេង (ប៉ាន់ស្មាន)</string>
<string name="device_settings_info_battery_health_right_label">សុខភាពថ្ម ស្ដាំ (ប៉ាន់ស្មាន)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">នៅកំពុងកំណត់ សូមត្រលប់មកពេលក្រោយ</string>
<string name="device_settings_info_details_label">ព័ត៌មានលម័អិតឧបករណ៍</string>
<string name="device_settings_info_details_action">បង់ហាញព័ត៌មានលម័អិតឧបករណ៍</string>
<string name="device_settings_info_status_label">ស្ថានភាព</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Guhertinên giştî yên ku bandorê li tevahiya sepanê dikin.</string>
<string name="settings_overview_hide_unmatched_label">Amûrên neçêkirî veşêre</string>
<string name="settings_overview_hide_unmatched_description">Amûrên nêzîk ên ku bi ti profilên te re li hev nakin di nêrîna giştî de nîşan nede, mînak AirPods-ên kesên din.</string>
<string name="device_battery_estimate_toggle_label">Texmînên Pîlandinê</string>
<string name="device_battery_estimate_card_desc">Fêr bike ka ev amûr çawa bar bibe û xurin bike da ku hûn karibe dem ê mayînê, dem ê barkirînê û tenduristiya pîlandinê texmîn bikin.</string>
<string name="device_battery_estimate_reset_action">Daneyên Fêrkirî Sifir Bike</string>
<string name="device_battery_estimate_reset_desc">Ji jiyana pîlandinê ya rêz dîsa destpê bike.</string>
<string name="device_battery_estimate_reset_confirm_title">Daneyên Fêrkirî Sifir Bike?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod wê xurina pîvandî ya vî amûrê, leza barkirînê û tenduristiya pîlandinê ji bîr bike, û ji jiyana pîlandinê ya rêz dîsa destpê bike.</string>
<string name="settings_acknowledgements_label">Spasdarî</string>
<string name="settings_debug_autoreports_label">Raporên xeletiyan ên otomatîk</string>
<string name="settings_debug_autoreports_description">Pirsgirêkan bixweber rapor dike, mînak hûrguliyên li ser têkçûna sepanê da ku ez bikaribim fêhm bikim ka meriv çawa wê sererast bike.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Zelabûna paşxistê</string>
<string name="widget_config_show_device_label">Navê cihazê nîşan bide</string>
<string name="widget_config_reset_label">Vegere bergûzariyên niyşê</string>
<string name="widget_config_preview_resize_hint">Tu dikarî pêvekê piştî danîna wê li ser ekrana xwe ya malê guhêrbikî.</string>
<string name="widget_config_preview_resize_hint">Tu dikarî pîvaza widgetê piştî danîna wê li ser ekrana malê ya te biguherîne.</string>
<string name="widget_config_custom_label">Xweser</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Tarî</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Cara dawîn hat dîtin: %s</string>
<string name="first_seen_x">Cara yekem hat dîtin: %s</string>
<string name="battery_cached_label">Dawî zanëbû · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Agahdariyan nîşan bide</string>
<string name="permission_post_notifications_description">"Destûrê bide CAPod ku li ser AirPods-ên we agahdariyan nîşan bide, mînak rewşa wan a niha dema girêdayî ne."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Serî ya Pod Rast</string>
<string name="device_settings_info_left_bonded_label">Çepê Girêdayî</string>
<string name="device_settings_info_right_bonded_label">Rastê Girêdayî</string>
<string name="device_settings_info_battery_health_label">Xwendiya Batarayê (texmînî)</string>
<string name="device_settings_info_battery_health_left_label">Xwendiya Batarayê ya Çepê (tex.)</string>
<string name="device_settings_info_battery_health_right_label">Xwendiya Batarayê ya Rastê (tex.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Hîn tê diyarkirin, dûre kontrol bike</string>
<string name="device_settings_info_details_label">Hûrguliyîn Amûrê</string>
<string name="device_settings_info_details_action">Hûrguliyîn amûrê nîşan bide</string>
<string name="device_settings_info_status_label">Rewş</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">ಇಡೀ ಅಪ್ಲಿಕೇಶನ್‌ನ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುವ ಸಾಮಾನ್ಯ ಹೊಂದಾಣಿಕೆಗಳು.</string>
<string name="settings_overview_hide_unmatched_label">ಹೊಂದಿಕೆಯಾಗದ ಸಾಧನಗಳನ್ನು ಮರೆಮಾಡಿ</string>
<string name="settings_overview_hide_unmatched_description">ನಿಮ್ಮ ಯಾವುದೇ ಪ್ರೊಫೈಲ್‌ಗಳಿಗೆ ಹೊಂದಿಕೆಯಾಗದ ಹತ್ತಿರದ ಸಾಧನಗಳನ್ನು ಅವಲೋಕನದಲ್ಲಿ ತೋರಿಸಬೇಡಿ, ಉದಾ. ಇತರ ಜನರ AirPods.</string>
<string name="device_battery_estimate_toggle_label">ಬ್ಯಾಟರಿ ಅಂದಾಜುಗಳು</string>
<string name="device_battery_estimate_card_desc">ಈ ಸಾಧನದ ಚಾರ್ಜಿಂಗ ಮತ್ತು ಡ್ರೈನ್ ವೇಗವನ್ನು ಕಲಿತುಕೊಂಡು ಉಳಿದ ಸಮಯ, ಚಾರ್ಜ ಆಗುವವರೆಗೆ ಸಮಯ ಮತ್ತು ಬ್ಯಾಟರಿ ಆರೋಗ್ಯವನ್ನು ಅಂದಾಜು ಮಾಡಿ.</string>
<string name="device_battery_estimate_reset_action">ಕಲಿತ ಮಾಹಿತಿ ಮರುಸೆಟ್ ಮಾಡಿ</string>
<string name="device_battery_estimate_reset_desc">ರೇಟ್ ಮಾಡಿದ ಬ್ಯಾಟರಿ ಆಯುಷ್ಯದಿಂದ ಮತ್ತೆ ಪ್ರಾರಂಭಿಸಿ.</string>
<string name="device_battery_estimate_reset_confirm_title">ಕಲಿತ ಮಾಹಿತಿ ಮರುಸೆಟ್ ಮಾಡಬೇಕೇ?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod ಈ ಸಾಧನದ ಅಳೆದ ಡ್ರೈನ್, ಚಾರ್ಜಿಂಗ ವೇಗ ಮತ್ತು ಬ್ಯಾಟರಿ ಆರೋಗ್ಯವನ್ನು ಮರೆಯುತ್ತದೆ ಮತ್ತು ರೇಟ್ ಮಾಡಿದ ಬ್ಯಾಟರಿ ಆಯುಷ್ಯದಿಂದ ಮತ್ತೆ ಪ್ರಾರಂಭಿಸುತ್ತದೆ.</string>
<string name="settings_acknowledgements_label">ಕೃತಜ್ಞತೆಗಳು</string>
<string name="settings_debug_autoreports_label">ಸ್ವಯಂಚಾಲಿತ ಬಗ್ ವರದಿಗಳು</string>
<string name="settings_debug_autoreports_description">ಸಮಸ್ಯೆಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ವರದಿ ಮಾಡುತ್ತದೆ, ಉದಾ. ಅಪ್ಲಿಕೇಶನ್ ಕ್ರ್ಯಾಶ್ ಕುರಿತು ವಿವರಗಳು ಇದರಿಂದ ಅದನ್ನು ಹೇಗೆ ಸರಿಪಡಿಸುವುದು ಎಂದು ನಾನು ಕಂಡುಹಿಡಿಯಬಹುದು.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">ಹಿನ್ನೆಲೆ ಪಾರದರ್ಶಕತೆ</string>
<string name="widget_config_show_device_label">ಸಾಧನ ಹೆಸರು ತೋರಿಸಿ</string>
<string name="widget_config_reset_label">ಪೂರ್ವನಿರ್ಧಾರಿತಕ್ಕೆ ಮರುಹೊಂದಿಸಿ</string>
<string name="widget_config_preview_resize_hint">ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್ ಇಟ್ಟ ನಂತರ ನೀವು ಅದನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸಬಹುದು.</string>
<string name="widget_config_preview_resize_hint">ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್ ಇರಿಸಿದ ನಂತರ ನೀವು ಅದನ್ನು ಮರುಗಾತ್ರಿಸಬಹುದು.</string>
<string name="widget_config_custom_label">ಕಸ್ಟಮ್</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">ಗಾಢ</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">ಕೊನೆಯ ಬಾರಿ ಕಂಡಿದ್ದು: %s</string>
<string name="first_seen_x">ಮೊದಲ ಬಾರಿ ಕಂಡಿದ್ದು: %s</string>
<string name="battery_cached_label">ಕೊನೆಯಾಗಿ ತಿಳಿದಿದ್ದು · %s</string>
<string name="battery_time_remaining_format_hm">%1$dಘಂ %2$dನಿ</string>
<string name="battery_time_remaining_format_h">%1$dಘಂ</string>
<string name="battery_time_remaining_format_m">%1$dನಿ</string>
<string name="permission_post_notifications_label">ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸಿ</string>
<string name="permission_post_notifications_description">"ನಿಮ್ಮ AirPods ಬಗ್ಗೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸಲು CAPod ಅನ್ನು ಅನುಮತಿಸಿ, ಉದಾ. ಸಂಪರ್ಕಗೊಂಡಾಗ ಅವುಗಳ ಪ್ರಸ್ತುತ ಸ್ಥಿತಿ."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">ಬಲ ಪಾಡ್ ಸೀರಿಯಲ್</string>
<string name="device_settings_info_left_bonded_label">ಎಡ ಬಂಧಿತ</string>
<string name="device_settings_info_right_bonded_label">ಬಲ ಬಂಧಿತ</string>
<string name="device_settings_info_battery_health_label">ಬ್ಯಾಟರಿ ಆರೋಗ್ಯ (ಅಂದಾಜು)</string>
<string name="device_settings_info_battery_health_left_label">ಎಡ ಬ್ಯಾಟರಿ ಆರೋಗ್ಯ (ಅಂದಾಜು)</string>
<string name="device_settings_info_battery_health_right_label">ಬಲ ಬ್ಯಾಟರಿ ಆರೋಗ್ಯ (ಅಂದಾಜು)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">ಇನ್ನೂ ನಿರ್ಧರಿಸುತ್ತಿದೆ, ನಂತರ ಪರಿಶೀಲಿಸಿ</string>
<string name="device_settings_info_details_label">ಸಾಧನ ವಿವರಗಳು</string>
<string name="device_settings_info_details_action">ಸಾಧನ ವಿವರಗಳನ್ನು ತೋರಿಸಿ</string>
<string name="device_settings_info_status_label">ಸ್ಥಿತಿ</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">앱 전체에 영향을 미치는 일반적인 조정.</string>
<string name="settings_overview_hide_unmatched_label">일치하지 않는 기기 숨기기</string>
<string name="settings_overview_hide_unmatched_description">개요에서 프로필과 일치하지 않는 주변 기기(예: 다른 사람의 AirPods)를 표시하지 않습니다.</string>
<string name="device_battery_estimate_toggle_label">배터리 추정</string>
<string name="device_battery_estimate_card_desc">이 기기의 충전 및 방전 방식을 학습하여 남은 시간, 충전 시간 및 배터리 상태를 추정합니다.</string>
<string name="device_battery_estimate_reset_action">학습된 데이터 초기화</string>
<string name="device_battery_estimate_reset_desc">정격 배터리 수명부터 다시 시작됩니다.</string>
<string name="device_battery_estimate_reset_confirm_title">학습된 데이터를 초기화하시겠습니까?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod가 이 기기의 측정된 방전량, 충전 속도 및 배터리 상태를 잊고 정격 배터리 수명부터 다시 시작합니다.</string>
<string name="settings_acknowledgements_label">감사의 말</string>
<string name="settings_debug_autoreports_label">자동 버그 보고서</string>
<string name="settings_debug_autoreports_description">앱 충돌에 대한 세부 정보와 같이 문제를 자동으로 보고하여 해결 방법을 파악할 수 있도록 합니다.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">배경 투명도</string>
<string name="widget_config_show_device_label">기기 이름 표시</string>
<string name="widget_config_reset_label">기본값으로 재설정</string>
<string name="widget_config_preview_resize_hint">홈 화면에 위젯을 배치한 후 크기를 조할 수 있습니다.</string>
<string name="widget_config_preview_resize_hint">홈 화면에 위젯을 배치한 후 크기를 조할 수 있습니다.</string>
<string name="widget_config_custom_label">사용자 정의</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">어두움</string>
@@ -276,6 +282,9 @@
<string name="last_seen_x">마지막 업데이트: %s</string>
<string name="first_seen_x">처음 업데이트: %s</string>
<string name="battery_cached_label">마지막으로 확인 · %s</string>
<string name="battery_time_remaining_format_hm">%1$d시간 %2$d분</string>
<string name="battery_time_remaining_format_h">%1$d시간</string>
<string name="battery_time_remaining_format_m">%1$d분</string>
<string name="permission_post_notifications_label">알림 표시</string>
<string name="permission_post_notifications_description">"CAPod이 현재 연결 상태 등 AirPods에 대한 알림을 표시할 수 있도록 허용합니다."</string>
<!-- Device profiles -->
@@ -420,6 +429,11 @@
<string name="device_settings_info_right_serial_label">오른쪽 이어팟 일련번호</string>
<string name="device_settings_info_left_bonded_label">왼쪽 연결됨</string>
<string name="device_settings_info_right_bonded_label">오른쪽 연결됨</string>
<string name="device_settings_info_battery_health_label">배터리 건강도 (추정)</string>
<string name="device_settings_info_battery_health_left_label">좌측 배터리 건강도 (추정)</string>
<string name="device_settings_info_battery_health_right_label">우측 배터리 건강도 (추정)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">계산 중입니다. 나중에 다시 확인하세요</string>
<string name="device_settings_info_details_label">기기 세부정보</string>
<string name="device_settings_info_details_action">기기 세부정보 보기</string>
<string name="device_settings_info_status_label">상태</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Бүткүл колдонмого таасир этүүчү жалпы оңдоолор.</string>
<string name="settings_overview_hide_unmatched_label">Дал келбеген түзмөктөрдү жашыруу</string>
<string name="settings_overview_hide_unmatched_description">Профилдериңиздин эч бирине дал келбеген жакын жердеги түзмөктөрдү, мисалы башка адамдардын AirPods\'ун, чолпондо көрсөтпөө.</string>
<string name="device_battery_estimate_toggle_label">Батарея болжолдоосу</string>
<string name="device_battery_estimate_card_desc">Бул түзүлүштүн кеминтүүсүнүн жана зарядтоо ылдамдыгын үйрөнүп, калган убакытты, зарядталуу убакыты жана батарейанын ден соолугун баалоо үчүн.</string>
<string name="device_battery_estimate_reset_action">Үйрөнүлгөн маалыматты тазалоо</string>
<string name="device_battery_estimate_reset_desc">Номиналдык батарея жашоо убакытынан кайра баштоо</string>
<string name="device_battery_estimate_reset_confirm_title">Үйрөнүлгөн маалыматты тазалообу?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod бул түзүлүштүн өлчөнгөн кеминтүүсү, зарядтоо ылдамдыгы жана батарейанын ден соолугу туралуу маалыматын унутуп, номиналдык батарея жашоо убакытынан баштажы керек.</string>
<string name="settings_acknowledgements_label">Ыраазычылыктар</string>
<string name="settings_debug_autoreports_label">Автоматтык ката отчеттору</string>
<string name="settings_debug_autoreports_description">Маселелерди автоматтык түрдө кабарлайт, мис., колдонмонун бузулушу жөнүндө маалымат, ошентип мен аны кантип оңдоону аныктай алам.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Фондун тунуктугу</string>
<string name="widget_config_show_device_label">Түзүлмөнүн атын көрсөтүү</string>
<string name="widget_config_reset_label">Демейки параметрлерге өндөрүү</string>
<string name="widget_config_preview_resize_hint">Виджетти башкы экранга жайгаштыргандан кийин, анын өлчөмүн өзгөртө аласыз.</string>
<string name="widget_config_preview_resize_hint">Виджетти үй экранына орноттон кийин анын өлчөмүн өзгөртө аласыз.</string>
<string name="widget_config_custom_label">Ыңгайлаштырылган</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Караңгы</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Акыркы көрүлгөн: %s</string>
<string name="first_seen_x">Биринчи көрүлгөн: %s</string>
<string name="battery_cached_label">Акыркы белгилүү · %s</string>
<string name="battery_time_remaining_format_hm">%1$dч %2$dмүн</string>
<string name="battery_time_remaining_format_h">%1$dч</string>
<string name="battery_time_remaining_format_m">%1$dмүн</string>
<string name="permission_post_notifications_label">Билдирүүлөрдү көрсөтүү</string>
<string name="permission_post_notifications_description">"CAPod'го AirPods жөнүндө билдирүүлөрдү көрсөтүүгө уруксат бериңиз, мис., учурдагы абалы туташып турганда."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Оң гарнитуранын сериал номери</string>
<string name="device_settings_info_left_bonded_label">Сол жакка байланган</string>
<string name="device_settings_info_right_bonded_label">Оң жакка байланган</string>
<string name="device_settings_info_battery_health_label">Батарея абалы (болжолдуу)</string>
<string name="device_settings_info_battery_health_left_label">Сол батарея абалы (б.)</string>
<string name="device_settings_info_battery_health_right_label">Оң батарея абалы (б.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Дагы аныкталуп жатат, кийинчерээк текшеп көрүңүз</string>
<string name="device_settings_info_details_label">Түзмөк деталдары</string>
<string name="device_settings_info_details_action">Түзмөк деталдарын көрсөтүү</string>
<string name="device_settings_info_status_label">Абалы</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">ການປັບແຕ່ງທົ່ວໄປທີ່ສົ່ງຜົນກະທົບຕໍ່ແອັບທັງໝົດ.</string>
<string name="settings_overview_hide_unmatched_label">຋່ອນອຸປະກອນທີ່ໄມ່ຕໍງກັນ</string>
<string name="settings_overview_hide_unmatched_description">ບໍ່ສະແດງອຸປະກອນໃກ້ທີ່ໄມ່ຕໍງກັບໂປຣໄຟລ໌ຂອງທ່ານໃນບັນທຶກປະກອບການ, ເຊັ່ນ: AirPods ຂອງຄນອື່ນ.</string>
<string name="device_battery_estimate_toggle_label">ການປະເມີນແບັດເຕີຣີ</string>
<string name="device_battery_estimate_card_desc">ຮຽນຮູ້ວິທີການສາກແລະລະບາຍຂອງອຸປະກອນນີ້ ເພື່ອຄາດຄະເນເວລາທີ່ຍັງເຫຼືອ, ເວລາໃນການສາກສົມບູນ, ແລະ ສະຖານະພາບແບັດເຕີຣີ</string>
<string name="device_battery_estimate_reset_action">ຕັ້ງໃໝ່ຂໍ້ມູນທີ່ຮຽນຮູ້</string>
<string name="device_battery_estimate_reset_desc">ເລີ່ມຕົ້ນໃໝ່ຈາກອາຍຸແບັດເຕີຣີມາດຕະຖານ</string>
<string name="device_battery_estimate_reset_confirm_title">ຕັ້ງໃໝ່ຂໍ້ມູນທີ່ຮຽນຮູ້ບໍ?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod ຈະລືມຂໍ້ມູນການລະບາຍ, ຄວາມໄວໃນການສາກ ແລະ ສະຖານະພາບແບັດເຕີຣີຂອງອຸປະກອນນີ້, ແລະ ເລີ່ມຕົ້ນໃໝ່ຈາກອາຍຸແບັດເຕີຣີມາດຕະຖານ</string>
<string name="settings_acknowledgements_label">ການຮັບຮູ້</string>
<string name="settings_debug_autoreports_label">ລາຍງານຂໍ້ບົກພ່ອງອັດຕະໂນມັດ</string>
<string name="settings_debug_autoreports_description">ລາຍງານບັນຫາໂດຍອັດຕະໂນມັດ, ຕົວຢ່າງ: ລາຍລະອຽດກ່ຽວກັບການຂັດຂ້ອງຂອງແອັບ ເພື່ອໃຫ້ຂ້ອຍສາມາດຫາວິທີແກ້ໄຂໄດ້.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">ຄວາມໂປ່ງໃສພື້ນຫຼັງ</string>
<string name="widget_config_show_device_label">ສະແດງຊື່ອຸປະກອນ</string>
<string name="widget_config_reset_label">ຣີເຊັດເປັນຄ່າເລີ່ມຕົ້ນ</string>
<string name="widget_config_preview_resize_hint">ທ່ານສາມາດປັບຂະໜາດ widget ຫຼັງຈາກວາງມັນໄວ້ໃນໜ້າຈໍຫຼັກຂອງທ່ານ.</string>
<string name="widget_config_preview_resize_hint">ທ່ານສາມາດປ່ຽນຂະໜາດວິດເຈັດໄດ້ຫຼັງຈາກວາງມັນໄວ້ໃນໜ້າຈໍຫຼັກຂອງທ່ານ</string>
<string name="widget_config_custom_label">ກຳນົດເອງ</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">ມືດ</string>
@@ -276,6 +282,9 @@
<string name="last_seen_x">ເຫັນຫຼ້າສຸດ: %s</string>
<string name="first_seen_x">ເຫັນຄັ້ງທຳອິດ: %s</string>
<string name="battery_cached_label">ຮູ້ຈັກລ່າສຸດ · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">ສະແດງການແຈ້ງເຕືອນ</string>
<string name="permission_post_notifications_description">"ອະນຸຍາດໃຫ້ CAPod ສະແດງການແຈ້ງເຕືອນກ່ຽວກັບ AirPods ຂອງທ່ານ, ຕົວຢ່າງ: ສະຖານະປັດຈຸບັນໃນຂະນະເຊື່ອມຕໍ່."</string>
<!-- Device profiles -->
@@ -418,6 +427,11 @@
<string name="device_settings_info_right_serial_label">ໝາຍເລກ Pod ຂວາ</string>
<string name="device_settings_info_left_bonded_label">ຊ້າຍໄດ້ຜູກມັດ</string>
<string name="device_settings_info_right_bonded_label">ຂວາໄດ້ຜູກມັດ</string>
<string name="device_settings_info_battery_health_label">ສະຖານະພາບແບັດເຕີຣີ (ປະເມີນ)</string>
<string name="device_settings_info_battery_health_left_label">ສະຖານະພາບແບັດເຕີຣີຊ້າຍ (ປະເມີນ)</string>
<string name="device_settings_info_battery_health_right_label">ສະຖານະພາບແບັດເຕີຣີຂວາ (ປະເມີນ)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">ຍັງກຳລັງກຳນົດ, ກະລຸນາກັບມາບໍ່ດົນ</string>
<string name="device_settings_info_details_label">ຂໍ້ມູນອຸປະກອນ</string>
<string name="device_settings_info_details_action">ສະແດງຂໍ້ມູນອຸປະກອນ</string>
<string name="device_settings_info_status_label">ສົຖະນະ</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Bendrieji pakeitimai, turintys įtakos visai programai.</string>
<string name="settings_overview_hide_unmatched_label">Slėpti neatitinkančius įrenginius</string>
<string name="settings_overview_hide_unmatched_description">Nerodykite netoliese esančių įrenginius, kurie neatitinka nė vieno jūsų profilio apžvalgoje, pvz. kitų žmonų AirPods.</string>
<string name="device_battery_estimate_toggle_label">Baterijos įverčiai</string>
<string name="device_battery_estimate_card_desc">Sužinokite, kaip šis įrenginys kraunasi ir išsikrauna, kad galėtumėte įvertinti likusį laiką, laiką iki pilno įkrovimo ir baterijos būklę.</string>
<string name="device_battery_estimate_reset_action">Iš naujo nustatyti išmoktus duomenis</string>
<string name="device_battery_estimate_reset_desc">Pradėkite iš naujo nuo nurodytosios baterijos veikimo trukmės.</string>
<string name="device_battery_estimate_reset_confirm_title">Iš naujo nustatyti išmoktus duomenis?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod pamiršs šio įrenginio išmatuotą energijos sąnaudą, įkrovimo greitį ir baterijos būklę, ir pradės iš naujo nuo nurodytosios baterijos veikimo trukmės.</string>
<string name="settings_acknowledgements_label">Padėkos</string>
<string name="settings_debug_autoreports_label">Automatinės klaidų ataskaitos</string>
<string name="settings_debug_autoreports_description">Automatiškai praneša apie problemas, pvz., išsamią informaciją apie programos strigtį, kad galėčiau išsiaiškinti, kaip ją ištaisyti.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Fono permatomumas</string>
<string name="widget_config_show_device_label">Rodyti įrenginio pavadinimą</string>
<string name="widget_config_reset_label">Atstatyti į numatytuosius nustatymus</string>
<string name="widget_config_preview_resize_hint">Galite keisti valdiklio dydį po to, kai jį įdėsite į pagrindinį ekraną.</string>
<string name="widget_config_preview_resize_hint">Galite pakeisti priedo dydį, jį padėję savo pradžios ekrane.</string>
<string name="widget_config_custom_label">Pasirinktinis</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Tamsi</string>
@@ -282,6 +288,9 @@
<string name="last_seen_x">Paskutinį kartą matytas: %s</string>
<string name="first_seen_x">Pirmą kartą matytas: %s</string>
<string name="battery_cached_label">Paskutinis žinomas · %s</string>
<string name="battery_time_remaining_format_hm">%1$dv %2$dmin</string>
<string name="battery_time_remaining_format_h">%1$dv</string>
<string name="battery_time_remaining_format_m">%1$dmin</string>
<string name="permission_post_notifications_label">Rodyti pranešimus</string>
<string name="permission_post_notifications_description">"Leiskite CAPod rodyti pranešimus apie jūsų „AirPods\", pvz., jų dabartinę būseną prisijungus."</string>
<!-- Device profiles -->
@@ -433,6 +442,11 @@
<string name="device_settings_info_right_serial_label">Dešiniojo ausinuko serijos numeris</string>
<string name="device_settings_info_left_bonded_label">Kairė susieta</string>
<string name="device_settings_info_right_bonded_label">Dešinė susieta</string>
<string name="device_settings_info_battery_health_label">Baterijos sveikata (apskaičiuota)</string>
<string name="device_settings_info_battery_health_left_label">Kairės baterijos sveikata (apskaič.)</string>
<string name="device_settings_info_battery_health_right_label">Dešinės baterijos sveikata (apskaič.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Dar nustatoma, grįžkite vėliau</string>
<string name="device_settings_info_details_label">Įrenginio informacija</string>
<string name="device_settings_info_details_action">Rodyti įrenginio informaciją</string>
<string name="device_settings_info_status_label">Būsena</string>
+30 -16
View File
@@ -1,34 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="general_share_action">Dalīties</string>
<string name="general_share_action">Kopīgot</string>
<string name="general_done_action">Gatavs</string>
<string name="general_cancel_action">Atcelt</string>
<string name="general_copy_action">Kopēt</string>
<string name="general_copy_action">Ievietot starpliktuvē</string>
<string name="general_thank_you_label">Paldies</string>
<string name="general_upgrade_action">Jaunināt</string>
<string name="common_feature_requires_pro_msg">Jauniniet, lai atbloķētu.</string>
<string name="common_upgrade_required_label">Nepieciešams jauninājums</string>
<string name="general_upgrade_action">Uzlabot</string>
<string name="common_feature_requires_pro_msg">Jāuzlabo, lai atslēgtu.</string>
<string name="common_upgrade_required_label">Nepieciešama uzlabošana</string>
<string name="general_donate_action">Ziedot</string>
<string name="general_check_action">Pārbaudīt</string>
<string name="general_close_action">Aizvērt</string>
<string name="general_edit_action">Rediģēt</string>
<string name="general_edit_action">Labot</string>
<string name="general_save_action">Saglabāt</string>
<string name="general_guide_action">Ceļvedis</string>
<string name="general_continue_action">Turpināt</string>
<string name="general_show_action">Rādīt</string>
<string name="general_hide_action">Paslēpt</string>
<string name="general_example_label">Piem.: %s</string>
<string name="upgrade_capod_label">Jaunināt CAPod</string>
<string name="upgrade_capod_description">Iegūstiet papildu funkcijas un atbalstiet izstrādātāju.</string>
<string name="upgrade_preamble">CAPod ir izstrādājusi viena persona. Jaunināšana atbloķē papildu funkcijas un palīdz uzturēt lietotni pie dzīvības.</string>
<string name="upgrade_benefit_themes">Tēmu pielāgošana</string>
<string name="upgrade_benefit_autoplay">Automātiska atskaņošana un pauze</string>
<string name="upgrade_benefit_popups">Uznirstošs logs, atverot futrāli un savienojoties</string>
<string name="upgrade_capod_label">Uzlabot CAPod</string>
<string name="upgrade_capod_description">Iegūsti papildu iespējas un atbalsti izstrādātāju!</string>
<string name="upgrade_preamble">CAPod ir izstrādājis viens cilvēks. Uzlabošana atslēdz papildu iespējas un palīdz uzturēt lietotnes esamību.</string>
<string name="upgrade_benefit_themes">Izskata pielāgošana</string>
<string name="upgrade_benefit_autoplay">Automātiska atskaņošana un apturēšana</string>
<string name="upgrade_benefit_popups">Uznirstošs logs pēc futrāļa atvēršanas un savienošanās</string>
<string name="upgrade_benefit_widgets">Sākuma ekrāna logrīki</string>
<string name="upgrade_benefit_device_settings">Papildu ierīces iestatījumi</string>
<string name="upgrade_benefit_device_controls">Ierīces vadīklēšana — troksnis, kātieni un vairāk</string>
<string name="upgrade_benefit_support">Atbalstiet izstrādātāju</string>
<string name="upgrade_benefit_disclaimer">Funkciju pieejamība atkarīga no jūsu ausţu un ierīces.</string>
<string name="upgrade_benefit_device_controls">Ierīces vadīklas — troksnis, kātieni un vairāk</string>
<string name="upgrade_benefit_support">Izstrādātāja atbalstīšana</string>
<string name="upgrade_benefit_disclaimer">Iespēju pieejamība atkarīga no austiņam un ierīces.</string>
<string name="upgrade_screen_options_description">Vienādas funkcijas, atšķirīgas cenas. Abonements ietver bezmaksas izmēģinājumu un var tikt atcelts jebkurā laikā.</string>
<string name="upgrade_screen_subscription_trial_action">Sākt bezmaksas izmēģinājumu</string>
<string name="upgrade_screen_subscription_action">Abonēt gadā</string>
@@ -122,6 +122,12 @@
<string name="settings_general_description">Vispārīgi pielāgojumi, kas ietekmē visu lietotni.</string>
<string name="settings_overview_hide_unmatched_label">Slēpt neatbilstošās ierīces</string>
<string name="settings_overview_hide_unmatched_description">Nerādīt tuvumā esošās ierīces, kas neatbilst nevienam no jūsu profiliem pārskatā, piemēram, citu cilvēku AirPods.</string>
<string name="device_battery_estimate_toggle_label">Baterijas novērtējumi</string>
<string name="device_battery_estimate_card_desc">Uzziniet, kā šī ierīce tiek uzlādēta un iztukšojas, lai novērtētu atlikušo laiku, laiku līdz pilnai uzlādei un baterijas stāvokli.</string>
<string name="device_battery_estimate_reset_action">Atiestatīt apgūtos datus</string>
<string name="device_battery_estimate_reset_desc">Sāciet no jauna ar norādīto baterijas darbības ilgumu.</string>
<string name="device_battery_estimate_reset_confirm_title">Atiestatīt apgūtos datus?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod aizmirsīs šīs ierīces izmērīto baterijas noplūdi, uzlādes ātrumu un baterijas stāvokli, un sāks no jauna ar norādīto baterijas darbības ilgumu.</string>
<string name="settings_acknowledgements_label">Pateicības</string>
<string name="settings_debug_autoreports_label">Automātiski kļūdu ziņojumi</string>
<string name="settings_debug_autoreports_description">Automātiski ziņo par problēmām, piem., informāciju par lietotnes avāriju, lai es varētu izdomāt, kā to labot.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Fona caursspīdīgums</string>
<string name="widget_config_show_device_label">Rādīt ierīces nosaukumu</string>
<string name="widget_config_reset_label">Atiestatīt noklusējumos</string>
<string name="widget_config_preview_resize_hint">Varat mainīt logrīka izmēru pēc tā novietošanas sākumekrānā.</string>
<string name="widget_config_preview_resize_hint">Pēc logrīka novietošanas uz sākuma ekrāna varat mainīt tā lielumu.</string>
<string name="widget_config_custom_label">Pielāgots</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Tumšs</string>
@@ -280,6 +286,9 @@
<string name="last_seen_x">Pēdējoreiz redzēta: %s</string>
<string name="first_seen_x">Pirmo reizi redzēta: %s</string>
<string name="battery_cached_label">Pēdējoreiz zināms · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Rādīt paziņojumus</string>
<string name="permission_post_notifications_description">"Atļaujiet CAPod rādīt paziņojumus par jūsu AirPods, piem., to pašreizējo statusu, kamēr tie ir savienoti."</string>
<!-- Device profiles -->
@@ -428,6 +437,11 @@
<string name="device_settings_info_right_serial_label">Labās austiņas sērijas numurs</string>
<string name="device_settings_info_left_bonded_label">Kreisais savienots</string>
<string name="device_settings_info_right_bonded_label">Labais savienots</string>
<string name="device_settings_info_battery_health_label">Baterijas stāvoklis (novērtēts)</string>
<string name="device_settings_info_battery_health_left_label">Kreisās baterijas stāvoklis (novērt.)</string>
<string name="device_settings_info_battery_health_right_label">Labās baterijas stāvoklis (novērt.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Joprojām tiek aprēķināts, vēlāk pārbaudiet</string>
<string name="device_settings_info_details_label">Ierīces informācija</string>
<string name="device_settings_info_details_action">Rādīt ierīces informāciju</string>
<string name="device_settings_info_status_label">Stāvoklis</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Општи прилагодувања што влијаат на целата апликација.</string>
<string name="settings_overview_hide_unmatched_label">Скриј неподударни уреди</string>
<string name="settings_overview_hide_unmatched_description">Не прикажувај блиски уреди кои не се поклопуваат со ниеден профил во прегледот, на пр. AirPods на други луѓе.</string>
<string name="device_battery_estimate_toggle_label">Процени на батеријата</string>
<string name="device_battery_estimate_card_desc">Научи како овој уред се полни и се празни за да процениш преостанатото време, времето до полнење и здравјето на батеријата.</string>
<string name="device_battery_estimate_reset_action">Ресетирај научени податоци</string>
<string name="device_battery_estimate_reset_desc">Почни наново од номиналниот век на батеријата.</string>
<string name="device_battery_estimate_reset_confirm_title">Ресетирај научени податоци?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod ќе ги заборави измерената потрошња на овој уред, брзината на полнење и здравјето на батеријата, и ќе почне наново од номиналниот век на батеријата.</string>
<string name="settings_acknowledgements_label">Благодарници</string>
<string name="settings_debug_autoreports_label">Автоматски извештаи за грешки</string>
<string name="settings_debug_autoreports_description">Автоматски пријавува проблеми, на пр. детали за пад на апликацијата за да можам да откријам како да го поправам.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Транспарентност на позадина</string>
<string name="widget_config_show_device_label">Прикажи име на уред</string>
<string name="widget_config_reset_label">Врати на стандардни</string>
<string name="widget_config_preview_resize_hint">Можете да го промените големината на виџетот откако ќе го поставите на почетниот екран.</string>
<string name="widget_config_preview_resize_hint">Можеш да ја промениш големината на виџетот откако ќе го поставиш на домашниот екран.</string>
<string name="widget_config_custom_label">Прилагодено</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Темно</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Последно видено: %s</string>
<string name="first_seen_x">Прво видено: %s</string>
<string name="battery_cached_label">Последно познато · %s</string>
<string name="battery_time_remaining_format_hm">%1$dч %2$dм</string>
<string name="battery_time_remaining_format_h">%1$dч</string>
<string name="battery_time_remaining_format_m">%1$dм</string>
<string name="permission_post_notifications_label">Прикажи известувања</string>
<string name="permission_post_notifications_description">"Дозволете CAPod да прикажува известувања за вашите AirPods, на пр. нивниот тековен статус додека се поврзани."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Сериски број на десниот слушалец</string>
<string name="device_settings_info_left_bonded_label">Лево поврзано</string>
<string name="device_settings_info_right_bonded_label">Десно поврзано</string>
<string name="device_settings_info_battery_health_label">Здравје на батеријата (проценето)</string>
<string name="device_settings_info_battery_health_left_label">Здравје на левата батерија (проц.)</string>
<string name="device_settings_info_battery_health_right_label">Здравје на десната батерија (проц.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Се пресметува, врати се подоцна</string>
<string name="device_settings_info_details_label">Детали за уредот</string>
<string name="device_settings_info_details_action">Покажи детали за уредот</string>
<string name="device_settings_info_status_label">Статус</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">മുഴുവൻ ആപ്പിനെയും ബാധിക്കുന്ന പൊതുവായ മാറ്റങ്ങൾ.</string>
<string name="settings_overview_hide_unmatched_label">പൊരുത്തപ്പെടാത്ത ഉപകരണങ്ങൾ മറയ്ക്കുക</string>
<string name="settings_overview_hide_unmatched_description">നിങ്ങളുടെ ഒരു പ്രൊഫൈലുമായും പൊരുത്തപ്പെടാത്ത അടുത്തുള്ള ഉപകരണങ്ങൾ അവലോകനത്തിൽ കാണിക്കരുത്, ഉദാ. മറ്റുള്ളവരുടെ AirPods.</string>
<string name="device_battery_estimate_toggle_label">ബാറ്ററി കണക്കുമാനം</string>
<string name="device_battery_estimate_card_desc">ഈ ഉപകരണം എങ്ങനെ ചാർജ് ചെയ്യുകയും വെറ്റുകയും ചെയ്യുന്നുവെന്ന് പഠിച്ച് ശേഷിക്കുന്ന സമയം, ചാർജാകാനുള്ള സമയം, ബാറ്ററി ആരോഗ്യം എന്നിവ കണക്കാക്കുക</string>
<string name="device_battery_estimate_reset_action">പഠിപ്പിച്ച ഡാറ്റ പുനരാരംഭിക്കുക</string>
<string name="device_battery_estimate_reset_desc">നിർദ്ദിഷ്ട ബാറ്ററി കാലാവധിയിൽ നിന്ന് വീണ്ടും ആരംഭിക്കുക</string>
<string name="device_battery_estimate_reset_confirm_title">പഠിപ്പിച്ച ഡാറ്റ പുനരാരംഭിക്കണോ?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod ഈ ഉപകരണത്തിന്റെ അളന്ന നഷ്ടം, ചാർജിംഗ് വേഗത, ബാറ്ററി ആരോഗ്യം എന്നിവ മറന്ന് നിർദ്ദിഷ്ട ബാറ്ററി കാലാവധിയിൽ നിന്ന് വീണ്ടും ആരംഭിക്കും</string>
<string name="settings_acknowledgements_label">അംഗീകാരങ്ങൾ</string>
<string name="settings_debug_autoreports_label">യാന്ത്രിക ബഗ് റിപ്പോർട്ടുകൾ</string>
<string name="settings_debug_autoreports_description">പ്രശ്നങ്ങൾ യാന്ത്രികമായി റിപ്പോർട്ട് ചെയ്യുന്നു, ഉദാ. ആപ്പ് ക്രാഷിനെക്കുറിച്ചുള്ള വിശദാംശങ്ങൾ, അതുവഴി എനിക്കത് എങ്ങനെ പരിഹരിക്കാമെന്ന് കണ്ടെത്താനാകും.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">പശ്ചാത്തല സ്വച്ഛത</string>
<string name="widget_config_show_device_label">ഉപകരണത്തിന്റെ പേര് കാണിക്കുക</string>
<string name="widget_config_reset_label">ഡിഫോള്‍ട്ടിലേക്ക് പുനഃസജ്ജമാക്കുക</string>
<string name="widget_config_preview_resize_hint">ഹോം സ്ക്രീനിൽ വിജറ്റ് വെച്ചതിനുശേഷം നിങ്ങൾക്ക് അതിന്റെ വലപ്പം മാറ്റാൻ കഴിയും.</string>
<string name="widget_config_preview_resize_hint">നിങ്ങൾ വിജെറ്റ് നിങ്ങളുടെ ഹോം സ്ക്രീനിൽ സ്ഥാപിച്ച ശേഷം അതിന്റെ വലിപ്പം മാറ്റാ</string>
<string name="widget_config_custom_label">കസ്റ്റം</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">ഗാഢം</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">അവസാനം കണ്ടത്: %s</string>
<string name="first_seen_x">ആദ്യം കണ്ടത്: %s</string>
<string name="battery_cached_label">അവസാനമായി അറിയപ്പെടുന്നത് · %s</string>
<string name="battery_time_remaining_format_hm">%1$dമ %2$dമി</string>
<string name="battery_time_remaining_format_h">%1$dമ</string>
<string name="battery_time_remaining_format_m">%1$dമി</string>
<string name="permission_post_notifications_label">അറിയിപ്പുകൾ കാണിക്കുക</string>
<string name="permission_post_notifications_description">"നിങ്ങളുടെ AirPods-നെ കുറിച്ചുള്ള അറിയിപ്പുകൾ കാണിക്കാൻ CAPod-നെ അനുവദിക്കുക, ഉദാ. കണക്റ്റുചെയ്തിരിക്കുമ്പോൾ അവയുടെ നിലവിലെ നില."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">വലതു പോഡ് സീരിയൽ</string>
<string name="device_settings_info_left_bonded_label">ഇടത് ബോണ്ട് ചെയ്തത്</string>
<string name="device_settings_info_right_bonded_label">വലത് ബോണ്ട് ചെയ്തത്</string>
<string name="device_settings_info_battery_health_label">ബാറ്ററി ആരോഗ്യം (കണക്കാക്കപ്പെട്ടത്)</string>
<string name="device_settings_info_battery_health_left_label">ഇടത് ബാറ്ററി ആരോഗ്യം (കണ.)</string>
<string name="device_settings_info_battery_health_right_label">വലത് ബാറ്ററി ആരോഗ്യം (കണ.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">ഇപ്പോഴും നിർണയിക്കുന്നു, പിന്നീട് നോക്കുക</string>
<string name="device_settings_info_details_label">ഡിവൈസ് വിവരങ്ങൾ</string>
<string name="device_settings_info_details_action">ഡിവൈസ് വിവരങ്ങൾ കാണിക്കുക</string>
<string name="device_settings_info_status_label">നില</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Апп-н бүх хэсэгт нөлөөлөх ерөнхий тохируулгууд.</string>
<string name="settings_overview_hide_unmatched_label">Тохирохгүй төхөөрөмжүүдийг нуух</string>
<string name="settings_overview_hide_unmatched_description">Таны профайлтай тохирохгүй ойрхон байгаа төхөөрөмжүүдийг, тухайлбал бусад хүмүүсийн AirPods-ийг тоймд харуулахгүй.</string>
<string name="device_battery_estimate_toggle_label">Батерейны үнэлгээ</string>
<string name="device_battery_estimate_card_desc">Энэ төхөөрөмжийн цэнэглэлт, цэнэг ашиглалтын байдлыг сураад үлдсэн цаг, цэнэглэгдэх хүртэлх цаг, батерейны байдлыг үнэлнэ.</string>
<string name="device_battery_estimate_reset_action">Сурсан өгөгдлийг сэргээх</string>
<string name="device_battery_estimate_reset_desc">Үнэлгээтэй батерейны хугацаанаас эхлүүлэх</string>
<string name="device_battery_estimate_reset_confirm_title">Сурсан өгөгдлийг сэргээх үү?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod энэ төхөөрөмжийн хэмжсэн цэнэг ашигалт, цэнэглэх хурд болон батерейны байдлыг мартаж, үнэлгээтэй батерейны хугацаанаас эхлүүлэх болно.</string>
<string name="settings_acknowledgements_label">Талархал</string>
<string name="settings_debug_autoreports_label">Автомат алдааны тайлан</string>
<string name="settings_debug_autoreports_description">Апп-н ослын дэлгэрэнгүй мэдээлэл гэх мэт асуудлуудыг автоматаар мэдээлдэг бөгөөд ингэснээр би түүнийг хэрхэн засахаа олж мэдэх боломжтой.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Фоны тунгалаг байдал</string>
<string name="widget_config_show_device_label">Төхөөрөмжийн нэрийг харуулах</string>
<string name="widget_config_reset_label">Анхны утга руу сэргээх</string>
<string name="widget_config_preview_resize_hint">Та виджетийг нүүр дэлгэцэндээ байрлуулсны дараа хэмжээг нь өөрчилж болно.</string>
<string name="widget_config_preview_resize_hint">Нүүр дэлгэцэд байрлуулсаны дараа виджетийн хэмжээг өөрчилж болно.</string>
<string name="widget_config_custom_label">Нэмэлт</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Сүүдэр</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Сүүлд харагдсан: %s</string>
<string name="first_seen_x">Анх харагдсан: %s</string>
<string name="battery_cached_label">Сүүлд мэдэгдсэн · %s</string>
<string name="battery_time_remaining_format_hm">%1$dц %2$dм</string>
<string name="battery_time_remaining_format_h">%1$dц</string>
<string name="battery_time_remaining_format_m">%1$dм</string>
<string name="permission_post_notifications_label">Мэдэгдэл харуулах</string>
<string name="permission_post_notifications_description">"CAPod-д таны AirPods-ийн талаарх мэдэгдлүүдийг, жишээ нь холбогдсон үеийн одоогийн төлөвийг харуулах зөвшөөрөл олгоно уу."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Баруун чихэвчийн серийн дугаар</string>
<string name="device_settings_info_left_bonded_label">Зүүн холбосон</string>
<string name="device_settings_info_right_bonded_label">Баруун холбосон</string>
<string name="device_settings_info_battery_health_label">Батерейны байдал (тооцоолсон)</string>
<string name="device_settings_info_battery_health_left_label">Зүүн батерейны байдал (т.)</string>
<string name="device_settings_info_battery_health_right_label">Баруун батерейны байдал (т.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Одоо тооцоолж байна, дараа дахин үзээрэй</string>
<string name="device_settings_info_details_label">Төхөөрөмжийн дэлгэрэнгүй мэдээлэл</string>
<string name="device_settings_info_details_action">Төхөөрөмжийн дэлгэрэнгүй мэдээлэл үзэх</string>
<string name="device_settings_info_status_label">Төлөв</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">संपूर्ण अॅपवर परिणाम करणारे सामान्य बदल.</string>
<string name="settings_overview_hide_unmatched_label">न जुळणारी उपकरणे लपवा</string>
<string name="settings_overview_hide_unmatched_description">आढावा विभागात तुमच्या कोणत्याही प्रोफाइलशी न जुळणारी जवळची उपकरणे दाखवू नका, उदा. इतर लोकांचे AirPods.</string>
<string name="device_battery_estimate_toggle_label">बॅटरी अंदाज</string>
<string name="device_battery_estimate_card_desc">या डिव्हाइसचे चार्जिंग आणि डिस्चार्जिंग समजून घ्या आणि उरलेली वेळ, चार्ज होण्याची वेळ आणि बॅटरीचे आरोग्य अंदाजित करा.</string>
<string name="device_battery_estimate_reset_action">शिकलेला डेटा रीसेट करा</string>
<string name="device_battery_estimate_reset_desc">रेट केलेल्या बॅटरी आयुष्यापासून पुन्हा सुरुवात करा.</string>
<string name="device_battery_estimate_reset_confirm_title">शिकलेला डेटा रीसेट करायचे?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod या डिव्हाइसचा मोजलेला ड्रेन, चार्जिंग गती आणि बॅटरी आरोग्य विसरून देईल आणि रेट केलेल्या बॅटरी आयुष्यापासून पुन्हा सुरुवात करेल.</string>
<string name="settings_acknowledgements_label">पोचपावती</string>
<string name="settings_debug_autoreports_label">स्वयंचलित बग अहवाल</string>
<string name="settings_debug_autoreports_description">समस्या स्वयंचलितपणे नोंदवते, उदा. अॅप क्रॅशबद्दल तपशील जेणेकरून मी ते कसे दुरुस्त करायचे हे शोधू शकेन.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">पार्श्वभूमी पारदर्शकता</string>
<string name="widget_config_show_device_label">डिव्हाइस नाव दाखवा</string>
<string name="widget_config_reset_label">डिफॉल्टवर रीसेट करा</string>
<string name="widget_config_preview_resize_hint">तुम्ही विजेट तुमच्या होम स्क्रीनवर ठेवल्यानंतर त्याचा आकार बदलू शकता.</string>
<string name="widget_config_preview_resize_hint">विजेट तुमच्या होम स्क्रीनवर ठेवल्यानंतर तुम्ही त्याचा आकार बदलू शकता.</string>
<string name="widget_config_custom_label">कस्टम</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">गाढ</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">शेवटचे दिसले: %s</string>
<string name="first_seen_x">पहिल्यांदा दिसले: %s</string>
<string name="battery_cached_label">शेवटचे ज्ञात · %s</string>
<string name="battery_time_remaining_format_hm">%1$dता %2$dमि</string>
<string name="battery_time_remaining_format_h">%1$dता</string>
<string name="battery_time_remaining_format_m">%1$dमि</string>
<string name="permission_post_notifications_label">सूचना दर्शवा</string>
<string name="permission_post_notifications_description">"CAPod ला तुमच्या AirPods बद्दल सूचना दर्शवण्याची परवानगी द्या, उदा. कनेक्ट असताना त्यांची सध्याची स्थिती."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">उजव्या पॉडचा सीरियल</string>
<string name="device_settings_info_left_bonded_label">डावे बाँडेड</string>
<string name="device_settings_info_right_bonded_label">उजवे बाँडेड</string>
<string name="device_settings_info_battery_health_label">बॅटरी आरोग्य (अंदाजे)</string>
<string name="device_settings_info_battery_health_left_label">डावे बॅटरी आरोग्य (अंदाजे)</string>
<string name="device_settings_info_battery_health_right_label">उजवे बॅटरी आरोग्य (अंदाजे)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">अजूनही निर्धारण होत आहे, नंतर परत पहा</string>
<string name="device_settings_info_details_label">डिव्हाइस तपशिल</string>
<string name="device_settings_info_details_action">डिव्हाइस तपशिल दाखवा</string>
<string name="device_settings_info_status_label">स्थिती</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Penyesuaian umum yang mempengaruhi keseluruhan apl.</string>
<string name="settings_overview_hide_unmatched_label">Sembunyikan peranti yang tidak sepadan</string>
<string name="settings_overview_hide_unmatched_description">Jangan tunjukkan peranti berdekatan yang tidak sepadan dengan mana-mana profil anda dalam gambaran keseluruhan, cth. AirPods orang lain.</string>
<string name="device_battery_estimate_toggle_label">Anggaran Bateri</string>
<string name="device_battery_estimate_card_desc">Pelajari bagaimana peranti ini mengisi daya dan berkurang untuk menganggar masa baki, masa sehingga diisi daya sepenuhnya, dan kesihatan bateri.</string>
<string name="device_battery_estimate_reset_action">Tetapkan semula data yang dipelajari</string>
<string name="device_battery_estimate_reset_desc">Mulai semula daripada hayat bateri yang dinilai.</string>
<string name="device_battery_estimate_reset_confirm_title">Tetapkan semula data yang dipelajari?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod akan melupakan penyusutan terukur peranti ini, kecepatan pengisian daya, dan kesihatan bateri, serta memulai semula daripada hayat bateri yang dinilai.</string>
<string name="settings_acknowledgements_label">Penghargaan</string>
<string name="settings_debug_autoreports_label">Laporan pepijat automatik</string>
<string name="settings_debug_autoreports_description">Melaporkan isu secara automatik, cth. butiran mengenai ranap apl supaya saya boleh memikirkan cara untuk membetulkannya.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Ketelusan latar belakang</string>
<string name="widget_config_show_device_label">Tampilkan nama peranti</string>
<string name="widget_config_reset_label">Tetapkan semula ke nilai lalai</string>
<string name="widget_config_preview_resize_hint">Anda boleh mengubah saiz widget selepas meletakkannya pada skrin utama anda.</string>
<string name="widget_config_preview_resize_hint">Anda boleh mengubah saiz widget selepas meletakkannya di skrin utama anda.</string>
<string name="widget_config_custom_label">Tersuai</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Gelap</string>
@@ -276,6 +282,9 @@
<string name="last_seen_x">Terakhir dilihat: %s</string>
<string name="first_seen_x">Pertama kali dilihat: %s</string>
<string name="battery_cached_label">Terakhir diketahui · %s</string>
<string name="battery_time_remaining_format_hm">%1$dj %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dj</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Tunjukkan pemberitahuan</string>
<string name="permission_post_notifications_description">"Benarkan CAPod menunjukkan pemberitahuan tentang AirPods anda, cth. status semasa mereka semasa disambungkan."</string>
<!-- Device profiles -->
@@ -418,6 +427,11 @@
<string name="device_settings_info_right_serial_label">Nombor Siri Pod Kanan</string>
<string name="device_settings_info_left_bonded_label">Kiri Terikat</string>
<string name="device_settings_info_right_bonded_label">Kanan Terikat</string>
<string name="device_settings_info_battery_health_label">Kesihatan Bateri (anggaran)</string>
<string name="device_settings_info_battery_health_left_label">Kesihatan Bateri Kiri (ang.)</string>
<string name="device_settings_info_battery_health_right_label">Kesihatan Bateri Kanan (ang.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Sedang menentukan, periksa kembali kemudian</string>
<string name="device_settings_info_details_label">Butiran Peranti</string>
<string name="device_settings_info_details_action">Tunjuk butiran peranti</string>
<string name="device_settings_info_status_label">Status</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">အက်ပ်တစ်ခုလုံးကို သက်ရောက်မှုရှိသော ယေဘုယျပြင်ဆင်မှုများ။</string>
<string name="settings_overview_hide_unmatched_label">မကိုက်ညီသော ကိရိယာများ ဝှက်ရန်</string>
<string name="settings_overview_hide_unmatched_description">သင့်ပရိုဖိုင်များနှင့် မကိုက်ညီသော အနီးနားရှိ ကိရိယာများကို မည်သည့်အကြောင်းကြောင့်မဆို ခြုံငုံသုံးသပ်ချက်တွင် မပြပါနှင့်၊ ဥပမာ အခြားသူများ၏ AirPods။</string>
<string name="device_battery_estimate_toggle_label">ဘက်ထရီ ခန့်မှန်းချက်များ</string>
<string name="device_battery_estimate_card_desc">ဤကိရိယာ မည်သို့ အားသွင်းကြောင်းနှင့် စွမ်းအားကုန်ကြောင်းကို လေ့လာခြင်းဖြင့် ကျန်ချိန်၊ အားပြည့်ချိန်နှင့် ဘက်ထရီကျန်းမာရေးကို ခန့်မှန်းပါ။</string>
<string name="device_battery_estimate_reset_action">သင်ယူထားသော အချက်အလက်များ ပြန်လည်သတ်မှတ်ရန်</string>
<string name="device_battery_estimate_reset_desc">သတ်မှတ်ထားသော ဘက်ထရီသက်တမ်းမှ အစပြန်စတင်ပါ။</string>
<string name="device_battery_estimate_reset_confirm_title">သင်ယူထားသော အချက်အလက်များ ပြန်လည်သတ်မှတ်မည်လား</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod သည် ဤကိရိယာ၏ တိုင်းတာထားသော စွမ်းအားကုန်ဆုံးမှု၊ အားသွင်းနှုန်းနှင့် ဘက်ထရီကျန်းမာရေးကို မေ့ပျောက်စေပြီး၊ သတ်မှတ်ထားသော ဘက်ထရီသက်တမ်းမှ အစပြန်စတင်ပါမည်။</string>
<string name="settings_acknowledgements_label">ကျေးဇူးတင်လွှာ</string>
<string name="settings_debug_autoreports_label">အလိုအလြှောက်ချွတ်ယွင်းချက်အစီရင်ခံစာများ</string>
<string name="settings_debug_autoreports_description">ပြဿနာများကို အလိုအလြှောက်အစီရင်ခံပါသည်၊ ဥပမာ အက်ပ်ပျက်ကျမှုအကြောင်းအသေးစိတ်များ၊ သို့မှသာ ၎င်းကိုမည်သို့ဖြေရှင်းရမည်ကို ကျွန်ုပ်သိရှိနိုင်မည်ဖြစ်သည်။</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">နောက်ခံ ခွဲခြားမှု</string>
<string name="widget_config_show_device_label">စက်ပစ္စည်း အမည် ပြသရန်</string>
<string name="widget_config_reset_label">ပုံမှန်သို့ ပြန်လည်ချိန်ညှိသည်</string>
<string name="widget_config_preview_resize_hint">သင့် ပင်မစခရင်တွင် widget ချထားပြီးနောက် ၎င်းကို အရွယ်အစား ပြောင်းလဲနိုင်သည်။</string>
<string name="widget_config_preview_resize_hint">widget ကို သင့်ပင်မစာမျက်နှာတွင် ထားရှိပြီးနောက် အရွယ်အစား ပြန်လည်ချိန်ညှိနိုင်ပါသည်။</string>
<string name="widget_config_custom_label">အလိုအလျောက်</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">အမှောင်</string>
@@ -276,6 +282,9 @@
<string name="last_seen_x">နောက်ဆုံးတွေ့ရှိချိန်: %s</string>
<string name="first_seen_x">ပထမဆုံးတွေ့ရှိချိန်: %s</string>
<string name="battery_cached_label">နောက်ဆုံးသိသည် · %s</string>
<string name="battery_time_remaining_format_hm">%1$dနာရီ %2$dမိနစ်</string>
<string name="battery_time_remaining_format_h">%1$dနာရီ</string>
<string name="battery_time_remaining_format_m">%1$dမိနစ်</string>
<string name="permission_post_notifications_label">အသိပေးချက်များပြသရန်</string>
<string name="permission_post_notifications_description">"သင့် AirPods အကြောင်း အသိပေးချက်များပြသရန် CAPod ကိုခွင့်ပြုပါ၊ ဥပမာ ချိတ်ဆက်ထားစဉ် ၎င်းတို့၏ လက်ရှိအခြေအနေ။"</string>
<!-- Device profiles -->
@@ -418,6 +427,11 @@
<string name="device_settings_info_right_serial_label">ညာဘက် Pod စီရီရယ်နံပါတ်</string>
<string name="device_settings_info_left_bonded_label">ဘယ်ဘက် ချိတ်ဆက်ထားသည်</string>
<string name="device_settings_info_right_bonded_label">ညာဘက် ချိတ်ဆက်ထားသည်</string>
<string name="device_settings_info_battery_health_label">ဘက်ထရီကျန်းမာရေး (ခန့်မှန်း)</string>
<string name="device_settings_info_battery_health_left_label">ဘယ်ဘက် ဘက်ထရီကျန်းမာရေး (ခန့်မှန်း)</string>
<string name="device_settings_info_battery_health_right_label">ညာဘက် ဘက်ထရီကျန်းမာရေး (ခန့်မှန်း)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">ယခုသတ်မှတ်နေဆဲဖြစ်ပါသည်၊ နောက်မှ ပြန်စစ်ပါ</string>
<string name="device_settings_info_details_label">ကိရိယာ အသေးစိတ်</string>
<string name="device_settings_info_details_action">ကိရိယာ အသေးစိတ်ကို ပြသပါ</string>
<string name="device_settings_info_status_label">အခြေအနေ</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Generelle justeringer som påvirker hele appen.</string>
<string name="settings_overview_hide_unmatched_label">Skjul ikke-matchende enheter</string>
<string name="settings_overview_hide_unmatched_description">Ikke vis enheter i nærheten som ikke samsvarer med noen av profilene dine i oversikten, f.eks. andre personers AirPods.</string>
<string name="device_battery_estimate_toggle_label">Batteriestimater</string>
<string name="device_battery_estimate_card_desc">Lær hvordan denne enheten lades og tømmes for å estimere gjenværende tid, tid til fulladning, og batterihelse.</string>
<string name="device_battery_estimate_reset_action">Nullstill lærte data</string>
<string name="device_battery_estimate_reset_desc">Start på nytt fra batteriets angitte levetid.</string>
<string name="device_battery_estimate_reset_confirm_title">Nullstill lærte data?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod vil glemme denne enhetens målte drenering, ladefart og batterihelse, og starte på nytt fra batteriets angitte levetid.</string>
<string name="settings_acknowledgements_label">Anerkjennelser</string>
<string name="settings_debug_autoreports_label">Automatiske feilrapporter</string>
<string name="settings_debug_autoreports_description">Rapporterer problemer automatisk, f.eks. detaljer om en appkrasj slik at jeg kan finne ut hvordan jeg kan fikse det.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Bakgrunnsgjennomsiktighet</string>
<string name="widget_config_show_device_label">Vis enhetsnavn</string>
<string name="widget_config_reset_label">Tilbakestill til standardinnstillinger</string>
<string name="widget_config_preview_resize_hint">Du kan endre størrelsen på widgeten etter at du har plassert den på startskjermen.</string>
<string name="widget_config_preview_resize_hint">Du kan endre størrelse på widgeten etter å ha plassert den på hjemmeskjermen.</string>
<string name="widget_config_custom_label">Egendefinert</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Mørk</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Sist sett: %s</string>
<string name="first_seen_x">Først sett: %s</string>
<string name="battery_cached_label">Sist kjent · %s</string>
<string name="battery_time_remaining_format_hm">%1$dt %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dt</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Vis varslinger</string>
<string name="permission_post_notifications_description">"Tillat CAPod å vise varslinger om AirPods, f.eks. nåværende status mens de er tilkoblet."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Høyre pod-serienummer</string>
<string name="device_settings_info_left_bonded_label">Venstre tilkoblet</string>
<string name="device_settings_info_right_bonded_label">Høyre tilkoblet</string>
<string name="device_settings_info_battery_health_label">Batterihelse (estimert)</string>
<string name="device_settings_info_battery_health_left_label">Venstre batterihelse (est.)</string>
<string name="device_settings_info_battery_health_right_label">Høyre batterihelse (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Bestemmer fortsatt, kom tilbake senere</string>
<string name="device_settings_info_details_label">Enhetsdetaljer</string>
<string name="device_settings_info_details_action">Vis enhetsdetaljer</string>
<string name="device_settings_info_status_label">Status</string>
@@ -122,6 +122,12 @@
<string name="settings_general_description">सम्पूर्ण एपलाई असर गर्ने सामान्य ट्वीकहरू।</string>
<string name="settings_overview_hide_unmatched_label">नमिलेका यन्त्रहरू लुकाउनुस्</string>
<string name="settings_overview_hide_unmatched_description">अवलोकनमा तपाईंको कुनै पनि प्रोफाइलसँग नमिल्ने नजिकका यन्त्रहरू नदेखाउनुस्, जस्तै अरू मान्छेका AirPods।</string>
<string name="device_battery_estimate_toggle_label">ब्याटरी अनुमान</string>
<string name="device_battery_estimate_card_desc">यो उपकरण कसरी चार्ज र डिस्चार्ज हुन्छ भनेर सिख्नुहोस् र बाकी समय, चार्ज हुन लाग्ने समय, र ब्याटरी स्वास्थ्य अनुमान गर्नुहोस्।</string>
<string name="device_battery_estimate_reset_action">सिखेको डेटा रिसेट गर्नुहोस्</string>
<string name="device_battery_estimate_reset_desc">रेट गरिएको ब्याटरी जीवनबाट फेरि सुरु गर्नुहोस्।</string>
<string name="device_battery_estimate_reset_confirm_title">सिखेको डेटा रिसेट गर्नुहोस्?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod यो उपकरणको मापन गरिएको ड्रेन, चार्जिङ गति र ब्याटरी स्वास्थ्य भुल्नेछ, र रेट गरिएको ब्याटरी जीवनबाट फेरि सुरु गर्नेछ।</string>
<string name="settings_acknowledgements_label">स्वीकृतिहरू</string>
<string name="settings_debug_autoreports_label">स्वचालित बग रिपोर्टहरू</string>
<string name="settings_debug_autoreports_description">समस्याहरू स्वचालित रूपमा रिपोर्ट गर्दछ, उदाहरणका लागि एप क्र्यास बारे विवरणहरू ताकि म यसलाई कसरी समाधान गर्ने भनेर पत्ता लगाउन सकूँ।</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">अन्तिम देखिएको: %s</string>
<string name="first_seen_x">पहिलो पटक देखिएको: %s</string>
<string name="battery_cached_label">अन्तिम थाहा · %s</string>
<string name="battery_time_remaining_format_hm">%1$dघ %2$dमि</string>
<string name="battery_time_remaining_format_h">%1$dघ</string>
<string name="battery_time_remaining_format_m">%1$dमि</string>
<string name="permission_post_notifications_label">सूचनाहरू देखाउनुहोस्</string>
<string name="permission_post_notifications_description">"CAPod लाई तपाईंको AirPods बारे सूचनाहरू देखाउन अनुमति दिनुहोस्, जस्तै जडान भएको बेला तिनीहरूको हालको स्थिति।"</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">दायाँ पड सिरियल</string>
<string name="device_settings_info_left_bonded_label">बायाँ बन्डेड</string>
<string name="device_settings_info_right_bonded_label">दायाँ बन्डेड</string>
<string name="device_settings_info_battery_health_label">ब्याटरी स्वास्थ्य (अनुमानित)</string>
<string name="device_settings_info_battery_health_left_label">बायाँ ब्याटरी स्वास्थ्य (अनु।)</string>
<string name="device_settings_info_battery_health_right_label">दायाँ ब्याटरी स्वास्थ्य (अनु।)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">अझै निर्धारण गरिँदैछ, पछि जाँच गर्नुहोस्</string>
<string name="device_settings_info_details_label">उपकरण विवरण</string>
<string name="device_settings_info_details_action">उपकरण विवरण देखाउनुहोस्</string>
<string name="device_settings_info_status_label">स्थिति</string>
+14
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Algemene aanpassingen die de hele app beïnvloeden.</string>
<string name="settings_overview_hide_unmatched_label">Niet-overeenkomende apparaten verbergen</string>
<string name="settings_overview_hide_unmatched_description">Verberg apparaten in het overzicht die niet overeenkomen met je profielen, bijv. AirPods van anderen.</string>
<string name="device_battery_estimate_toggle_label">Batterijschattingen</string>
<string name="device_battery_estimate_card_desc">Leer hoe dit apparaat oplaadt en ontlaadt om de resterende tijd, oplaadtijd en batterijgezondheid in te schatten.</string>
<string name="device_battery_estimate_reset_action">Leergegevens wissen</string>
<string name="device_battery_estimate_reset_desc">Opnieuw beginnen met de nominale batterijduur.</string>
<string name="device_battery_estimate_reset_confirm_title">Leergegevens wissen?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod zal het verbruik, de oplaadsnelheid en batterijgezondheid van dit apparaat vergeten en opnieuw met de nominale batterijduur beginnen.</string>
<string name="settings_acknowledgements_label">Dankbetuigingen</string>
<string name="settings_debug_autoreports_label">Automatische bugrapporten</string>
<string name="settings_debug_autoreports_description">Rapporteert automatisch problemen, b.v. details over een app-crash, zodat ik kan uitzoeken hoe ik dit kan oplossen.</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Laatst gezien: %s</string>
<string name="first_seen_x">Voor het eerst gezien: %s</string>
<string name="battery_cached_label">Laatst bekend \u00B7 %s</string>
<string name="battery_time_remaining_format_hm">%1$du %2$dm</string>
<string name="battery_time_remaining_format_h">%1$du</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Toon notificaties</string>
<string name="permission_post_notifications_description">"Sta CAPod toe om meldingen over je AirPods weer te geven, bijv. over hun huidige status wanneer ze verbonden zijn."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Rechter Pod Serienummer</string>
<string name="device_settings_info_left_bonded_label">Links Gekoppeld</string>
<string name="device_settings_info_right_bonded_label">Rechts Gekoppeld</string>
<string name="device_settings_info_battery_health_label">Batterijgezondheid (geschat)</string>
<string name="device_settings_info_battery_health_left_label">Linker batterijgezondheid (gesch.)</string>
<string name="device_settings_info_battery_health_right_label">Rechter batterijgezondheid (gesch.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Nog aan het bepalen, kom later terug</string>
<string name="device_settings_info_details_label">Apparaatdetails</string>
<string name="device_settings_info_details_action">Apparaatdetails weergeven</string>
<string name="device_settings_info_status_label">Status</string>
+14
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ogólne opcje, które mają wpływ na całą aplikację.</string>
<string name="settings_overview_hide_unmatched_label">Ukryj niedopasowane urządzenia</string>
<string name="settings_overview_hide_unmatched_description">Nie pokazuj w przeglądzie pobliskich urządzeń, które nie pasują do żadnego z twoich profili, np. AirPods innych osób.</string>
<string name="device_battery_estimate_toggle_label">Szacunkowy czas pracy baterii</string>
<string name="device_battery_estimate_card_desc">Dowiedz się, jak urządzenie się ładuje i rozładowuje, aby oszacować pozostały czas, czas do pełnego naładowania i stan baterii.</string>
<string name="device_battery_estimate_reset_action">Zresetuj zapamiętane dane</string>
<string name="device_battery_estimate_reset_desc">Zacznij od nowa od podanego czasu pracy baterii.</string>
<string name="device_battery_estimate_reset_confirm_title">Zresetować zapamiętane dane?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod zapomni o zmierzonej szybkości rozładowania, szybkości ładowania i stanie baterii tego urządzenia i zacznie od nowa od podanego czasu pracy baterii.</string>
<string name="settings_acknowledgements_label">Podziękowania</string>
<string name="settings_debug_autoreports_label">Automatyczne raportowanie błędów</string>
<string name="settings_debug_autoreports_description">Automatyczne raportowanie problemów, na przykład w przypadku awarii aplikacji, tak aby można było łatwo ją naprawić.</string>
@@ -284,6 +290,9 @@ K4r0lSz;</string>
<string name="last_seen_x">Ostatnio widziane: %s</string>
<string name="first_seen_x">Widziane pierwszy raz: %s</string>
<string name="battery_cached_label">Ostatnio znane · %s</string>
<string name="battery_time_remaining_format_hm">%1$dg %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dg</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Wyświetl powiadomienia</string>
<string name="permission_post_notifications_description">"Zezwól CAPod na wyświetlanie powiadomień o słuchawkach AirPods, np. ich aktualnym statusie podczas połączenia."</string>
<!-- Device profiles -->
@@ -435,6 +444,11 @@ K4r0lSz;</string>
<string name="device_settings_info_right_serial_label">Numer seryjny prawej słuchawki</string>
<string name="device_settings_info_left_bonded_label">Lewy sparowany</string>
<string name="device_settings_info_right_bonded_label">Prawy sparowany</string>
<string name="device_settings_info_battery_health_label">Kondycja baterii (szacunkowa)</string>
<string name="device_settings_info_battery_health_left_label">Szacowany stan lewej baterii</string>
<string name="device_settings_info_battery_health_right_label">Szacowany stan prawej baterii</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Nadal się ustala, sprawdź później</string>
<string name="device_settings_info_details_label">Szczegóły urządzenia</string>
<string name="device_settings_info_details_action">Pokaż szczegóły urządzenia</string>
<string name="device_settings_info_status_label">Stan</string>
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ajustes gerais que afetam todo o aplicativo.</string>
<string name="settings_overview_hide_unmatched_label">Ocultar dispositivos sem correspondência</string>
<string name="settings_overview_hide_unmatched_description">Não exibir dispositivos próximos que não correspondam a nenhum dos seus perfis na visão geral, por ex., AirPods de outras pessoas.</string>
<string name="device_battery_estimate_toggle_label">Estimativas de bateria</string>
<string name="device_battery_estimate_card_desc">Aprenda como esse dispositivo carrega e descarrega para estimar o tempo restante, tempo até estar carregado e saúde da bateria.</string>
<string name="device_battery_estimate_reset_action">Redefinir dados aprendidos</string>
<string name="device_battery_estimate_reset_desc">Comece novamente com a vida útil nominal da bateria.</string>
<string name="device_battery_estimate_reset_confirm_title">Redefinir dados aprendidos?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod esquecerá a drenagem medida deste dispositivo, velocidade de carregamento e saúde da bateria, e começará novamente com a vida útil nominal da bateria.</string>
<string name="settings_acknowledgements_label">Agradecimentos</string>
<string name="settings_debug_autoreports_label">Relatórios automáticos de bugs</string>
<string name="settings_debug_autoreports_description">Relata problemas automaticamente, por exemplo. detalhes sobre uma falha de aplicativo para que eu possa descobrir como corrigi-lo.</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Visto por último: %s</string>
<string name="first_seen_x">Visto pela primeira vez: %s</string>
<string name="battery_cached_label">Visto por último · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostrar notificações</string>
<string name="permission_post_notifications_description">"Permitir que o CAPod mostre notificações sobre seus AirPods, por exemplo, o status atual enquanto conectado."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Serial do pod direito</string>
<string name="device_settings_info_left_bonded_label">Esquerdo Vinculado</string>
<string name="device_settings_info_right_bonded_label">Direito Vinculado</string>
<string name="device_settings_info_battery_health_label">Saúde da Bateria (estimada)</string>
<string name="device_settings_info_battery_health_left_label">Saúde da Bateria Esquerda (est.)</string>
<string name="device_settings_info_battery_health_right_label">Saúde da Bateria Direita (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Ainda em determinação, volte mais tarde</string>
<string name="device_settings_info_details_label">Detalhes do dispositivo</string>
<string name="device_settings_info_details_action">Mostrar detalhes do dispositivo</string>
<string name="device_settings_info_status_label">Status</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ajustes gerais que afetam toda a aplicação.</string>
<string name="settings_overview_hide_unmatched_label">Ocultar dispositivos sem correspondência</string>
<string name="settings_overview_hide_unmatched_description">Não mostrar na vista geral os dispositivos próximos que não correspondem a nenhum dos teus perfis, por ex. os AirPods de outras pessoas.</string>
<string name="device_battery_estimate_toggle_label">Estimativas de bateria</string>
<string name="device_battery_estimate_card_desc">Saiba como este dispositivo carrega e descarrega para estimar o tempo restante, tempo até estar totalmente carregado e saúde da bateria.</string>
<string name="device_battery_estimate_reset_action">Repor dados aprendidos</string>
<string name="device_battery_estimate_reset_desc">Recomeçar a partir da duração da bateria nominal.</string>
<string name="device_battery_estimate_reset_confirm_title">Repor dados aprendidos?</string>
<string name="device_battery_estimate_reset_confirm_message">O CAPod esquecerá o consumo medido, velocidade de carregamento e saúde da bateria deste dispositivo, e recomeçará a partir da duração da bateria nominal.</string>
<string name="settings_acknowledgements_label">Agradecimentos</string>
<string name="settings_debug_autoreports_label">Relatórios de erros automáticos</string>
<string name="settings_debug_autoreports_description">Relata problemas automaticamente, p. ex., detalhes sobre uma falha da aplicação para que eu possa descobrir como corrigi-la.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparência do fundo</string>
<string name="widget_config_show_device_label">Mostrar nome do dispositivo</string>
<string name="widget_config_reset_label">Restaurar padrões</string>
<string name="widget_config_preview_resize_hint">Pode redimensionar o widget depois de o colocar no seu ecrã inicial.</string>
<string name="widget_config_preview_resize_hint">Pode redimensionar o widget após colocar no seu ecrã inicial.</string>
<string name="widget_config_custom_label">Personalizado</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Escuro</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Visto pela última vez: %s</string>
<string name="first_seen_x">Visto pela primeira vez: %s</string>
<string name="battery_cached_label">Último conhecido · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mostrar notificações</string>
<string name="permission_post_notifications_description">"Permitir que o CAPod mostre notificações sobre os seus AirPods, p. ex., o estado atual enquanto ligado."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Número de Série do Auricular Direito</string>
<string name="device_settings_info_left_bonded_label">Esquerdo Vinculado</string>
<string name="device_settings_info_right_bonded_label">Direito Vinculado</string>
<string name="device_settings_info_battery_health_label">Saúde da Bateria (estimada)</string>
<string name="device_settings_info_battery_health_left_label">Saúde da Bateria Esquerda (est.)</string>
<string name="device_settings_info_battery_health_right_label">Saúde da Bateria Direita (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Ainda a determinar, volte mais tarde</string>
<string name="device_settings_info_details_label">Detalhes do Dispositivo</string>
<string name="device_settings_info_details_action">Mostrar detalhes do dispositivo</string>
<string name="device_settings_info_status_label">Estado</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Adattaziuns generalas che concernan l\'entira app.</string>
<string name="settings_overview_hide_unmatched_label">Zuppentar apparats betg concordants</string>
<string name="settings_overview_hide_unmatched_description">Na mussa betg ils apparats visadaifer che na correspundan a nagin da tis profils en la survista, p.ex. ils AirPods d\'autras persunas.</string>
<string name="device_battery_estimate_toggle_label">Estimaziuns da bateria</string>
<string name="device_battery_estimate_card_desc">Sclapina co quest dispositiv sa chargia e deschargia per estimar il temp restant, il temp fin ch\'el vegn chargià e la salutica da bateria.</string>
<string name="device_battery_estimate_reset_action">Redatg da datas appresas</string>
<string name="device_battery_estimate_reset_desc">Recomenzar da la vita da bateria valitada.</string>
<string name="device_battery_estimate_reset_confirm_title">Redatg da datas appresas?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod vegn a scerder ils datas dal consumo mesirà, la velocitad da chargia e la salutica da bateria, e vegn a recomenzar da la vita da bateria valitada.</string>
<string name="settings_acknowledgements_label">Remerciaments</string>
<string name="settings_debug_autoreports_label">Raports da bugs automatics</string>
<string name="settings_debug_autoreports_description">Raporta automaticamain problems, p.ex. detagls davart in crash da l\'app, uschia che jau poss chapir co reparar quai.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparenza dal fonds</string>
<string name="widget_config_show_device_label">Mussar nom dal dispositiv</string>
<string name="widget_config_reset_label">Reinizialischar als standardis</string>
<string name="widget_config_preview_resize_hint">Ti pos midiar la dimensiun dal widget suenter haver collocat il sin tia pantschina da chasa.</string>
<string name="widget_config_preview_resize_hint">Tu pudas reglar il widget suenter che tu l\'has purtà silla pagina principala da tes dispositiv.</string>
<string name="widget_config_custom_label">Persunalisà</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Stgir</string>
@@ -278,6 +284,9 @@
<string name="last_seen_x">Vis l\'ultima giada: %s</string>
<string name="first_seen_x">Vis l\'emprima giada: %s</string>
<string name="battery_cached_label">Ultim enconuschent · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Mussar notificaziuns</string>
<string name="permission_post_notifications_description">"Permetter a CAPod da mussar notificaziuns davart Voss AirPods, p.ex. il stadi actual durant ch'els èn connectads."</string>
<!-- Device profiles -->
@@ -423,6 +432,11 @@
<string name="device_settings_info_right_serial_label">Numer serial da l\'AirPod dretg</string>
<string name="device_settings_info_left_bonded_label">Sanglà Seniester</string>
<string name="device_settings_info_right_bonded_label">Sanglà Dretg</string>
<string name="device_settings_info_battery_health_label">Sanadad da la bataria (estimada)</string>
<string name="device_settings_info_battery_health_left_label">Sanadad sanestra da la bataria (est.)</string>
<string name="device_settings_info_battery_health_right_label">Sanadad dretta da la bataria (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Simpel a determinar, controllescha pli tard</string>
<string name="device_settings_info_details_label">Detagls da l\'apparat</string>
<string name="device_settings_info_details_action">Mussar detagls da l\'apparat</string>
<string name="device_settings_info_status_label">Status</string>
+15 -1
View File
@@ -122,6 +122,12 @@
<string name="settings_general_description">Ajustări generale care afectează întreaga aplicație.</string>
<string name="settings_overview_hide_unmatched_label">Ascunde dispozitivele nepotrivite</string>
<string name="settings_overview_hide_unmatched_description">Nu afișa dispozitivele din apropiere care nu corespund niciunui profil al tău în prezentare generală, de ex. AirPods-urile altor persoane.</string>
<string name="device_battery_estimate_toggle_label">Estimări ale bateriei</string>
<string name="device_battery_estimate_card_desc">Aflați cum se încarcă și se descarcă dispozitivul pentru a estima timpul rămas, timpul până la încărcare și starea bateriei.</string>
<string name="device_battery_estimate_reset_action">Resetează datele învățate</string>
<string name="device_battery_estimate_reset_desc">Porniți din nou de la durata de viață a bateriei nominale.</string>
<string name="device_battery_estimate_reset_confirm_title">Resetezi datele învățate?</string>
<string name="device_battery_estimate_reset_confirm_message">CAPod va uita consumul măsurat al acestui dispozitiv, viteza de încărcare și starea bateriei, și va porni din nou de la durata de viață a bateriei nominale.</string>
<string name="settings_acknowledgements_label">Mulțumiri</string>
<string name="settings_debug_autoreports_label">Rapoarte automate de erori</string>
<string name="settings_debug_autoreports_description">Raportează automat probleme, ex: detalii despre o blocare a aplicației, astfel încât să îmi dau seama cum să o rezolv.</string>
@@ -156,7 +162,7 @@
<string name="widget_config_transparency_label">Transparența fundalului</string>
<string name="widget_config_show_device_label">Afișează numele dispozitivului</string>
<string name="widget_config_reset_label">Resetează la setări implicite</string>
<string name="widget_config_preview_resize_hint">Puteți redimensiona widgetul după ce îl plasați pe ecranul de pornire.</string>
<string name="widget_config_preview_resize_hint">Puteți redimensiona widgetul după plasarea lui pe ecranul de pornire.</string>
<string name="widget_config_custom_label">Personalizat</string>
<string name="widget_config_preset_material_you">Material You</string>
<string name="widget_config_preset_dark">Întunecat</string>
@@ -280,6 +286,9 @@
<string name="last_seen_x">Ultima detectare: %s</string>
<string name="first_seen_x">Prima detectare: %s</string>
<string name="battery_cached_label">Ultimul cunoscut · %s</string>
<string name="battery_time_remaining_format_hm">%1$dh %2$dm</string>
<string name="battery_time_remaining_format_h">%1$dh</string>
<string name="battery_time_remaining_format_m">%1$dm</string>
<string name="permission_post_notifications_label">Afișare notificări</string>
<string name="permission_post_notifications_description">"Permiteți CAPod să afișeze notificări despre AirPods-urile dvs., de ex. starea curentă în timp ce sunt conectate."</string>
<!-- Device profiles -->
@@ -428,6 +437,11 @@
<string name="device_settings_info_right_serial_label">Serial pod drept</string>
<string name="device_settings_info_left_bonded_label">Stânga asociată</string>
<string name="device_settings_info_right_bonded_label">Dreapta asociată</string>
<string name="device_settings_info_battery_health_label">Sănătatea bateriei (estimată)</string>
<string name="device_settings_info_battery_health_left_label">Sănătatea bateriei din stânga (est.)</string>
<string name="device_settings_info_battery_health_right_label">Sănătatea bateriei din dreapta (est.)</string>
<string name="device_settings_info_battery_health_value">~%1$d%%</string>
<string name="device_settings_info_battery_health_pending">Se mai determină, revino mai târziu</string>
<string name="device_settings_info_details_label">Detalii dispozitiv</string>
<string name="device_settings_info_details_action">Arată detalii dispozitiv</string>
<string name="device_settings_info_status_label">Stare</string>

Some files were not shown because too many files have changed in this diff Show More