mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Compare commits
46
Commits
v5.1.5-rc0
...
v5.1.9-rc0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fba2d3a59d | ||
|
|
0abba4ece2 | ||
|
|
e9b0694b67 | ||
|
|
49bbf1bc85 | ||
|
|
f5b6a5c605 | ||
|
|
fccc6cedd5 | ||
|
|
a03766448e | ||
|
|
f0c9d318ff | ||
|
|
5c5cc6f28e | ||
|
|
246b96bfb4 | ||
|
|
352e020b54 | ||
|
|
72f5690ea6 | ||
|
|
1ed1199574 | ||
|
|
fd4a43de96 | ||
|
|
e406fd4cc1 | ||
|
|
1e555cc712 | ||
|
|
227a6b2a7b | ||
|
|
5f661d71a6 | ||
|
|
2804543b9d | ||
|
|
380397321c | ||
|
|
d9c8902d34 | ||
|
|
a1b9abd4cb | ||
|
|
5d30844fa8 | ||
|
|
af1b1b4249 | ||
|
|
2cce9ae56c | ||
|
|
4bab7c61a9 | ||
|
|
81e2bc1dba | ||
|
|
df2783d8a1 | ||
|
|
20a6953b54 | ||
|
|
fdd4f69247 | ||
|
|
5ee777b196 | ||
|
|
dd827af3cd | ||
|
|
948205667b | ||
|
|
dbbfa75429 | ||
|
|
78790f73df | ||
|
|
2950d8b3f9 | ||
|
|
4d3a9bc165 | ||
|
|
7d57845502 | ||
|
|
83ef111599 | ||
|
|
0e6848d886 | ||
|
|
1eeeb63a76 | ||
|
|
a4ba302223 | ||
|
|
8c9094d85a | ||
|
|
4529b107dd | ||
|
|
17663bc759 | ||
|
|
278257998b |
Binary file not shown.
|
Before Width: | Height: | Size: 332 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 760 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 761 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 605 KiB |
@@ -81,8 +81,7 @@ Currently supported models:
|
||||
|
||||
## Screenshots
|
||||
|
||||
<img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/1.png" width="200"><img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/2.png" width="200"><img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/3.png" width="200"><img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/4.png" width="200">
|
||||
<img src="https://raw.githubusercontent.com/d4rken-org/capod/main/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png" width="200">
|
||||
<img src="https://github.com/d4rken-org/capod/raw/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1_dashboard_light.png" width="100"><img src="https://github.com/d4rken-org/capod/raw/main/fastlane/metadata/android/en-US/images/phoneScreenshots/2_dashboard_dark.png" width="100"><img src="https://github.com/d4rken-org/capod/raw/main/fastlane/metadata/android/en-US/images/phoneScreenshots/3_case_popup.png" width="100"><img src="https://github.com/d4rken-org/capod/raw/main/fastlane/metadata/android/en-US/images/phoneScreenshots/4_widget_configuration.png" width="100"><img src="https://github.com/d4rken-org/capod/raw/main/fastlane/metadata/android/en-US/images/phoneScreenshots/5_device_profiles.png" width="100"><img src="https://github.com/d4rken-org/capod/raw/main/fastlane/metadata/android/en-US/images/phoneScreenshots/6_add_profile.png" width="100"><img src="https://github.com/d4rken-org/capod/raw/main/fastlane/metadata/android/en-US/images/phoneScreenshots/7_device_settings_reactions.png" width="100">
|
||||
|
||||
## Thanks to
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
@@ -57,7 +57,7 @@ internal fun DashboardContent(showAap: Boolean = false) = PreviewWrapper {
|
||||
now = MOCK_NOW,
|
||||
permissions = emptySet(),
|
||||
devices = devices,
|
||||
isDebugMode = false,
|
||||
isDebug = false,
|
||||
isBluetoothEnabled = true,
|
||||
profiles = listOf(
|
||||
MockPodDataProvider.profile("My AirPods Pro", PodModel.AIRPODS_PRO2),
|
||||
|
||||
@@ -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="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>
|
||||
</resources>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrades_gplay_unavailable_error">خدمات Google Play غير متاحة.</string>
|
||||
<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_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="upgrade_badge_label">احترافي</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="upgrade_badge_label">النسخة الاحترافية</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package eu.darken.capod.common
|
||||
|
||||
import android.media.AudioManager
|
||||
import android.media.AudioPlaybackConfiguration
|
||||
import android.os.Build
|
||||
import android.view.KeyEvent
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -14,48 +17,103 @@ class MediaControl @Inject constructor(
|
||||
private val audioManager: AudioManager,
|
||||
private val timeSource: TimeSource,
|
||||
) {
|
||||
private var capPauseExpiryElapsedRealtime: Long = 0L
|
||||
/**
|
||||
* Set when [sendPause] dispatches a pause we expect to take effect, cleared when [sendPlay]
|
||||
* dispatches a resume or when music transitions inactive→active from any source. Read by
|
||||
* [PlayPause] to gate auto-resume on pod-in: only resume if we're the ones who paused.
|
||||
*
|
||||
* "Sticky" — no time-based expiry. The original 15-second window was too short for typical
|
||||
* pod-out conversations and had a known race where music starting again via another source
|
||||
* (e.g. user manually resumed) could trigger a stray play-key dispatch on the next pod-in.
|
||||
*/
|
||||
@Volatile private var capPaused: Boolean = false
|
||||
@Volatile private var lastKnownMusicActive: Boolean = false
|
||||
|
||||
private val playbackCallback = object : AudioManager.AudioPlaybackCallback() {
|
||||
override fun onPlaybackConfigChanged(configs: List<AudioPlaybackConfiguration>) {
|
||||
val nowActive = audioManager.isMusicActive
|
||||
if (!lastKnownMusicActive && nowActive) {
|
||||
// Music started by some source (could be us via sendPlay or someone else).
|
||||
// Either way, our pause memory is stale — drop it so a future pod-in doesn't
|
||||
// re-fire a play key over already-active music.
|
||||
capPaused = false
|
||||
}
|
||||
lastKnownMusicActive = nowActive
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
// Seed the active flag from current state so we won't miss the next inactive→active
|
||||
// transition if music is already playing when MediaControl is constructed.
|
||||
lastKnownMusicActive = audioManager.isMusicActive
|
||||
audioManager.registerAudioPlaybackCallback(playbackCallback, null)
|
||||
}
|
||||
|
||||
val isPlaying: Boolean
|
||||
get() = audioManager.isMusicActive
|
||||
|
||||
val wasRecentlyPausedByCap: Boolean
|
||||
get() = capPauseExpiryElapsedRealtime > timeSource.elapsedRealtime()
|
||||
get() = capPaused
|
||||
|
||||
suspend fun sendPlay() {
|
||||
log(TAG, INFO) { "sendPlay()" }
|
||||
if (audioManager.isMusicActive && !wasRecentlyPausedByCap) {
|
||||
if (audioManager.isMusicActive && !capPaused) {
|
||||
log(TAG, INFO) { "Music is already playing, not sending play" }
|
||||
return
|
||||
}
|
||||
sendKey(KeyEvent.KEYCODE_MEDIA_PLAY)
|
||||
clearRecentCapPause()
|
||||
capPaused = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a MEDIA_PAUSE key event if music is currently playing.
|
||||
*
|
||||
* Returns `true` when a key event was actually dispatched (and the 15-second
|
||||
* [wasRecentlyPausedByCap] window was set), `false` when the call was a no-op because
|
||||
* nothing was playing. Callers that need to distinguish "we actually paused" from "there
|
||||
* was nothing to pause" — e.g. the sleep reaction, which gates its notification and
|
||||
* cooldown on a real pause — should branch on the return value rather than checking
|
||||
* [isPlaying] themselves to avoid a check-then-act race with the audio system.
|
||||
* @param rememberForResume When `true`, arms [wasRecentlyPausedByCap] so a subsequent
|
||||
* pod-in transition can auto-resume — set this only from the auto-pause / ear-detection
|
||||
* flow. When `false` (default), the dispatched pause clears any pending auto-resume —
|
||||
* this is the path for stem-press play/pause, sleep detection, and anywhere else CAPod
|
||||
* is relaying an explicit user choice to stop playback. Matches Apple's iOS/macOS
|
||||
* behavior where only ear-removal auto-pauses are eligible for auto-resume.
|
||||
*
|
||||
* Returns `true` when a key event was actually dispatched, `false` when the call was a
|
||||
* no-op because nothing was playing. A no-op leaves the existing flag state untouched
|
||||
* (so a sleep-reaction firing while music is already paused doesn't accidentally cancel
|
||||
* a pending auto-resume from a recent ear-removal pause). Callers that need to
|
||||
* distinguish "we actually paused" from "there was nothing to pause" — e.g. the sleep
|
||||
* reaction, which gates its notification and cooldown on a real pause — should branch
|
||||
* on the return value rather than checking [isPlaying] themselves to avoid a
|
||||
* check-then-act race with the audio system.
|
||||
*/
|
||||
suspend fun sendPause(): Boolean {
|
||||
log(TAG, INFO) { "sendPause()" }
|
||||
suspend fun sendPause(rememberForResume: Boolean = false): Boolean {
|
||||
log(TAG, INFO) { "sendPause(rememberForResume=$rememberForResume)" }
|
||||
if (!audioManager.isMusicActive) {
|
||||
log(TAG, INFO) { "Music is not playing, not sending pause" }
|
||||
return false
|
||||
}
|
||||
// Set BEFORE the suspending sendKey() call. If we set after, an inactive→active
|
||||
// playback callback that fires during the dispatch (e.g. a fast user resume on the
|
||||
// phone, or another app grabbing audio focus and immediately starting) could clear
|
||||
// capPaused mid-dispatch and we'd then overwrite it back to true on a stale pause.
|
||||
// This single explicit assignment also covers the contract that an explicit user
|
||||
// pause cancels a pending auto-resume.
|
||||
capPaused = rememberForResume
|
||||
sendKey(KeyEvent.KEYCODE_MEDIA_PAUSE)
|
||||
markRecentCapPause()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches MEDIA_STOP and clears any pending auto-resume — Stop is an explicit user
|
||||
* "stay stopped" action, so a later pod-in must not auto-resume from a prior auto-pause.
|
||||
*/
|
||||
suspend fun sendStop() {
|
||||
log(TAG, INFO) { "sendStop()" }
|
||||
capPaused = false
|
||||
sendKey(KeyEvent.KEYCODE_MEDIA_STOP)
|
||||
}
|
||||
|
||||
suspend fun sendPlayPause() {
|
||||
log(TAG) { "sendPlayPause()" }
|
||||
if (wasRecentlyPausedByCap) {
|
||||
if (capPaused) {
|
||||
sendPlay()
|
||||
return
|
||||
}
|
||||
@@ -101,16 +159,71 @@ class MediaControl @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun markRecentCapPause() {
|
||||
capPauseExpiryElapsedRealtime = timeSource.elapsedRealtime() + RECENT_CAP_PAUSE_WINDOW_MS
|
||||
/** Current STREAM_MUSIC volume index. Used to detect user-initiated volume changes after a duck. */
|
||||
fun currentMusicVolume(): Int = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
|
||||
/**
|
||||
* Lowers STREAM_MUSIC volume by [reductionPercent] (relative to the current level) and returns
|
||||
* the prior + the volume actually applied, so the caller can later restore it and detect whether
|
||||
* the user changed the volume in the meantime.
|
||||
*
|
||||
* Returns `null` (no-op) when nothing is playing, the device has fixed volume, or the computed
|
||||
* target wouldn't actually lower the volume. No [AudioManager.FLAG_SHOW_UI] — this fires on a
|
||||
* frequent push event and the volume panel flashing would be noisy. The applied target is read
|
||||
* back from the system because Bluetooth absolute-volume routes can quantize the requested value.
|
||||
*/
|
||||
fun duckMusicVolume(reductionPercent: Int): VolumeDuck? {
|
||||
if (!audioManager.isMusicActive) {
|
||||
log(TAG, INFO) { "duckMusicVolume: nothing playing, skipping" }
|
||||
return null
|
||||
}
|
||||
if (audioManager.isVolumeFixed) {
|
||||
log(TAG, INFO) { "duckMusicVolume: device has fixed volume, skipping" }
|
||||
return null
|
||||
}
|
||||
val percent = reductionPercent.coerceIn(0, 100)
|
||||
val max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
val min = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
val prior = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
val target = (prior * (100 - percent) / 100).coerceIn(min, max)
|
||||
if (target >= prior) {
|
||||
log(TAG, INFO) { "duckMusicVolume: target $target >= current $prior, skipping" }
|
||||
return null
|
||||
}
|
||||
return try {
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, target, 0)
|
||||
val applied = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
log(TAG, INFO) { "duckMusicVolume($percent%): $prior -> $applied (requested $target)" }
|
||||
VolumeDuck(priorVolume = prior, appliedVolume = applied)
|
||||
} catch (e: SecurityException) {
|
||||
// setStreamVolume throws under Do-Not-Disturb without notification policy access.
|
||||
log(TAG, WARN) { "duckMusicVolume: setStreamVolume denied: ${e.message}" }
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearRecentCapPause() {
|
||||
capPauseExpiryElapsedRealtime = 0L
|
||||
/** Restores STREAM_MUSIC to [priorVolume]. No-op on fixed-volume devices; denials are logged. */
|
||||
fun restoreMusicVolume(priorVolume: Int) {
|
||||
if (audioManager.isVolumeFixed) return
|
||||
try {
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, priorVolume, 0)
|
||||
log(TAG, INFO) { "restoreMusicVolume($priorVolume)" }
|
||||
} catch (e: SecurityException) {
|
||||
log(TAG, WARN) { "restoreMusicVolume: setStreamVolume denied: ${e.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
/** Snapshot of a volume duck so the caller can restore the prior level and detect user changes. */
|
||||
data class VolumeDuck(
|
||||
val priorVolume: Int,
|
||||
val appliedVolume: Int,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("MediaControl")
|
||||
private const val RECENT_CAP_PAUSE_WINDOW_MS = 15_000L
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import javax.inject.Singleton
|
||||
class BleScanner @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val fakeBleData: FakeBleData,
|
||||
private val scanResultForwarder: BleScanResultForwarder,
|
||||
private val timeSource: TimeSource,
|
||||
) {
|
||||
@@ -206,7 +205,6 @@ class BleScanner @Inject constructor(
|
||||
log(TAG) { "BleScanner stopped" }
|
||||
}
|
||||
}
|
||||
.map { fakeBleData.maybeAddfakeData(it) }
|
||||
|
||||
private val receiverIntent by lazy {
|
||||
Intent(context, BleScanResultReceiver::class.java).apply {
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
@@ -23,6 +24,7 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
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.common.permissions.Permission
|
||||
import eu.darken.capod.pods.core.apple.ble.protocol.ContinuityProtocol
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -390,9 +392,6 @@ class BluetoothManager2 @Inject constructor(
|
||||
initialValue = emptySet(),
|
||||
)
|
||||
|
||||
private var _isNudgeAvailable: Boolean = true
|
||||
val isNudgeAvailable: Boolean get() = _isNudgeAvailable
|
||||
|
||||
/**
|
||||
* Set the Android-local alias for a bonded [device]. Updates what Android's system Bluetooth
|
||||
* settings display without touching the AirPods firmware itself. Uses reflection on the hidden
|
||||
@@ -425,29 +424,42 @@ class BluetoothManager2 @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun nudgeConnection(device: BluetoothDevice2): Boolean = getBluetoothProfile().map { bluetoothProfile ->
|
||||
try {
|
||||
log(TAG) { "Nudging Android connection to $device" }
|
||||
suspend fun nudgeConnection(device: BluetoothDevice2): NudgeAttemptResult =
|
||||
getBluetoothProfile().map { bluetoothProfile ->
|
||||
try {
|
||||
log(TAG) { "Nudging Android connection to $device" }
|
||||
|
||||
val target = device.internal ?: return@map false
|
||||
val connectMethod = BluetoothHeadset::class.java.getDeclaredMethod(
|
||||
"connect", BluetoothDevice::class.java
|
||||
).apply { isAccessible = true }
|
||||
val target = device.internal ?: return@map NudgeAttemptResult.Rejected
|
||||
val connectMethod = BluetoothHeadset::class.java.getDeclaredMethod(
|
||||
"connect", BluetoothDevice::class.java
|
||||
).apply { isAccessible = true }
|
||||
|
||||
val accepted = connectMethod.invoke(bluetoothProfile.proxy, target) as? Boolean ?: false
|
||||
log(TAG) { "Nudged connection to $device — accepted=$accepted" }
|
||||
accepted
|
||||
} catch (e: Exception) {
|
||||
val isSecurityException = e is SecurityException ||
|
||||
(e is java.lang.reflect.InvocationTargetException && e.cause is SecurityException)
|
||||
if (isSecurityException) {
|
||||
log(TAG, ERROR) { "nudgeConnection is permanently unavailable: missing MODIFY_PHONE_STATE permission" }
|
||||
_isNudgeAvailable = false
|
||||
val accepted = connectMethod.invoke(bluetoothProfile.proxy, target) as? Boolean ?: false
|
||||
log(TAG) { "Nudged connection to $device — accepted=$accepted" }
|
||||
if (accepted) NudgeAttemptResult.Accepted else NudgeAttemptResult.Rejected
|
||||
} catch (e: Exception) {
|
||||
val isSecurityException = e is SecurityException ||
|
||||
(e is java.lang.reflect.InvocationTargetException && e.cause is SecurityException)
|
||||
if (!isSecurityException) {
|
||||
Bugs.report(tag = TAG, "BluetoothHeadset.connect(device) is unavailable", exception = e)
|
||||
return@map NudgeAttemptResult.Rejected
|
||||
}
|
||||
// BLUETOOTH_CONNECT is only a runtime permission on Android 12+. On older
|
||||
// versions a SecurityException can't be a missing-permission failure for that
|
||||
// permission — it's hidden-API enforcement (e.g. MODIFY_PHONE_STATE) and we
|
||||
// should persist BROKEN.
|
||||
val missingBtConnect = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
|
||||
!Permission.BLUETOOTH_CONNECT.isGranted(context)
|
||||
if (missingBtConnect) {
|
||||
log(TAG, WARN) { "nudgeConnection failed because BLUETOOTH_CONNECT is not granted" }
|
||||
NudgeAttemptResult.UnavailableMissingPermission
|
||||
} else {
|
||||
log(TAG, ERROR) { "nudgeConnection is permanently unavailable on this device: $e" }
|
||||
Bugs.report(tag = TAG, "BluetoothHeadset.connect(device) is unavailable", exception = e)
|
||||
NudgeAttemptResult.UnavailableHiddenApi
|
||||
}
|
||||
}
|
||||
Bugs.report(tag = TAG, "BluetoothHeadset.connect(device) is unavailable", exception = e)
|
||||
false
|
||||
}
|
||||
}.first()
|
||||
}.first()
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Bluetooth", "Manager2")
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
package eu.darken.capod.common.bluetooth
|
||||
|
||||
import dagger.Reusable
|
||||
import eu.darken.capod.common.TimeSource
|
||||
import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.fromHex
|
||||
import javax.inject.Inject
|
||||
import kotlin.random.Random
|
||||
import eu.darken.capod.common.datastore.valueBlocking
|
||||
|
||||
@Reusable
|
||||
class FakeBleData @Inject constructor(
|
||||
private val debugSettings: DebugSettings,
|
||||
private val timeSource: TimeSource,
|
||||
) {
|
||||
|
||||
fun maybeAddfakeData(originals: Collection<BleScanResult>): Collection<BleScanResult> {
|
||||
if (!debugSettings.showFakeData.valueBlocking) return originals
|
||||
return originals + getFakeData()
|
||||
}
|
||||
|
||||
fun getFakeData(): Collection<BleScanResult> {
|
||||
val fakeDevices = mutableListOf<BleScanResult>()
|
||||
val now = timeSource.now()
|
||||
val generatedAt = timeSource.elapsedRealtimeNanos()
|
||||
// AirPods Gen1
|
||||
BleScanResult(
|
||||
receivedAt = now,
|
||||
address = "78:73:AF:B4:85:22",
|
||||
rssi = Random.nextInt(100) * -1,
|
||||
generatedAtNanos = generatedAt + 100,
|
||||
manufacturerSpecificData = mapOf(76 to "07 19 01 02 20 75 AA B6 31 00 05 9C 5A A4 5D C0 2C A0 B4 6F B9 ED 8E CE 03 97 CA".fromHex())
|
||||
).run {
|
||||
fakeDevices.add(this)
|
||||
}
|
||||
|
||||
// AirPods Gen2
|
||||
BleScanResult(
|
||||
receivedAt = now,
|
||||
address = "78:73:FF:B4:85:5E",
|
||||
rssi = Random.nextInt(100) * -1,
|
||||
generatedAtNanos = generatedAt + 100,
|
||||
manufacturerSpecificData = mapOf(76 to "07 19 01 0F 20 75 AA B6 31 00 05 9C 5A A4 5D C0 2C A0 B4 6F B9 ED 8E CE 03 97 CA".fromHex())
|
||||
).run {
|
||||
fakeDevices.add(this)
|
||||
}
|
||||
|
||||
// AirPods Gen3
|
||||
BleScanResult(
|
||||
receivedAt = now,
|
||||
address = "4E:9E:D1:49:D2:6D",
|
||||
rssi = Random.nextInt(15, 75) * -1,
|
||||
generatedAtNanos = generatedAt + 200,
|
||||
manufacturerSpecificData = mapOf(76 to "07 19 01 13 20 55 AF 56 31 00 06 6F E4 DF 10 AF 10 60 81 03 3B 76 D9 C7 11 22 88".fromHex())
|
||||
).run {
|
||||
fakeDevices.add(this)
|
||||
}
|
||||
// AirPods Max
|
||||
BleScanResult(
|
||||
receivedAt = now,
|
||||
address = "7E:E5:C7:65:D2:B5",
|
||||
rssi = Random.nextInt(15, 75) * -1,
|
||||
generatedAtNanos = generatedAt + 300,
|
||||
manufacturerSpecificData = mapOf(76 to "07 19 01 0A 20 02 05 80 04 0F 44 A7 60 9B F8 3C FD B1 D8 1C 61 EA 82 60 A3 2C 4E".fromHex())
|
||||
).run {
|
||||
fakeDevices.add(this)
|
||||
}
|
||||
// BeatsFlex
|
||||
BleScanResult(
|
||||
receivedAt = now,
|
||||
address = "5E:9E:D1:49:D2:6D",
|
||||
rssi = Random.nextInt(15, 75) * -1,
|
||||
generatedAtNanos = generatedAt + 400,
|
||||
manufacturerSpecificData = mapOf(76 to "07 19 01 10 20 0A F4 8F 00 01 00 C4 71 9F 9C EF A2 E3 BA 66 FE 1D 45 9F C9 2F A0".fromHex())
|
||||
).run {
|
||||
fakeDevices.add(this)
|
||||
}
|
||||
|
||||
// Tws i99999
|
||||
BleScanResult(
|
||||
receivedAt = now,
|
||||
address = "5E:9E:D1:29:D2:6D",
|
||||
rssi = Random.nextInt(15, 75) * -1,
|
||||
generatedAtNanos = generatedAt + 400,
|
||||
manufacturerSpecificData = mapOf(76 to "07 13 01 02 20 71 AA 37 32 00 10 00 64 64 FF 00 00 00 00 00 00".fromHex())
|
||||
).run {
|
||||
fakeDevices.add(this)
|
||||
}
|
||||
|
||||
// Unknown Device
|
||||
BleScanResult(
|
||||
receivedAt = now,
|
||||
address = "6E:9E:D1:49:D2:6D",
|
||||
rssi = Random.nextInt(15, 75) * -1,
|
||||
generatedAtNanos = generatedAt + 500,
|
||||
manufacturerSpecificData = mapOf(76 to "07 19 01 FF 20 0A F4 8F 00 01 00 C4 71 9F 9C EF A2 E3 BA 66 FE 1D 45 9F C9 2F A0".fromHex())
|
||||
).run {
|
||||
fakeDevices.add(this)
|
||||
}
|
||||
|
||||
return fakeDevices
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package eu.darken.capod.common.bluetooth
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class NudgeAvailability {
|
||||
@SerialName("nudge.unknown") UNKNOWN,
|
||||
@SerialName("nudge.available") AVAILABLE,
|
||||
@SerialName("nudge.broken") BROKEN,
|
||||
}
|
||||
|
||||
enum class NudgeAttemptResult {
|
||||
Accepted,
|
||||
Rejected,
|
||||
UnavailableHiddenApi,
|
||||
UnavailableMissingPermission,
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package eu.darken.capod.common.bluetooth
|
||||
|
||||
import eu.darken.capod.common.coroutine.AppScope
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.datastore.value
|
||||
import eu.darken.capod.common.datastore.valueBlocking
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NudgeCapabilityStore @Inject constructor(
|
||||
generalSettings: GeneralSettings,
|
||||
@AppScope private val appScope: CoroutineScope,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
) {
|
||||
|
||||
private val persistedValue = generalSettings.nudgeAvailability
|
||||
|
||||
// Resolve the persisted value synchronously at construction so consumers that read
|
||||
// `availability.value` (resolver, AutoConnect precondition, UI state) never see the
|
||||
// synthetic UNKNOWN seed before the first DataStore emission arrives. On a known-broken
|
||||
// device this prevents one bonus ALWAYS-mode + nudge-attempt cycle per cold start.
|
||||
val availability: StateFlow<NudgeAvailability> = persistedValue.flow
|
||||
.stateIn(
|
||||
scope = appScope + dispatcherProvider.IO,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = persistedValue.valueBlocking,
|
||||
)
|
||||
|
||||
fun record(result: NudgeAttemptResult) {
|
||||
val verdict = verdictFor(result) ?: return
|
||||
if (availability.value == verdict) return
|
||||
log(TAG, INFO) { "Recording nudge verdict: $verdict (from $result)" }
|
||||
appScope.launch(dispatcherProvider.IO) {
|
||||
persistedValue.value(verdict)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Bluetooth", "NudgeCapabilityStore")
|
||||
|
||||
internal fun verdictFor(result: NudgeAttemptResult): NudgeAvailability? = when (result) {
|
||||
NudgeAttemptResult.Accepted -> NudgeAvailability.AVAILABLE
|
||||
NudgeAttemptResult.UnavailableHiddenApi -> NudgeAvailability.BROKEN
|
||||
NudgeAttemptResult.Rejected,
|
||||
NudgeAttemptResult.UnavailableMissingPermission -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -23,9 +24,10 @@ class DataStoreValue<T>(
|
||||
) {
|
||||
val keyName: String get() = key.name
|
||||
|
||||
val flow: Flow<T> = dataStore.data.map { prefs ->
|
||||
reader(prefs[key])
|
||||
}
|
||||
val flow: Flow<T> = dataStore.data
|
||||
.map { prefs -> prefs[key] }
|
||||
.distinctUntilChanged()
|
||||
.map { raw -> reader(raw) }
|
||||
|
||||
data class Updated<T>(val old: T, val new: T)
|
||||
|
||||
|
||||
@@ -8,9 +8,19 @@ import eu.darken.capod.common.debug.logging.asLog
|
||||
import eu.darken.capod.common.debug.logging.asLogSummary
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
object Bugs {
|
||||
var reporter: AutomaticBugReporter? = null
|
||||
|
||||
/**
|
||||
* True while the debug log recorder is actively recording. Surfaces extra
|
||||
* diagnostic UI (e.g. raw BLE payload hex on device cards) so users helping
|
||||
* to add support for new headphones can capture useful data without a
|
||||
* permanent debug toggle.
|
||||
*/
|
||||
val isDebug = MutableStateFlow(false)
|
||||
|
||||
fun report(
|
||||
tag: String,
|
||||
message: String,
|
||||
|
||||
@@ -28,10 +28,4 @@ class DebugSettings @Inject constructor(
|
||||
// Reporting is opt-out for gplay, and opt-in for github builds
|
||||
defaultValue = BuildConfigWrap.FLAVOR == BuildConfigWrap.Flavor.GPLAY
|
||||
)
|
||||
val isDebugModeEnabled = dataStore.createValue("debug.mode.enabled", false)
|
||||
|
||||
val showFakeData = dataStore.createValue("debug.fakedata.enabled", false)
|
||||
|
||||
val showUnfiltered = dataStore.createValue("debug.blescanner.unfiltered.enabled", false)
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import eu.darken.capod.common.SystemTimeSource
|
||||
import eu.darken.capod.common.TimeSource
|
||||
import eu.darken.capod.common.coroutine.AppScope
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.Bugs
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
@@ -126,6 +127,10 @@ class RecorderModule @Inject constructor(
|
||||
}
|
||||
}
|
||||
.launchIn(appScope)
|
||||
|
||||
internalState.flow
|
||||
.onEach { Bugs.isDebug.value = it.isRecording }
|
||||
.launchIn(appScope)
|
||||
}
|
||||
|
||||
private fun createSessionDir(): File {
|
||||
|
||||
@@ -36,9 +36,6 @@ object Nav {
|
||||
@Serializable
|
||||
data object General : Settings
|
||||
|
||||
@Serializable
|
||||
data object Debug : Settings
|
||||
|
||||
@Serializable
|
||||
data object Support : Settings
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.datastore.preferences.preferencesDataStore
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import eu.darken.capod.common.BuildConfigWrap
|
||||
import eu.darken.capod.common.bluetooth.BluetoothAddress
|
||||
import eu.darken.capod.common.bluetooth.NudgeAvailability
|
||||
import eu.darken.capod.common.datastore.createValue
|
||||
import eu.darken.capod.common.serialization.ByteArrayBase64Serializer
|
||||
import eu.darken.capod.common.serialization.SerializationCapod
|
||||
@@ -36,8 +37,6 @@ class GeneralSettings @Inject constructor(
|
||||
|
||||
private val dataStore: DataStore<Preferences> get() = context.dataStore
|
||||
|
||||
val monitorMode = dataStore.createValue("core.monitor.mode", MonitorMode.AUTOMATIC, json, onErrorFallbackToDefault = true)
|
||||
|
||||
val useExtraMonitorNotification = dataStore.createValue("core.monitor.notification.connected", false)
|
||||
|
||||
val keepConnectedNotificationAfterDisconnect =
|
||||
@@ -74,10 +73,18 @@ class GeneralSettings @Inject constructor(
|
||||
val isOffloadedBatchingDisabled = dataStore.createValue("core.compat.offloaded.batching.disabled", false)
|
||||
val useIndirectScanResultCallback = dataStore.createValue("core.compat.indirectcallback.enabled", false)
|
||||
|
||||
/** Runtime-detected verdict for whether `BluetoothHeadset.connect()` reflection works on this device. */
|
||||
val nudgeAvailability = dataStore.createValue(
|
||||
"core.bluetooth.nudge.availability", NudgeAvailability.UNKNOWN, json,
|
||||
onErrorFallbackToDefault = true,
|
||||
)
|
||||
|
||||
val isOnboardingDone = dataStore.createValue("core.onboarding.done", false)
|
||||
|
||||
val reactionsHintDismissed = dataStore.createValue("ui.hint.reactions_per_device.dismissed", false)
|
||||
|
||||
val hideUnmatchedDevices = dataStore.createValue("ui.overview.unmatched.hidden", false)
|
||||
|
||||
val themeMode = dataStore.createValue(
|
||||
"core.ui.theme.mode", ThemeMode.SYSTEM, json,
|
||||
onErrorFallbackToDefault = BuildConfigWrap.BUILD_TYPE != BuildConfigWrap.BuildType.DEV,
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
package eu.darken.capod.main.core
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import eu.darken.capod.R
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class MonitorMode(
|
||||
@StringRes val labelRes: Int
|
||||
val intensity: Int,
|
||||
) {
|
||||
@SerialName("monitor.mode.manual") MANUAL(
|
||||
R.string.settings_monitor_mode_manual_label
|
||||
),
|
||||
@SerialName("monitor.mode.automatic") AUTOMATIC(
|
||||
R.string.settings_monitor_mode_automatic_label
|
||||
),
|
||||
@SerialName("monitor.mode.always") ALWAYS(
|
||||
R.string.settings_monitor_mode_always_label
|
||||
),
|
||||
}
|
||||
@SerialName("monitor.mode.manual") MANUAL(intensity = 0),
|
||||
@SerialName("monitor.mode.automatic") AUTOMATIC(intensity = 1),
|
||||
@SerialName("monitor.mode.always") ALWAYS(intensity = 2),
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
import eu.darken.capod.common.permissions.isRequired
|
||||
import eu.darken.capod.monitor.core.MonitorModeResolver
|
||||
import eu.darken.capod.profiles.core.toReactionConfig
|
||||
import eu.darken.capod.profiles.core.DeviceProfilesRepo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -21,7 +22,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class PermissionTool @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val generalSettings: GeneralSettings,
|
||||
private val monitorModeResolver: MonitorModeResolver,
|
||||
private val profilesRepo: DeviceProfilesRepo,
|
||||
) {
|
||||
private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID())
|
||||
@@ -41,13 +42,11 @@ class PermissionTool @Inject constructor(
|
||||
|
||||
val missingPermissions: Flow<Set<Permission>> = combine(
|
||||
permissionCheckTrigger,
|
||||
generalSettings.monitorMode.flow,
|
||||
monitorModeResolver.effectiveMode,
|
||||
anyPopupEnabled,
|
||||
) { _, monitorMode, showPopUp ->
|
||||
Permission.entries
|
||||
.filter { it != Permission.IGNORE_BATTERY_OPTIMIZATION || monitorMode == MonitorMode.ALWAYS }
|
||||
.filter { it != Permission.ACCESS_BACKGROUND_LOCATION || monitorMode == MonitorMode.ALWAYS }
|
||||
.filter { it != Permission.SYSTEM_ALERT_WINDOW || showPopUp }
|
||||
.filter { isApplicable(it, monitorMode, showPopUp) }
|
||||
.filter { it.isRequired(context) }
|
||||
.toSet()
|
||||
}
|
||||
@@ -61,5 +60,24 @@ class PermissionTool @Inject constructor(
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("PermissionTool")
|
||||
|
||||
/**
|
||||
* Whether [permission] is applicable for the user's current configuration.
|
||||
* Three permissions are conditional on monitor mode or popup usage:
|
||||
* - [Permission.IGNORE_BATTERY_OPTIMIZATION] only when always-on scanning is needed
|
||||
* - [Permission.ACCESS_BACKGROUND_LOCATION] same
|
||||
* - [Permission.SYSTEM_ALERT_WINDOW] only when at least one popup reaction is enabled
|
||||
* Everything else is unconditionally applicable.
|
||||
*/
|
||||
internal fun isApplicable(
|
||||
permission: Permission,
|
||||
monitorMode: MonitorMode,
|
||||
anyPopupEnabled: Boolean,
|
||||
): Boolean = when (permission) {
|
||||
Permission.IGNORE_BATTERY_OPTIMIZATION,
|
||||
Permission.ACCESS_BACKGROUND_LOCATION -> monitorMode == MonitorMode.ALWAYS
|
||||
Permission.SYSTEM_ALERT_WINDOW -> anyPopupEnabled
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ import eu.darken.capod.pods.core.apple.aap.protocol.AapDeviceInfo
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.HasStateDetection
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
|
||||
import eu.darken.capod.reaction.core.charged.ChargedSlotScope
|
||||
import eu.darken.capod.reaction.core.conversation.ConversationAction
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
@@ -148,6 +150,8 @@ fun DeviceSettingsScreenHost(
|
||||
onNavigateUp = { vm.navUp() },
|
||||
onAncModeChange = { vm.setAncMode(it) },
|
||||
onConversationalAwarenessChange = { vm.setConversationalAwareness(it) },
|
||||
onConversationActionChange = { vm.setConversationAction(it) },
|
||||
onConversationVolumeReductionChange = { vm.setConversationVolumeReduction(it) },
|
||||
onNcWithOneAirPodChange = { vm.setNcWithOneAirPod(it) },
|
||||
onPersonalizedVolumeChange = { vm.setPersonalizedVolume(it) },
|
||||
onToneVolumeChange = { vm.setToneVolume(it) },
|
||||
@@ -167,11 +171,14 @@ fun DeviceSettingsScreenHost(
|
||||
onOnePodModeChange = { vm.setOnePodMode(it) },
|
||||
onAutoPlayChange = { vm.setAutoPlay(it) },
|
||||
onAutoPauseChange = { vm.setAutoPause(it) },
|
||||
onStartMusicOnWearChange = { vm.setStartMusicOnWear(it) },
|
||||
onAutoConnectChange = { vm.setAutoConnect(it) },
|
||||
onAutoConnectConditionChange = { vm.setAutoConnectCondition(it) },
|
||||
onShowPopUpOnCaseOpenChange = { vm.setShowPopUpOnCaseOpen(it) },
|
||||
onShowPopUpOnConnectionChange = { vm.setShowPopUpOnConnection(it) },
|
||||
onFixMonitorMode = { vm.setMonitorModeAutomatic() },
|
||||
onNotifyWhenChargedChange = { vm.setNotifyWhenCharged(it) },
|
||||
onChargedThresholdChange = { vm.setChargedThreshold(it) },
|
||||
onChargedSlotScopeChange = { vm.setChargedSlotScope(it) },
|
||||
onOpenIssueTracker = { vm.openIssueTracker() },
|
||||
onOpenAapTracker = { vm.openAapCompatibilityTracker() },
|
||||
)
|
||||
@@ -186,6 +193,8 @@ fun DeviceSettingsScreen(
|
||||
onNavigateUp: () -> Unit,
|
||||
onAncModeChange: (AapSetting.AncMode.Value) -> Unit = {},
|
||||
onConversationalAwarenessChange: (Boolean) -> Unit = {},
|
||||
onConversationActionChange: (ConversationAction) -> Unit = {},
|
||||
onConversationVolumeReductionChange: (Int) -> Unit = {},
|
||||
onNcWithOneAirPodChange: (Boolean) -> Unit = {},
|
||||
onPersonalizedVolumeChange: (Boolean) -> Unit = {},
|
||||
onToneVolumeChange: (Int) -> Unit = {},
|
||||
@@ -205,11 +214,14 @@ fun DeviceSettingsScreen(
|
||||
onOnePodModeChange: (Boolean) -> Unit = {},
|
||||
onAutoPlayChange: (Boolean) -> Unit = {},
|
||||
onAutoPauseChange: (Boolean) -> Unit = {},
|
||||
onStartMusicOnWearChange: (Boolean) -> Unit = {},
|
||||
onAutoConnectChange: (Boolean) -> Unit = {},
|
||||
onAutoConnectConditionChange: (AutoConnectCondition) -> Unit = {},
|
||||
onShowPopUpOnCaseOpenChange: (Boolean) -> Unit = {},
|
||||
onShowPopUpOnConnectionChange: (Boolean) -> Unit = {},
|
||||
onFixMonitorMode: () -> Unit = {},
|
||||
onNotifyWhenChargedChange: (Boolean) -> Unit = {},
|
||||
onChargedThresholdChange: (Int) -> Unit = {},
|
||||
onChargedSlotScopeChange: (ChargedSlotScope) -> Unit = {},
|
||||
onOpenIssueTracker: () -> Unit = {},
|
||||
onOpenAapTracker: () -> Unit = {},
|
||||
) {
|
||||
@@ -339,17 +351,21 @@ fun DeviceSettingsScreen(
|
||||
device = device,
|
||||
features = features,
|
||||
isPro = isPro,
|
||||
monitorMode = state.monitorMode,
|
||||
onAutoPlayChange = onAutoPlayChange,
|
||||
onAutoPauseChange = onAutoPauseChange,
|
||||
onStartMusicOnWearChange = onStartMusicOnWearChange,
|
||||
onOnePodModeChange = onOnePodModeChange,
|
||||
onConversationalAwarenessChange = onConversationalAwarenessChange,
|
||||
onConversationActionChange = onConversationActionChange,
|
||||
onConversationVolumeReductionChange = onConversationVolumeReductionChange,
|
||||
onSleepDetectionChange = onSleepDetectionChange,
|
||||
onAutoConnectChange = onAutoConnectChange,
|
||||
onAutoConnectConditionChange = onAutoConnectConditionChange,
|
||||
onShowPopUpOnCaseOpenChange = onShowPopUpOnCaseOpenChange,
|
||||
onShowPopUpOnConnectionChange = onShowPopUpOnConnectionChange,
|
||||
onFixMonitorMode = onFixMonitorMode,
|
||||
onNotifyWhenChargedChange = onNotifyWhenChargedChange,
|
||||
onChargedThresholdChange = onChargedThresholdChange,
|
||||
onChargedSlotScopeChange = onChargedSlotScopeChange,
|
||||
onOpenIssueTracker = onOpenIssueTracker,
|
||||
)
|
||||
}
|
||||
|
||||
+60
-22
@@ -5,8 +5,10 @@ import eu.darken.capod.common.SystemTimeSource
|
||||
import eu.darken.capod.common.TimeSource
|
||||
import eu.darken.capod.common.WebpageTool
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.bluetooth.NudgeAttemptResult
|
||||
import eu.darken.capod.common.bluetooth.NudgeAvailability
|
||||
import eu.darken.capod.common.bluetooth.NudgeCapabilityStore
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.datastore.value
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
@@ -16,9 +18,9 @@ import eu.darken.capod.common.navigation.Nav
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
import eu.darken.capod.common.upgrade.isPro
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
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.resolvedAncCycleMask
|
||||
import eu.darken.capod.pods.core.apple.aap.AapConnectionManager
|
||||
@@ -29,6 +31,8 @@ import eu.darken.capod.profiles.core.DeviceProfilesRepo
|
||||
import eu.darken.capod.profiles.core.ProfileId
|
||||
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
|
||||
import eu.darken.capod.reaction.core.stem.StemAction
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -51,7 +55,8 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
private val upgradeRepo: UpgradeRepo,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val profilesRepo: DeviceProfilesRepo,
|
||||
private val generalSettings: GeneralSettings,
|
||||
private val monitorModeResolver: MonitorModeResolver,
|
||||
private val nudgeCapabilityStore: NudgeCapabilityStore,
|
||||
private val timeSource: TimeSource,
|
||||
private val webpageTool: WebpageTool,
|
||||
) : ViewModel4(dispatcherProvider) {
|
||||
@@ -114,8 +119,9 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
// The Bluetooth HEADSET profile lookup can be slow to produce its first value.
|
||||
// Seed this branch so the screen can render immediately after navigation.
|
||||
bluetoothManager.connectedDevices.onStart { emit(emptyList()) },
|
||||
generalSettings.monitorMode.flow,
|
||||
monitorModeResolver.effectiveMode,
|
||||
profilesRepo.profiles,
|
||||
nudgeCapabilityStore.availability,
|
||||
) { args ->
|
||||
val device = args[1] as PodDevice?
|
||||
val upgrade = args[2] as UpgradeRepo.Info
|
||||
@@ -127,6 +133,7 @@ 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>()
|
||||
.firstOrNull { it.id == profileId }
|
||||
?.stemActions
|
||||
@@ -142,7 +149,7 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
device = device,
|
||||
now = timeSource.now(),
|
||||
isPro = upgrade.isPro,
|
||||
isNudgeAvailable = bluetoothManager.isNudgeAvailable,
|
||||
isNudgeAvailable = nudgeAvailability != NudgeAvailability.BROKEN,
|
||||
isForceConnecting = forcing,
|
||||
isClassicallyConnected = device?.address?.let { it in connectedAddresses } == true,
|
||||
monitorMode = monitorMode,
|
||||
@@ -206,18 +213,19 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
events.tryEmit(Event.OpenBluetoothSettings)
|
||||
return@launch
|
||||
}
|
||||
if (!bluetoothManager.isNudgeAvailable) {
|
||||
if (nudgeCapabilityStore.availability.value == NudgeAvailability.BROKEN) {
|
||||
events.tryEmit(Event.OpenBluetoothSettings)
|
||||
return@launch
|
||||
}
|
||||
val accepted = try {
|
||||
val result = try {
|
||||
bluetoothManager.nudgeConnection(bonded)
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "nudgeConnection threw: ${e.message}" }
|
||||
false
|
||||
NudgeAttemptResult.Rejected
|
||||
}
|
||||
log(TAG, INFO) { "nudgeConnection($bonded) accepted=$accepted" }
|
||||
if (!accepted) {
|
||||
log(TAG, INFO) { "nudgeConnection($bonded) result=$result" }
|
||||
nudgeCapabilityStore.record(result)
|
||||
if (result != NudgeAttemptResult.Accepted) {
|
||||
events.tryEmit(Event.OpenBluetoothSettings)
|
||||
}
|
||||
} finally {
|
||||
@@ -382,6 +390,15 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
syncEarDetection(autoPause = enabled)
|
||||
}
|
||||
|
||||
fun setStartMusicOnWear(enabled: Boolean) = launch {
|
||||
log(TAG, INFO) { "setStartMusicOnWear($enabled)" }
|
||||
if (enabled && !upgradeRepo.isPro()) {
|
||||
navTo(Nav.Main.Upgrade)
|
||||
return@launch
|
||||
}
|
||||
updateProfileNow { it.copy(startMusicOnWear = enabled) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the device-side Automatic Ear Detection setting in sync with the
|
||||
* auto-play / auto-pause reaction toggles. When either reaction is active
|
||||
@@ -405,15 +422,6 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
fun setAutoConnect(enabled: Boolean) = launch {
|
||||
log(TAG, INFO) { "setAutoConnect($enabled)" }
|
||||
updateProfileNow { it.copy(autoConnect = enabled) }
|
||||
if (enabled) {
|
||||
// Auto-connect requires ALWAYS mode to react to BLE/case/ear events when
|
||||
// nothing is connected. We intentionally do NOT revert on disable — the user
|
||||
// may have set ALWAYS manually, or another profile may still need it.
|
||||
if (generalSettings.monitorMode.value() != MonitorMode.ALWAYS) {
|
||||
log(TAG, INFO) { "Forcing monitorMode to ALWAYS because autoConnect was enabled" }
|
||||
generalSettings.monitorMode.value(MonitorMode.ALWAYS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setAutoConnectCondition(condition: AutoConnectCondition) = launch {
|
||||
@@ -431,11 +439,41 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
proGatedReaction(enabled) { it.copy(showPopUpOnConnection = enabled) }
|
||||
}
|
||||
|
||||
fun setMonitorModeAutomatic() = launch {
|
||||
log(TAG, INFO) { "setMonitorModeAutomatic()" }
|
||||
generalSettings.monitorMode.value(MonitorMode.AUTOMATIC)
|
||||
fun setNotifyWhenCharged(enabled: Boolean) {
|
||||
log(TAG, INFO) { "setNotifyWhenCharged($enabled)" }
|
||||
proGatedReaction(enabled) { it.copy(notifyWhenCharged = enabled) }
|
||||
}
|
||||
|
||||
fun setChargedSlotScope(scope: ChargedSlotScope) = launch {
|
||||
log(TAG, INFO) { "setChargedSlotScope($scope)" }
|
||||
updateProfileNow { it.copy(chargedSlotScope = scope) }
|
||||
}
|
||||
|
||||
fun setChargedThreshold(percent: Int) = launch {
|
||||
log(TAG, INFO) { "setChargedThreshold($percent)" }
|
||||
val snapped = (percent.toFloat() / ReactionConfig.CHARGED_THRESHOLD_STEP)
|
||||
.let { Math.round(it) * ReactionConfig.CHARGED_THRESHOLD_STEP }
|
||||
.coerceIn(ReactionConfig.MIN_CHARGED_THRESHOLD, ReactionConfig.MAX_CHARGED_THRESHOLD)
|
||||
updateProfileNow { it.copy(chargedThreshold = snapped) }
|
||||
}
|
||||
|
||||
fun setConversationAction(action: ConversationAction) = launch {
|
||||
log(TAG, INFO) { "setConversationAction($action)" }
|
||||
// The action picker is only shown while Conversation Awareness is already enabled (the pod
|
||||
// emits no speaking frames otherwise), so no need to auto-enable it here.
|
||||
if (action != ConversationAction.NOTHING && !upgradeRepo.isPro()) {
|
||||
navTo(Nav.Main.Upgrade)
|
||||
return@launch
|
||||
}
|
||||
updateProfileNow { it.copy(conversationAction = action) }
|
||||
}
|
||||
|
||||
fun setConversationVolumeReduction(percent: Int) = launch {
|
||||
log(TAG, INFO) { "setConversationVolumeReduction($percent)" }
|
||||
updateProfileNow { it.copy(conversationVolumeReduction = percent) }
|
||||
}
|
||||
|
||||
|
||||
fun navToPressControls() = launch {
|
||||
log(TAG, INFO) { "navToPressControls()" }
|
||||
val profileId = targetProfileId.value ?: return@launch
|
||||
|
||||
@@ -4,12 +4,15 @@ import android.os.Build
|
||||
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
|
||||
import androidx.compose.material.icons.twotone.Nightlight
|
||||
import androidx.compose.material.icons.twotone.PauseCircle
|
||||
import androidx.compose.material.icons.twotone.PlayCircle
|
||||
import androidx.compose.material.icons.twotone.RecordVoiceOver
|
||||
import androidx.compose.material.icons.twotone.Workspaces
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -18,6 +21,7 @@ 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
|
||||
@@ -29,39 +33,51 @@ import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.settings.InfoBoxType
|
||||
import eu.darken.capod.common.settings.SettingsBaseItem
|
||||
import eu.darken.capod.common.settings.SettingsCategoryHeader
|
||||
import eu.darken.capod.common.settings.SettingsInfoBox
|
||||
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.core.MonitorMode
|
||||
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
|
||||
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
|
||||
internal fun ReactionsCard(
|
||||
device: PodDevice,
|
||||
features: PodModel.Features,
|
||||
isPro: Boolean,
|
||||
monitorMode: MonitorMode,
|
||||
onAutoPlayChange: (Boolean) -> Unit = {},
|
||||
onAutoPauseChange: (Boolean) -> Unit = {},
|
||||
onStartMusicOnWearChange: (Boolean) -> Unit = {},
|
||||
onOnePodModeChange: (Boolean) -> Unit = {},
|
||||
onConversationalAwarenessChange: (Boolean) -> Unit = {},
|
||||
onConversationActionChange: (ConversationAction) -> Unit = {},
|
||||
onConversationVolumeReductionChange: (Int) -> Unit = {},
|
||||
onSleepDetectionChange: (Boolean) -> Unit = {},
|
||||
onAutoConnectChange: (Boolean) -> Unit = {},
|
||||
onAutoConnectConditionChange: (AutoConnectCondition) -> Unit = {},
|
||||
onShowPopUpOnCaseOpenChange: (Boolean) -> Unit = {},
|
||||
onShowPopUpOnConnectionChange: (Boolean) -> Unit = {},
|
||||
onFixMonitorMode: () -> Unit = {},
|
||||
onNotifyWhenChargedChange: (Boolean) -> Unit = {},
|
||||
onChargedThresholdChange: (Int) -> Unit = {},
|
||||
onChargedSlotScopeChange: (ChargedSlotScope) -> Unit = {},
|
||||
onOpenIssueTracker: () -> Unit = {},
|
||||
) {
|
||||
val reactions = device.reactions
|
||||
val enabled = device.isAapReady
|
||||
|
||||
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) {
|
||||
@@ -73,6 +89,16 @@ internal fun ReactionsCard(
|
||||
onCheckedChange = onAutoPlayChange,
|
||||
requiresUpgrade = !isPro,
|
||||
)
|
||||
if (reactions.autoPlay) {
|
||||
SettingsSwitchItem(
|
||||
icon = Icons.TwoTone.PlayCircle,
|
||||
title = stringResource(R.string.settings_start_music_on_wear_label),
|
||||
subtitle = stringResource(R.string.settings_start_music_on_wear_description),
|
||||
checked = reactions.startMusicOnWear,
|
||||
onCheckedChange = onStartMusicOnWearChange,
|
||||
requiresUpgrade = !isPro,
|
||||
)
|
||||
}
|
||||
SettingsSwitchItem(
|
||||
icon = Icons.TwoTone.PauseCircle,
|
||||
title = stringResource(R.string.settings_autopause_label),
|
||||
@@ -117,20 +143,49 @@ internal fun ReactionsCard(
|
||||
}
|
||||
if (device.isAapConnected) {
|
||||
val convAwareness = device.conversationalAwareness
|
||||
val hasAnyAapReaction =
|
||||
(features.hasConversationAwareness && convAwareness != null) ||
|
||||
features.hasSleepDetection
|
||||
if (features.hasConversationAwareness && convAwareness != null) {
|
||||
val showConversationAwareness = features.hasConversationAwareness && convAwareness != null
|
||||
val hasAnyAapReaction = showConversationAwareness || features.hasSleepDetection
|
||||
if (showConversationAwareness) {
|
||||
SettingsCategoryHeader(text = stringResource(R.string.conversation_awareness_label))
|
||||
SettingsSwitchItem(
|
||||
icon = Icons.TwoTone.Hearing,
|
||||
title = stringResource(R.string.conversation_awareness_label),
|
||||
subtitle = stringResource(R.string.device_settings_conversation_awareness_description),
|
||||
checked = convAwareness.enabled,
|
||||
checked = convAwareness?.enabled == true,
|
||||
onCheckedChange = onConversationalAwarenessChange,
|
||||
enabled = enabled,
|
||||
)
|
||||
// The "when you start speaking" reaction only fires while CA is on (the pod emits no
|
||||
// speaking frames otherwise), so only surface it once CA is actually enabled.
|
||||
if (convAwareness?.enabled == true) {
|
||||
SettingsBaseItem(
|
||||
icon = Icons.TwoTone.RecordVoiceOver,
|
||||
title = stringResource(R.string.settings_conversation_action_label),
|
||||
subtitle = stringResource(reactions.conversationAction.labelRes),
|
||||
onClick = { showConversationActionDialog = true },
|
||||
enabled = enabled,
|
||||
requiresUpgrade = !isPro,
|
||||
)
|
||||
if (reactions.conversationAction == ConversationAction.LOWER_VOLUME) {
|
||||
var sliderValue by remember(reactions.conversationVolumeReduction) {
|
||||
mutableIntStateOf(reactions.conversationVolumeReduction)
|
||||
}
|
||||
SettingsSliderItem(
|
||||
icon = Icons.AutoMirrored.TwoTone.VolumeDown,
|
||||
title = stringResource(R.string.settings_conversation_volume_reduction_label),
|
||||
value = sliderValue.toFloat(),
|
||||
onValueChange = { sliderValue = it.toInt() },
|
||||
onValueChangeFinished = { onConversationVolumeReductionChange(sliderValue) },
|
||||
valueRange = ReactionConfig.MIN_CONVERSATION_VOLUME_REDUCTION.toFloat()..
|
||||
ReactionConfig.MAX_CONVERSATION_VOLUME_REDUCTION.toFloat(),
|
||||
enabled = enabled,
|
||||
valueLabel = { "${it.toInt()}%" },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (features.hasSleepDetection) {
|
||||
if (showConversationAwareness) ReactionsDivider()
|
||||
val sleepDet = device.sleepDetection
|
||||
?: AapSetting.SleepDetection(enabled = true)
|
||||
SettingsSwitchItem(
|
||||
@@ -198,21 +253,67 @@ internal fun ReactionsCard(
|
||||
requiresUpgrade = !isPro,
|
||||
)
|
||||
val anyPopupEnabled = reactions.showPopUpOnCaseOpen || reactions.showPopUpOnConnection
|
||||
if (anyPopupEnabled && monitorMode == MonitorMode.MANUAL) {
|
||||
SettingsInfoBox(
|
||||
text = stringResource(R.string.settings_popup_warning_manual_mode),
|
||||
type = InfoBoxType.WARNING,
|
||||
action = {
|
||||
TextButton(onClick = onFixMonitorMode) {
|
||||
Text(stringResource(R.string.general_fix_it_action))
|
||||
}
|
||||
},
|
||||
)
|
||||
} else if (anyPopupEnabled) {
|
||||
if (anyPopupEnabled) {
|
||||
SettingsInfoBox(
|
||||
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) {
|
||||
ConversationActionDialog(
|
||||
current = reactions.conversationAction,
|
||||
onSelect = {
|
||||
onConversationActionChange(it)
|
||||
showConversationActionDialog = false
|
||||
},
|
||||
onDismiss = { showConversationActionDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (showChargedScopeDialog) {
|
||||
ChargedSlotScopeDialog(
|
||||
current = reactions.chargedSlotScope,
|
||||
onSelect = {
|
||||
onChargedSlotScopeChange(it)
|
||||
showChargedScopeDialog = false
|
||||
},
|
||||
onDismiss = { showChargedScopeDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (showAutoConnectConditionDialog) {
|
||||
@@ -246,7 +347,6 @@ private fun ReactionsCardPreview() = PreviewWrapper {
|
||||
device = device,
|
||||
features = device.model.features,
|
||||
isPro = state.isPro,
|
||||
monitorMode = state.monitorMode,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -259,6 +359,5 @@ private fun ReactionsCardNonProPreview() = PreviewWrapper {
|
||||
device = device,
|
||||
features = device.model.features,
|
||||
isPro = state.isPro,
|
||||
monitorMode = state.monitorMode,
|
||||
)
|
||||
}
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package eu.darken.capod.main.ui.devicesettings.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
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.reaction.core.charged.ChargedSlotScope
|
||||
|
||||
@Composable
|
||||
internal fun ChargedSlotScopeDialog(
|
||||
current: ChargedSlotScope,
|
||||
onSelect: (ChargedSlotScope) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(text = stringResource(R.string.settings_charged_scope_label)) },
|
||||
text = {
|
||||
Column(Modifier.selectableGroup()) {
|
||||
ChargedSlotScope.entries.forEach { scope ->
|
||||
val isSelected = scope == current
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = isSelected,
|
||||
onClick = { onSelect(scope) },
|
||||
role = Role.RadioButton,
|
||||
)
|
||||
.padding(vertical = 12.dp, horizontal = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
RadioButton(selected = isSelected, onClick = null)
|
||||
Text(
|
||||
text = stringResource(scope.labelRes),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview2
|
||||
@Composable
|
||||
private fun ChargedSlotScopeDialogPreview() = PreviewWrapper {
|
||||
ChargedSlotScopeDialog(
|
||||
current = ChargedSlotScope.PODS_AND_CASE,
|
||||
onSelect = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package eu.darken.capod.main.ui.devicesettings.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
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.reaction.core.conversation.ConversationAction
|
||||
|
||||
@Composable
|
||||
internal fun ConversationActionDialog(
|
||||
current: ConversationAction,
|
||||
onSelect: (ConversationAction) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(text = stringResource(R.string.settings_conversation_action_label)) },
|
||||
text = {
|
||||
Column(Modifier.selectableGroup()) {
|
||||
ConversationAction.entries.forEach { action ->
|
||||
val isSelected = action == current
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = isSelected,
|
||||
onClick = { onSelect(action) },
|
||||
role = Role.RadioButton,
|
||||
)
|
||||
.padding(vertical = 12.dp, horizontal = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
RadioButton(selected = isSelected, onClick = null)
|
||||
Text(
|
||||
text = stringResource(action.labelRes),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(android.R.string.cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview2
|
||||
@Composable
|
||||
private fun ConversationActionDialogPreview() = PreviewWrapper {
|
||||
ConversationActionDialog(
|
||||
current = ConversationAction.LOWER_VOLUME,
|
||||
onSelect = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
@@ -61,6 +61,7 @@ import eu.darken.capod.main.ui.overview.cards.NoProfilesCard
|
||||
import eu.darken.capod.main.ui.overview.cards.PermissionCard
|
||||
import eu.darken.capod.main.ui.overview.cards.ReactionsMovedHintCard
|
||||
import eu.darken.capod.main.ui.overview.cards.SinglePodsCard
|
||||
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
|
||||
@@ -172,6 +173,7 @@ fun OverviewScreenHost(vm: OverviewViewModel = hiltViewModel()) {
|
||||
},
|
||||
onManageDevices = { vm.goToDeviceManager() },
|
||||
onSettings = { vm.goToSettings() },
|
||||
onTroubleShooter = { vm.goToTroubleShooter() },
|
||||
onUpgrade = { vm.onUpgrade() },
|
||||
onToggleUnmatched = { vm.toggleUnmatchedDevices() },
|
||||
onAncModeChange = { device, mode -> vm.setAncMode(device, mode) },
|
||||
@@ -194,6 +196,7 @@ fun OverviewScreen(
|
||||
onBluetoothSettings: () -> Unit,
|
||||
onManageDevices: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
onTroubleShooter: () -> Unit = {},
|
||||
onUpgrade: () -> Unit,
|
||||
onToggleUnmatched: () -> Unit,
|
||||
onAncModeChange: (PodDevice, AapSetting.AncMode.Value) -> Unit = { _, _ -> },
|
||||
@@ -327,7 +330,7 @@ fun OverviewScreen(
|
||||
PodDeviceCard(
|
||||
device = device,
|
||||
isPro = state.upgradeInfo.isPro,
|
||||
showDebug = state.isDebugMode,
|
||||
showDebug = state.isDebug,
|
||||
now = state.now,
|
||||
isCollapsed = isCollapsed,
|
||||
onToggleCollapse = if (isToggleable) {
|
||||
@@ -351,18 +354,25 @@ fun OverviewScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// 4c. Troubleshooter suggestion — connected via audio but no live data is reaching us
|
||||
if (state.showTroubleshootSuggestion) {
|
||||
item(key = "troubleshoot_suggestion") {
|
||||
TroubleshootSuggestionCard(onTroubleShooter = onTroubleShooter)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Monitoring active card
|
||||
if (state.profiles.isNotEmpty() && state.devices.isEmpty()) {
|
||||
if (state.profiles.isNotEmpty() && state.profiledDevices.isEmpty() && !state.shouldShowUnmatchedSection) {
|
||||
item(key = "monitoring_active") {
|
||||
MonitoringActiveCard()
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Unmatched devices section
|
||||
if (state.unmatchedDevices.isNotEmpty()) {
|
||||
if (state.shouldShowUnmatchedSection) {
|
||||
item(key = "unmatched_header") {
|
||||
UnmatchedDevicesCard(
|
||||
count = state.unmatchedDevices.size,
|
||||
count = state.visibleUnmatchedDevices.size,
|
||||
isExpanded = state.showUnmatchedDevices,
|
||||
onToggle = onToggleUnmatched,
|
||||
)
|
||||
@@ -370,13 +380,13 @@ fun OverviewScreen(
|
||||
|
||||
if (state.showUnmatchedDevices) {
|
||||
itemsIndexed(
|
||||
items = state.unmatchedDevices,
|
||||
items = state.visibleUnmatchedDevices,
|
||||
key = { index, device -> unmatchedDeviceKey(device, index) },
|
||||
) { _, device ->
|
||||
PodDeviceCard(
|
||||
device = device,
|
||||
isPro = state.upgradeInfo.isPro,
|
||||
showDebug = state.isDebugMode,
|
||||
showDebug = state.isDebug,
|
||||
now = state.now,
|
||||
onAncModeChange = { mode -> onAncModeChange(device, mode) },
|
||||
onUpgrade = onUpgrade,
|
||||
@@ -438,7 +448,7 @@ private fun OverviewScreenWithDevicesPreview() = PreviewWrapper {
|
||||
MockPodDataProvider.singlePodMonitored(),
|
||||
MockPodDataProvider.unknownMonitored(),
|
||||
),
|
||||
isDebugMode = false,
|
||||
isDebug = false,
|
||||
isBluetoothEnabled = true,
|
||||
profiles = listOf(
|
||||
MockPodDataProvider.profile("My AirPods Pro", PodModel.AIRPODS_PRO2),
|
||||
@@ -465,7 +475,7 @@ private fun OverviewScreenEmptyPreview() = PreviewWrapper {
|
||||
now = SystemTimeSource.now(),
|
||||
permissions = emptySet(),
|
||||
devices = emptyList(),
|
||||
isDebugMode = false,
|
||||
isDebug = false,
|
||||
isBluetoothEnabled = true,
|
||||
profiles = listOf(MockPodDataProvider.profile("My AirPods", PodModel.AIRPODS_PRO2)),
|
||||
upgradeInfo = MockPodDataProvider.fossInfo(),
|
||||
@@ -488,7 +498,7 @@ private fun OverviewScreenNoProfilesPreview() = PreviewWrapper {
|
||||
now = SystemTimeSource.now(),
|
||||
permissions = emptySet(),
|
||||
devices = emptyList(),
|
||||
isDebugMode = false,
|
||||
isDebug = false,
|
||||
isBluetoothEnabled = true,
|
||||
profiles = emptyList(),
|
||||
upgradeInfo = MockPodDataProvider.gplayInfo(),
|
||||
@@ -511,7 +521,7 @@ private fun OverviewScreenBluetoothOffPreview() = PreviewWrapper {
|
||||
now = SystemTimeSource.now(),
|
||||
permissions = emptySet(),
|
||||
devices = emptyList(),
|
||||
isDebugMode = false,
|
||||
isDebug = false,
|
||||
isBluetoothEnabled = false,
|
||||
profiles = listOf(MockPodDataProvider.profile("My AirPods", PodModel.AIRPODS_PRO2)),
|
||||
upgradeInfo = MockPodDataProvider.fossInfo(isPro = true),
|
||||
|
||||
@@ -5,8 +5,7 @@ import eu.darken.capod.common.TimeSource
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.datastore.value
|
||||
import eu.darken.capod.common.datastore.valueBlocking
|
||||
import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.debug.Bugs
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
@@ -22,6 +21,7 @@ import eu.darken.capod.main.core.GeneralSettings
|
||||
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.MonitorModeResolver
|
||||
import eu.darken.capod.monitor.core.PodDevice
|
||||
import eu.darken.capod.monitor.core.tierRank
|
||||
import eu.darken.capod.monitor.core.worker.MonitorControl
|
||||
@@ -34,12 +34,16 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.combine as combineFlows
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -50,11 +54,11 @@ class OverviewViewModel @Inject constructor(
|
||||
private val deviceMonitor: DeviceMonitor,
|
||||
private val permissionTool: PermissionTool,
|
||||
private val generalSettings: GeneralSettings,
|
||||
debugSettings: DebugSettings,
|
||||
private val upgradeRepo: UpgradeRepo,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val profilesRepo: DeviceProfilesRepo,
|
||||
private val aapManager: AapConnectionManager,
|
||||
private val monitorModeResolver: MonitorModeResolver,
|
||||
private val timeSource: TimeSource,
|
||||
) : ViewModel4(dispatcherProvider) {
|
||||
|
||||
@@ -77,29 +81,83 @@ class OverviewViewModel @Inject constructor(
|
||||
private val showUnmatchedDevices = MutableStateFlow(false)
|
||||
private val userExpansionOverrides = MutableStateFlow<Set<String>>(emptySet())
|
||||
|
||||
val workerAutolaunch = permissionTool.missingScanPermissions
|
||||
.onEach { permissions ->
|
||||
if (permissions.isNotEmpty()) {
|
||||
log(TAG) { "Missing scan permissions: $permissions" }
|
||||
private data class OverviewUiSettings(
|
||||
val reactionsHintDismissed: Boolean,
|
||||
val hideUnmatchedDevices: Boolean,
|
||||
val showTroubleshootSuggestion: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* True when a profile is connected to the system (audio) but CAPod has *no* live data for any
|
||||
* device — the classic "broadcasts are being dropped by the ROM" symptom (e.g. some HyperOS
|
||||
* phones, see #603). Surfaced as a hint pointing at the Troubleshooter, which can probe and
|
||||
* persist a working compatibility combo. Debounced so it doesn't flash during the brief window
|
||||
* between an audio connection and the first BLE broadcast. Suppressed whenever any pod is live,
|
||||
* so it never claims "no data" while data is visibly arriving (incl. the #603 duplicate state).
|
||||
*/
|
||||
private val troubleshootSuggestion = combineFlows(
|
||||
profilesRepo.profiles,
|
||||
bluetoothManager.connectedDevices.onStart { emit(emptyList()) },
|
||||
deviceMonitor.devices,
|
||||
permissionTool.missingScanPermissions,
|
||||
) { profiles, connectedDevices, devices, missingScanPermissions ->
|
||||
val connectedAddresses = connectedDevices.mapTo(mutableSetOf()) { it.address }
|
||||
val anyProfileConnected = profiles.any { it.address != null && it.address in connectedAddresses }
|
||||
val anyLivePod = devices.any { it.isLive }
|
||||
missingScanPermissions.isEmpty() && anyProfileConnected && !anyLivePod
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { conditionMet ->
|
||||
if (conditionMet) flow {
|
||||
delay(TROUBLESHOOT_SUGGESTION_DELAY_MS)
|
||||
emit(true)
|
||||
} else flowOf(false)
|
||||
}
|
||||
.onStart { emit(false) }
|
||||
.distinctUntilChanged()
|
||||
|
||||
private val overviewUiSettings = combineFlows(
|
||||
generalSettings.reactionsHintDismissed.flow,
|
||||
generalSettings.hideUnmatchedDevices.flow,
|
||||
troubleshootSuggestion,
|
||||
) { reactionsHintDismissed, hideUnmatched, showTroubleshootSuggestion ->
|
||||
OverviewUiSettings(reactionsHintDismissed, hideUnmatched, showTroubleshootSuggestion)
|
||||
}
|
||||
|
||||
init {
|
||||
// When the persistent "hide unmatched" setting is enabled, reset the in-session expand
|
||||
// toggle so the section reappears collapsed if the user later disables the setting.
|
||||
launch {
|
||||
generalSettings.hideUnmatchedDevices.flow
|
||||
.filter { it }
|
||||
.collect { showUnmatchedDevices.value = false }
|
||||
}
|
||||
}
|
||||
|
||||
val workerAutolaunch = combine(
|
||||
permissionTool.missingScanPermissions,
|
||||
monitorModeResolver.effectiveMode,
|
||||
// BluetoothManager2.connectedDevices is gated by a slow HEADSET-profile lookup. Without
|
||||
// an onStart seed the combine wouldn't fire until that profile is bound, so ALWAYS mode
|
||||
// would silently delay autolaunch until the first connected-device emission.
|
||||
bluetoothManager.connectedDevices
|
||||
.onStart { emit(emptyList()) }
|
||||
.map { it.isNotEmpty() }
|
||||
.distinctUntilChanged(),
|
||||
) { missing, mode, anyConnected -> Triple(missing, mode, anyConnected) }
|
||||
.distinctUntilChanged()
|
||||
.onEach { (missing, mode, anyConnected) ->
|
||||
if (missing.isNotEmpty()) {
|
||||
log(TAG) { "Missing scan permissions: $missing" }
|
||||
return@onEach
|
||||
}
|
||||
|
||||
val shouldStart = when (generalSettings.monitorMode.valueBlocking) {
|
||||
val shouldStart = when (mode) {
|
||||
MonitorMode.MANUAL -> false
|
||||
MonitorMode.AUTOMATIC -> {
|
||||
try {
|
||||
val devices = withTimeoutOrNull(5_000) { bluetoothManager.connectedDevices.first() }
|
||||
devices?.isNotEmpty() == true
|
||||
} catch (e: SecurityException) {
|
||||
log(TAG) { "Can't check connected devices without BLUETOOTH_CONNECT: ${e.message}" }
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
MonitorMode.AUTOMATIC -> anyConnected
|
||||
MonitorMode.ALWAYS -> true
|
||||
}
|
||||
if (shouldStart) {
|
||||
log(TAG) { "Starting monitor" }
|
||||
log(TAG) { "Starting monitor (mode=$mode)" }
|
||||
monitorControl.startMonitor()
|
||||
}
|
||||
}
|
||||
@@ -126,15 +184,15 @@ class OverviewViewModel @Inject constructor(
|
||||
updateTicker,
|
||||
permissionTool.missingPermissions,
|
||||
pods,
|
||||
debugSettings.isDebugModeEnabled.flow,
|
||||
Bugs.isDebug,
|
||||
bluetoothManager.isBluetoothEnabled,
|
||||
profilesRepo.profiles,
|
||||
upgradeRepo.upgradeInfo,
|
||||
showUnmatchedDevices,
|
||||
userExpansionOverrides,
|
||||
generalSettings.reactionsHintDismissed.flow,
|
||||
overviewUiSettings,
|
||||
profilesRepo.hadLegacyReactionData,
|
||||
) { _, permissions, devices, isDebugMode, isBluetoothEnabled, profiles, upgradeInfo, showUnmatched, expandedIds, reactionsHintDismissed, hadLegacyReactionData ->
|
||||
) { _, permissions, devices, isDebug, isBluetoothEnabled, profiles, upgradeInfo, showUnmatched, expandedIds, uiSettings, hadLegacyReactionData ->
|
||||
// Prune stale overrides (profiles that no longer exist)
|
||||
val currentProfileIds = profiles.map { it.id }.toSet()
|
||||
val prunedExpandedIds = expandedIds.filter { it in currentProfileIds }.toSet()
|
||||
@@ -143,13 +201,15 @@ class OverviewViewModel @Inject constructor(
|
||||
now = timeSource.now(),
|
||||
permissions = permissions,
|
||||
devices = devices,
|
||||
isDebugMode = isDebugMode,
|
||||
isDebug = isDebug,
|
||||
isBluetoothEnabled = isBluetoothEnabled,
|
||||
profiles = profiles,
|
||||
upgradeInfo = upgradeInfo,
|
||||
showUnmatchedDevices = showUnmatched,
|
||||
userExpandedIds = prunedExpandedIds,
|
||||
showReactionsHint = hadLegacyReactionData && !reactionsHintDismissed,
|
||||
showReactionsHint = hadLegacyReactionData && !uiSettings.reactionsHintDismissed,
|
||||
hideUnmatchedDevices = uiSettings.hideUnmatchedDevices,
|
||||
showTroubleshootSuggestion = uiSettings.showTroubleshootSuggestion,
|
||||
)
|
||||
}.asLiveState()
|
||||
|
||||
@@ -159,13 +219,15 @@ class OverviewViewModel @Inject constructor(
|
||||
val now: Instant,
|
||||
val permissions: Set<Permission>,
|
||||
val devices: List<PodDevice>,
|
||||
val isDebugMode: Boolean,
|
||||
val isDebug: Boolean,
|
||||
val isBluetoothEnabled: Boolean,
|
||||
val profiles: List<DeviceProfile>,
|
||||
val upgradeInfo: UpgradeRepo.Info,
|
||||
val showUnmatchedDevices: Boolean,
|
||||
val userExpandedIds: Set<String> = emptySet(),
|
||||
val showReactionsHint: Boolean = false,
|
||||
val hideUnmatchedDevices: Boolean = false,
|
||||
val showTroubleshootSuggestion: Boolean = false,
|
||||
) {
|
||||
val isScanBlocked: Boolean get() = permissions.any { it.isScanBlocking }
|
||||
|
||||
@@ -186,6 +248,11 @@ class OverviewViewModel @Inject constructor(
|
||||
val hiddenProfiledDeviceCount: Int get() = profiledDevices.size - visibleProfiledDevices.size
|
||||
val unmatchedDevices: List<PodDevice> get() = devices.filter { it.profileId == null }
|
||||
|
||||
/** Unmatched devices actually shown on the dashboard (empty when the hide setting is on). */
|
||||
val visibleUnmatchedDevices: List<PodDevice>
|
||||
get() = if (hideUnmatchedDevices) emptyList() else unmatchedDevices
|
||||
val shouldShowUnmatchedSection: Boolean get() = visibleUnmatchedDevices.isNotEmpty()
|
||||
|
||||
val bluetoothIconState: BluetoothIconState
|
||||
get() = when {
|
||||
isScanBlocked -> BluetoothIconState.HIDDEN
|
||||
@@ -224,6 +291,11 @@ class OverviewViewModel @Inject constructor(
|
||||
navTo(Nav.Settings.Index)
|
||||
}
|
||||
|
||||
fun goToTroubleShooter() {
|
||||
log(TAG, INFO) { "goToTroubleShooter()" }
|
||||
navTo(Nav.Main.TroubleShooter)
|
||||
}
|
||||
|
||||
fun goToDeviceManager() {
|
||||
log(TAG, INFO) { "goToDeviceManager()" }
|
||||
navTo(Nav.Main.DeviceManager)
|
||||
@@ -284,6 +356,14 @@ class OverviewViewModel @Inject constructor(
|
||||
|
||||
companion object {
|
||||
private const val FREE_DEVICE_LIMIT = 1
|
||||
|
||||
/**
|
||||
* How long the "connected but no live data" condition must hold before the troubleshooter
|
||||
* hint appears, so it doesn't flash during the gap between an audio connection and the first
|
||||
* BLE broadcast.
|
||||
*/
|
||||
private const val TROUBLESHOOT_SUGGESTION_DELAY_MS = 15_000L
|
||||
|
||||
private val TAG = logTag("Overview", "VM")
|
||||
}
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package eu.darken.capod.main.ui.overview.cards
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.Troubleshoot
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun TroubleshootSuggestionCard(onTroubleShooter: () -> Unit) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Troubleshoot,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.overview_troubleshoot_suggestion_label),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.overview_troubleshoot_suggestion_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Button(
|
||||
onClick = onTroubleShooter,
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
) {
|
||||
Text(text = stringResource(R.string.overview_troubleshoot_suggestion_action))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview2
|
||||
@Composable
|
||||
private fun TroubleshootSuggestionCardPreview() = PreviewWrapper {
|
||||
TroubleshootSuggestionCard(onTroubleShooter = {})
|
||||
}
|
||||
+17
-101
@@ -1,44 +1,32 @@
|
||||
package eu.darken.capod.main.ui.settings.general
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.twotone.ArrowBack
|
||||
import androidx.compose.material.icons.twotone.AccountTree
|
||||
import androidx.compose.material.icons.twotone.BugReport
|
||||
import androidx.compose.material.icons.twotone.Contrast
|
||||
import androidx.compose.material.icons.twotone.DarkMode
|
||||
import androidx.compose.material.icons.twotone.DisabledVisible
|
||||
import androidx.compose.material.icons.twotone.FilterList
|
||||
import androidx.compose.material.icons.automirrored.twotone.Message
|
||||
import androidx.compose.material.icons.twotone.Notifications
|
||||
import androidx.compose.material.icons.twotone.Palette
|
||||
import androidx.compose.material.icons.twotone.VisibilityOff
|
||||
import androidx.compose.material.icons.automirrored.twotone.ViewList
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import eu.darken.capod.R
|
||||
@@ -55,7 +43,6 @@ import eu.darken.capod.common.theming.ThemeColor
|
||||
import eu.darken.capod.common.theming.ThemeMode
|
||||
import eu.darken.capod.common.theming.ThemeState
|
||||
import eu.darken.capod.common.theming.ThemeStyle
|
||||
import eu.darken.capod.main.core.MonitorMode
|
||||
|
||||
@Composable
|
||||
fun GeneralSettingsScreenHost(vm: GeneralSettingsViewModel = hiltViewModel()) {
|
||||
@@ -67,13 +54,12 @@ fun GeneralSettingsScreenHost(vm: GeneralSettingsViewModel = hiltViewModel()) {
|
||||
GeneralSettingsScreen(
|
||||
state = it,
|
||||
onNavigateUp = { vm.navUp() },
|
||||
onMonitorModeSelected = { mode -> vm.setMonitorMode(mode) },
|
||||
onShowConnectedNotificationChanged = { enabled -> vm.setShowConnectedNotification(enabled) },
|
||||
onKeepNotificationAfterDisconnectChanged = { enabled -> vm.setKeepNotificationAfterDisconnect(enabled) },
|
||||
onDebugSettings = { vm.goToDebugSettings() },
|
||||
onOffloadedFilteringDisabledChanged = { disabled -> vm.setOffloadedFilteringDisabled(disabled) },
|
||||
onOffloadedBatchingDisabledChanged = { disabled -> vm.setOffloadedBatchingDisabled(disabled) },
|
||||
onUseIndirectScanResultCallbackChanged = { enabled -> vm.setUseIndirectScanResultCallback(enabled) },
|
||||
onHideUnmatchedDevicesChanged = { enabled -> vm.setHideUnmatchedDevices(enabled) },
|
||||
onThemeModeSelected = { mode -> vm.setThemeMode(mode) },
|
||||
onThemeStyleSelected = { style -> vm.setThemeStyle(style) },
|
||||
onThemeColorSelected = { color -> vm.setThemeColor(color) },
|
||||
@@ -86,20 +72,18 @@ fun GeneralSettingsScreenHost(vm: GeneralSettingsViewModel = hiltViewModel()) {
|
||||
fun GeneralSettingsScreen(
|
||||
state: GeneralSettingsViewModel.State,
|
||||
onNavigateUp: () -> Unit,
|
||||
onMonitorModeSelected: (MonitorMode) -> Unit,
|
||||
onShowConnectedNotificationChanged: (Boolean) -> Unit,
|
||||
onKeepNotificationAfterDisconnectChanged: (Boolean) -> Unit,
|
||||
onDebugSettings: () -> Unit,
|
||||
onOffloadedFilteringDisabledChanged: (Boolean) -> Unit,
|
||||
onOffloadedBatchingDisabledChanged: (Boolean) -> Unit,
|
||||
onUseIndirectScanResultCallbackChanged: (Boolean) -> Unit,
|
||||
onHideUnmatchedDevicesChanged: (Boolean) -> Unit,
|
||||
onThemeModeSelected: (ThemeMode) -> Unit = {},
|
||||
onThemeStyleSelected: (ThemeStyle) -> Unit = {},
|
||||
onThemeColorSelected: (ThemeColor) -> Unit = {},
|
||||
onUpgrade: () -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showMonitorModeDialog by remember { mutableStateOf(false) }
|
||||
var showColorDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val isMaterialYouActive = state.themeState.style == ThemeStyle.MATERIAL_YOU &&
|
||||
@@ -122,17 +106,6 @@ fun GeneralSettingsScreen(
|
||||
},
|
||||
) { innerPadding ->
|
||||
LazyColumn(modifier = Modifier.padding(innerPadding)) {
|
||||
item {
|
||||
SettingsCategoryHeader(text = stringResource(R.string.settings_category_monitoring_label))
|
||||
}
|
||||
item {
|
||||
SettingsBaseItem(
|
||||
title = stringResource(R.string.settings_monitor_mode_label),
|
||||
subtitle = stringResource(state.monitorMode.labelRes),
|
||||
icon = Icons.TwoTone.DisabledVisible,
|
||||
onClick = { showMonitorModeDialog = true },
|
||||
)
|
||||
}
|
||||
item {
|
||||
SettingsCategoryHeader(text = stringResource(R.string.settings_category_appearance_label))
|
||||
}
|
||||
@@ -229,10 +202,17 @@ fun GeneralSettingsScreen(
|
||||
}
|
||||
item {
|
||||
SettingsBaseItem(
|
||||
title = stringResource(R.string.settings_debug_label),
|
||||
subtitle = stringResource(R.string.settings_debug_description),
|
||||
icon = Icons.TwoTone.BugReport,
|
||||
onClick = onDebugSettings,
|
||||
title = stringResource(R.string.settings_overview_hide_unmatched_label),
|
||||
subtitle = stringResource(R.string.settings_overview_hide_unmatched_description),
|
||||
icon = Icons.TwoTone.VisibilityOff,
|
||||
onClick = { onHideUnmatchedDevicesChanged(!state.hideUnmatchedDevices) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = state.hideUnmatchedDevices,
|
||||
onCheckedChange = onHideUnmatchedDevicesChanged,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
item {
|
||||
@@ -286,20 +266,6 @@ fun GeneralSettingsScreen(
|
||||
}
|
||||
}
|
||||
|
||||
if (showMonitorModeDialog) {
|
||||
ListPreferenceDialog(
|
||||
title = stringResource(R.string.settings_monitor_mode_label),
|
||||
entries = MonitorMode.entries,
|
||||
selectedEntry = state.monitorMode,
|
||||
onEntrySelected = {
|
||||
onMonitorModeSelected(it)
|
||||
showMonitorModeDialog = false
|
||||
},
|
||||
entryLabel = { stringResource(it.labelRes) },
|
||||
onDismiss = { showMonitorModeDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (showColorDialog) {
|
||||
ThemeColorSelectorDialog(
|
||||
selectedColor = state.themeState.color,
|
||||
@@ -314,12 +280,12 @@ fun GeneralSettingsScreen(
|
||||
|
||||
private fun previewGeneralState(isPro: Boolean) = GeneralSettingsViewModel.State(
|
||||
isPro = isPro,
|
||||
monitorMode = MonitorMode.AUTOMATIC,
|
||||
showConnectedNotification = true,
|
||||
keepNotificationAfterDisconnect = false,
|
||||
isOffloadedFilteringDisabled = false,
|
||||
isOffloadedBatchingDisabled = false,
|
||||
useIndirectScanResultCallback = false,
|
||||
hideUnmatchedDevices = false,
|
||||
themeState = ThemeState(),
|
||||
)
|
||||
|
||||
@@ -329,13 +295,12 @@ private fun GeneralSettingsScreenProPreview() = PreviewWrapper {
|
||||
GeneralSettingsScreen(
|
||||
state = previewGeneralState(isPro = true),
|
||||
onNavigateUp = {},
|
||||
onMonitorModeSelected = {},
|
||||
onShowConnectedNotificationChanged = {},
|
||||
onKeepNotificationAfterDisconnectChanged = {},
|
||||
onDebugSettings = {},
|
||||
onOffloadedFilteringDisabledChanged = {},
|
||||
onOffloadedBatchingDisabledChanged = {},
|
||||
onUseIndirectScanResultCallbackChanged = {},
|
||||
onHideUnmatchedDevicesChanged = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -345,60 +310,11 @@ private fun GeneralSettingsScreenNonProPreview() = PreviewWrapper {
|
||||
GeneralSettingsScreen(
|
||||
state = previewGeneralState(isPro = false),
|
||||
onNavigateUp = {},
|
||||
onMonitorModeSelected = {},
|
||||
onShowConnectedNotificationChanged = {},
|
||||
onKeepNotificationAfterDisconnectChanged = {},
|
||||
onDebugSettings = {},
|
||||
onOffloadedFilteringDisabledChanged = {},
|
||||
onOffloadedBatchingDisabledChanged = {},
|
||||
onUseIndirectScanResultCallbackChanged = {},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T> ListPreferenceDialog(
|
||||
title: String,
|
||||
entries: List<T>,
|
||||
selectedEntry: T,
|
||||
onEntrySelected: (T) -> Unit,
|
||||
entryLabel: @Composable (T) -> String,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(text = title) },
|
||||
text = {
|
||||
Column(Modifier.selectableGroup()) {
|
||||
entries.forEach { entry ->
|
||||
val isSelected = entry == selectedEntry
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = isSelected,
|
||||
onClick = { onEntrySelected(entry) },
|
||||
role = Role.RadioButton,
|
||||
)
|
||||
.padding(vertical = 12.dp, horizontal = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
RadioButton(
|
||||
selected = isSelected,
|
||||
onClick = null,
|
||||
)
|
||||
Text(
|
||||
text = entryLabel(entry),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(android.R.string.cancel))
|
||||
}
|
||||
},
|
||||
onHideUnmatchedDevicesChanged = {},
|
||||
)
|
||||
}
|
||||
|
||||
+13
-19
@@ -13,7 +13,6 @@ import eu.darken.capod.common.theming.ThemeStyle
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.main.core.MonitorMode
|
||||
import eu.darken.capod.main.core.themeState
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
@@ -30,12 +29,12 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
|
||||
data class State(
|
||||
val isPro: Boolean,
|
||||
val monitorMode: MonitorMode,
|
||||
val showConnectedNotification: Boolean,
|
||||
val keepNotificationAfterDisconnect: Boolean,
|
||||
val isOffloadedFilteringDisabled: Boolean,
|
||||
val isOffloadedBatchingDisabled: Boolean,
|
||||
val useIndirectScanResultCallback: Boolean,
|
||||
val hideUnmatchedDevices: Boolean,
|
||||
val themeState: ThemeState,
|
||||
)
|
||||
|
||||
@@ -43,12 +42,11 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
|
||||
val state = combine(
|
||||
combine(
|
||||
generalSettings.monitorMode.flow,
|
||||
generalSettings.useExtraMonitorNotification.flow,
|
||||
generalSettings.keepConnectedNotificationAfterDisconnect.flow,
|
||||
) { monitorMode, showNotif, keepNotif ->
|
||||
) { showNotif, keepNotif ->
|
||||
@Suppress("USELESS_CAST")
|
||||
arrayOf<Any>(monitorMode as Any, showNotif as Any, keepNotif as Any)
|
||||
arrayOf<Any>(showNotif as Any, keepNotif as Any)
|
||||
},
|
||||
combine(
|
||||
generalSettings.isOffloadedFilteringDisabled.flow,
|
||||
@@ -60,24 +58,20 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
},
|
||||
generalSettings.themeState,
|
||||
isPro,
|
||||
) { general, compat, themeState, isPro ->
|
||||
generalSettings.hideUnmatchedDevices.flow,
|
||||
) { general, compat, themeState, isPro, hideUnmatched ->
|
||||
State(
|
||||
isPro = isPro,
|
||||
monitorMode = general[0] as MonitorMode,
|
||||
showConnectedNotification = general[1] as Boolean,
|
||||
keepNotificationAfterDisconnect = general[2] as Boolean,
|
||||
showConnectedNotification = general[0] as Boolean,
|
||||
keepNotificationAfterDisconnect = general[1] as Boolean,
|
||||
isOffloadedFilteringDisabled = compat[0] as Boolean,
|
||||
isOffloadedBatchingDisabled = compat[1] as Boolean,
|
||||
useIndirectScanResultCallback = compat[2] as Boolean,
|
||||
hideUnmatchedDevices = hideUnmatched,
|
||||
themeState = themeState,
|
||||
)
|
||||
}.asLiveState()
|
||||
|
||||
fun setMonitorMode(mode: MonitorMode) {
|
||||
log(TAG, INFO) { "setMonitorMode($mode)" }
|
||||
generalSettings.monitorMode.valueBlocking = mode
|
||||
}
|
||||
|
||||
fun setShowConnectedNotification(enabled: Boolean) {
|
||||
log(TAG, INFO) { "setShowConnectedNotification($enabled)" }
|
||||
generalSettings.useExtraMonitorNotification.valueBlocking = enabled
|
||||
@@ -103,6 +97,11 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
generalSettings.useIndirectScanResultCallback.valueBlocking = enabled
|
||||
}
|
||||
|
||||
fun setHideUnmatchedDevices(enabled: Boolean) {
|
||||
log(TAG, INFO) { "setHideUnmatchedDevices($enabled)" }
|
||||
generalSettings.hideUnmatchedDevices.valueBlocking = enabled
|
||||
}
|
||||
|
||||
fun setThemeMode(mode: ThemeMode) = launch {
|
||||
log(TAG, INFO) { "setThemeMode($mode)" }
|
||||
if (isPro.first()) {
|
||||
@@ -135,11 +134,6 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
navTo(Nav.Main.Upgrade)
|
||||
}
|
||||
|
||||
fun goToDebugSettings() {
|
||||
log(TAG, INFO) { "goToDebugSettings()" }
|
||||
navTo(Nav.Settings.Debug)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Settings", "General", "VM")
|
||||
}
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package eu.darken.capod.main.ui.settings.general.debug
|
||||
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import eu.darken.capod.common.navigation.Nav
|
||||
import eu.darken.capod.common.navigation.NavigationEntry
|
||||
import javax.inject.Inject
|
||||
|
||||
class DebugSettingsNavigation @Inject constructor() : NavigationEntry {
|
||||
override fun EntryProviderScope<NavKey>.setup() {
|
||||
entry<Nav.Settings.Debug> { DebugSettingsScreenHost() }
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class Mod {
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun bind(entry: DebugSettingsNavigation): NavigationEntry
|
||||
}
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
package eu.darken.capod.main.ui.settings.general.debug
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.twotone.ArrowBack
|
||||
import androidx.compose.material.icons.twotone.BugReport
|
||||
import androidx.compose.material.icons.twotone.DataArray
|
||||
import androidx.compose.material.icons.twotone.DevicesOther
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.settings.SettingsBaseItem
|
||||
|
||||
@Composable
|
||||
fun DebugSettingsScreenHost(vm: DebugSettingsViewModel = hiltViewModel()) {
|
||||
ErrorEventHandler(vm)
|
||||
NavigationEventHandler(vm)
|
||||
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
DebugSettingsScreen(
|
||||
state = it,
|
||||
onNavigateUp = { vm.navUp() },
|
||||
onDebugModeChanged = { enabled -> vm.setDebugModeEnabled(enabled) },
|
||||
onShowFakeDataChanged = { enabled -> vm.setShowFakeData(enabled) },
|
||||
onShowUnfilteredChanged = { enabled -> vm.setShowUnfiltered(enabled) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DebugSettingsScreen(
|
||||
state: DebugSettingsViewModel.State,
|
||||
onNavigateUp: () -> Unit,
|
||||
onDebugModeChanged: (Boolean) -> Unit,
|
||||
onShowFakeDataChanged: (Boolean) -> Unit,
|
||||
onShowUnfilteredChanged: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(text = stringResource(R.string.settings_debug_label)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateUp) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.TwoTone.ArrowBack,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
LazyColumn(modifier = Modifier.padding(innerPadding)) {
|
||||
item {
|
||||
SettingsBaseItem(
|
||||
title = stringResource(R.string.settings_debug_mode_label),
|
||||
subtitle = stringResource(R.string.settings_debug_mode_description),
|
||||
icon = Icons.TwoTone.BugReport,
|
||||
onClick = { onDebugModeChanged(!state.isDebugModeEnabled) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = state.isDebugModeEnabled,
|
||||
onCheckedChange = onDebugModeChanged,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
item {
|
||||
SettingsBaseItem(
|
||||
title = stringResource(R.string.settings_fake_data_label),
|
||||
subtitle = stringResource(R.string.settings_fake_data_description),
|
||||
icon = Icons.TwoTone.DataArray,
|
||||
onClick = { onShowFakeDataChanged(!state.showFakeData) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = state.showFakeData,
|
||||
onCheckedChange = onShowFakeDataChanged,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
item {
|
||||
SettingsBaseItem(
|
||||
title = stringResource(R.string.settings_blescanner_unfiltered_label),
|
||||
subtitle = stringResource(R.string.settings_blescanner_unfiltered_description),
|
||||
icon = Icons.TwoTone.DevicesOther,
|
||||
onClick = { onShowUnfilteredChanged(!state.showUnfiltered) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = state.showUnfiltered,
|
||||
onCheckedChange = onShowUnfilteredChanged,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview2
|
||||
@Composable
|
||||
private fun DebugSettingsScreenPreview() = PreviewWrapper {
|
||||
DebugSettingsScreen(
|
||||
state = DebugSettingsViewModel.State(
|
||||
isDebugModeEnabled = true,
|
||||
showFakeData = false,
|
||||
showUnfiltered = false,
|
||||
),
|
||||
onNavigateUp = {},
|
||||
onDebugModeChanged = {},
|
||||
onShowFakeDataChanged = {},
|
||||
onShowUnfilteredChanged = {},
|
||||
)
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package eu.darken.capod.main.ui.settings.general.debug
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import javax.inject.Inject
|
||||
import eu.darken.capod.common.datastore.valueBlocking
|
||||
|
||||
@HiltViewModel
|
||||
class DebugSettingsViewModel @Inject constructor(
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
private val debugSettings: DebugSettings,
|
||||
) : ViewModel4(dispatcherProvider) {
|
||||
|
||||
data class State(
|
||||
val isDebugModeEnabled: Boolean,
|
||||
val showFakeData: Boolean,
|
||||
val showUnfiltered: Boolean,
|
||||
)
|
||||
|
||||
val state = combine(
|
||||
debugSettings.isDebugModeEnabled.flow,
|
||||
debugSettings.showFakeData.flow,
|
||||
debugSettings.showUnfiltered.flow,
|
||||
) { debugMode, fakeData, unfiltered ->
|
||||
State(
|
||||
isDebugModeEnabled = debugMode,
|
||||
showFakeData = fakeData,
|
||||
showUnfiltered = unfiltered,
|
||||
)
|
||||
}.asLiveState()
|
||||
|
||||
fun setDebugModeEnabled(enabled: Boolean) {
|
||||
log(TAG, INFO) { "setDebugModeEnabled($enabled)" }
|
||||
debugSettings.isDebugModeEnabled.valueBlocking = enabled
|
||||
}
|
||||
|
||||
fun setShowFakeData(enabled: Boolean) {
|
||||
log(TAG, INFO) { "setShowFakeData($enabled)" }
|
||||
debugSettings.showFakeData.valueBlocking = enabled
|
||||
}
|
||||
|
||||
fun setShowUnfiltered(enabled: Boolean) {
|
||||
log(TAG, INFO) { "setShowUnfiltered($enabled)" }
|
||||
debugSettings.showUnfiltered.valueBlocking = enabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Settings", "Debug", "VM")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package eu.darken.capod.monitor.core
|
||||
|
||||
import eu.darken.capod.common.bluetooth.NudgeAvailability
|
||||
import eu.darken.capod.common.bluetooth.NudgeCapabilityStore
|
||||
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.main.core.MonitorMode
|
||||
import eu.darken.capod.profiles.core.DeviceProfile
|
||||
import eu.darken.capod.profiles.core.DeviceProfilesRepo
|
||||
import eu.darken.capod.profiles.core.toReactionConfig
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class MonitorModeResolver @Inject constructor(
|
||||
private val profilesRepo: DeviceProfilesRepo,
|
||||
private val nudgeCapabilityStore: NudgeCapabilityStore,
|
||||
) {
|
||||
|
||||
val effectiveMode: Flow<MonitorMode> = combine(
|
||||
profilesRepo.profiles,
|
||||
nudgeCapabilityStore.availability,
|
||||
) { profiles, nudge ->
|
||||
val primary = profiles.firstOrNull() ?: return@combine MonitorMode.MANUAL
|
||||
primary.requiredMode(nudge)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEach { log(TAG, VERBOSE) { "effectiveMode = $it" } }
|
||||
|
||||
private fun DeviceProfile.requiredMode(nudge: NudgeAvailability): MonitorMode = when {
|
||||
// Match AutoConnect.kt's isNullOrEmpty check — a blank/legacy "" address is no address.
|
||||
address.isNullOrBlank() -> MonitorMode.MANUAL
|
||||
toReactionConfig().autoConnect && nudge != NudgeAvailability.BROKEN -> MonitorMode.ALWAYS
|
||||
else -> MonitorMode.AUTOMATIC
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Monitor", "ModeResolver")
|
||||
}
|
||||
}
|
||||
@@ -267,6 +267,14 @@ data class PodDevice(
|
||||
val isEitherPodInEar: Boolean?
|
||||
get() = aap?.isEitherPodInEar ?: (ble as? HasEarDetectionDual)?.isEitherPodInEar
|
||||
|
||||
/**
|
||||
* True when ear detection comes from a live AAP reading. The BLE ear-detection bits are
|
||||
* unreliable for pods resting in the case (they report phantom "in ear" and disagree
|
||||
* between the two pods' advertisements), so consumers that must not act on a false "worn"
|
||||
* gate on this.
|
||||
*/
|
||||
val hasAapEarDetection: Boolean get() = aap?.aapEarDetection != null
|
||||
|
||||
val caseLidState: DualApplePods.LidState?
|
||||
get() = (ble as? DualApplePods)?.caseLidState
|
||||
|
||||
|
||||
@@ -9,20 +9,22 @@ import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
import eu.darken.capod.monitor.core.ble.BlePodMonitor
|
||||
import eu.darken.capod.pods.core.apple.PodModel
|
||||
import eu.darken.capod.pods.core.apple.aap.AapConnectionManager
|
||||
import eu.darken.capod.pods.core.apple.aap.AapDisconnectEvent
|
||||
import eu.darken.capod.pods.core.apple.aap.AapPodState
|
||||
import eu.darken.capod.profiles.core.AppleDeviceProfile
|
||||
import eu.darken.capod.profiles.core.DeviceProfilesRepo
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
@@ -37,6 +39,21 @@ class AapAutoConnect @Inject constructor(
|
||||
private val activeReconnects = java.util.Collections.synchronizedSet(mutableSetOf<String>())
|
||||
private val processedModelCorrections = java.util.Collections.synchronizedSet(mutableSetOf<String>())
|
||||
|
||||
/**
|
||||
* Per-address count of consecutive connection attempts that never reached READY (failed socket
|
||||
* connects and handshake-timeout disconnects). Drives the escalating reconnect cooldown so a
|
||||
* device that keeps stalling its handshake in a crowded RF environment doesn't churn a tight
|
||||
* reconnect loop. Reset to 0 once a session works (a was-ready disconnect) or the device leaves.
|
||||
*/
|
||||
private val preReadyFailures = ConcurrentHashMap<String, Int>()
|
||||
|
||||
/** Cooldown (ms) to wait before the next attempt, given how many consecutive pre-READY failures we've seen. */
|
||||
private fun cooldownFor(address: String): Long {
|
||||
val failures = preReadyFailures[address] ?: 0
|
||||
if (failures <= 0) return 0L
|
||||
return HANDSHAKE_BACKOFF[minOf(failures - 1, HANDSHAKE_BACKOFF.lastIndex)]
|
||||
}
|
||||
|
||||
fun monitor(): Flow<Unit> = merge(
|
||||
initialConnect(),
|
||||
reconnectOnDisconnect(),
|
||||
@@ -74,104 +91,150 @@ class AapAutoConnect @Inject constructor(
|
||||
return
|
||||
}
|
||||
|
||||
// Escalating cooldown carried over from prior failed attempts (socket failures or handshake
|
||||
// stalls). 0 on the first attempt so a healthy device connects without delay.
|
||||
val cooldown = cooldownFor(address)
|
||||
if (cooldown > 0) {
|
||||
log(TAG) { "AAP initial connect cooldown ${cooldown}ms for $address (failures=${preReadyFailures[address]})" }
|
||||
delay(cooldown)
|
||||
}
|
||||
|
||||
log(TAG) { "AAP connecting to $address (${profile.label})" }
|
||||
try {
|
||||
aapManager.connect(address, bonded.internal!!, profile.model)
|
||||
log(TAG) { "AAP connected to $address" }
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "AAP initial connect failed for $address: ${e.message}" }
|
||||
|
||||
for ((attempt, delayMs) in RETRY_DELAYS.withIndex()) {
|
||||
delay(delayMs)
|
||||
|
||||
// Bail out if classic BT disconnected
|
||||
// Bail out if classic BT disconnected — device left, clear the penalty
|
||||
val currentConnected = bluetoothManager.connectedDevices.first().map { it.address }.toSet()
|
||||
if (address !in currentConnected) {
|
||||
log(TAG) { "AAP initial retry: $address no longer classically connected, stopping" }
|
||||
break
|
||||
preReadyFailures.remove(address)
|
||||
return
|
||||
}
|
||||
|
||||
val retryState = aapManager.allStates.value[address]
|
||||
if (retryState != null && retryState.connectionState != AapPodState.ConnectionState.DISCONNECTED) {
|
||||
log(TAG) { "AAP initial retry: $address already reconnected, stopping" }
|
||||
break
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
log(TAG) { "AAP initial retry ${attempt + 1} for $address after ${delayMs}ms" }
|
||||
aapManager.connect(address, bonded.internal!!, profile.model)
|
||||
log(TAG) { "AAP connected to $address on retry ${attempt + 1}" }
|
||||
break
|
||||
return
|
||||
} catch (retryException: Exception) {
|
||||
log(TAG, WARN) { "AAP initial retry ${attempt + 1} failed for $address: ${retryException.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
// Every attempt this episode failed at the socket level — escalate the next round's cooldown.
|
||||
preReadyFailures.merge(address, 1, Int::plus)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reconnectOnDisconnect(): Flow<Unit> = aapManager.disconnectEvents
|
||||
.onEach { address ->
|
||||
if (!activeReconnects.add(address)) {
|
||||
log(TAG, VERBOSE) { "AAP reconnect already in progress for $address, skipping" }
|
||||
return@onEach
|
||||
}
|
||||
|
||||
try {
|
||||
for ((attempt, delayMs) in RETRY_DELAYS.withIndex()) {
|
||||
delay(delayMs)
|
||||
|
||||
// Check if still profiled
|
||||
val profile = profilesRepo.profiles.first()
|
||||
.firstOrNull { it.address == address }
|
||||
if (profile == null) {
|
||||
log(TAG) { "AAP reconnect: $address no longer profiled, stopping" }
|
||||
break
|
||||
}
|
||||
|
||||
// Check if still bonded
|
||||
val bonded = bluetoothManager.bondedDevices().first()
|
||||
.firstOrNull { it.address == address }
|
||||
if (bonded == null) {
|
||||
log(TAG) { "AAP reconnect: $address no longer bonded, stopping" }
|
||||
break
|
||||
}
|
||||
|
||||
// Check if still classically connected
|
||||
val currentConnected = bluetoothManager.connectedDevices.first().map { it.address }.toSet()
|
||||
if (address !in currentConnected) {
|
||||
log(TAG) { "AAP reconnect: $address no longer classically connected, stopping" }
|
||||
break
|
||||
}
|
||||
|
||||
// Check if still visible in BLE
|
||||
val bleDevices = blePodMonitor.devices.first()
|
||||
if (bleDevices.none { it.meta?.profile?.address == address }) {
|
||||
log(TAG) { "AAP reconnect: $address no longer visible in BLE, stopping" }
|
||||
break
|
||||
}
|
||||
|
||||
// Check if already reconnected (e.g., by initialConnect)
|
||||
val currentState = aapManager.allStates.value[address]
|
||||
if (currentState != null && currentState.connectionState != AapPodState.ConnectionState.DISCONNECTED) {
|
||||
log(TAG) { "AAP reconnect: $address already reconnected" }
|
||||
break
|
||||
}
|
||||
|
||||
try {
|
||||
log(TAG) { "AAP reconnect attempt ${attempt + 1} for $address in ${delayMs}ms" }
|
||||
aapManager.connect(address, bonded.internal!!, profile.model)
|
||||
log(TAG) { "AAP reconnected to $address" }
|
||||
break
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "AAP reconnect attempt ${attempt + 1} failed for $address: ${e.message}" }
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
activeReconnects.remove(address)
|
||||
}
|
||||
// Process each disconnect on its own child coroutine so a long per-address backoff never blocks
|
||||
// another device's reconnect (the collector would otherwise serialise events). channelFlow gives
|
||||
// us a scope to launch into; it intentionally emits nothing — it stays subscribed for its lifetime.
|
||||
private fun reconnectOnDisconnect(): Flow<Unit> = channelFlow<Unit> {
|
||||
aapManager.disconnectEvents.collect { event ->
|
||||
launch { handleReconnect(event) }
|
||||
}
|
||||
.map { } // SharedFlow<BluetoothAddress> → Flow<Unit>
|
||||
.setupCommonEventHandlers(TAG) { "reconnect" }
|
||||
}.setupCommonEventHandlers(TAG) { "reconnect" }
|
||||
|
||||
private suspend fun handleReconnect(event: AapDisconnectEvent) {
|
||||
val address = event.address
|
||||
|
||||
// A session that actually worked resets the penalty (prompt reconnect). One that never reached
|
||||
// READY is a failed handshake/short session — escalate so a persistent stall backs off.
|
||||
if (event.wasEverReady) {
|
||||
preReadyFailures.remove(address)
|
||||
} else {
|
||||
preReadyFailures.merge(address, 1, Int::plus)
|
||||
}
|
||||
|
||||
if (!activeReconnects.add(address)) {
|
||||
log(TAG, VERBOSE) { "AAP reconnect already in progress for $address, skipping" }
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Escalating cooldown before the normal retry cadence when handshakes keep stalling.
|
||||
val cooldown = cooldownFor(address)
|
||||
if (cooldown > 0) {
|
||||
log(TAG) { "AAP reconnect cooldown ${cooldown}ms for $address (failures=${preReadyFailures[address]})" }
|
||||
delay(cooldown)
|
||||
}
|
||||
|
||||
for ((attempt, delayMs) in RETRY_DELAYS.withIndex()) {
|
||||
delay(delayMs)
|
||||
|
||||
// Check if still profiled
|
||||
val profile = profilesRepo.profiles.first()
|
||||
.firstOrNull { it.address == address }
|
||||
if (profile == null) {
|
||||
log(TAG) { "AAP reconnect: $address no longer profiled, stopping" }
|
||||
preReadyFailures.remove(address)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if still bonded
|
||||
val bonded = bluetoothManager.bondedDevices().first()
|
||||
.firstOrNull { it.address == address }
|
||||
if (bonded == null) {
|
||||
log(TAG) { "AAP reconnect: $address no longer bonded, stopping" }
|
||||
preReadyFailures.remove(address)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if still classically connected
|
||||
val currentConnected = bluetoothManager.connectedDevices.first().map { it.address }.toSet()
|
||||
if (address !in currentConnected) {
|
||||
log(TAG) { "AAP reconnect: $address no longer classically connected, stopping" }
|
||||
preReadyFailures.remove(address)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if still visible in BLE
|
||||
val bleDevices = blePodMonitor.devices.first()
|
||||
if (bleDevices.none { it.meta?.profile?.address == address }) {
|
||||
log(TAG) { "AAP reconnect: $address no longer visible in BLE, stopping" }
|
||||
preReadyFailures.remove(address)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if already reconnected (e.g., by initialConnect)
|
||||
val currentState = aapManager.allStates.value[address]
|
||||
if (currentState != null && currentState.connectionState != AapPodState.ConnectionState.DISCONNECTED) {
|
||||
log(TAG) { "AAP reconnect: $address already reconnected" }
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
log(TAG) { "AAP reconnect attempt ${attempt + 1} for $address in ${delayMs}ms" }
|
||||
aapManager.connect(address, bonded.internal!!, profile.model)
|
||||
log(TAG) { "AAP reconnected to $address" }
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "AAP reconnect attempt ${attempt + 1} failed for $address: ${e.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
// Every attempt threw at the socket level (the device is still present but won't accept a
|
||||
// socket) — escalate so we don't hammer it. Mirrors connectWithRetries. Handshake stalls
|
||||
// don't reach here: their reconnect socket succeeds and we return above; they escalate via
|
||||
// the never-ready disconnect event instead.
|
||||
preReadyFailures.merge(address, 1, Int::plus)
|
||||
} finally {
|
||||
activeReconnects.remove(address)
|
||||
}
|
||||
}
|
||||
|
||||
private fun correctModelOnDeviceInfo(): Flow<Unit> = aapManager.allStates
|
||||
.map { states -> states.mapValues { (_, state) -> state.deviceInfo?.modelNumber } }
|
||||
@@ -224,5 +287,12 @@ class AapAutoConnect @Inject constructor(
|
||||
companion object {
|
||||
private val TAG = logTag("Monitor", "AapAutoConnect")
|
||||
internal val RETRY_DELAYS = longArrayOf(3_000, 3_000, 3_000, 5_000, 5_000, 10_000, 10_000)
|
||||
|
||||
/**
|
||||
* Escalating cooldown (ms) applied before a reconnect/connect attempt, indexed by
|
||||
* consecutive pre-READY failures minus one. Caps at 60s so a device that perpetually stalls
|
||||
* its handshake in crowded RF settles into a slow ~minute cadence instead of a tight loop.
|
||||
*/
|
||||
internal val HANDSHAKE_BACKOFF = longArrayOf(5_000, 15_000, 30_000, 60_000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class StemPressReaction @Inject constructor(
|
||||
is StemAction.PreviousTrack -> mediaControl.sendKey(KeyEvent.KEYCODE_MEDIA_PREVIOUS)
|
||||
is StemAction.VolumeUp -> mediaControl.adjustVolumeUp()
|
||||
is StemAction.VolumeDown -> mediaControl.adjustVolumeDown()
|
||||
is StemAction.Stop -> mediaControl.sendKey(KeyEvent.KEYCODE_MEDIA_STOP)
|
||||
is StemAction.Stop -> mediaControl.sendStop()
|
||||
is StemAction.FastForward -> mediaControl.sendKey(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD)
|
||||
is StemAction.Rewind -> mediaControl.sendKey(KeyEvent.KEYCODE_MEDIA_REWIND)
|
||||
is StemAction.MuteToggle -> mediaControl.toggleMuteMusic()
|
||||
|
||||
@@ -8,7 +8,6 @@ import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.bluetooth.ScannerMode
|
||||
import eu.darken.capod.common.bluetooth.onlyNewAndUnique
|
||||
import eu.darken.capod.common.coroutine.AppScope
|
||||
import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.debug.logging.Logging
|
||||
import eu.darken.capod.common.debug.logging.asLog
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
@@ -30,6 +29,7 @@ import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -52,7 +52,6 @@ class BlePodMonitor @Inject constructor(
|
||||
private val timeSource: TimeSource,
|
||||
private val generalSettings: GeneralSettings,
|
||||
bluetoothManager: BluetoothManager2,
|
||||
private val debugSettings: DebugSettings,
|
||||
private val permissionTool: PermissionTool,
|
||||
private val profilesRepo: DeviceProfilesRepo,
|
||||
) {
|
||||
@@ -60,6 +59,62 @@ class BlePodMonitor @Inject constructor(
|
||||
private val deviceCache = mutableMapOf<BlePodSnapshot.Id, BlePodSnapshot>()
|
||||
private val cacheLock = Mutex()
|
||||
|
||||
/**
|
||||
* Drops all cached observations. The troubleshooter calls this between probe attempts so a
|
||||
* previous compat combo's cached devices (kept up to [STALE_DEVICE_TIMEOUT]) can't leak into
|
||||
* the next attempt and falsely satisfy it — including via [preferCaseContextPod], which would
|
||||
* otherwise hand back a stale snapshot whose timestamp predates the fresh scan.
|
||||
*/
|
||||
suspend fun clearDeviceCache() = cacheLock.withLock {
|
||||
log(TAG) { "clearDeviceCache()" }
|
||||
deviceCache.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Ephemeral override that disables the proximity-pairing scan filter so
|
||||
* the troubleshooter can collect raw BLE broadcasts. Resets to false on
|
||||
* every process start; the troubleshooter is the only writer.
|
||||
*/
|
||||
private val unfilteredOverride = MutableStateFlow(false)
|
||||
|
||||
fun setUnfilteredOverride(enabled: Boolean) {
|
||||
unfilteredOverride.value = enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Ephemeral override for the three BLE compatibility options. The troubleshooter uses this to
|
||||
* probe combinations without writing the user's persisted settings: while set, it fully replaces
|
||||
* the persisted compat values for the active scan. Resets to null on every process start; the
|
||||
* troubleshooter is the only writer and always clears it when finished, so clearing it restores
|
||||
* the user's original settings for free.
|
||||
*/
|
||||
private val compatOverride = MutableStateFlow<CompatOverride?>(null)
|
||||
|
||||
fun setCompatOverride(override: CompatOverride?) {
|
||||
log(TAG) { "setCompatOverride($override)" }
|
||||
compatOverride.value = override
|
||||
}
|
||||
|
||||
data class CompatOverride(
|
||||
val offloadedFilteringDisabled: Boolean,
|
||||
val offloadedBatchingDisabled: Boolean,
|
||||
val indirectCallback: Boolean,
|
||||
)
|
||||
|
||||
/** Persisted compat settings, transparently replaced by [compatOverride] while it is set. */
|
||||
private val effectiveCompat: Flow<CompatOverride> = combine(
|
||||
compatOverride,
|
||||
generalSettings.isOffloadedFilteringDisabled.flow,
|
||||
generalSettings.isOffloadedBatchingDisabled.flow,
|
||||
generalSettings.useIndirectScanResultCallback.flow,
|
||||
) { override, filteringDisabled, batchingDisabled, indirectCallback ->
|
||||
override ?: CompatOverride(
|
||||
offloadedFilteringDisabled = filteringDisabled,
|
||||
offloadedBatchingDisabled = batchingDisabled,
|
||||
indirectCallback = indirectCallback,
|
||||
)
|
||||
}
|
||||
|
||||
val devices: Flow<List<BlePodSnapshot>> = combine(
|
||||
permissionTool.missingScanPermissions,
|
||||
bluetoothManager.isBluetoothEnabled
|
||||
@@ -134,23 +189,15 @@ class BlePodMonitor @Inject constructor(
|
||||
|
||||
private fun createBleScanner() = combine(
|
||||
bleScanModeController.scannerMode,
|
||||
debugSettings.showUnfiltered.flow,
|
||||
generalSettings.isOffloadedBatchingDisabled.flow,
|
||||
generalSettings.isOffloadedFilteringDisabled.flow,
|
||||
generalSettings.useIndirectScanResultCallback.flow,
|
||||
) {
|
||||
scannermode,
|
||||
showUnfiltered,
|
||||
isOffloadedBatchingDisabled,
|
||||
isOffloadedFilteringDisabled,
|
||||
useIndirectScanResultCallback,
|
||||
->
|
||||
unfilteredOverride,
|
||||
effectiveCompat,
|
||||
) { scannermode, showUnfiltered, compat ->
|
||||
ScannerOptions(
|
||||
scannerMode = scannermode,
|
||||
showUnfiltered = showUnfiltered,
|
||||
offloadedFilteringDisabled = isOffloadedFilteringDisabled,
|
||||
offloadedBatchingDisabled = isOffloadedBatchingDisabled,
|
||||
disableDirectCallback = useIndirectScanResultCallback,
|
||||
offloadedFilteringDisabled = compat.offloadedFilteringDisabled,
|
||||
offloadedBatchingDisabled = compat.offloadedBatchingDisabled,
|
||||
disableDirectCallback = compat.indirectCallback,
|
||||
)
|
||||
}
|
||||
.throttleLatest(1000)
|
||||
|
||||
@@ -15,7 +15,6 @@ import eu.darken.capod.common.bluetooth.BluetoothAddress
|
||||
import eu.darken.capod.common.bluetooth.BluetoothDevice2
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.datastore.valueBlocking
|
||||
import eu.darken.capod.common.debug.Bugs
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
@@ -30,6 +29,7 @@ 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.MonitorModeResolver
|
||||
import eu.darken.capod.monitor.core.PodDevice
|
||||
import eu.darken.capod.monitor.core.ble.BlePodMonitor
|
||||
import eu.darken.capod.monitor.core.primaryDevice
|
||||
@@ -42,6 +42,9 @@ import eu.darken.capod.profiles.core.DeviceProfilesRepo
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnect
|
||||
import eu.darken.capod.reaction.core.playpause.PlayPause
|
||||
import eu.darken.capod.reaction.core.popup.PopUpReaction
|
||||
import eu.darken.capod.reaction.core.conversation.ConversationReaction
|
||||
import eu.darken.capod.reaction.core.charged.ChargedReaction
|
||||
import eu.darken.capod.reaction.core.charged.ChargedReactionNotifications
|
||||
import eu.darken.capod.reaction.core.sleep.SleepReaction
|
||||
import eu.darken.capod.reaction.ui.popup.PopUpWindow
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -78,9 +81,13 @@ class MonitorService : Service() {
|
||||
@Inject lateinit var autoConnect: AutoConnect
|
||||
@Inject lateinit var popUpReaction: PopUpReaction
|
||||
@Inject lateinit var sleepReaction: SleepReaction
|
||||
@Inject lateinit var chargedReaction: ChargedReaction
|
||||
@Inject lateinit var chargedReactionNotifications: ChargedReactionNotifications
|
||||
@Inject lateinit var conversationReaction: ConversationReaction
|
||||
@Inject lateinit var popUpWindow: PopUpWindow
|
||||
@Inject lateinit var profilesRepo: DeviceProfilesRepo
|
||||
@Inject lateinit var aapConnectionManager: AapConnectionManager
|
||||
@Inject lateinit var monitorModeResolver: MonitorModeResolver
|
||||
|
||||
private val monitorScope = MonitorCoroutineScope()
|
||||
private var monitoringJob: Job? = null
|
||||
@@ -88,6 +95,10 @@ class MonitorService : Service() {
|
||||
private var foregroundStartFailed = false
|
||||
private var injectionComplete = false
|
||||
|
||||
@Volatile
|
||||
private var latestNotificationSettings: NotificationSettings =
|
||||
NotificationSettings(useExtraNotification = false, keepAfterDisconnect = false)
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun promoteToForeground(notification: Notification): Boolean {
|
||||
return try {
|
||||
@@ -188,27 +199,53 @@ class MonitorService : Service() {
|
||||
}
|
||||
|
||||
private suspend fun doMonitor() {
|
||||
// Seed snapshot eagerly so onDestroy() has correct values even if we abort below.
|
||||
latestNotificationSettings = NotificationSettings(
|
||||
useExtraNotification = generalSettings.useExtraMonitorNotification.flow.first(),
|
||||
keepAfterDisconnect = generalSettings.keepConnectedNotificationAfterDisconnect.flow.first(),
|
||||
)
|
||||
|
||||
val permissionsMissingOnStart = permissionTool.missingScanPermissions.first()
|
||||
if (permissionsMissingOnStart.isNotEmpty()) {
|
||||
log(TAG, WARN) { "Aborting, missing scan permissions: $permissionsMissingOnStart" }
|
||||
return
|
||||
}
|
||||
|
||||
val monitorJob = deviceMonitor.primaryDevice()
|
||||
val deviceFlow = deviceMonitor.primaryDevice()
|
||||
.setupCommonEventHandlers(TAG) { "BlePodMonitor" }
|
||||
.distinctUntilChangedBy { it?.toNotificationKey() }
|
||||
.throttleLatest(1000)
|
||||
.onEach { currentDevice ->
|
||||
val useExtraNotification = generalSettings.useExtraMonitorNotification.valueBlocking
|
||||
|
||||
val notificationSettingsFlow = combine(
|
||||
generalSettings.useExtraMonitorNotification.flow,
|
||||
generalSettings.keepConnectedNotificationAfterDisconnect.flow,
|
||||
) { useExtra, keepAfter ->
|
||||
NotificationSettings(useExtraNotification = useExtra, keepAfterDisconnect = keepAfter)
|
||||
}
|
||||
|
||||
val monitorJob = combine(deviceFlow, notificationSettingsFlow) { currentDevice, settings ->
|
||||
currentDevice to settings
|
||||
}
|
||||
.onEach { (currentDevice, settings) ->
|
||||
latestNotificationSettings = settings
|
||||
|
||||
notificationManager.notify(
|
||||
MonitorNotifications.NOTIFICATION_ID,
|
||||
notifications.getNotification(currentDevice, showHint = useExtraNotification),
|
||||
notifications.getNotification(
|
||||
currentDevice,
|
||||
showHint = settings.useExtraNotification,
|
||||
),
|
||||
)
|
||||
if (generalSettings.useExtraMonitorNotification.valueBlocking && currentDevice != null) {
|
||||
notificationManager.notify(
|
||||
|
||||
when (val action = decideExtraNotificationAction(currentDevice, settings)) {
|
||||
is ExtraNotificationAction.Post -> notificationManager.notify(
|
||||
MonitorNotifications.NOTIFICATION_ID_CONNECTED,
|
||||
notifications.getNotificationConnected(currentDevice),
|
||||
notifications.getNotificationConnected(action.device),
|
||||
)
|
||||
ExtraNotificationAction.Cancel -> notificationManager.cancel(
|
||||
MonitorNotifications.NOTIFICATION_ID_CONNECTED
|
||||
)
|
||||
ExtraNotificationAction.KeepExisting -> Unit
|
||||
}
|
||||
}
|
||||
.catch {
|
||||
@@ -224,7 +261,7 @@ class MonitorService : Service() {
|
||||
emptyFlow()
|
||||
} else {
|
||||
combine(
|
||||
generalSettings.monitorMode.flow,
|
||||
monitorModeResolver.effectiveMode,
|
||||
profilesRepo.profiles,
|
||||
bluetoothManager.connectedDevices,
|
||||
aapConnectionManager.allStates,
|
||||
@@ -300,6 +337,28 @@ class MonitorService : Service() {
|
||||
.catch { log(TAG, WARN) { "sleepReaction failed:\n${it.asLog()}" } }
|
||||
.launchIn(monitorScope)
|
||||
|
||||
chargedReaction.monitor()
|
||||
.onEach { event ->
|
||||
when (event) {
|
||||
is ChargedReaction.Event.ShowNotification -> chargedReactionNotifications.show(
|
||||
profileId = event.profileId,
|
||||
deviceLabel = event.deviceLabel,
|
||||
thresholdPercent = event.thresholdPercent,
|
||||
)
|
||||
|
||||
is ChargedReaction.Event.CancelNotification ->
|
||||
chargedReactionNotifications.cancel(event.profileId)
|
||||
}
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "chargedReaction" }
|
||||
.catch { log(TAG, WARN) { "chargedReaction failed:\n${it.asLog()}" } }
|
||||
.launchIn(monitorScope)
|
||||
|
||||
conversationReaction.monitor()
|
||||
.setupCommonEventHandlers(TAG) { "conversationReaction" }
|
||||
.catch { log(TAG, WARN) { "conversationReaction failed:\n${it.asLog()}" } }
|
||||
.launchIn(monitorScope)
|
||||
|
||||
log(TAG, VERBOSE) { "Monitor job is active" }
|
||||
monitorJob.join()
|
||||
log(TAG, VERBOSE) { "Monitor job quit" }
|
||||
@@ -310,7 +369,13 @@ class MonitorService : Service() {
|
||||
monitorScope.cancel("Service destroyed")
|
||||
|
||||
if (injectionComplete) {
|
||||
if (generalSettings.useExtraMonitorNotification.valueBlocking && !generalSettings.keepConnectedNotificationAfterDisconnect.valueBlocking) {
|
||||
try {
|
||||
chargedReactionNotifications.cancelAll()
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "Failed to cancel charged notifications: ${e.message}" }
|
||||
}
|
||||
val snapshot = latestNotificationSettings
|
||||
if (snapshot.useExtraNotification && !snapshot.keepAfterDisconnect) {
|
||||
try {
|
||||
notificationManager.cancel(MonitorNotifications.NOTIFICATION_ID_CONNECTED)
|
||||
} catch (e: Exception) {
|
||||
@@ -363,6 +428,7 @@ private data class NotificationDeviceKey(
|
||||
val profileId: String?,
|
||||
val label: String?,
|
||||
val model: PodModel,
|
||||
val isLive: Boolean,
|
||||
val hasDualPods: Boolean,
|
||||
val hasCase: Boolean,
|
||||
val hasEarDetection: Boolean,
|
||||
@@ -387,6 +453,7 @@ private fun PodDevice.toNotificationKey(): NotificationDeviceKey = NotificationD
|
||||
profileId = profileId,
|
||||
label = label,
|
||||
model = model,
|
||||
isLive = isLive,
|
||||
hasDualPods = hasDualPods,
|
||||
hasCase = hasCase,
|
||||
hasEarDetection = hasEarDetection,
|
||||
@@ -406,3 +473,24 @@ private fun PodDevice.toNotificationKey(): NotificationDeviceKey = NotificationD
|
||||
rightPodIcon = rightPodIcon,
|
||||
caseIcon = caseIcon,
|
||||
)
|
||||
|
||||
internal data class NotificationSettings(
|
||||
val useExtraNotification: Boolean,
|
||||
val keepAfterDisconnect: Boolean,
|
||||
)
|
||||
|
||||
internal sealed interface ExtraNotificationAction {
|
||||
object Cancel : ExtraNotificationAction
|
||||
data class Post(val device: PodDevice) : ExtraNotificationAction
|
||||
object KeepExisting : ExtraNotificationAction
|
||||
}
|
||||
|
||||
internal fun decideExtraNotificationAction(
|
||||
device: PodDevice?,
|
||||
settings: NotificationSettings,
|
||||
): ExtraNotificationAction = when {
|
||||
!settings.useExtraNotification -> ExtraNotificationAction.Cancel
|
||||
device != null && device.isLive -> ExtraNotificationAction.Post(device)
|
||||
settings.keepAfterDisconnect -> ExtraNotificationAction.KeepExisting
|
||||
else -> ExtraNotificationAction.Cancel
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import eu.darken.capod.pods.core.apple.PodModel
|
||||
import eu.darken.capod.pods.core.apple.aap.engine.AapConnection
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapCommand
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapDeviceProfile
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.ConversationAwarenessEvent
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.KeyExchangeResult
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.StemPressEvent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -53,8 +54,8 @@ class AapConnectionManager @Inject constructor(
|
||||
private val _allStates = MutableStateFlow<Map<BluetoothAddress, AapPodState>>(emptyMap())
|
||||
val allStates: StateFlow<Map<BluetoothAddress, AapPodState>> = _allStates.asStateFlow()
|
||||
|
||||
private val _disconnectEvents = MutableSharedFlow<BluetoothAddress>(extraBufferCapacity = 16)
|
||||
val disconnectEvents: SharedFlow<BluetoothAddress> = _disconnectEvents.asSharedFlow()
|
||||
private val _disconnectEvents = MutableSharedFlow<AapDisconnectEvent>(extraBufferCapacity = 16)
|
||||
val disconnectEvents: SharedFlow<AapDisconnectEvent> = _disconnectEvents.asSharedFlow()
|
||||
|
||||
/** Emits when a connection receives private keys (IRK/ENC) from the device. */
|
||||
private val _keysReceived = MutableSharedFlow<Pair<BluetoothAddress, KeyExchangeResult>>(extraBufferCapacity = 16)
|
||||
@@ -73,6 +74,17 @@ class AapConnectionManager @Inject constructor(
|
||||
private val _sleepEvents = MutableSharedFlow<BluetoothAddress>(extraBufferCapacity = 16)
|
||||
val sleepEvents: SharedFlow<BluetoothAddress> = _sleepEvents.asSharedFlow()
|
||||
|
||||
/**
|
||||
* Emits when a connected device reports a Conversational Awareness transition (START / RESUME /
|
||||
* HOLD / STOP, AAP command 0x4B). Paired with the origin address so the conversation reaction
|
||||
* can gate on the primary device. Every well-formed frame is classified and emitted (unknown
|
||||
* status bytes map to HOLD); only structurally malformed frames are dropped (see [AapSessionEngine]).
|
||||
*/
|
||||
private val _conversationalAwarenessEvents =
|
||||
MutableSharedFlow<Pair<BluetoothAddress, ConversationAwarenessEvent>>(extraBufferCapacity = 16)
|
||||
val conversationalAwarenessEvents: SharedFlow<Pair<BluetoothAddress, ConversationAwarenessEvent>> =
|
||||
_conversationalAwarenessEvents.asSharedFlow()
|
||||
|
||||
/** Emits when a SetAncMode(OFF) command was rejected by the device (inferred by the engine). */
|
||||
private val _offRejectedEvents = MutableSharedFlow<BluetoothAddress>(extraBufferCapacity = 16)
|
||||
val offRejectedEvents: SharedFlow<BluetoothAddress> = _offRejectedEvents.asSharedFlow()
|
||||
@@ -140,6 +152,13 @@ class AapConnectionManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
// Forward conversational-awareness speaking transitions from this connection (child coroutine).
|
||||
launch {
|
||||
connection.conversationalAwarenessEvents.collect { event ->
|
||||
_conversationalAwarenessEvents.tryEmit(address to event)
|
||||
}
|
||||
}
|
||||
|
||||
// Forward OFF-rejection events from this connection (child coroutine)
|
||||
launch {
|
||||
connection.offRejected.collect {
|
||||
@@ -169,7 +188,7 @@ class AapConnectionManager @Inject constructor(
|
||||
}
|
||||
|
||||
if (!wasIntentional) {
|
||||
_disconnectEvents.tryEmit(address)
|
||||
_disconnectEvents.tryEmit(AapDisconnectEvent(address, connection.wasEverReady))
|
||||
}
|
||||
|
||||
// End this collector coroutine (also cancels child key-forwarding coroutine)
|
||||
@@ -198,3 +217,13 @@ class AapConnectionManager @Inject constructor(
|
||||
connection.send(command)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted on an unintentional AAP disconnect. [wasEverReady] tells the reconnect layer whether this
|
||||
* session ever completed the handshake (reached READY) — a never-ready disconnect is a failed
|
||||
* handshake/short session and feeds the escalating reconnect backoff; a was-ready drop reconnects promptly.
|
||||
*/
|
||||
data class AapDisconnectEvent(
|
||||
val address: BluetoothAddress,
|
||||
val wasEverReady: Boolean,
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ import eu.darken.capod.pods.core.apple.aap.protocol.AapDeviceProfile
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapFramer
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapPacket
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapSleepEvent
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.ConversationAwarenessEvent
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.KeyExchangeResult
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.StemPressEvent
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
@@ -22,12 +23,14 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.time.Duration
|
||||
@@ -45,19 +48,25 @@ internal class AapConnection(
|
||||
private val socketFactory: L2capSocketFactory,
|
||||
timeSource: TimeSource,
|
||||
private val connectTimeout: Duration = DEFAULT_CONNECT_TIMEOUT,
|
||||
private val handshakeTimeout: Duration = DEFAULT_HANDSHAKE_TIMEOUT,
|
||||
) {
|
||||
|
||||
private val engine = AapSessionEngine(profile, timeSource)
|
||||
|
||||
/** True once this connection's session reached READY at least once. Survives the engine reset. */
|
||||
val wasEverReady: Boolean get() = engine.wasEverReady
|
||||
|
||||
val state: StateFlow<AapPodState> get() = engine.state
|
||||
val keysReceived: SharedFlow<KeyExchangeResult> get() = engine.keysReceived
|
||||
val stemPressEvents: SharedFlow<StemPressEvent> get() = engine.stemPressEvents
|
||||
val sleepEvents: SharedFlow<AapSleepEvent> get() = engine.sleepEvents
|
||||
val conversationalAwarenessEvents: SharedFlow<ConversationAwarenessEvent> get() = engine.conversationalAwarenessEvents
|
||||
val offRejected: SharedFlow<Unit> get() = engine.offRejected
|
||||
val settingRejected: SharedFlow<AapCommand> get() = engine.settingRejected
|
||||
|
||||
private var socket: BluetoothSocket? = null
|
||||
private var readerJob: Job? = null
|
||||
private val disconnected = AtomicBoolean(false)
|
||||
private val writeMutex = Mutex()
|
||||
private val framer = AapFramer()
|
||||
|
||||
@@ -107,6 +116,27 @@ internal class AapConnection(
|
||||
|
||||
// Launch read loop in the provided scope — connect() returns immediately
|
||||
readerJob = scope.launch(Dispatchers.IO) { readLoop(sock) }
|
||||
|
||||
// Handshake watchdog: the socket can connect and the handshake be sent, but if the peer
|
||||
// never replies the engine sits in HANDSHAKING forever (blocking read, no deadline).
|
||||
// Bound it: if neither READY nor DISCONNECTED is reached in time, tear the socket down so
|
||||
// the reconnect path can recover. READY / an earlier DISCONNECTED end the wait with no action.
|
||||
scope.launch {
|
||||
val settled = withTimeoutOrNull(handshakeTimeout) {
|
||||
state.first {
|
||||
it.connectionState == AapPodState.ConnectionState.READY ||
|
||||
it.connectionState == AapPodState.ConnectionState.DISCONNECTED
|
||||
}
|
||||
}
|
||||
// Re-check after the timeout: READY may have landed in the boundary race between
|
||||
// withTimeoutOrNull cancelling and us getting here — don't tear down a live session.
|
||||
if (settled == null && state.value.connectionState != AapPodState.ConnectionState.READY) {
|
||||
log(TAG, Logging.Priority.WARN) {
|
||||
"Handshake timed out after $handshakeTimeout for ${device.address} — disconnecting"
|
||||
}
|
||||
disconnect()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log(TAG, Logging.Priority.ERROR) { "Connection failed: $e" }
|
||||
cleanupSocket()
|
||||
@@ -116,6 +146,9 @@ internal class AapConnection(
|
||||
}
|
||||
|
||||
suspend fun disconnect() = withContext(Dispatchers.IO) {
|
||||
// Idempotent: the handshake watchdog and the manager's DISCONNECTED observer can both reach
|
||||
// here for the same dying session. Run the teardown exactly once.
|
||||
if (!disconnected.compareAndSet(false, true)) return@withContext
|
||||
log(TAG, Logging.Priority.INFO) { "Disconnecting" }
|
||||
readerJob?.cancel()
|
||||
readerJob = null
|
||||
@@ -259,6 +292,13 @@ internal class AapConnection(
|
||||
companion object {
|
||||
private const val PSM = 0x1001
|
||||
internal val DEFAULT_CONNECT_TIMEOUT = 5.seconds
|
||||
|
||||
/**
|
||||
* Upper bound on the post-handshake wait for the first sign of life (READY). Normal
|
||||
* handshakes complete in well under 2s; 10s tolerates a congested 2.4 GHz band before
|
||||
* giving up so the reconnect path can recover instead of wedging in HANDSHAKING forever.
|
||||
*/
|
||||
internal val DEFAULT_HANDSHAKE_TIMEOUT = 10.seconds
|
||||
private val TAG = logTag("AAP", "Connection")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import eu.darken.capod.pods.core.apple.aap.protocol.AapMessageType
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapPacket
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapSleepEvent
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.ConversationAwarenessEvent
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.KeyExchangeResult
|
||||
import eu.darken.capod.pods.core.apple.aap.protocol.StemPressEvent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -54,6 +55,11 @@ internal class AapSessionEngine(
|
||||
MutableSharedFlow<AapSleepEvent>(extraBufferCapacity = 4, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
val sleepEvents: SharedFlow<AapSleepEvent> = _sleepEvents.asSharedFlow()
|
||||
|
||||
private val _conversationalAwarenessEvents =
|
||||
MutableSharedFlow<ConversationAwarenessEvent>(extraBufferCapacity = 8, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
val conversationalAwarenessEvents: SharedFlow<ConversationAwarenessEvent> =
|
||||
_conversationalAwarenessEvents.asSharedFlow()
|
||||
|
||||
private val _offRejected =
|
||||
MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
val offRejected: SharedFlow<Unit> = _offRejected.asSharedFlow()
|
||||
@@ -80,6 +86,15 @@ internal class AapSessionEngine(
|
||||
private var activeSendRaw: (suspend (AapCommand) -> Unit)? = null
|
||||
private var runtimeState = EngineRuntimeState()
|
||||
|
||||
/**
|
||||
* True once this session has reached [AapPodState.ConnectionState.READY] at least once.
|
||||
* Deliberately NOT cleared by [reset] — consumers read it at disconnect time to tell a
|
||||
* working session that dropped apart from one that never completed the handshake (drives
|
||||
* the reconnect backoff). Cleared only when a fresh session starts.
|
||||
*/
|
||||
private var _wasEverReady: Boolean = false
|
||||
val wasEverReady: Boolean get() = _wasEverReady
|
||||
|
||||
fun start(scope: CoroutineScope) {
|
||||
dispatch(AapEngineEvent.SessionStarted(scope))
|
||||
}
|
||||
@@ -124,6 +139,7 @@ internal class AapSessionEngine(
|
||||
when (event) {
|
||||
is AapEngineEvent.SessionStarted -> {
|
||||
scope = event.scope
|
||||
_wasEverReady = false
|
||||
runtimeState = runtimeState.copy(handshakeResponseReceived = false)
|
||||
_state.value = _state.value.copy(connectionState = AapPodState.ConnectionState.CONNECTING)
|
||||
}
|
||||
@@ -153,11 +169,14 @@ internal class AapSessionEngine(
|
||||
private fun handleConnectResponse(packet: AapPacket.ConnectResponse) {
|
||||
if (packet.status != 0) {
|
||||
log(TAG, ERROR) {
|
||||
"ConnectResponse failed: status=0x${"%04X".format(packet.status)} " +
|
||||
"ConnectResponse rejected (status=0x${"%04X".format(packet.status)}) — tearing down: " +
|
||||
"major=${packet.major} minor=${packet.minor} " +
|
||||
"features=0x${"%016X".format(packet.features.toLong())}"
|
||||
}
|
||||
_state.value = _state.value.copy(connectResponseStatus = packet.status)
|
||||
// Hard protocol rejection: don't wait out the handshake watchdog — disconnect now so
|
||||
// the reconnect path can recover (or back off). Nested dispatch mirrors the existing
|
||||
// inbound-update re-dispatch pattern.
|
||||
dispatch(AapEngineEvent.ResetRequested)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -186,6 +205,7 @@ internal class AapSessionEngine(
|
||||
_state.value.connectionState == AapPodState.ConnectionState.HANDSHAKING
|
||||
) {
|
||||
runtimeState = runtimeState.copy(handshakeResponseReceived = true)
|
||||
_wasEverReady = true
|
||||
_state.value = _state.value.copy(connectionState = AapPodState.ConnectionState.READY)
|
||||
log(TAG) { "Connection READY" }
|
||||
}
|
||||
@@ -301,6 +321,13 @@ internal class AapSessionEngine(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Re-emit every (well-formed) Conversational Awareness frame as a classified event for the
|
||||
// conversation reaction: START / RESUME / HOLD / STOP. The decoder already dropped malformed
|
||||
// frames (rawValue stays null only in that case). The raw payload remains logged.
|
||||
if (value is AapSetting.ConversationalAwarenessState) {
|
||||
value.rawValue?.let { _conversationalAwarenessEvents.tryEmit(ConversationAwarenessEvent.fromStatus(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleTimerFired(key: EngineTimerKey) {
|
||||
|
||||
@@ -99,9 +99,18 @@ sealed class AapSetting {
|
||||
val level: Int,
|
||||
) : AapSetting()
|
||||
|
||||
/** Push-only from device — reports speaking detection state (command 0x4B). */
|
||||
/**
|
||||
* Push-only from device — reports speaking detection state (command 0x4B).
|
||||
*
|
||||
* [rawValue] is the status byte: the last byte of the 4-byte `02 00 01 XX` form (or the single
|
||||
* byte of the legacy form), preserved so consumers can classify it. [speaking] is `true` while
|
||||
* the wearer is talking — onset (`1`, `2`) or resumed after a pause (`5`); every other value
|
||||
* (`0`, `3`, `4`, `0x0B`, …) is `false`. START/RESUME/HOLD/STOP classification for the reaction
|
||||
* lives in [ConversationAwarenessEvent].
|
||||
*/
|
||||
data class ConversationalAwarenessState(
|
||||
val speaking: Boolean,
|
||||
val rawValue: Int? = null,
|
||||
) : AapSetting()
|
||||
|
||||
data class MicrophoneMode(
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package eu.darken.capod.pods.core.apple.aap.protocol
|
||||
|
||||
/**
|
||||
* Classified Conversational Awareness signal derived from the status byte of a `0x4B` frame.
|
||||
*
|
||||
* Status-byte mapping, derived from a labelled capture set across AirPods Pro 3 and Pro 2 USB-C
|
||||
* (`protocol-research/conversationalawareness/` — both models share one firmware train and emit
|
||||
* byte-identical sequences). Each scenario was captured with a known action (normal talking,
|
||||
* bursty talking, single-pod, volume-up abort, pod removal/case):
|
||||
* - `1`, `2` → [START] (conversation onset / wind-up → engage the reaction). A conversation always
|
||||
* opens `1` then `2`; re-onset only ever happens after a terminal.
|
||||
* - `5` → [RESUME] (speech resumed after a pause; the wind-down was aborted, CA stays engaged). In
|
||||
* bursty speech the pod cycles `3,5,3,5,…`; `5` is NEVER a terminal. Misreading `5` as a stop was
|
||||
* the root cause of the premature-resume bug — see [ConversationReaction].
|
||||
* - `6`, `8`, `9` → [STOP] (conversation ended → disengage). The usual terminal is the `8`→`9`
|
||||
* pair; `6` is a standalone terminal seen live on Pro 2 USB-C fw `…6814`. Pod removal / case-close
|
||||
* also emit `8`,`9` (sometimes with no prior `1`,`2`).
|
||||
* - any other value (`3` pause, `4` / `0x0B` wind-down, `7` abort, and anything unrecognised)
|
||||
* → [HOLD]: a transitional "possible/real wind-down" frame. The real wind-down runs `3→0x0B→4`
|
||||
* then the `8,9` terminal; `7` precedes an aborted terminal. Unknown values are deliberately
|
||||
* classified as HOLD (arm the safety fuse, never resume immediately) rather than guessed at.
|
||||
*
|
||||
* The pod sends NO frames during active speech — it stays engaged (and silent) for as long as it
|
||||
* hears nearby voices, 20-30s+ observed. So frame-silence must NOT be read as "speaking ended".
|
||||
* A HOLD means the wind-down may have begun; with only ONE pod worn the `8,9` terminal is
|
||||
* deterministically dropped — the flurry ends on `4` (#608, reproduced on Pro 3 and Pro 2) — so
|
||||
* [ConversationReaction] treats a HOLD as "terminal imminent" and arms a short fuse, with a long
|
||||
* stale backstop for a fully-dropped flurry. A RESUME (`5`) cancels that fuse and re-arms the long
|
||||
* backstop; trailing frames after a terminal are ignored once disengaged.
|
||||
*/
|
||||
enum class ConversationAwarenessEvent {
|
||||
START,
|
||||
RESUME,
|
||||
HOLD,
|
||||
STOP,
|
||||
;
|
||||
|
||||
companion object {
|
||||
val SPEAKING_STATUSES = setOf(1, 2)
|
||||
val RESUME_STATUSES = setOf(5)
|
||||
val STOPPED_STATUSES = setOf(6, 8, 9)
|
||||
|
||||
fun fromStatus(status: Int): ConversationAwarenessEvent = when (status) {
|
||||
in SPEAKING_STATUSES -> START
|
||||
in RESUME_STATUSES -> RESUME
|
||||
in STOPPED_STATUSES -> STOP
|
||||
else -> HOLD
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-5
@@ -183,12 +183,27 @@ class DefaultAapDeviceProfile(
|
||||
return AapSetting.PmeConfig::class to AapSetting.PmeConfig(sets)
|
||||
}
|
||||
|
||||
// Conversation Awareness State is a separate command type (push-only)
|
||||
// Conversation Awareness State is a separate command type (push-only). Two payload shapes
|
||||
// are seen: a legacy single byte, and the 4-byte `02 00 01 <status>` form on Pro 3 fw 81.x.
|
||||
// The status (speaking marker) is the last byte in both — but we validate the shape rather
|
||||
// than blindly taking payload.last(), so a truncated/garbled frame can't be misread as a
|
||||
// speaking transition. Unrecognised shapes return null → logged as an unhandled message.
|
||||
if (message.commandType == AapMessageType.CONVERSATIONAL_AWARENESS.value) {
|
||||
if (message.payload.isEmpty()) return null
|
||||
val value = message.payload[0].toInt() and 0xFF
|
||||
val speaking = value == 0x01
|
||||
return AapSetting.ConversationalAwarenessState::class to AapSetting.ConversationalAwarenessState(speaking)
|
||||
val p = message.payload
|
||||
val status = when {
|
||||
p.size == 1 -> p[0].toInt() and 0xFF
|
||||
p.size == 4 &&
|
||||
p[0] == 0x02.toByte() && p[1] == 0x00.toByte() && p[2] == 0x01.toByte() ->
|
||||
p[3].toInt() and 0xFF
|
||||
else -> return null
|
||||
}
|
||||
// speaking = the wearer is currently speaking: onset (1,2) or resumed after a pause (5).
|
||||
// See ConversationAwarenessEvent. (Consumers read rawValue for full classification; this
|
||||
// bool just exposes "is talking now", so a resume must count as speaking too.)
|
||||
val speaking = status in ConversationAwarenessEvent.SPEAKING_STATUSES ||
|
||||
status in ConversationAwarenessEvent.RESUME_STATUSES
|
||||
return AapSetting.ConversationalAwarenessState::class to
|
||||
AapSetting.ConversationalAwarenessState(speaking, rawValue = status)
|
||||
}
|
||||
|
||||
if (message.commandType != AapMessageType.CONTROL.value) return null
|
||||
|
||||
@@ -4,6 +4,14 @@ import eu.darken.capod.common.bluetooth.BleScanResult
|
||||
import eu.darken.capod.pods.core.apple.ble.history.KnownDevice
|
||||
import eu.darken.capod.pods.core.apple.ble.protocol.ProximityMessage
|
||||
import eu.darken.capod.pods.core.apple.ble.protocol.ProximityPayload
|
||||
import java.time.Duration
|
||||
|
||||
/**
|
||||
* How far back to look when recovering the lid state from a recent in-case-pod broadcast.
|
||||
* Time-bounded rather than count-bounded: BLE scan batching means a fixed number of frames is not a
|
||||
* stable time window, and an old reading must not be allowed to resurrect a stale OPEN/CLOSED.
|
||||
*/
|
||||
private val MAX_LID_RECOVERY_AGE: Duration = Duration.ofSeconds(2)
|
||||
|
||||
interface ApplePodsFactory {
|
||||
fun isResponsible(message: ProximityMessage): Boolean
|
||||
@@ -22,7 +30,8 @@ interface ApplePodsFactory {
|
||||
fun KnownDevice.getLatestCaseBattery(): Float? = this.lastCaseBattery
|
||||
|
||||
fun KnownDevice.getLatestCaseLidState(basic: DualApplePods): DualApplePods.LidState? {
|
||||
// A pod broadcasting from inside the case has authoritative case state
|
||||
// A pod broadcasting from inside the case has authoritative case state. Out-of-case frames
|
||||
// (one pod removed) report UNKNOWN via DualApplePods.caseLidState and are skipped here.
|
||||
if (basic.hasCaseContext && basic.caseLidState in setOf(
|
||||
DualApplePods.LidState.OPEN,
|
||||
DualApplePods.LidState.CLOSED,
|
||||
@@ -31,11 +40,14 @@ interface ApplePodsFactory {
|
||||
return basic.caseLidState
|
||||
}
|
||||
|
||||
// Current pod lacks case context (e.g. pod on desk) or reports UNKNOWN.
|
||||
// Check recent history for a sibling broadcast that has case context.
|
||||
val fromCaseContext = history
|
||||
.takeLast(4)
|
||||
// Current pod lacks case context (e.g. pod on desk) or reports UNKNOWN (out-of-case pod's
|
||||
// stale lid byte). Recover the last authoritative reading from a recent in-case broadcast,
|
||||
// bounded by time so a missed CLOSED can't keep a stale OPEN alive across scan gaps.
|
||||
val recentHistory = history
|
||||
.filterIsInstance<DualApplePods>()
|
||||
.filter { Duration.between(it.seenLastAt, basic.seenLastAt).abs() <= MAX_LID_RECOVERY_AGE }
|
||||
|
||||
val fromCaseContext = recentHistory
|
||||
.lastOrNull { it.hasCaseContext && it.caseLidState != DualApplePods.LidState.UNKNOWN }
|
||||
?.caseLidState
|
||||
|
||||
@@ -44,10 +56,8 @@ interface ApplePodsFactory {
|
||||
// No case-context broadcast in recent history — current value is best we have
|
||||
if (basic.caseLidState != DualApplePods.LidState.UNKNOWN) return basic.caseLidState
|
||||
|
||||
// Last resort: any non-UNKNOWN from history
|
||||
return history
|
||||
.takeLast(2)
|
||||
.filterIsInstance<DualApplePods>()
|
||||
// Last resort: any non-UNKNOWN from recent history
|
||||
return recentHistory
|
||||
.lastOrNull { it.caseLidState != DualApplePods.LidState.UNKNOWN }
|
||||
?.caseLidState
|
||||
?: DualApplePods.LidState.NOT_IN_CASE
|
||||
|
||||
@@ -148,7 +148,14 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
|
||||
get() = isThisPodInThecase || isOnePodInCase || areBothPodsInCase
|
||||
|
||||
val caseLidState: LidState
|
||||
get() = LidState.fromRaw(pubCaseLidState, hasCaseContext)
|
||||
get() = LidState.fromRaw(
|
||||
raw = pubCaseLidState,
|
||||
hasCaseContext = hasCaseContext,
|
||||
// The lid bit is only trustworthy from a pod broadcasting inside the case (bit 6), or
|
||||
// while both pods are in the case (bit 2). A bit4-only frame comes from the out-of-case
|
||||
// pod and carries a stale lid byte (see LidState.fromRaw).
|
||||
lidReadingReliable = isThisPodInThecase || areBothPodsInCase,
|
||||
)
|
||||
|
||||
/**
|
||||
* TODO this is glitchy
|
||||
@@ -165,8 +172,20 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
|
||||
UNKNOWN;
|
||||
|
||||
companion object {
|
||||
fun fromRaw(raw: UByte, hasCaseContext: Boolean): LidState {
|
||||
/**
|
||||
* Derives the lid state from the raw lid byte.
|
||||
*
|
||||
* The open/closed bit is only meaningful when broadcast by a pod that is itself inside
|
||||
* the case ([isThisPodInThecase]) or while both pods are in the case ([areBothPodsInCase]).
|
||||
* When only one pod is in the case and the *other*, out-of-case pod is the one
|
||||
* broadcasting (bit4-only), its lid byte is stale and decodes to a phantom OPEN even while
|
||||
* the case is physically shut (verified on AirPods Pro 1 & Pro 3). Such frames must report
|
||||
* [UNKNOWN] so they don't drive case-open reactions; consumers recover the real state from
|
||||
* an in-case-pod broadcast instead.
|
||||
*/
|
||||
fun fromRaw(raw: UByte, hasCaseContext: Boolean, lidReadingReliable: Boolean): LidState {
|
||||
if (!hasCaseContext) return NOT_IN_CASE
|
||||
if (!lidReadingReliable) return UNKNOWN
|
||||
|
||||
return when ((raw.toInt() shr 3) and 0x01) {
|
||||
0 -> OPEN
|
||||
|
||||
@@ -16,6 +16,12 @@ data class KnownDevice(
|
||||
val seenCounter: Int,
|
||||
val history: List<ApplePods>,
|
||||
val lastCaseBattery: Float?,
|
||||
/**
|
||||
* Profile id this device has ever IRK-resolved to, or null if it has only ever been seen as a
|
||||
* keyless device. Durable — kept even when [history] is trimmed — so the weak history-recovery
|
||||
* paths can reliably refuse to hand an IRK-backed identity to a foreign same-model frame.
|
||||
*/
|
||||
val boundProfileId: String? = null,
|
||||
) {
|
||||
val lastPayload: ProximityPayload
|
||||
get() = history.last().payload
|
||||
|
||||
@@ -76,7 +76,8 @@ class PodHistoryRepo @Inject constructor(
|
||||
val caseIgnored = if (current is DualApplePods) {
|
||||
val target = current.getCaseMatchMarkings()
|
||||
knownDevices.values.filter { known ->
|
||||
known.history.filterIsInstance<DualApplePods>().any { it.getCaseMatchMarkings() == target }
|
||||
known.history.filterIsInstance<DualApplePods>().any { it.getCaseMatchMarkings() == target } &&
|
||||
known.isIrkConsistentWith(current)
|
||||
}
|
||||
} else {
|
||||
emptySet()
|
||||
@@ -143,7 +144,7 @@ class PodHistoryRepo @Inject constructor(
|
||||
if (recognizedDevice == null) {
|
||||
val currentMarkers = payload.getFuzzyIdentifier()
|
||||
recognizedDevice = knownDevices.values
|
||||
.firstOrNull { it.lastPayload.getFuzzyIdentifier() == currentMarkers }
|
||||
.firstOrNull { it.lastPayload.getFuzzyIdentifier() == currentMarkers && it.isIrkConsistentWith(current) }
|
||||
?.also { log(TAG, DEBUG) { "search1: Similarity match for device=${current.model}" } }
|
||||
}
|
||||
|
||||
@@ -154,6 +155,24 @@ class PodHistoryRepo @Inject constructor(
|
||||
return recognizedDevice
|
||||
}
|
||||
|
||||
/**
|
||||
* The weak history paths (fuzzy markers, case-ignored markings) match on low-entropy broadcast
|
||||
* data — model + battery nibbles + colour — which a stranger's same-model AirPods at a similar
|
||||
* battery satisfies. Those paths must not let a foreign frame inherit the identity of one of OUR
|
||||
* keyed (IRK-backed) devices, or the foreign snapshot poisons that device's cache slot and the
|
||||
* dashboard card glitches (Symptom B).
|
||||
*
|
||||
* A [KnownDevice] is "IRK-backed" if it has ever resolved against a profile's IRK
|
||||
* ([KnownDevice.boundProfileId], latched durably so history trimming can't erode it). Such a
|
||||
* device may only be claimed via the strong address/IRK paths, or by a current frame that
|
||||
* resolved to the SAME profile. Keyless devices (never IRK-backed) are unaffected — fuzzy
|
||||
* recovery still works for them exactly as before.
|
||||
*/
|
||||
private fun KnownDevice.isIrkConsistentWith(current: ApplePods): Boolean {
|
||||
val bound = boundProfileId ?: return true // not IRK-backed -> keyless, weak match allowed
|
||||
return current.meta.profile?.id == bound
|
||||
}
|
||||
|
||||
fun updateHistory(device: ApplePods) {
|
||||
val existing = knownDevices[device.identifier]
|
||||
|
||||
@@ -162,12 +181,16 @@ class PodHistoryRepo @Inject constructor(
|
||||
.mapNotNull { it.batteryCasePercent }
|
||||
.lastOrNull()
|
||||
|
||||
// Latch the IRK-resolved profile id (never cleared by an unbound frame), so it survives history trimming.
|
||||
val boundProfileId = device.meta.profile?.id?.takeIf { device.meta.isIRKMatch }
|
||||
|
||||
knownDevices[device.identifier] = if (existing != null) {
|
||||
val history = existing.history.plus(device)
|
||||
existing.copy(
|
||||
seenCounter = existing.seenCounter + 1,
|
||||
history = history,
|
||||
lastCaseBattery = history.determineLatestCaseBattery() ?: existing.lastCaseBattery
|
||||
lastCaseBattery = history.determineLatestCaseBattery() ?: existing.lastCaseBattery,
|
||||
boundProfileId = boundProfileId ?: existing.boundProfileId,
|
||||
)
|
||||
} else {
|
||||
log(TAG, DEBUG) { "Creating new history for ${device.logSummary()}" }
|
||||
@@ -177,7 +200,8 @@ class PodHistoryRepo @Inject constructor(
|
||||
seenFirstAt = device.seenFirstAt,
|
||||
seenCounter = 1,
|
||||
history = history,
|
||||
lastCaseBattery = history.determineLatestCaseBattery()
|
||||
lastCaseBattery = history.determineLatestCaseBattery(),
|
||||
boundProfileId = boundProfileId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package eu.darken.capod.pods.core.apple.ble.protocol
|
||||
|
||||
import android.bluetooth.le.ScanFilter
|
||||
import dagger.Reusable
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.DEBUG
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@@ -16,6 +18,17 @@ object ProximityPairing {
|
||||
return null
|
||||
}
|
||||
|
||||
// Pods also emit type 0x07 frames whose payload does not use the plaintext status
|
||||
// format, e.g. prefix 0x07 from the identity address on connect (#603). Their bytes
|
||||
// are garbage at the standard offsets and would create a ghost device.
|
||||
if (message.data.firstOrNull() != PAIRING_MESSAGE_PREFIX) {
|
||||
log(TAG, DEBUG) {
|
||||
val hex = message.data.joinToString(" ") { "%02X".format(it.toInt()) }
|
||||
"Dropping proximity pairing message with unsupported prefix: [$hex]"
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return ProximityMessage(
|
||||
type = message.type,
|
||||
length = message.length,
|
||||
@@ -49,4 +62,9 @@ object ProximityPairing {
|
||||
|
||||
// This is the default message length among official Apple devices, clones may have different length
|
||||
internal const val PAIRING_MESSAGE_LENGTH = 25
|
||||
|
||||
// First payload byte of every known plaintext status broadcast, including pairing mode
|
||||
internal val PAIRING_MESSAGE_PREFIX = 0x01.toUByte()
|
||||
|
||||
private val TAG = logTag("Pod", "Apple", "ProximityPairing")
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import eu.darken.capod.pods.core.apple.PodModel
|
||||
import eu.darken.capod.pods.core.apple.ble.protocol.IdentityResolvingKey
|
||||
import eu.darken.capod.pods.core.apple.ble.protocol.ProximityEncryptionKey
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
|
||||
import eu.darken.capod.reaction.core.charged.ChargedSlotScope
|
||||
import eu.darken.capod.reaction.core.conversation.ConversationAction
|
||||
import eu.darken.capod.reaction.core.stem.StemActionsConfig
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.SerialName
|
||||
@@ -25,11 +27,17 @@ data class AppleDeviceProfile(
|
||||
@SerialName("address") override val address: String? = null,
|
||||
@SerialName("reactionAutoPause") val autoPause: Boolean = false,
|
||||
@SerialName("reactionAutoPlay") val autoPlay: Boolean = false,
|
||||
@SerialName("reactionStartMusicOnWear") val startMusicOnWear: Boolean = false,
|
||||
@SerialName("reactionOnePodMode") val onePodMode: Boolean = false,
|
||||
@SerialName("reactionAutoConnect") val autoConnect: Boolean = false,
|
||||
@SerialName("reactionAutoConnectCondition") val autoConnectCondition: AutoConnectCondition = AutoConnectCondition.WHEN_SEEN,
|
||||
@SerialName("reactionShowPopUpOnCaseOpen") val showPopUpOnCaseOpen: Boolean = false,
|
||||
@SerialName("reactionShowPopUpOnConnection") val showPopUpOnConnection: Boolean = false,
|
||||
@SerialName("reactionConversationAction") val conversationAction: ConversationAction = ConversationAction.NOTHING,
|
||||
@SerialName("reactionConversationVolumeReduction") val conversationVolumeReduction: Int = ReactionConfig.DEFAULT_CONVERSATION_VOLUME_REDUCTION,
|
||||
@SerialName("reactionNotifyWhenCharged") val notifyWhenCharged: Boolean = false,
|
||||
@SerialName("reactionChargedThreshold") val chargedThreshold: Int = ReactionConfig.DEFAULT_CHARGED_THRESHOLD,
|
||||
@SerialName("reactionChargedSlotScope") val chargedSlotScope: ChargedSlotScope = ChargedSlotScope.PODS_AND_CASE,
|
||||
/**
|
||||
* 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
|
||||
@@ -49,11 +57,17 @@ data class AppleDeviceProfile(
|
||||
get() = ReactionConfig(
|
||||
autoPause = autoPause,
|
||||
autoPlay = autoPlay,
|
||||
startMusicOnWear = startMusicOnWear,
|
||||
onePodMode = onePodMode,
|
||||
autoConnect = autoConnect,
|
||||
autoConnectCondition = autoConnectCondition,
|
||||
showPopUpOnCaseOpen = showPopUpOnCaseOpen,
|
||||
showPopUpOnConnection = showPopUpOnConnection,
|
||||
conversationAction = conversationAction,
|
||||
conversationVolumeReduction = conversationVolumeReduction,
|
||||
notifyWhenCharged = notifyWhenCharged,
|
||||
chargedThreshold = chargedThreshold,
|
||||
chargedSlotScope = chargedSlotScope,
|
||||
)
|
||||
|
||||
override fun toString(): String = "AppleDeviceProfile(" +
|
||||
@@ -62,10 +76,16 @@ data class AppleDeviceProfile(
|
||||
"identityKey=${if (identityKey == null) "null" else "<redacted>"}, " +
|
||||
"encryptionKey=${if (encryptionKey == null) "null" else "<redacted>"}, " +
|
||||
"address=$address, autoPause=$autoPause, autoPlay=$autoPlay, " +
|
||||
"startMusicOnWear=$startMusicOnWear, " +
|
||||
"onePodMode=$onePodMode, autoConnect=$autoConnect, " +
|
||||
"autoConnectCondition=$autoConnectCondition, " +
|
||||
"showPopUpOnCaseOpen=$showPopUpOnCaseOpen, " +
|
||||
"showPopUpOnConnection=$showPopUpOnConnection, " +
|
||||
"conversationAction=$conversationAction, " +
|
||||
"conversationVolumeReduction=$conversationVolumeReduction, " +
|
||||
"notifyWhenCharged=$notifyWhenCharged, " +
|
||||
"chargedThreshold=$chargedThreshold, " +
|
||||
"chargedSlotScope=$chargedSlotScope, " +
|
||||
"learnedAllowOffEnabled=$learnedAllowOffEnabled, " +
|
||||
"lastRequestedListeningModeCycleMask=$lastRequestedListeningModeCycleMask, " +
|
||||
"stemActions=$stemActions" +
|
||||
|
||||
@@ -1,16 +1,39 @@
|
||||
package eu.darken.capod.profiles.core
|
||||
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
|
||||
import eu.darken.capod.reaction.core.charged.ChargedSlotScope
|
||||
import eu.darken.capod.reaction.core.conversation.ConversationAction
|
||||
|
||||
data class ReactionConfig(
|
||||
val autoPause: Boolean = false,
|
||||
val autoPlay: Boolean = false,
|
||||
val startMusicOnWear: Boolean = false,
|
||||
val onePodMode: Boolean = false,
|
||||
val autoConnect: Boolean = false,
|
||||
val autoConnectCondition: AutoConnectCondition = AutoConnectCondition.WHEN_SEEN,
|
||||
val showPopUpOnCaseOpen: Boolean = false,
|
||||
val showPopUpOnConnection: Boolean = false,
|
||||
)
|
||||
val conversationAction: ConversationAction = ConversationAction.NOTHING,
|
||||
/** Percentage to lower media volume by when [conversationAction] is LOWER_VOLUME (clamped 10..90 on use). */
|
||||
val conversationVolumeReduction: Int = DEFAULT_CONVERSATION_VOLUME_REDUCTION,
|
||||
val notifyWhenCharged: Boolean = false,
|
||||
/** Battery percentage at which the charged notification fires (clamped 50..100 on use). */
|
||||
val chargedThreshold: Int = DEFAULT_CHARGED_THRESHOLD,
|
||||
val chargedSlotScope: ChargedSlotScope = ChargedSlotScope.PODS_AND_CASE,
|
||||
) {
|
||||
companion object {
|
||||
const val DEFAULT_CHARGED_THRESHOLD = 100
|
||||
const val MIN_CHARGED_THRESHOLD = 50
|
||||
const val MAX_CHARGED_THRESHOLD = 100
|
||||
const val CHARGED_THRESHOLD_STEP = 10
|
||||
|
||||
const val DEFAULT_CONVERSATION_VOLUME_REDUCTION = 50
|
||||
const val MIN_CONVERSATION_VOLUME_REDUCTION = 10
|
||||
|
||||
/** 100% lowers media volume all the way to 0 — i.e. full mute while speaking. */
|
||||
const val MAX_CONVERSATION_VOLUME_REDUCTION = 100
|
||||
}
|
||||
}
|
||||
|
||||
interface HasReactionConfig {
|
||||
val reactionConfig: ReactionConfig
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package eu.darken.capod.reaction.core.autoconnect
|
||||
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.bluetooth.NudgeAvailability
|
||||
import eu.darken.capod.common.bluetooth.NudgeCapabilityStore
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
@@ -26,6 +28,7 @@ import javax.inject.Singleton
|
||||
class AutoConnect @Inject constructor(
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val deviceMonitor: DeviceMonitor,
|
||||
private val nudgeCapabilityStore: NudgeCapabilityStore,
|
||||
) {
|
||||
|
||||
fun monitor(): Flow<Unit> = deviceMonitor.primaryDevice()
|
||||
@@ -36,7 +39,9 @@ class AutoConnect @Inject constructor(
|
||||
combine(
|
||||
bluetoothManager.connectedDevices,
|
||||
deviceMonitor.primaryDevice().filterNotNull().distinctUntilChangedBy {
|
||||
Triple(it.rawDataHex, it.reactions.autoConnectCondition, it.reactions.onePodMode)
|
||||
// Include caseLidState: for the CASE_OPEN condition it is derived from history
|
||||
// and can flip to OPEN while the selected frame's raw bytes are unchanged.
|
||||
listOf(it.rawDataHex, it.reactions.autoConnectCondition, it.reactions.onePodMode, it.caseLidState)
|
||||
},
|
||||
) { connectedDevices, mainDevice ->
|
||||
connectedDevices to mainDevice
|
||||
@@ -98,13 +103,14 @@ class AutoConnect @Inject constructor(
|
||||
return@map
|
||||
}
|
||||
|
||||
if (!bluetoothManager.isNudgeAvailable) {
|
||||
log(TAG, WARN) { "nudgeConnection is not available on this device, skipping" }
|
||||
if (nudgeCapabilityStore.availability.value == NudgeAvailability.BROKEN) {
|
||||
log(TAG, WARN) { "nudgeConnection is known broken on this device, skipping" }
|
||||
return@map
|
||||
}
|
||||
|
||||
val result = bluetoothManager.nudgeConnection(bondedDevice)
|
||||
log(TAG) { "nudgeConnection($bondedDevice) returned $result" }
|
||||
nudgeCapabilityStore.record(result)
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "monitor" }
|
||||
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
package eu.darken.capod.reaction.core.charged
|
||||
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
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.monitor.core.devicesWithProfiles
|
||||
import eu.darken.capod.pods.core.apple.aap.AapPodState
|
||||
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.DualApplePods
|
||||
import eu.darken.capod.pods.core.apple.ble.SingleBlePodSnapshot
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.HasCase
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.HasChargeDetection
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.HasChargeDetectionDual
|
||||
import eu.darken.capod.profiles.core.AppleDeviceProfile
|
||||
import eu.darken.capod.profiles.core.DeviceProfilesRepo
|
||||
import eu.darken.capod.profiles.core.ReactionConfig
|
||||
import eu.darken.capod.reaction.core.charged.ChargingSessionStateMachine.Input
|
||||
import eu.darken.capod.reaction.core.charged.ChargingSessionStateMachine.Output
|
||||
import eu.darken.capod.reaction.core.charged.ChargingSessionStateMachine.Slot
|
||||
import eu.darken.capod.reaction.core.charged.ChargingSessionStateMachine.SlotData
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ChargedReaction @Inject constructor(
|
||||
private val deviceMonitor: DeviceMonitor,
|
||||
private val profilesRepo: DeviceProfilesRepo,
|
||||
) {
|
||||
|
||||
sealed class Event {
|
||||
data class ShowNotification(
|
||||
val profileId: String,
|
||||
val deviceLabel: String,
|
||||
val thresholdPercent: Int,
|
||||
) : Event()
|
||||
|
||||
data class CancelNotification(val profileId: String) : Event()
|
||||
}
|
||||
|
||||
private data class ChargedConfig(val scope: ChargedSlotScope, val thresholdPercent: Int)
|
||||
|
||||
fun monitor(): Flow<Event> = flow {
|
||||
// Machines live in the collection scope: a monitor (service) restart starts fresh sessions.
|
||||
val machines = mutableMapOf<String, ChargingSessionStateMachine>()
|
||||
val configs = mutableMapOf<String, ChargedConfig>()
|
||||
combine(
|
||||
profilesRepo.profiles,
|
||||
deviceMonitor.devicesWithProfiles(),
|
||||
) { profiles, devices -> profiles.filterIsInstance<AppleDeviceProfile>() to devices }
|
||||
.collect { (profiles, devices) ->
|
||||
val enabled = profiles.filter { it.notifyWhenCharged }.associateBy { it.id }
|
||||
|
||||
// Profile deleted or toggle disabled → reset; a missing device snapshot is
|
||||
// handled below as staleness, never as a reset.
|
||||
machines.keys.filter { it !in enabled }.forEach { profileId ->
|
||||
val output = machines.remove(profileId)!!.process(Input.Reset)
|
||||
configs.remove(profileId)
|
||||
if (output == Output.CANCEL) emit(Event.CancelNotification(profileId))
|
||||
}
|
||||
|
||||
for (profile in enabled.values) {
|
||||
val machine = machines.getOrPut(profile.id) { ChargingSessionStateMachine() }
|
||||
val thresholdPercent = profile.chargedThreshold.coerceIn(
|
||||
ReactionConfig.MIN_CHARGED_THRESHOLD,
|
||||
ReactionConfig.MAX_CHARGED_THRESHOLD,
|
||||
)
|
||||
// A persisted CASE scope on a caseless model (restored backup, model change)
|
||||
// would otherwise produce an eternally-empty slot set.
|
||||
val scope = if (profile.model.features.hasCase) profile.chargedSlotScope else ChargedSlotScope.PODS
|
||||
val config = ChargedConfig(scope, thresholdPercent)
|
||||
val previousConfig = configs.put(profile.id, config)
|
||||
if (previousConfig != null && previousConfig != config) {
|
||||
// Scope/threshold changed: stale highwater slots and an already-shown
|
||||
// notification don't fit the new settings — start evaluation over.
|
||||
log(TAG, INFO) { "Settings changed for ${profile.id} ($previousConfig -> $config), resetting" }
|
||||
if (machine.process(Input.Reset) == Output.CANCEL) {
|
||||
emit(Event.CancelNotification(profile.id))
|
||||
}
|
||||
}
|
||||
val device = devices.firstOrNull { it.profileId == profile.id }
|
||||
val slots = device?.liveChargingSlots(scope).orEmpty()
|
||||
// No live slot data (device gone, lid closed, or AAP hasn't pushed battery
|
||||
// yet) is treated as staleness — the session suspends instead of resetting.
|
||||
val input = if (device == null || slots.isEmpty()) {
|
||||
Input.StaleUpdate
|
||||
} else {
|
||||
Input.LiveUpdate(
|
||||
slots = slots,
|
||||
threshold = thresholdPercent / 100f,
|
||||
activity = device.chargingActivity(),
|
||||
)
|
||||
}
|
||||
val phaseBefore = machine.phase
|
||||
val output = machine.process(input)
|
||||
if (machine.phase != phaseBefore) {
|
||||
log(TAG, VERBOSE) {
|
||||
"${profile.id}: $phaseBefore -> ${machine.phase} (output=$output, input=$input)"
|
||||
}
|
||||
}
|
||||
when (output) {
|
||||
Output.SHOW -> {
|
||||
val label = device?.label ?: profile.label
|
||||
log(TAG, INFO) { "Charge complete for ${profile.id} ($label) at $thresholdPercent%" }
|
||||
emit(Event.ShowNotification(profile.id, label, thresholdPercent))
|
||||
}
|
||||
|
||||
Output.CANCEL -> {
|
||||
log(TAG, INFO) { "Charging session over for ${profile.id}, cancelling notification" }
|
||||
emit(Event.CancelNotification(profile.id))
|
||||
}
|
||||
|
||||
Output.NONE -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "chargedReaction" }
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reaction", "Charged")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-slot live readings only — AAP first, then live BLE. The [PodDevice] facade getters are
|
||||
* deliberately bypassed because they silently fall back to the disk cache; stale cached values
|
||||
* must never start, complete, or end a charging session.
|
||||
*/
|
||||
/**
|
||||
* Signals that the user is handling the device, used to dismiss an already-shown notification.
|
||||
* All sources are live (ear detection: AAP/BLE; lid: BLE; DISCONNECTED slots: AAP) — the cache
|
||||
* never feeds in here.
|
||||
*/
|
||||
internal fun PodDevice.chargingActivity(): ChargingSessionStateMachine.Activity =
|
||||
ChargingSessionStateMachine.Activity(
|
||||
// Only trust AAP ear detection — BLE ear bits are phantom for in-case pods (see
|
||||
// PodDevice.hasAapEarDetection). Unknown when there's no AAP session, which is the
|
||||
// normal state while charging in the case, so phantom "worn" never dismisses.
|
||||
wornSlots = when {
|
||||
!hasAapEarDetection -> null
|
||||
hasDualPods -> buildSet {
|
||||
if (isLeftInEar == true) add(Slot.LEFT)
|
||||
if (isRightInEar == true) add(Slot.RIGHT)
|
||||
}
|
||||
else -> if (isBeingWorn == true) setOf(Slot.HEADSET) else emptySet()
|
||||
},
|
||||
lid = when (caseLidState) {
|
||||
DualApplePods.LidState.OPEN -> ChargingSessionStateMachine.Lid.OPEN
|
||||
DualApplePods.LidState.CLOSED -> ChargingSessionStateMachine.Lid.CLOSED
|
||||
DualApplePods.LidState.NOT_IN_CASE -> ChargingSessionStateMachine.Lid.NOT_IN_CASE
|
||||
else -> null
|
||||
},
|
||||
disconnectedSlots = buildSet {
|
||||
if (leftPodChargingState == AapPodState.ChargingState.DISCONNECTED) add(Slot.LEFT)
|
||||
if (rightPodChargingState == AapPodState.ChargingState.DISCONNECTED) add(Slot.RIGHT)
|
||||
if (caseChargingState == AapPodState.ChargingState.DISCONNECTED) add(Slot.CASE)
|
||||
if (headsetChargingState == AapPodState.ChargingState.DISCONNECTED) add(Slot.HEADSET)
|
||||
},
|
||||
)
|
||||
|
||||
internal fun PodDevice.liveChargingSlots(scope: ChargedSlotScope): Map<Slot, SlotData> {
|
||||
val slots = mutableMapOf<Slot, SlotData>()
|
||||
fun add(slot: Slot, battery: Float?, charging: Boolean?) {
|
||||
if (battery == null || battery < 0f || charging == null) return
|
||||
slots[slot] = SlotData(battery = battery, isCharging = charging)
|
||||
}
|
||||
if (scope != ChargedSlotScope.CASE) {
|
||||
add(
|
||||
Slot.LEFT,
|
||||
aap?.batteryLeft ?: (ble as? DualBlePodSnapshot)?.batteryLeftPodPercent,
|
||||
aap?.isLeftCharging ?: (ble as? HasChargeDetectionDual)?.isLeftPodCharging,
|
||||
)
|
||||
add(
|
||||
Slot.RIGHT,
|
||||
aap?.batteryRight ?: (ble as? DualBlePodSnapshot)?.batteryRightPodPercent,
|
||||
aap?.isRightCharging ?: (ble as? HasChargeDetectionDual)?.isRightPodCharging,
|
||||
)
|
||||
add(
|
||||
Slot.HEADSET,
|
||||
aap?.batteryHeadset ?: (ble as? SingleBlePodSnapshot)?.batteryHeadsetPercent,
|
||||
aap?.isHeadsetCharging ?: (ble as? HasChargeDetection)?.isHeadsetBeingCharged,
|
||||
)
|
||||
}
|
||||
if (scope != ChargedSlotScope.PODS) {
|
||||
add(
|
||||
Slot.CASE,
|
||||
aap?.batteryCase ?: (ble as? HasCase)?.batteryCasePercent,
|
||||
aap?.isCaseCharging ?: (ble as? HasCase)?.isCaseCharging,
|
||||
)
|
||||
}
|
||||
return slots
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package eu.darken.capod.reaction.core.charged
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationCompat
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.BuildConfigWrap
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.notifications.PendingIntentCompat
|
||||
import eu.darken.capod.main.ui.MainActivity
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ChargedReactionNotifications @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val notificationManager: NotificationManager,
|
||||
) {
|
||||
|
||||
init {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
context.getString(R.string.reaction_charged_channel_label),
|
||||
NotificationManager.IMPORTANCE_LOW,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun show(profileId: String, deviceLabel: String, thresholdPercent: Int) {
|
||||
if (!notificationManager.areNotificationsEnabled()) {
|
||||
log(TAG, WARN) { "Notifications disabled — charged notification suppressed" }
|
||||
return
|
||||
}
|
||||
val openPi = PendingIntent.getActivity(
|
||||
context,
|
||||
PENDING_INTENT_REQUEST_CODE,
|
||||
Intent(context, MainActivity::class.java),
|
||||
PendingIntentCompat.FLAG_IMMUTABLE,
|
||||
)
|
||||
val text = if (thresholdPercent >= 100) {
|
||||
context.getString(R.string.reaction_charged_notification_text_full, deviceLabel)
|
||||
} else {
|
||||
context.getString(R.string.reaction_charged_notification_text_partial, deviceLabel, thresholdPercent)
|
||||
}
|
||||
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.device_earbuds_generic_both)
|
||||
.setContentTitle(context.getString(R.string.reaction_charged_notification_title))
|
||||
.setContentText(text)
|
||||
.setStyle(NotificationCompat.BigTextStyle().bigText(text))
|
||||
.setContentIntent(openPi)
|
||||
.setAutoCancel(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.build()
|
||||
notificationManager.notify(profileId.toTag(), NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
fun cancel(profileId: String) {
|
||||
notificationManager.cancel(profileId.toTag(), NOTIFICATION_ID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops every charged notification still on screen. Used when monitoring stops — without a
|
||||
* running monitor the auto-dismiss-on-unplug promise can't be kept, so don't leave them up.
|
||||
*/
|
||||
fun cancelAll() {
|
||||
notificationManager.activeNotifications
|
||||
.filter { it.id == NOTIFICATION_ID && it.tag?.startsWith(TAG_PREFIX) == true }
|
||||
.forEach { notificationManager.cancel(it.tag, it.id) }
|
||||
}
|
||||
|
||||
private fun String.toTag() = "$TAG_PREFIX$this"
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reaction", "Charged", "Notifications")
|
||||
private val CHANNEL_ID = "${BuildConfigWrap.APPLICATION_ID}.notification.channel.reaction.charged"
|
||||
private const val NOTIFICATION_ID = 4
|
||||
private const val TAG_PREFIX = "charged:"
|
||||
private const val PENDING_INTENT_REQUEST_CODE = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package eu.darken.capod.reaction.core.charged
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import eu.darken.capod.R
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/** Which battery slots the charged notification watches. */
|
||||
@Serializable
|
||||
enum class ChargedSlotScope(
|
||||
@StringRes val labelRes: Int,
|
||||
) {
|
||||
@SerialName("charged.scope.pods") PODS(R.string.settings_charged_scope_pods_label),
|
||||
@SerialName("charged.scope.case") CASE(R.string.settings_charged_scope_case_label),
|
||||
@SerialName("charged.scope.podsandcase") PODS_AND_CASE(R.string.settings_charged_scope_both_label),
|
||||
}
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
package eu.darken.capod.reaction.core.charged
|
||||
|
||||
/**
|
||||
* Tracks one charging session for one profile and decides when the "charged" notification
|
||||
* should be shown or cancelled. Pure Kotlin, no Android/DI/Flow dependencies; instances are
|
||||
* driven sequentially from a single coroutine (no internal locking).
|
||||
*
|
||||
* A session starts when any slot reports charging and collects every slot that charges while
|
||||
* the session runs. Per-slot battery highwater marks decide completion: a slot that stops
|
||||
* charging at/above the threshold finished naturally (firmware flips the charging flag off at
|
||||
* 100%), while a slot that stops below the threshold was genuinely unplugged and resets the
|
||||
* session. Stale data suspends the session instead of resetting it — brief BLE gaps while the
|
||||
* device sits on the charger must not lose progress.
|
||||
*
|
||||
* After firing, the notification is taken down again when the user demonstrably reacted to it:
|
||||
* battery discharging below the threshold, a new charging session starting, or any activity
|
||||
* signal showing the pods are being handled (worn-pod set changed, pod removed from the case,
|
||||
* case lid moved). Activity is judged against baselines captured at fire time so pre-existing
|
||||
* state doesn't dismiss — only a change does:
|
||||
* - Worn pods and DISCONNECTED slots baseline on the firing frame. They only apply to
|
||||
* sessions containing pod slots — wearing pods says nothing about a case-only charge.
|
||||
* - The lid signal baselines on the UNION of values seen before the fire (this session plus
|
||||
* a few frames leading into it). With one pod worn and one charging, both pods broadcast
|
||||
* and the monitor's dedup alternates between their frames (OPEN from the in-case pod,
|
||||
* NOT_IN_CASE from the worn pod) — a single-frame baseline would read that steady flapping
|
||||
* as activity. Lid movement is a handling signal for any session, including case-only.
|
||||
*
|
||||
* An activity dismissal latches into [Phase.DISMISSED] so a still-full, still-charging device
|
||||
* can't immediately re-fire.
|
||||
*/
|
||||
class ChargingSessionStateMachine {
|
||||
|
||||
enum class Slot { LEFT, RIGHT, CASE, HEADSET }
|
||||
|
||||
/** [battery] is a fraction 0..1, matching the monitor layer's battery values. */
|
||||
data class SlotData(val battery: Float, val isCharging: Boolean)
|
||||
|
||||
/**
|
||||
* Case posture from the BLE lid byte. NOT_IN_CASE isn't a lid position — it means the
|
||||
* broadcasting pod is physically outside the case, which is just as much a handling
|
||||
* signal as a lid movement (it's the only BLE-side trace of "pod taken out").
|
||||
*/
|
||||
enum class Lid { OPEN, CLOSED, NOT_IN_CASE }
|
||||
|
||||
/** Signals that the user is physically handling the device. All optional/unknown-safe. */
|
||||
data class Activity(
|
||||
/** Pods currently worn (per-side); null when no ear data at all. */
|
||||
val wornSlots: Set<Slot>? = null,
|
||||
val lid: Lid? = null,
|
||||
/** Slots physically absent from the case (AAP ChargingState.DISCONNECTED). */
|
||||
val disconnectedSlots: Set<Slot> = emptySet(),
|
||||
)
|
||||
|
||||
sealed interface Input {
|
||||
/**
|
||||
* Fresh data from a live source (BLE/AAP). [slots] only contains slots with live
|
||||
* readings — cached values must never be fed in here. [threshold] is a fraction 0..1.
|
||||
*/
|
||||
data class LiveUpdate(
|
||||
val slots: Map<Slot, SlotData>,
|
||||
val threshold: Float,
|
||||
val activity: Activity = Activity(),
|
||||
) : Input
|
||||
|
||||
/** Device has no live data right now (out of range, lid closed, cache-only). */
|
||||
data object StaleUpdate : Input
|
||||
|
||||
/** Toggle disabled, settings changed, or profile deleted. */
|
||||
data object Reset : Input
|
||||
}
|
||||
|
||||
enum class Output { NONE, SHOW, CANCEL }
|
||||
|
||||
enum class Phase { IDLE, CHARGING, FIRED, DISMISSED, SUSPENDED }
|
||||
|
||||
var phase: Phase = Phase.IDLE
|
||||
private set
|
||||
|
||||
/** Phase to resume into when live data returns; only meaningful while SUSPENDED. */
|
||||
private var suspendedFrom: Phase = Phase.IDLE
|
||||
|
||||
/** Battery highwater per slot that joined the session. */
|
||||
private val highwater = mutableMapOf<Slot, Float>()
|
||||
|
||||
/**
|
||||
* Rolling window of the most recent lid observations across all phases. Seeds the at-fire
|
||||
* baseline so the OPEN/NOT_IN_CASE alternation (worn pod broadcasts NOT_IN_CASE while the
|
||||
* in-case pod broadcasts OPEN; the monitor's dedup serves them in turn) is absorbed — even
|
||||
* for an instant fire right after a settings-change reset, since the window is never cleared.
|
||||
*/
|
||||
private val recentLids = ArrayDeque<Lid>()
|
||||
|
||||
// Activity baselines frozen at fire; see class docs.
|
||||
private val baselineLids = mutableSetOf<Lid>()
|
||||
private var baselineWorn: Set<Slot>? = null
|
||||
private var baselineDisconnected: Set<Slot> = emptySet()
|
||||
|
||||
fun process(input: Input): Output {
|
||||
if (input is Input.LiveUpdate) input.activity.lid?.let(::recordRecentLid)
|
||||
return when (phase) {
|
||||
Phase.IDLE -> processIdle(input)
|
||||
Phase.CHARGING -> processCharging(input)
|
||||
Phase.FIRED -> processFired(input)
|
||||
Phase.DISMISSED -> processDismissed(input)
|
||||
Phase.SUSPENDED -> processSuspended(input)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processIdle(input: Input): Output {
|
||||
if (input !is Input.LiveUpdate) return Output.NONE
|
||||
val charging = input.slots.filterValues { it.isCharging }
|
||||
if (charging.isEmpty()) return Output.NONE
|
||||
highwater.clear()
|
||||
charging.forEach { (slot, data) -> highwater[slot] = data.battery }
|
||||
phase = Phase.CHARGING
|
||||
return fireIfComplete(input)
|
||||
}
|
||||
|
||||
private fun processCharging(input: Input): Output = when (input) {
|
||||
is Input.Reset -> reset(cancel = false)
|
||||
is Input.StaleUpdate -> suspend(Phase.CHARGING)
|
||||
is Input.LiveUpdate -> {
|
||||
// Charging slots join the session (e.g. case plugged in later); slots already in
|
||||
// it advance their mark; slots absent from this update stay frozen.
|
||||
input.slots.forEach { (slot, data) ->
|
||||
if (data.isCharging || slot in highwater) {
|
||||
highwater[slot] = maxOf(highwater[slot] ?: 0f, data.battery)
|
||||
}
|
||||
}
|
||||
val unplugged = highwater.any { (slot, mark) ->
|
||||
input.slots[slot]?.isCharging == false && mark < input.threshold
|
||||
}
|
||||
if (unplugged) reset(cancel = false) else fireIfComplete(input)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processFired(input: Input): Output = when (input) {
|
||||
is Input.Reset -> reset(cancel = true)
|
||||
is Input.StaleUpdate -> suspend(Phase.FIRED)
|
||||
is Input.LiveUpdate -> when {
|
||||
// "All slots stopped charging" alone is NOT an unplug signal — firmware flips the
|
||||
// charging flag off at 100% while still on power. Slots that never charged this
|
||||
// session (e.g. an idle half-empty case) carry no signal either. The session ends
|
||||
// on: a new charge context, discharge below threshold, or user activity.
|
||||
isNewChargeContext(input) -> {
|
||||
// Only CANCEL is reported now; the next update finds IDLE and starts (and, if
|
||||
// the data already satisfies the threshold, fires) the new session.
|
||||
phase = Phase.IDLE
|
||||
highwater.clear()
|
||||
Output.CANCEL
|
||||
}
|
||||
|
||||
isSessionDischarged(input) -> reset(cancel = true)
|
||||
|
||||
isActivityDetected(input.activity) -> {
|
||||
phase = Phase.DISMISSED
|
||||
Output.CANCEL
|
||||
}
|
||||
|
||||
else -> Output.NONE
|
||||
}
|
||||
}
|
||||
|
||||
/** Like FIRED, but the notification is already gone — same exits, silent. */
|
||||
private fun processDismissed(input: Input): Output = when (input) {
|
||||
is Input.Reset -> reset(cancel = false)
|
||||
is Input.StaleUpdate -> suspend(Phase.DISMISSED)
|
||||
is Input.LiveUpdate -> when {
|
||||
isNewChargeContext(input) -> {
|
||||
// Nothing to cancel, so unlike FIRED the new session can start right away.
|
||||
phase = Phase.IDLE
|
||||
highwater.clear()
|
||||
processIdle(input)
|
||||
}
|
||||
|
||||
isSessionDischarged(input) -> reset(cancel = false)
|
||||
|
||||
else -> Output.NONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun processSuspended(input: Input): Output = when (input) {
|
||||
is Input.Reset -> reset(cancel = suspendedFrom == Phase.FIRED)
|
||||
is Input.StaleUpdate -> Output.NONE
|
||||
is Input.LiveUpdate -> {
|
||||
phase = suspendedFrom
|
||||
if (phase == Phase.CHARGING && hasRegressed(input)) {
|
||||
// Battery dropped well below a session mark while we were blind — the device
|
||||
// was unplugged and used in the meantime. Start over with the fresh data.
|
||||
phase = Phase.IDLE
|
||||
highwater.clear()
|
||||
processIdle(input)
|
||||
} else {
|
||||
process(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Something is charging below the threshold — a fresh charging session is beginning. */
|
||||
private fun isNewChargeContext(input: Input.LiveUpdate): Boolean =
|
||||
input.slots.any { it.value.isCharging && it.value.battery < input.threshold }
|
||||
|
||||
/** A session slot is off power and drained below the threshold — device is in use. */
|
||||
private fun isSessionDischarged(input: Input.LiveUpdate): Boolean = highwater.keys.any { slot ->
|
||||
input.slots[slot]?.let { !it.isCharging && it.battery < input.threshold } == true
|
||||
}
|
||||
|
||||
private fun isActivityDetected(activity: Activity): Boolean {
|
||||
// Worn pods and pod removal only speak about pod slots; a case-only session ignores them.
|
||||
if (highwater.keys.any { it != Slot.CASE }) {
|
||||
activity.wornSlots?.let { worn ->
|
||||
when (val baseline = baselineWorn) {
|
||||
// No ear data at fire time: pods can't be worn while charging in the case,
|
||||
// so any worn pod is activity — only "nothing worn" is adoptable as baseline.
|
||||
null -> if (worn.isNotEmpty()) return true else baselineWorn = emptySet()
|
||||
else -> if (worn != baseline) return true
|
||||
}
|
||||
}
|
||||
val newlyDisconnected = activity.disconnectedSlots.any {
|
||||
it != Slot.CASE && it in highwater && it !in baselineDisconnected
|
||||
}
|
||||
if (newlyDisconnected) return true
|
||||
}
|
||||
activity.lid?.let { lid ->
|
||||
if (baselineLids.isEmpty()) {
|
||||
// Lid never observed pre-fire: first sighting is the baseline — it was most
|
||||
// likely in that posture all along (open is the normal charging posture).
|
||||
baselineLids.add(lid)
|
||||
} else if (lid !in baselineLids) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun hasRegressed(input: Input.LiveUpdate): Boolean = highwater.any { (slot, mark) ->
|
||||
input.slots[slot]?.let { it.battery < mark - REGRESSION_TOLERANCE } == true
|
||||
}
|
||||
|
||||
private fun recordRecentLid(lid: Lid) {
|
||||
recentLids.addLast(lid)
|
||||
while (recentLids.size > RECENT_LID_FRAMES) recentLids.removeFirst()
|
||||
}
|
||||
|
||||
private fun fireIfComplete(input: Input.LiveUpdate): Output {
|
||||
if (highwater.isEmpty() || highwater.any { it.value < input.threshold }) return Output.NONE
|
||||
phase = Phase.FIRED
|
||||
baselineLids.clear()
|
||||
baselineLids += recentLids
|
||||
baselineWorn = input.activity.wornSlots
|
||||
baselineDisconnected = input.activity.disconnectedSlots.intersect(highwater.keys)
|
||||
return Output.SHOW
|
||||
}
|
||||
|
||||
private fun reset(cancel: Boolean): Output {
|
||||
phase = Phase.IDLE
|
||||
highwater.clear()
|
||||
// recentLids is an ambient rolling window — intentionally kept across resets so an
|
||||
// instant re-fire after a settings change still has the recent lid alternation.
|
||||
baselineLids.clear()
|
||||
baselineWorn = null
|
||||
baselineDisconnected = emptySet()
|
||||
return if (cancel) Output.CANCEL else Output.NONE
|
||||
}
|
||||
|
||||
private fun suspend(from: Phase): Output {
|
||||
suspendedFrom = from
|
||||
phase = Phase.SUSPENDED
|
||||
return Output.NONE
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Battery drop (fraction) below a slot's highwater mark that counts as a real
|
||||
* regression on resume. Public BLE reports in 10% steps, so a single-step flicker
|
||||
* (exactly 0.1) must not reset the session.
|
||||
*/
|
||||
private const val REGRESSION_TOLERANCE = 0.1f
|
||||
|
||||
/**
|
||||
* Pre-fire lid observations kept as baseline context. At the observed 0.5-1.5s
|
||||
* emission cadence this is a few seconds — enough to capture dedup alternation
|
||||
* leading into an instant fire without dragging in long-stale postures.
|
||||
*/
|
||||
private const val RECENT_LID_FRAMES = 8
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package eu.darken.capod.reaction.core.conversation
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import eu.darken.capod.R
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* What CAPod does when the pods report that the wearer started speaking (Conversational
|
||||
* Awareness). On Android the firmware does not duck audio on its own, so CAPod performs the
|
||||
* reaction itself. The action is reverted when speaking stops.
|
||||
*
|
||||
* Extend by adding entries — the persisted [SerialName] strings are stable identifiers.
|
||||
*/
|
||||
@Serializable
|
||||
enum class ConversationAction(
|
||||
val identifier: String,
|
||||
@StringRes val labelRes: Int,
|
||||
) {
|
||||
@SerialName("conversation.action.nothing")
|
||||
NOTHING("conversation.action.nothing", R.string.settings_conversation_action_nothing_label),
|
||||
|
||||
@SerialName("conversation.action.lower_volume")
|
||||
LOWER_VOLUME("conversation.action.lower_volume", R.string.settings_conversation_action_lower_volume_label),
|
||||
|
||||
@SerialName("conversation.action.pause")
|
||||
PAUSE("conversation.action.pause", R.string.settings_conversation_action_pause_label),
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
package eu.darken.capod.reaction.core.conversation
|
||||
|
||||
import eu.darken.capod.common.MediaControl
|
||||
import eu.darken.capod.common.TimeSource
|
||||
import eu.darken.capod.common.bluetooth.BluetoothAddress
|
||||
import eu.darken.capod.common.coroutine.AppScope
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
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.monitor.core.primaryDevice
|
||||
import eu.darken.capod.pods.core.apple.aap.AapConnectionManager
|
||||
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.aap.protocol.ConversationAwarenessEvent
|
||||
import eu.darken.capod.profiles.core.ReactionConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Reacts to Conversational Awareness speaking transitions (AAP `0x4B`) by either lowering media
|
||||
* volume or pausing, per the primary device's [ReactionConfig.conversationAction], and reverts when
|
||||
* speaking stops. On Android the pod firmware does not duck audio itself, so CAPod performs it.
|
||||
*
|
||||
* Disengage is driven by the pod's explicit end-of-speech frame ([ConversationAwarenessEvent.STOP],
|
||||
* status `8,9`). The pod sends NO frames during active speech — it stays engaged for as long as it
|
||||
* hears nearby voices (observed: CA held engaged 21-32s with zero `0x4B` frames, indefinitely
|
||||
* against ambient noise). So frame-silence must NOT be read as "speaking ended"; after a START only
|
||||
* the long [STALE_TIMEOUT] backstop applies, and a link drop is handled by the owner-disconnect revert.
|
||||
*
|
||||
* A [ConversationAwarenessEvent.HOLD] frame (`3` pause, `0x0B`/`4` wind-down, `7` abort) is evidence
|
||||
* the wind-down may have begun. The real wind-down is a short flurry `3→0x0B→4` then the `8,9`
|
||||
* terminal — but the terminal is sometimes dropped entirely (single-pod wear: fw `…6589`/`…6861`
|
||||
* emitted `3,0xB,4` then nothing, stranding the volume low — #608). So a HOLD frame re-arms the
|
||||
* timer with the short [WIND_DOWN_TIMEOUT] fuse: if no terminal (or resume) follows, speech is
|
||||
* treated as ended anyway.
|
||||
*
|
||||
* A [ConversationAwarenessEvent.RESUME] frame (`5`) means speech resumed after a pause — in bursty
|
||||
* talking the pod cycles `3,5,3,5,…` while CA stays engaged. It is NOT a terminal: it cancels any
|
||||
* armed wind-down fuse and re-arms the long backstop, so media stays paused/ducked through the whole
|
||||
* conversation. (Treating `5` as a stop was the premature-resume + stuck bug.)
|
||||
*
|
||||
* Removing/inserting a pod makes the firmware re-key CA — it emits a terminal then a fresh onset
|
||||
* around the physical change even though the conversation continues. A terminal arriving within
|
||||
* [EAR_TRANSITION_WINDOW] of an AAP ear-detection change is therefore treated as a re-key artifact:
|
||||
* it defers to the wind-down fuse instead of disengaging, so a brief pod removal mid-conversation
|
||||
* neither strands a pause nor causes a duck→restore→re-duck volume blip.
|
||||
*
|
||||
* State is a single global slot (media volume / playback is system-wide, not per-device) guarded by
|
||||
* a [Mutex] — events, AAP-state-removal, the stale timer, and monitor completion all mutate it.
|
||||
* Volume ducks are additionally reverted on owner-disconnect and monitor completion so a dropped
|
||||
* session never strands the user at low volume.
|
||||
*/
|
||||
@Singleton
|
||||
class ConversationReaction @Inject constructor(
|
||||
private val aapManager: AapConnectionManager,
|
||||
private val deviceMonitor: DeviceMonitor,
|
||||
private val mediaControl: MediaControl,
|
||||
@AppScope private val appScope: CoroutineScope,
|
||||
private val timeSource: TimeSource,
|
||||
) {
|
||||
|
||||
private sealed interface Kind {
|
||||
data object Paused : Kind
|
||||
data class Ducked(val priorVolume: Int, val appliedVolume: Int) : Kind
|
||||
}
|
||||
|
||||
private data class Active(
|
||||
val id: Long,
|
||||
val owner: BluetoothAddress,
|
||||
val kind: Kind,
|
||||
val at: Long,
|
||||
)
|
||||
|
||||
/** What the single pending [disengageJob] is currently waiting for. */
|
||||
private enum class TimerPhase {
|
||||
/** Long backstop while only START/RESUME seen — inferred end if it fires. */
|
||||
STALE_BACKSTOP,
|
||||
|
||||
/** Short fuse armed by a wind-down HOLD — terminal imminent (or dropped, #608). */
|
||||
WIND_DOWN_FUSE,
|
||||
|
||||
/** Brief wait on an uncorroborated cold terminal to see if a pod-removal ear change follows. */
|
||||
STOP_SETTLE,
|
||||
}
|
||||
|
||||
private val mutex = Mutex()
|
||||
private var active: Active? = null
|
||||
private var disengageJob: Job? = null
|
||||
private var timerPhase: TimerPhase? = null
|
||||
// Bumped on every (re)arm. A timer job captures its generation and only acts if still current —
|
||||
// guards against a stale job that already passed its delay (so cancel() no longer stops it) being
|
||||
// re-armed to the SAME phase while it blocks on the mutex. This is a runtime concurrency guard: the
|
||||
// single-threaded virtual-time test harness serializes the timer callback and event handlers, so a
|
||||
// re-arm there always cancels the prior job before it runs — the race is not reproducible in a unit
|
||||
// test (a test would exercise cancel(), not this check). Left deliberately uncovered, not an oversight.
|
||||
private var timerGeneration = 0L
|
||||
private var idCounter = 0L
|
||||
|
||||
// Per-owner AAP ear-detection tracking, so a CA terminal that lands right after a pod
|
||||
// removal/insertion can be recognised as a re-key artifact rather than a real conversation end.
|
||||
private val lastEarDetection = mutableMapOf<BluetoothAddress, AapSetting.EarDetection?>()
|
||||
private val earTransitionAt = mutableMapOf<BluetoothAddress, Long>()
|
||||
|
||||
fun monitor(): Flow<Unit> = merge(
|
||||
aapManager.conversationalAwarenessEvents.onEach { (address, event) -> onEvent(address, event) },
|
||||
// Tracks ear-detection transitions (for terminal-artifact suppression) and reverts a stranded
|
||||
// duck when the owning device leaves the AAP state map for any reason (intentional or not) —
|
||||
// disconnectEvents only fires for unintentional drops.
|
||||
aapManager.allStates.onEach { states -> onStatesUpdated(states) },
|
||||
)
|
||||
.map { }
|
||||
// Service stop / scope cancellation: undo any active duck so we don't leave volume lowered.
|
||||
.onCompletion { withContext(NonCancellable) { onMonitorCompleted() } }
|
||||
.setupCommonEventHandlers(TAG) { "conversationReaction" }
|
||||
|
||||
private suspend fun onEvent(address: BluetoothAddress, event: ConversationAwarenessEvent) = when (event) {
|
||||
ConversationAwarenessEvent.START -> onSpeakingStart(address)
|
||||
ConversationAwarenessEvent.RESUME -> onSpeakingResume(address)
|
||||
ConversationAwarenessEvent.HOLD -> onSpeakingHold(address)
|
||||
ConversationAwarenessEvent.STOP -> onSpeakingStop(address)
|
||||
}
|
||||
|
||||
private suspend fun onSpeakingStart(address: BluetoothAddress) {
|
||||
val primary = deviceMonitor.primaryDevice().first()
|
||||
if (primary?.address != address) {
|
||||
log(TAG) { "START from $address ignored — not primary device (primary=${primary?.address})" }
|
||||
return
|
||||
}
|
||||
val action = primary.reactions.conversationAction
|
||||
if (action == ConversationAction.NOTHING) return
|
||||
|
||||
mutex.withLock {
|
||||
val current = active
|
||||
if (current != null && current.owner == address) {
|
||||
// Duplicate START for the same speaker — don't re-act, just keep the session alive.
|
||||
// A START also cancels a pending wind-down fuse / settle: the wearer is speaking again.
|
||||
armTimer(current, TimerPhase.STALE_BACKSTOP)
|
||||
log(TAG) { "START from $address — already active ($action), keep-alive" }
|
||||
return
|
||||
}
|
||||
// A different device started speaking while we were active — undo the old one first and
|
||||
// cancel its pending timer (the no-op engage paths below would otherwise leak it).
|
||||
if (current != null) {
|
||||
revert(current, "superseded by $address")
|
||||
clearActive()
|
||||
}
|
||||
|
||||
when (action) {
|
||||
ConversationAction.PAUSE -> {
|
||||
val paused = mediaControl.sendPause(rememberForResume = false)
|
||||
if (paused) {
|
||||
val record = Active(nextId(), address, Kind.Paused, timeSource.elapsedRealtime())
|
||||
active = record
|
||||
armTimer(record, TimerPhase.STALE_BACKSTOP)
|
||||
log(TAG, INFO) { "START on $address → paused media" }
|
||||
} else {
|
||||
active = null
|
||||
log(TAG) { "START on $address → nothing playing, no pause" }
|
||||
}
|
||||
}
|
||||
|
||||
ConversationAction.LOWER_VOLUME -> {
|
||||
val reduction = primary.reactions.conversationVolumeReduction
|
||||
.coerceIn(
|
||||
ReactionConfig.MIN_CONVERSATION_VOLUME_REDUCTION,
|
||||
ReactionConfig.MAX_CONVERSATION_VOLUME_REDUCTION,
|
||||
)
|
||||
val duck = mediaControl.duckMusicVolume(reduction)
|
||||
if (duck != null) {
|
||||
val record = Active(
|
||||
nextId(),
|
||||
address,
|
||||
Kind.Ducked(duck.priorVolume, duck.appliedVolume),
|
||||
timeSource.elapsedRealtime(),
|
||||
)
|
||||
active = record
|
||||
armTimer(record, TimerPhase.STALE_BACKSTOP)
|
||||
log(TAG, INFO) { "START on $address → ducked volume ${duck.priorVolume}→${duck.appliedVolume}" }
|
||||
} else {
|
||||
active = null
|
||||
log(TAG) { "START on $address → duck no-op" }
|
||||
}
|
||||
}
|
||||
|
||||
ConversationAction.NOTHING -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Speech resumed (status `5`) after a pause — the wind-down was aborted, the wearer is talking
|
||||
* again. Cancel any armed wind-down fuse and re-arm the long [STALE_TIMEOUT] backstop, exactly
|
||||
* like a duplicate-START keep-alive, so media stays paused/ducked through the conversation. Does
|
||||
* NOT engage from scratch: a RESUME with no active session is ignored (a conversation always
|
||||
* opens with a START, and a stray `5` should never start a pause/duck on its own).
|
||||
*/
|
||||
private suspend fun onSpeakingResume(address: BluetoothAddress) = mutex.withLock {
|
||||
val current = active ?: return
|
||||
if (current.owner != address) return
|
||||
armTimer(current, TimerPhase.STALE_BACKSTOP)
|
||||
log(TAG) { "RESUME from $address — speech resumed, keep-alive" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitional wind-down frame (`3` pause, `0x0B`/`4` wind-down, `7` abort). The pod is silent
|
||||
* during active speech, so this frame means the wind-down may have begun and a terminal frame is
|
||||
* imminent — but firmware sometimes drops it (#608, single-pod wear). Arm the short fuse: if no
|
||||
* terminal (or a fresh START / RESUME) follows, disengage anyway.
|
||||
*/
|
||||
private suspend fun onSpeakingHold(address: BluetoothAddress) = mutex.withLock {
|
||||
val current = active ?: return
|
||||
if (current.owner != address) return
|
||||
armTimer(current, TimerPhase.WIND_DOWN_FUSE)
|
||||
}
|
||||
|
||||
private suspend fun onSpeakingStop(address: BluetoothAddress) {
|
||||
val primary = deviceMonitor.primaryDevice().first()
|
||||
mutex.withLock {
|
||||
val current = active ?: return
|
||||
if (current.owner != address) {
|
||||
log(TAG) { "STOP from $address ignored — owner is ${current.owner}" }
|
||||
return
|
||||
}
|
||||
// The 0x06 ear-detection frame may have arrived but not yet been processed by the
|
||||
// allStates collector — fold the latest snapshot in before deciding (in-app ordering race).
|
||||
refreshEarTransition(address)
|
||||
|
||||
// Pulling/inserting a pod makes the firmware re-key CA — a terminal (8,9) then a fresh
|
||||
// onset (1,2) around the physical change, even though the conversation continues.
|
||||
when {
|
||||
// Ear change already seen → re-key artifact. Defer to the fuse; a re-onset cancels it.
|
||||
isRecentEarTransition(address) -> {
|
||||
armTimer(current, TimerPhase.WIND_DOWN_FUSE)
|
||||
log(TAG) { "STOP from $address during ear transition — deferring to fuse" }
|
||||
}
|
||||
// Corroborated by a preceding wind-down (3,0xB,4 / abort 7): a real end → resume now.
|
||||
timerPhase == TimerPhase.WIND_DOWN_FUSE -> {
|
||||
clearActive()
|
||||
disengage(current, primary, "STOP on $address", applyAgeGuard = false)
|
||||
}
|
||||
// Cold terminal with no wind-down and no ear change yet. On primary-pod removal the
|
||||
// firmware sends the terminal tens of ms BEFORE the ear-detection frame (measured ~30ms
|
||||
// on Pro 3), so wait a short settle to see if an ear change lands before treating it as
|
||||
// a real end.
|
||||
else -> {
|
||||
armTimer(current, TimerPhase.STOP_SETTLE)
|
||||
log(TAG) { "STOP from $address — uncorroborated, settling" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Graceful disengage (STOP event or timer expiry). Must be called under [mutex]. [applyAgeGuard]
|
||||
* gates resume on [PAUSE_RESUME_WINDOW]: `true` only for the inferred stale-backstop end (we lost
|
||||
* track and timed out — surprise-resuming long after is worse than a stranded pause); `false` for
|
||||
* an explicit terminal or the wind-down fuse, where a recent/real end signal makes resume correct.
|
||||
*/
|
||||
private suspend fun disengage(record: Active, primary: PodDevice?, reason: String, applyAgeGuard: Boolean) {
|
||||
when (val kind = record.kind) {
|
||||
is Kind.Paused -> {
|
||||
// Resume the pause WE caused, regardless of the current action setting. Gating on
|
||||
// "action still == PAUSE" would strand media paused if the user switched the action
|
||||
// (or set NOTHING) mid-conversation — undoing our own side effect is the least
|
||||
// surprising behaviour. The remaining guards are about real device/playback state.
|
||||
val age = timeSource.elapsedRealtime() - record.at
|
||||
when {
|
||||
applyAgeGuard && age.milliseconds > PAUSE_RESUME_WINDOW ->
|
||||
log(TAG) { "$reason — resume skipped (stale, ${age}ms)" }
|
||||
primary?.address != record.owner ->
|
||||
log(TAG) { "$reason — resume skipped (primary switched)" }
|
||||
wornForResume(primary) == false ->
|
||||
log(TAG) { "$reason — resume skipped (not worn)" }
|
||||
mediaControl.isPlaying ->
|
||||
log(TAG) { "$reason — resume skipped (already playing)" }
|
||||
else -> {
|
||||
mediaControl.sendPlay()
|
||||
log(TAG, INFO) { "$reason → resumed media" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is Kind.Ducked -> revertDuck(kind, reason)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun onStatesUpdated(states: Map<BluetoothAddress, AapPodState>) = mutex.withLock {
|
||||
// Record when each device's AAP ear-detection last changed (pod removed/inserted/cased).
|
||||
for ((address, state) in states) recordEarIfChanged(address, state.aapEarDetection)
|
||||
lastEarDetection.keys.retainAll(states.keys)
|
||||
earTransitionAt.keys.retainAll(states.keys)
|
||||
|
||||
val current = active ?: return
|
||||
if (current.owner !in states.keys) {
|
||||
clearActive()
|
||||
revert(current, "owner ${current.owner} gone")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called under [mutex]. Notes an AAP ear-detection transition for [address]. The very
|
||||
* first observation of a device only seeds the baseline — it is not counted as a transition.
|
||||
*/
|
||||
private fun recordEarIfChanged(address: BluetoothAddress, ear: AapSetting.EarDetection?) {
|
||||
if (lastEarDetection.containsKey(address) && lastEarDetection[address] != ear) {
|
||||
earTransitionAt[address] = timeSource.elapsedRealtime()
|
||||
}
|
||||
lastEarDetection[address] = ear
|
||||
}
|
||||
|
||||
/** Must be called under [mutex]. Folds the latest allStates snapshot in for [address]. */
|
||||
private fun refreshEarTransition(address: BluetoothAddress) {
|
||||
recordEarIfChanged(address, aapManager.allStates.value[address]?.aapEarDetection)
|
||||
}
|
||||
|
||||
private suspend fun onMonitorCompleted() = mutex.withLock {
|
||||
val current = active ?: return
|
||||
clearActive()
|
||||
revert(current, "monitor completed")
|
||||
}
|
||||
|
||||
/** Forced revert (disconnect / supersede / shutdown): undo volume ducks; leave pauses as-is. */
|
||||
private fun revert(record: Active, reason: String) {
|
||||
when (val kind = record.kind) {
|
||||
is Kind.Ducked -> revertDuck(kind, reason)
|
||||
is Kind.Paused -> log(TAG) { "Clearing pause ($reason) — leaving playback as-is" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun revertDuck(kind: Kind.Ducked, reason: String) {
|
||||
// Restore to the pre-duck volume unconditionally — we do NOT gate on
|
||||
// currentMusicVolume() == appliedVolume. Such a guard skips the restore whenever anything
|
||||
// else moved the volume meanwhile (e.g. a concurrent volume-manager app), which lets the
|
||||
// baseline ratchet down across conversations. Restoring deterministically to the level we
|
||||
// saved at engage avoids that; the trade-off is overriding a deliberate mid-conversation
|
||||
// volume change (rare). Matches librepods' unconditional restore.
|
||||
log(TAG, INFO) { "Restoring volume to ${kind.priorVolume} (ducked to ${kind.appliedVolume}, now ${mediaControl.currentMusicVolume()}, $reason)" }
|
||||
mediaControl.restoreMusicVolume(kind.priorVolume)
|
||||
}
|
||||
|
||||
/** Must be called under [mutex]. Clears the active slot and cancels its pending timer. */
|
||||
private fun clearActive() {
|
||||
disengageJob?.cancel()
|
||||
disengageJob = null
|
||||
timerPhase = null
|
||||
active = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called under [mutex]. (Re)arms the single disengage timer for [record] in [phase],
|
||||
* replacing whatever was pending. On expiry [onTimerExpired] decides per phase. Guarded on both
|
||||
* [Active.id] and [phase] so a late timer can't act on a newer session or a re-armed phase.
|
||||
*/
|
||||
private fun armTimer(record: Active, phase: TimerPhase) {
|
||||
disengageJob?.cancel()
|
||||
timerPhase = phase
|
||||
val generation = ++timerGeneration
|
||||
val timeout = when (phase) {
|
||||
TimerPhase.STALE_BACKSTOP -> STALE_TIMEOUT
|
||||
TimerPhase.WIND_DOWN_FUSE -> WIND_DOWN_TIMEOUT
|
||||
TimerPhase.STOP_SETTLE -> STOP_SETTLE_DELAY
|
||||
}
|
||||
disengageJob = appScope.launch {
|
||||
delay(timeout)
|
||||
val primary = deviceMonitor.primaryDevice().first()
|
||||
mutex.withLock {
|
||||
// generation guards against a stale job re-armed to the same phase for the same record.
|
||||
if (active?.id == record.id && timerGeneration == generation) onTimerExpired(record, phase, primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called under [mutex] from inside the firing timer job. Detaches the slot WITHOUT
|
||||
* cancelling (we ARE that job — self-cancel could abort a following suspension), then acts:
|
||||
* - STOP_SETTLE + an ear change appeared meanwhile → re-key artifact, hand off to the fuse.
|
||||
* - otherwise force-end the session. The age guard applies only to the inferred STALE backstop;
|
||||
* the wind-down fuse and a settled cold terminal carry real/recent end evidence → resume regardless.
|
||||
*/
|
||||
private suspend fun onTimerExpired(record: Active, phase: TimerPhase, primary: PodDevice?) {
|
||||
disengageJob = null
|
||||
timerPhase = null
|
||||
if (phase == TimerPhase.STOP_SETTLE && isRecentEarTransition(record.owner)) {
|
||||
armTimer(record, TimerPhase.WIND_DOWN_FUSE)
|
||||
log(TAG) { "STOP settle elapsed for ${record.owner} — ear transition appeared, deferring to fuse" }
|
||||
return
|
||||
}
|
||||
active = null
|
||||
val reason = when (phase) {
|
||||
TimerPhase.STALE_BACKSTOP -> "no terminal within backstop"
|
||||
TimerPhase.WIND_DOWN_FUSE -> "no terminal within wind-down fuse"
|
||||
TimerPhase.STOP_SETTLE -> "cold terminal settled"
|
||||
}
|
||||
disengage(record, primary, reason, applyAgeGuard = phase == TimerPhase.STALE_BACKSTOP)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the pod is worn enough to resume into, honoring One-Pod Mode (mirrors
|
||||
* [eu.darken.capod.reaction.core.autoconnect.AutoConnect]): with One-Pod Mode on, a single pod
|
||||
* in ear counts as worn, falling back to the both-pods reading only when the single-pod signal is
|
||||
* unknown. Returns `null` when ear state is genuinely unknown — the caller treats only an explicit
|
||||
* `false` as not-worn, so an unknown stays lenient (resumes).
|
||||
*/
|
||||
private fun wornForResume(device: PodDevice): Boolean? =
|
||||
if (device.reactions.onePodMode) device.isEitherPodInEar ?: device.isBeingWorn
|
||||
else device.isBeingWorn
|
||||
|
||||
/** Must be called under [mutex]. True if [address] had an AAP ear-detection change very recently. */
|
||||
private fun isRecentEarTransition(address: BluetoothAddress): Boolean {
|
||||
val at = earTransitionAt[address] ?: return false
|
||||
return (timeSource.elapsedRealtime() - at).milliseconds <= EAR_TRANSITION_WINDOW
|
||||
}
|
||||
|
||||
private fun nextId(): Long = ++idCounter
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reaction", "Conversation")
|
||||
|
||||
/**
|
||||
* Backstop fuse while engaged with no wind-down evidence yet (START/RESUME frames seen).
|
||||
* Must stay LONG: the pod sends zero frames during active speech and stays engaged against
|
||||
* ambient noise, so a short timeout here resumes media mid-conversation (the original 12s
|
||||
* value did exactly that). Only recovers a session whose entire wind-down flurry was lost
|
||||
* while the link stays up. Kept longer than [PAUSE_RESUME_WINDOW] so a back-stopped pause
|
||||
* is never auto-resumed (only a duck is restored — a stranded low volume is the worse
|
||||
* failure).
|
||||
*/
|
||||
private val STALE_TIMEOUT = 5.minutes
|
||||
|
||||
/**
|
||||
* Short fuse armed by a transitional [ConversationAwarenessEvent.HOLD] frame: the wind-down
|
||||
* has begun, so a terminal frame should follow within seconds. With only one pod worn the
|
||||
* pod deterministically drops the terminal (#608, reproduced on Pro 3 and Pro 2) — this
|
||||
* fuse disengages instead of stranding the volume low for [STALE_TIMEOUT]. Must be ≥ ~5s:
|
||||
* gaps up to 2.8s were observed between consecutive wind-down frames, and each HOLD re-arms
|
||||
* this fuse. A fresh START or RESUME (`5`, speech resumed) re-arms the long fuse instead.
|
||||
*/
|
||||
private val WIND_DOWN_TIMEOUT = 6.seconds
|
||||
|
||||
/**
|
||||
* For an *inferred* end only (the [STALE_TIMEOUT] backstop fired — we lost track of the
|
||||
* conversation), a pause older than this no longer auto-resumes: surprise playback long after
|
||||
* is worse than a stranded pause. An explicit terminal frame, or the short wind-down fuse,
|
||||
* resumes regardless of age — those carry real/recent evidence the conversation just ended.
|
||||
*/
|
||||
private val PAUSE_RESUME_WINDOW = 2.minutes
|
||||
|
||||
/**
|
||||
* A CA terminal landing within this window of an AAP ear-detection change is treated as a pod
|
||||
* removal/insertion re-key artifact (firmware emits 8,9 then 1,2 around the physical change),
|
||||
* not a real end. The artifact terminal and the ear change land within tens of ms of each other
|
||||
* (measured ~30ms on Pro 3, either side depending on which pod); a real end is seconds away.
|
||||
* Kept well under that gap so genuine terminals aren't deferred to the fuse. (The ear-detection
|
||||
* 0x06 frames are emitted on removal even when the EarDetectionEnabled setting is off, so this
|
||||
* suppression does not depend on that setting being enabled.)
|
||||
*/
|
||||
private val EAR_TRANSITION_WINDOW = 2.seconds
|
||||
|
||||
/**
|
||||
* How long to wait on an *uncorroborated cold* terminal (no preceding wind-down HOLD, no ear
|
||||
* change yet) before treating it as a real end. Covers the reverse frame-ordering on
|
||||
* primary-pod removal, where the firmware sends the terminal tens of ms BEFORE the ear-detection
|
||||
* frame (measured ~30ms on Pro 3) — too early for the backward-looking check. Only cold
|
||||
* terminals settle; a normal end
|
||||
* (`…3,0xB,4,8`) and an abort (`7,8`) are already corroborated by the fuse and resume instantly,
|
||||
* so this adds no latency to genuine conversation ends.
|
||||
*/
|
||||
private val STOP_SETTLE_DELAY = 250.milliseconds
|
||||
}
|
||||
}
|
||||
@@ -179,6 +179,7 @@ class PlayPause @Inject constructor(
|
||||
onePodMode = reactions.onePodMode,
|
||||
isCurrentlyPlaying = isCurrentlyPlaying,
|
||||
wasRecentlyPausedByUs = wasRecentlyPausedByUs,
|
||||
startMusicOnWear = reactions.startMusicOnWear,
|
||||
)
|
||||
|
||||
// BLE-only autoplay confirmation only applies to UNAUTHENTICATED sources.
|
||||
@@ -204,6 +205,7 @@ class PlayPause @Inject constructor(
|
||||
shouldStageBleOnlyPlay = shouldStageBleOnlyPlay,
|
||||
isCurrentlyPlaying = isCurrentlyPlaying,
|
||||
wasRecentlyPausedByUs = wasRecentlyPausedByUs,
|
||||
startMusicOnWear = reactions.startMusicOnWear,
|
||||
)
|
||||
pendingPlayConfirmation = confirmation.pending
|
||||
|
||||
@@ -263,7 +265,7 @@ class PlayPause @Inject constructor(
|
||||
}
|
||||
|
||||
decision.shouldPause && reactions.autoPause -> {
|
||||
val pauseSent = mediaControl.sendPause()
|
||||
val pauseSent = mediaControl.sendPause(rememberForResume = true)
|
||||
log(TAG, INFO) {
|
||||
"autoPause triggered: source=$source, " +
|
||||
"wasWorn=${prevState.bothInEar}, isWorn=${currState.bothInEar}, " +
|
||||
@@ -288,10 +290,11 @@ class PlayPause @Inject constructor(
|
||||
onePodMode: Boolean,
|
||||
isCurrentlyPlaying: Boolean,
|
||||
wasRecentlyPausedByUs: Boolean = false,
|
||||
startMusicOnWear: Boolean = false,
|
||||
): PlayPauseDecision = if (onePodMode) {
|
||||
evaluateOnePodMode(previous, current, isCurrentlyPlaying, wasRecentlyPausedByUs)
|
||||
evaluateOnePodMode(previous, current, isCurrentlyPlaying, wasRecentlyPausedByUs, startMusicOnWear)
|
||||
} else {
|
||||
evaluateNormalMode(previous, current, isCurrentlyPlaying, wasRecentlyPausedByUs)
|
||||
evaluateNormalMode(previous, current, isCurrentlyPlaying, wasRecentlyPausedByUs, startMusicOnWear)
|
||||
}
|
||||
|
||||
private fun evaluateOnePodMode(
|
||||
@@ -299,6 +302,7 @@ class PlayPause @Inject constructor(
|
||||
current: EarDetectionState,
|
||||
isCurrentlyPlaying: Boolean,
|
||||
wasRecentlyPausedByUs: Boolean,
|
||||
startMusicOnWear: Boolean,
|
||||
): PlayPauseDecision {
|
||||
val netChange = current.podCount - previous.podCount
|
||||
|
||||
@@ -310,8 +314,13 @@ class PlayPause @Inject constructor(
|
||||
reason = "One-pod mode: pod(s) removed (net change: $netChange)"
|
||||
)
|
||||
|
||||
// Net increase: pod(s) inserted → play
|
||||
netChange > 0 && (!isCurrentlyPlaying || wasRecentlyPausedByUs) -> PlayPauseDecision(
|
||||
// Net increase: pod(s) inserted → play, but only when we paused recently OR the user
|
||||
// opted in to firing a play key on cold wear. Without an opt-in, suppress autoplay
|
||||
// so we never override a user-initiated pause or fire over silence.
|
||||
netChange > 0 && (
|
||||
wasRecentlyPausedByUs ||
|
||||
(startMusicOnWear && !isCurrentlyPlaying)
|
||||
) -> PlayPauseDecision(
|
||||
shouldPlay = true,
|
||||
shouldPause = false,
|
||||
reason = "One-pod mode: pod(s) inserted (net change: +$netChange)",
|
||||
@@ -332,13 +341,19 @@ class PlayPause @Inject constructor(
|
||||
current: EarDetectionState,
|
||||
isCurrentlyPlaying: Boolean,
|
||||
wasRecentlyPausedByCap: Boolean,
|
||||
startMusicOnWear: Boolean,
|
||||
): PlayPauseDecision {
|
||||
val wasWorn = previous.bothInEar
|
||||
val isWorn = current.bothInEar
|
||||
|
||||
return when {
|
||||
// Transition: not worn → worn, and not playing → play
|
||||
!wasWorn && isWorn && (!isCurrentlyPlaying || wasRecentlyPausedByCap) -> PlayPauseDecision(
|
||||
// Transition: not worn → worn → play, but only when we paused recently OR the user
|
||||
// opted in to firing a play key on cold wear. Without an opt-in, suppress autoplay
|
||||
// so we never override a user-initiated pause or fire over silence.
|
||||
!wasWorn && isWorn && (
|
||||
wasRecentlyPausedByCap ||
|
||||
(startMusicOnWear && !isCurrentlyPlaying)
|
||||
) -> PlayPauseDecision(
|
||||
shouldPlay = true,
|
||||
shouldPause = false,
|
||||
reason = "Normal mode: both pods in ear",
|
||||
@@ -371,6 +386,7 @@ class PlayPause @Inject constructor(
|
||||
shouldStageBleOnlyPlay: Boolean,
|
||||
isCurrentlyPlaying: Boolean,
|
||||
wasRecentlyPausedByUs: Boolean,
|
||||
startMusicOnWear: Boolean = false,
|
||||
): PlayConfirmationResult {
|
||||
val activePending = pending?.takeIf {
|
||||
it.profileId == profileId && it.onePodMode == onePodMode && autoPlayEnabled
|
||||
@@ -378,7 +394,10 @@ class PlayPause @Inject constructor(
|
||||
|
||||
if (activePending != null &&
|
||||
currentState == activePending.targetState &&
|
||||
(!isCurrentlyPlaying || wasRecentlyPausedByUs)
|
||||
(
|
||||
wasRecentlyPausedByUs ||
|
||||
(startMusicOnWear && !isCurrentlyPlaying)
|
||||
)
|
||||
) {
|
||||
return PlayConfirmationResult(
|
||||
decision = PlayPauseDecision(
|
||||
|
||||
@@ -13,9 +13,12 @@ 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
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import java.time.Duration
|
||||
@@ -34,8 +37,12 @@ class PopUpReaction @Inject constructor(
|
||||
|
||||
private fun monitorCase(): Flow<Event> = deviceMonitor.primaryDevice()
|
||||
.distinctUntilChangedBy {
|
||||
// Re-emit on profile changes (eligibility) AND on raw BLE state changes (lid).
|
||||
Triple(it?.profileId, it?.reactions?.showPopUpOnCaseOpen, it?.rawDataHex)
|
||||
// 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
|
||||
// flip OPEN<->CLOSED while the *selected* frame's raw bytes stay identical (e.g. a steady
|
||||
// out-of-case frame while a sibling in-case frame updates). Keying on rawDataHex alone
|
||||
// would swallow that transition and the popup would miss its show/hide.
|
||||
listOf(it?.profileId, it?.reactions?.showPopUpOnCaseOpen, it?.rawDataHex, it?.caseLidState)
|
||||
}
|
||||
.withPrevious()
|
||||
.setupCommonEventHandlers(TAG) { "popUpCase" }
|
||||
@@ -71,7 +78,7 @@ class PopUpReaction @Inject constructor(
|
||||
throttleCasePopUps(current)
|
||||
}
|
||||
|
||||
private fun throttleCasePopUps(current: PodDevice): Event? {
|
||||
internal fun throttleCasePopUps(current: PodDevice): Event? {
|
||||
val cooldownKey = current.profileId ?: current.identifier?.toString() ?: return null
|
||||
val now = timeSource.now()
|
||||
val lastShown = caseCoolDowns[cooldownKey]
|
||||
@@ -95,9 +102,9 @@ class PopUpReaction @Inject constructor(
|
||||
}
|
||||
|
||||
decision.shouldHide -> {
|
||||
if (!decision.shouldResetCooldown) {
|
||||
caseCoolDowns[cooldownKey] = now
|
||||
}
|
||||
// Don't stamp the cooldown on a non-CLOSED hide (UNKNOWN/NOT_IN_CASE). Refreshing it
|
||||
// here would let a transient UNKNOWN (e.g. a brief out-of-case frame) suppress a
|
||||
// genuine OPEN for the whole cooldown window. CLOSED still resets it above.
|
||||
Event.PopupHide(now)
|
||||
}
|
||||
|
||||
@@ -192,7 +199,48 @@ class PopUpReaction @Inject constructor(
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "popUpConnection" }
|
||||
|
||||
fun monitor(): Flow<Event> = merge(monitorCase(), monitorConnection())
|
||||
/**
|
||||
* Backstop for case-open popups that never receive a CLOSED frame because the device left BLE
|
||||
* range while the lid was open — otherwise the overlay lingers until manually dismissed (one of
|
||||
* the symptoms in #598). A ticker re-checks the primary device's freshness; once a previously
|
||||
* fresh OPEN broadcast goes stale past [CASE_OPEN_STALE_TIMEOUT] (no newer advertisement, or the
|
||||
* device dropped to cache-only), a single Hide is emitted. The lid-driven [monitorCase] still
|
||||
* handles the normal close; a redundant Hide here is harmless ([PopUpWindow.close] is idempotent).
|
||||
*/
|
||||
private fun monitorCaseStaleClose(): Flow<Event> = combine(
|
||||
deviceMonitor.primaryDevice(),
|
||||
staleCheckTicker(),
|
||||
) { device, _ -> isCaseOpenBroadcastFresh(device) }
|
||||
.distinctUntilChanged()
|
||||
.withPrevious()
|
||||
.mapNotNull { (wasFresh, isFresh) ->
|
||||
if (wasFresh == true && !isFresh) {
|
||||
log(TAG) { "Case-open broadcast went stale, emitting Hide" }
|
||||
Event.PopupHide(timeSource.now())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "popUpCaseStale" }
|
||||
|
||||
/** True while the primary device is eligible and currently advertising a fresh OPEN lid. */
|
||||
internal fun isCaseOpenBroadcastFresh(device: PodDevice?): Boolean {
|
||||
if (device?.reactions?.showPopUpOnCaseOpen != true) return false
|
||||
if (device.caseLidState != DualApplePods.LidState.OPEN) return false
|
||||
// Track BLE freshness specifically, not PodDevice.seenLastAt (which also counts AAP/cache):
|
||||
// the lid is a BLE-only signal, so a live AAP socket must not keep a stale OPEN on screen.
|
||||
val bleSeenLastAt = device.ble?.seenLastAt ?: return false
|
||||
return Duration.between(bleSeenLastAt, timeSource.now()) <= CASE_OPEN_STALE_TIMEOUT
|
||||
}
|
||||
|
||||
private fun staleCheckTicker(): Flow<Unit> = flow {
|
||||
while (true) {
|
||||
emit(Unit)
|
||||
delay(STALE_CHECK_INTERVAL.toMillis())
|
||||
}
|
||||
}
|
||||
|
||||
fun monitor(): Flow<Event> = merge(monitorCase(), monitorConnection(), monitorCaseStaleClose())
|
||||
|
||||
sealed class Event {
|
||||
data class PopupShow(
|
||||
@@ -296,5 +344,11 @@ class PopUpReaction @Inject constructor(
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reaction", "PopUp")
|
||||
|
||||
/** A case-open popup is force-dismissed once its OPEN broadcast hasn't refreshed for this long. */
|
||||
private val CASE_OPEN_STALE_TIMEOUT: Duration = Duration.ofSeconds(4)
|
||||
|
||||
/** How often the stale-close backstop re-evaluates freshness. */
|
||||
private val STALE_CHECK_INTERVAL: Duration = Duration.ofSeconds(2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import eu.darken.capod.common.TimeSource
|
||||
import eu.darken.capod.common.bluetooth.ScannerMode
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.datastore.valueBlocking
|
||||
import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
@@ -23,14 +23,13 @@ import eu.darken.capod.pods.core.unknown.UnknownSnapshotBle
|
||||
import eu.darken.capod.profiles.core.AppleDeviceProfile
|
||||
import eu.darken.capod.profiles.core.DeviceProfilesRepo
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -43,12 +42,14 @@ class TroubleShooterViewModel @Inject constructor(
|
||||
private val blePodMonitor: BlePodMonitor,
|
||||
private val bleScanModeController: BleScanModeController,
|
||||
private val deviceMonitor: DeviceMonitor,
|
||||
private val debugSettings: DebugSettings,
|
||||
private val timeSource: TimeSource,
|
||||
) : ViewModel4(dispatcherProvider) {
|
||||
|
||||
private val _bleState = MutableStateFlow<BleState>(BleState.Intro())
|
||||
|
||||
/** Guards against re-entrant runs (e.g. a double tap on "Try again"). */
|
||||
private val runLock = Mutex()
|
||||
|
||||
data class State(val bleState: BleState)
|
||||
|
||||
val state = _bleState.map { State(it) }.asLiveState()
|
||||
@@ -84,166 +85,184 @@ class TroubleShooterViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun troubleShootBle() = launch(context = dispatcherProvider.IO) {
|
||||
if (!runLock.tryLock()) {
|
||||
log(TAG, WARN) { "troubleShootBle() ignored, a run is already in progress" }
|
||||
return@launch
|
||||
}
|
||||
log(TAG, INFO) { "troubleShootBle()" }
|
||||
|
||||
bleScanModeController.withTemporaryOverride(ScannerMode.LOW_LATENCY) override@{
|
||||
run {
|
||||
progress("Checking for headphones...")
|
||||
val mainDevice = withTimeoutOrNull(STEP_TIME) {
|
||||
deviceMonitor.primaryDevice().filterNotNull().firstOrNull()
|
||||
}
|
||||
if (mainDevice != null) {
|
||||
success("Headphones found, nothing to troubleshoot.")
|
||||
return@override
|
||||
} else {
|
||||
progress("Headphones not detected.\n")
|
||||
}
|
||||
}
|
||||
try {
|
||||
bleScanModeController.withTemporaryOverride(ScannerMode.LOW_LATENCY) override@{
|
||||
// The combo under which supported headphones were detected (set in sweep 2).
|
||||
var supportedCombo: BlePodMonitor.CompatOverride? = null
|
||||
// Set only when the run reaches a terminal success. Persisted on exit; staying null
|
||||
// (any failure, or cancellation) means we just clear the override, which restores the
|
||||
// user's original — never-touched — settings.
|
||||
var comboToPersist: BlePodMonitor.CompatOverride? = null
|
||||
try {
|
||||
run {
|
||||
progress("Checking for headphones...")
|
||||
if (findLiveBleHeadphones()) {
|
||||
success("Headphones found, nothing to troubleshoot.")
|
||||
return@override
|
||||
} else {
|
||||
progress("Headphones not detected.\n")
|
||||
}
|
||||
}
|
||||
|
||||
val doScan: suspend (Boolean, Boolean, Boolean, Boolean) -> Collection<BlePodSnapshot> =
|
||||
{ hardwareFilteringDisabled,
|
||||
hardwareBatchingDisabled,
|
||||
indirectCallback,
|
||||
unfiltered ->
|
||||
val sb = StringBuilder("SCAN - Settings: ")
|
||||
sb.append("hardwareFilteringDisabled=$hardwareFilteringDisabled, ")
|
||||
sb.append("hardwareBatchingDisabled=$hardwareBatchingDisabled, ")
|
||||
sb.append("indirectCallback=$indirectCallback, ")
|
||||
sb.append("unfiltered=$unfiltered")
|
||||
progress(sb.toString())
|
||||
generalSettings.isOffloadedFilteringDisabled.valueBlocking = hardwareFilteringDisabled
|
||||
generalSettings.isOffloadedBatchingDisabled.valueBlocking = hardwareBatchingDisabled
|
||||
generalSettings.useIndirectScanResultCallback.valueBlocking = indirectCallback
|
||||
debugSettings.showUnfiltered.valueBlocking = unfiltered
|
||||
val doScan: suspend (BlePodMonitor.CompatOverride, Boolean) -> Collection<BlePodSnapshot> =
|
||||
{ combo, unfiltered ->
|
||||
progress(
|
||||
"SCAN - Settings: filteringDisabled=${combo.offloadedFilteringDisabled}, " +
|
||||
"batchingDisabled=${combo.offloadedBatchingDisabled}, " +
|
||||
"indirectCallback=${combo.indirectCallback}, unfiltered=$unfiltered"
|
||||
)
|
||||
blePodMonitor.setCompatOverride(combo)
|
||||
blePodMonitor.setUnfilteredOverride(unfiltered)
|
||||
val devices = collectFreshDevices()
|
||||
log(TAG) { "SCAN: Fresh BLE devices: $devices" }
|
||||
if (devices.isNotEmpty()) {
|
||||
progress("SCAN: Received data from ${devices.size} BLE devices")
|
||||
} else {
|
||||
progress("SCAN: No data received")
|
||||
}
|
||||
devices
|
||||
}
|
||||
|
||||
val start = timeSource.elapsedRealtime()
|
||||
val devices = withTimeoutOrNull(STEP_TIME) {
|
||||
blePodMonitor.devices
|
||||
.take(10)
|
||||
.takeWhile { timeSource.elapsedRealtime() - start < STEP_TIME - 1000 }
|
||||
.toList()
|
||||
.flatten()
|
||||
.distinctBy { it.address }
|
||||
} ?: emptyList()
|
||||
log(TAG) { "SCAN: BLE Devices: $devices" }
|
||||
if (devices.isNotEmpty()) {
|
||||
progress("SCAN: Received data from ${devices.size} BLE devices")
|
||||
devices
|
||||
} else {
|
||||
progress("SCAN: No data received")
|
||||
devices
|
||||
run {
|
||||
progress("Checking if we can receive BLE data at all.")
|
||||
val gotData = COMPAT_COMBOS.any { combo -> doScan(combo, true).isNotEmpty() }
|
||||
if (!gotData) {
|
||||
failure("Phone is not receiving BLE data.", BleState.Result.Failure.Type.PHONE)
|
||||
return@override
|
||||
}
|
||||
}
|
||||
|
||||
progress("We received at least some BLE data.\n")
|
||||
|
||||
run {
|
||||
progress("Checking for supported headphones.")
|
||||
supportedCombo = COMPAT_COMBOS.firstOrNull { combo ->
|
||||
doScan(combo, false).any { it !is UnknownSnapshotBle }
|
||||
}
|
||||
if (supportedCombo == null) {
|
||||
failure("No compatible headphones found", BleState.Result.Failure.Type.HEADPHONES)
|
||||
return@override
|
||||
}
|
||||
}
|
||||
|
||||
progress("Found some headphones that are supported by CAPod.\n")
|
||||
|
||||
run {
|
||||
progress("Checking for your headphones with new BLE settings...")
|
||||
if (findLiveBleHeadphones()) {
|
||||
comboToPersist = supportedCombo
|
||||
success("Found your headphones, new BLE settings worked :)!")
|
||||
return@override
|
||||
}
|
||||
}
|
||||
|
||||
progress("Still no headphones detected that count as yours.\n")
|
||||
|
||||
run {
|
||||
progress("Checking all closeby headphones.")
|
||||
|
||||
val otherDevices = collectFreshDevices()
|
||||
otherDevices.forEachIndexed { index, dev -> log(TAG) { "Device #$index: $dev" } }
|
||||
|
||||
val candidate = otherDevices
|
||||
.filter { it !is UnknownSnapshotBle }
|
||||
.maxByOrNull { it.signalQuality }
|
||||
|
||||
if (candidate == null) {
|
||||
failure(
|
||||
"No supported headphones found near your device.",
|
||||
BleState.Result.Failure.Type.HEADPHONES,
|
||||
)
|
||||
return@override
|
||||
}
|
||||
|
||||
progress("Headphones found nearby, but not detected as yours.\n")
|
||||
progress("Creating profile for closest headphones.")
|
||||
log(TAG, INFO) { "Candidate is $candidate" }
|
||||
|
||||
profilesRepo.addProfile(
|
||||
profile = AppleDeviceProfile(
|
||||
label = context.getString(R.string.troubleshooter_title),
|
||||
model = candidate.model,
|
||||
),
|
||||
addFirst = true,
|
||||
)
|
||||
|
||||
if (findLiveBleHeadphones()) {
|
||||
comboToPersist = supportedCombo
|
||||
success("Success! Detected your headphones.")
|
||||
} else {
|
||||
failure("No headphones detected near your device.", BleState.Result.Failure.Type.HEADPHONES)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
blePodMonitor.setUnfilteredOverride(false)
|
||||
try {
|
||||
// Persist the winning combo before dropping the override so the effective scan
|
||||
// settings stay equal with no restart flicker. Done in its own try so the
|
||||
// override is still cleared (restoring originals) even if a write throws.
|
||||
comboToPersist?.let { persistCompat(it) }
|
||||
} finally {
|
||||
blePodMonitor.setCompatOverride(null)
|
||||
}
|
||||
}
|
||||
|
||||
run {
|
||||
progress("Checking if we can receive BLE data at all.")
|
||||
if (doScan(false, false, false, true).isNotEmpty()) return@run
|
||||
if (doScan(false, false, true, true).isNotEmpty()) return@run
|
||||
if (doScan(true, true, true, true).isNotEmpty()) return@run
|
||||
if (doScan(true, true, false, true).isNotEmpty()) return@run
|
||||
if (doScan(true, false, true, true).isNotEmpty()) return@run
|
||||
if (doScan(true, false, false, true).isNotEmpty()) return@run
|
||||
if (doScan(false, true, true, true).isNotEmpty()) return@run
|
||||
if (doScan(false, true, false, true).isNotEmpty()) return@run
|
||||
|
||||
failure("Phone is not receiving BLE data.", BleState.Result.Failure.Type.PHONE)
|
||||
|
||||
generalSettings.isOffloadedFilteringDisabled.valueBlocking = false
|
||||
generalSettings.isOffloadedBatchingDisabled.valueBlocking = false
|
||||
generalSettings.useIndirectScanResultCallback.valueBlocking = false
|
||||
debugSettings.showUnfiltered.valueBlocking = false
|
||||
|
||||
return@override
|
||||
}
|
||||
|
||||
progress("We received at least some BLE data.\n")
|
||||
|
||||
run {
|
||||
progress("Checking for supported headphones.")
|
||||
|
||||
if (doScan(false, false, false, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
if (doScan(false, false, true, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
if (doScan(true, true, true, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
if (doScan(true, true, false, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
if (doScan(true, false, true, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
if (doScan(true, false, false, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
if (doScan(false, true, true, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
if (doScan(false, true, false, false).any { it !is UnknownSnapshotBle }) return@run
|
||||
|
||||
failure("No compatible headphones found", BleState.Result.Failure.Type.HEADPHONES)
|
||||
|
||||
generalSettings.isOffloadedFilteringDisabled.valueBlocking = false
|
||||
generalSettings.isOffloadedBatchingDisabled.valueBlocking = false
|
||||
generalSettings.useIndirectScanResultCallback.valueBlocking = false
|
||||
|
||||
return@override
|
||||
}
|
||||
|
||||
progress("Found some headphones that are supported by CAPod.\n")
|
||||
|
||||
run {
|
||||
progress("Checking for your headphones with new BLE settings...")
|
||||
val mainDevice = withTimeoutOrNull(STEP_TIME) {
|
||||
deviceMonitor.primaryDevice().filterNotNull().firstOrNull()
|
||||
}
|
||||
if (mainDevice != null) {
|
||||
success("Found your headphones, new BLE settings worked :)!")
|
||||
return@override
|
||||
}
|
||||
}
|
||||
|
||||
progress("Still no headphones detected that count as yours.\n")
|
||||
|
||||
run {
|
||||
progress("Checking all closeby headphones.")
|
||||
|
||||
val otherDevices = withTimeoutOrNull(STEP_TIME) {
|
||||
val start = timeSource.elapsedRealtime()
|
||||
blePodMonitor.devices
|
||||
.take(10)
|
||||
.takeWhile { timeSource.elapsedRealtime() - start < STEP_TIME - 1000 }
|
||||
.toList()
|
||||
.flatten()
|
||||
.distinctBy { it.address }
|
||||
} ?: emptyList()
|
||||
|
||||
otherDevices.forEachIndexed { index, dev -> log(TAG) { "Device #$index: $dev" } }
|
||||
|
||||
if (otherDevices.isEmpty()) {
|
||||
failure("No supported headphones found near your device.", BleState.Result.Failure.Type.HEADPHONES)
|
||||
return@override
|
||||
}
|
||||
|
||||
progress("Headphones found nearby, but not detected as yours.\n")
|
||||
progress("Creating profile for closest headphones.")
|
||||
|
||||
val candidate = otherDevices
|
||||
.filter { it !is UnknownSnapshotBle }
|
||||
.maxBy { it.signalQuality }
|
||||
|
||||
log(TAG, INFO) { "Candidate is $candidate" }
|
||||
|
||||
profilesRepo.addProfile(
|
||||
profile = AppleDeviceProfile(
|
||||
label = context.getString(R.string.troubleshooter_title),
|
||||
model = candidate.model,
|
||||
),
|
||||
addFirst = true,
|
||||
)
|
||||
|
||||
val mainDevice = withTimeoutOrNull(STEP_TIME) {
|
||||
deviceMonitor.primaryDevice().filterNotNull().firstOrNull()
|
||||
}
|
||||
|
||||
if (mainDevice != null) {
|
||||
success("Success! Detected your headphones.")
|
||||
} else {
|
||||
failure("No headphones detected near your device.", BleState.Result.Failure.Type.HEADPHONES)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
runLock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits up to [STEP_TIME] for the primary profile to be backed by a *fresh, live BLE*
|
||||
* observation. A cached-only / AAP-only primary does not count — the troubleshooter is about
|
||||
* whether BLE advertisements are actually reaching us. The freshness cutoff (snapshot seen at or
|
||||
* after this call) means it reflects the currently-active scan settings and doesn't rely on the
|
||||
* device cache having been cleared beforehand.
|
||||
*/
|
||||
private suspend fun findLiveBleHeadphones(): Boolean {
|
||||
val threshold = timeSource.now()
|
||||
return withTimeoutOrNull(STEP_TIME) {
|
||||
deviceMonitor.primaryDevice().firstOrNull { device ->
|
||||
device?.ble != null && (device.seenLastAt?.let { it >= threshold } == true)
|
||||
} != null
|
||||
} ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects BLE devices observed *after the current scan settings take effect*. Option changes
|
||||
* restart the scan after a throttle, and [BlePodMonitor] keeps a 20s device cache, so without a
|
||||
* freshness cutoff a stale observation from a previous combo could be mistaken for a "win".
|
||||
*/
|
||||
private suspend fun collectFreshDevices(): List<BlePodSnapshot> {
|
||||
// Drop anything cached under a previous combo so it can't satisfy this attempt.
|
||||
blePodMonitor.clearDeviceCache()
|
||||
val freshThreshold = timeSource.now().plusMillis(SCAN_SETTLE_MS)
|
||||
val start = timeSource.elapsedRealtime()
|
||||
val collected = mutableListOf<BlePodSnapshot>()
|
||||
// Accumulate as we go: a timeout must not discard what we already observed. toList() only
|
||||
// returns once the flow completes, which a quiet channel may never do within the window.
|
||||
withTimeoutOrNull(STEP_TIME) {
|
||||
blePodMonitor.devices
|
||||
.takeWhile { timeSource.elapsedRealtime() - start < STEP_TIME - 1000 }
|
||||
.collect { snapshots -> collected.addAll(snapshots) }
|
||||
}
|
||||
return collected
|
||||
.filter { it.seenLastAt >= freshThreshold }
|
||||
.distinctBy { it.address }
|
||||
}
|
||||
|
||||
private fun persistCompat(combo: BlePodMonitor.CompatOverride) {
|
||||
generalSettings.isOffloadedFilteringDisabled.valueBlocking = combo.offloadedFilteringDisabled
|
||||
generalSettings.isOffloadedBatchingDisabled.valueBlocking = combo.offloadedBatchingDisabled
|
||||
generalSettings.useIndirectScanResultCallback.valueBlocking = combo.indirectCallback
|
||||
}
|
||||
|
||||
sealed class BleState {
|
||||
class Intro : BleState()
|
||||
|
||||
@@ -294,6 +313,29 @@ class TroubleShooterViewModel @Inject constructor(
|
||||
companion object {
|
||||
const val STEP_TIME = 10 * 1000L
|
||||
|
||||
/**
|
||||
* Grace period after switching compat settings before an observation counts as "fresh".
|
||||
* Covers [BlePodMonitor]'s ~1s scan-option throttle plus the scanner restart.
|
||||
*/
|
||||
const val SCAN_SETTLE_MS = 1500L
|
||||
|
||||
/**
|
||||
* Compatibility combinations to probe, ordered fewest-disables-first so the first one that
|
||||
* works (and gets persisted) is the *minimal* set of overrides — e.g. a phone that only
|
||||
* needs batching disabled won't also get filtering disabled. Triple semantics:
|
||||
* (offloadedFilteringDisabled, offloadedBatchingDisabled, indirectCallback).
|
||||
*/
|
||||
val COMPAT_COMBOS: List<BlePodMonitor.CompatOverride> = listOf(
|
||||
BlePodMonitor.CompatOverride(false, false, false), // baseline (no overrides)
|
||||
BlePodMonitor.CompatOverride(false, true, false), // batching only
|
||||
BlePodMonitor.CompatOverride(true, false, false), // filtering only
|
||||
BlePodMonitor.CompatOverride(false, false, true), // indirect callback only
|
||||
BlePodMonitor.CompatOverride(false, true, true), // batching + indirect
|
||||
BlePodMonitor.CompatOverride(true, false, true), // filtering + indirect
|
||||
BlePodMonitor.CompatOverride(true, true, false), // filtering + batching
|
||||
BlePodMonitor.CompatOverride(true, true, true), // everything
|
||||
)
|
||||
|
||||
val TAG = logTag("TroubleShooter", "VM")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<clip>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="?android:attr/colorAccent" />
|
||||
<solid android:color="@color/brand_primary" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
android:id="@+id/pod_left_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/device_airpods_gen1_left" />
|
||||
|
||||
<ProgressBar
|
||||
@@ -58,8 +58,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
|
||||
<ImageView
|
||||
@@ -67,8 +67,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_hearing_24" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -88,8 +88,8 @@
|
||||
android:id="@+id/pod_case_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/device_airpods_gen1_case" />
|
||||
|
||||
<ProgressBar
|
||||
@@ -122,8 +122,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -142,8 +142,8 @@
|
||||
android:id="@+id/pod_right_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/device_airpods_gen1_right" />
|
||||
|
||||
<ProgressBar
|
||||
@@ -176,8 +176,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
|
||||
<ImageView
|
||||
@@ -185,8 +185,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_hearing_24" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
|
||||
<ImageView
|
||||
@@ -46,6 +48,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_hearing_24" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -76,6 +80,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -105,6 +111,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
|
||||
<ImageView
|
||||
@@ -113,6 +121,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_hearing_24" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
android:id="@+id/headphones_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/twotone_headphones_24" />
|
||||
|
||||
<TextView
|
||||
@@ -71,8 +71,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
|
||||
<ImageView
|
||||
@@ -80,8 +80,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tintMode="multiply"
|
||||
android:tint="?android:attr/colorAccent"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_hearing_24" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
android:id="@+id/headphones_battery_icon"
|
||||
style="@style/PodInfoItemIcon.Notification"
|
||||
android:layout_marginStart="12dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_battery_unknown_24" />
|
||||
|
||||
<TextView
|
||||
@@ -46,6 +48,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_power_24" />
|
||||
|
||||
<ImageView
|
||||
@@ -54,6 +58,8 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:tint="@color/notification_icon_tint"
|
||||
android:tintMode="src_in"
|
||||
android:src="@drawable/ic_baseline_hearing_24" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">As u onlangs gekoop het, kan dit \'n oomblik neem vir Google Play om te sinkroniseer.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Probeer weer oor \'n paar minute as u aankoop nie verskyn nie.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Maak seker dat u met dieselfde Google-rekening aangemeld is wat vir die aankoop gebruik is.</string>
|
||||
<string name="settings_monitor_mode_label">Monitormodus</string>
|
||||
<string name="settings_monitor_mode_description">Onder watter omstandighede hierdie toepassing Bluetooth-data monitor.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Ekstra kennisgewing</string>
|
||||
<string name="settings_monitor_connected_notification_description">Wys \'n ekstra kennisgewing wanneer \'n toestel gekoppel is. Dit laat jou toe om die permanente \"Geen toestelle\" kennisgewing te versteek deur die \"Toestelstatus\" kanaal te deaktiveer.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Hou kennisgewing ná ontkoppeling</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Outo-pouse</string>
|
||||
<string name="settings_autopause_description">Pouseer oudio wanneer die toestel uit jou oor verwyder word.</string>
|
||||
<string name="settings_autopplay_label">Outo-speel</string>
|
||||
<string name="settings_autoplay_description">Begin oudio-terugspeel wanneer toestel gedra word.</string>
|
||||
<string name="settings_autoplay_description">Hervat musiek wanneer jy jou pods weer dra, maar slegs as Outopouseer dit gestop het. Pouses wat jy self geaktiveer het (foon, AirPods-steel, slaap) bly gepouseer.</string>
|
||||
<string name="settings_start_music_on_wear_label">Begin musiek by dra</string>
|
||||
<string name="settings_start_music_on_wear_description">Probeer altyd musiek speel wanneer jy jou pods opsit, selfs na \'n handmatige pouseer.</string>
|
||||
<string name="settings_eardetection_info_label">Nota oor oorbespeuring</string>
|
||||
<string name="settings_eardetection_info_description">As oorbespeuring net vir een pod werk, is dit \'n Apple-beperking. Slegs die primêre pod (wat vir die mikrofoon gebruik word) word bespeur. Stel op Apple-toestelle op: Settings → Bluetooth → AirPods → Microphone.</string>
|
||||
<string name="settings_fake_data_label">Vals data</string>
|
||||
<string name="settings_fake_data_description">Wys vals data, d.w.s. simuleer toestelle wat nie bestaan nie.</string>
|
||||
<string name="settings_debug_label">Ontfoutinstellings</string>
|
||||
<string name="settings_debug_description">Addisionele instellings om probleme met die toepassing te help oplos.</string>
|
||||
<string name="settings_signal_minimum_label">Minimum seingehalte</string>
|
||||
<string name="settings_signal_minimum_description">Die minimum seingehalte wat \'n toestel moet hê om as joune beskou te word.</string>
|
||||
<string name="settings_autoconnect_label">Outo-koppel</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Wys verbindingsopspringer</string>
|
||||
<string name="settings_popup_connected_description">Wys \'n opspringer wanneer die toestel vir die eerste keer koppel.</string>
|
||||
<string name="settings_popup_info_not_in_app">Opspringvensters word slegs gewys as jy nie in die program is nie.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Opspringvensters benodig die monitor om te loop. Jou monitormodus is ingestel op \"Wanneer program oop is\", wat die monitor stop wanneer jy die program verlaat.</string>
|
||||
<string name="general_fix_it_action">Herstel dit</string>
|
||||
<string name="settings_charged_notification_label">Gelaaide kennisgewing</string>
|
||||
<string name="settings_charged_notification_description">Wys \'n kennisgewing sodra alles wat laai die teikenlaai vlak bereik.</string>
|
||||
<string name="settings_charged_threshold_label">Teikenlaai vlak</string>
|
||||
<string name="settings_charged_scope_label">Gemonitorde batterye</string>
|
||||
<string name="settings_charged_scope_pods_label">Slegs pods</string>
|
||||
<string name="settings_charged_scope_case_label">Slegs kis</string>
|
||||
<string name="settings_charged_scope_both_label">Pods en kis</string>
|
||||
<string name="device_settings_experimental_title">Eksperimentele kenmerk</string>
|
||||
<string name="device_settings_experimental_description">Hierdie kenmerk is nog nie op alle toestelle getoets nie. As iets nie korrek werk nie, open asseblief \'n kwessie met \'n foutopsporingsloglêer.</string>
|
||||
<string name="device_settings_experimental_action">Open Kwessiespoorder</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Ontfout</string>
|
||||
<string name="settings_general_label">Algemene instellings</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Ontfoutmodus</string>
|
||||
<string name="settings_debug_mode_description">Wys addisionele inligting om probleme op te los.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Ongefiltreerde BLE-data</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Verwyder enige filters van die BLE-skandeerder om alle uitgesaaide BLE-data te wys. Nuttig om ondersteuning vir nuwe koptelefoontipes by te voeg.</string>
|
||||
<string name="settings_compatibility_mode_label">Verenigbaarheidsmodus</string>
|
||||
<string name="settings_compatibility_mode_description">Deaktiveer optimalisasies om verenigbaarheid te verbeter. Probeer dit as jy geen data sien nie.</string>
|
||||
<string name="help_translate_label">Vertaling</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Hierdie widget vereis \'n direkte verbinding (AAP) met jou toestel. Maak seker jou toestel is gepaar en verbind in die program voor jy hierdie widget gebruik.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Hierdie toestel ondersteun nie geraasbeheersing nie</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Nie verbind nie</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Naby maar nie gekoppel nie</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Nie naby nie</string>
|
||||
<string name="anc_widget_aap_connecting_label">Verbind…</string>
|
||||
<string name="tile_anc_label">Geraaskontrole</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Toestemming benodig</string>
|
||||
<string name="tile_anc_subtitle_no_device">Geen toestel</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Geen geraaskontrole</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth af</string>
|
||||
<string name="widget_config_screen_title">Widget-konfigurasie</string>
|
||||
<string name="widget_configuration_title">Kies toestel</string>
|
||||
<string name="widget_configuration_description">Kies watter toestelprofiel hierdie legstuk moet wys.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth is gedeaktiveer, aktiveer dit ;)</string>
|
||||
<string name="overview_monitoring_active_label">Moniteer vir toestelle</string>
|
||||
<string name="overview_monitoring_active_description">Maak seker jou toestel is naby en aktief.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Verbind, maar geen data nie</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Jou toestel is verbind, maar CAPod ontvang geen lewende data daarvan nie. Jou foon het dalk \'n versoenbaarheidsopsie nodig — die probleemoplosser kan probeer om een outomaties te vind.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Voer probleemoplosser uit</string>
|
||||
<string name="overview_unmatched_devices_label">Ongepasde toestelle</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d toestel sonder ooreenstemmende profiel</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Die volgende toestemming is vereis:</string>
|
||||
<string name="permission_system_alert_window_label">Stelselwaarskuwingsvenster</string>
|
||||
<string name="permission_system_alert_window_description">Laat CAPod toe om oor ander toepassings te teken om die kenmerk Wys opspringer moontlik te maak.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Wanneer toepassing oop is</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Wanneer toestel gekoppel is</string>
|
||||
<string name="settings_monitor_mode_always_label">Altyd</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Wanneer gesien</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Kassie is oop</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">In oor</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Deursigtigheid</string>
|
||||
<string name="anc_mode_adaptive">Aanpasbaar</string>
|
||||
<string name="conversation_awareness_label">Gespreksbewustheid</string>
|
||||
<string name="settings_conversation_action_label">Wanneer jy begin praat</string>
|
||||
<string name="settings_conversation_action_nothing_label">Doen niks</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Verlaag volume</string>
|
||||
<string name="settings_conversation_action_pause_label">Pouseer media</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Volumevermindering</string>
|
||||
<string name="signal_badge_ble_cd">BLE-skandering aktief</string>
|
||||
<string name="signal_badge_key_irk_cd">Identiteit geverifieer</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Versleutelde verbinding</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Amber</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Gebruik muurpapierkleure</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitering</string>
|
||||
<string name="settings_category_appearance_label">Voorkoms</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Ongestoorde veranderinge</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Algemeen</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofoon</string>
|
||||
<string name="device_settings_microphone_mode_description">Watter AirPod as mikrofoon gebruik word</string>
|
||||
<string name="device_settings_microphone_mode_description">Watter oorfoon as mikrofoon gebruik word</string>
|
||||
<string name="device_settings_microphone_mode_auto">Outomaties</string>
|
||||
<string name="device_settings_microphone_mode_right">Regs</string>
|
||||
<string name="device_settings_microphone_mode_left">Links</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Slaapopsporing</string>
|
||||
<string name="reaction_sleep_notification_title">Gepauseer deur Slaapopsporing</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s het aangemeld dat jy aan die slaap geraak het, so jou musiek is gepauseer. Jy kan dit in die toestelinstellings deaktiveer.</string>
|
||||
<string name="reaction_charged_channel_label">Gelaaide kennisgewing</string>
|
||||
<string name="reaction_charged_notification_title">Laai voltooi</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s is volledig gelaai.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s is na %2$d%% gelaai.</string>
|
||||
<string name="device_settings_rename_label">Hernoem</string>
|
||||
<string name="device_settings_rename_hint">Toestelnaam</string>
|
||||
<string name="device_settings_rename_confirm">Hernoem</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">በቅርቡ ካዘዙ፣ Google Play ለማመሳሰል ትንሽ ጊዜ ሊወስድ ይችላል።</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">ግዢዎ ካልታየ ከጥቂት ደቂቃዎች በኋላ እንደገና ሞክር።</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">ለግዢው ጥቅም ላይ ከዋለው ጋር ተመሳሳይ Google መለያ ተጠቅመህ ገብተሃል ወይም ብለህ አረጋግጥ።</string>
|
||||
<string name="settings_monitor_mode_label">የክትትል ሁነታ</string>
|
||||
<string name="settings_monitor_mode_description">ይህ መተግበሪያ የብሉቱዝ መረጃን የሚከታተልበት ሁኔታዎች።</string>
|
||||
<string name="settings_monitor_connected_notification_label">ተጨማሪ ማሳወቂያ</string>
|
||||
<string name="settings_monitor_connected_notification_description">አንድ መሣሪያ ሲገናኝ ተጨማሪ ማሳወቂያ ያሳያል። ይህ የ \"የመሣሪያ ሁኔታ\" ቻናልን በማሰናከል ቋሚውን \"ምንም መሣሪያዎች የሉም\" የሚለውን ማሳወቂያ እንዲደብቁ ያስችልዎታል።</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">ከተነተቀ በኋላ ማሳወቂያውን ይያዙ</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">ራስ-ሰር ለአፍታ ማቆም</string>
|
||||
<string name="settings_autopause_description">መሳሪያውን ከጆሮዎ ሲያወጡ ድምጽን ለአፍታ ያቁሙ።</string>
|
||||
<string name="settings_autopplay_label">ራስ-ሰር አጫውት</string>
|
||||
<string name="settings_autoplay_description">መሳሪያው ሲለበስ የድምጽ መልሶ ማጫወት ይጀምሩ።</string>
|
||||
<string name="settings_autoplay_description">ፖዶቹን እንደገና ሲያጠቁ ሙዚቃን ቀጥል፣ ግን ራስ-አቆም ካቆመው ብቻ። ራስዎ ያቆሙት (ስልክ፣ የ AirPods ቀጭኑ፣ ወይም እንቅልፍ) ቆሞ ይቆያል።</string>
|
||||
<string name="settings_start_music_on_wear_label">ሲያጠቁ ሙዚቃ ጀምር</string>
|
||||
<string name="settings_start_music_on_wear_description">ፖዶቹን ሲያጠቁ ሁልጊዜ ሙዚቃ ለማጫወት ይሞክራል፣ እጅ ሲቆም ካቆሙ በኋላ እንኳን።</string>
|
||||
<string name="settings_eardetection_info_label">የጆሮ ማወቂያ ማስታወሻ</string>
|
||||
<string name="settings_eardetection_info_description">የጆሮ ማወቂያ ለአንድ ፖድ ብቻ የሚሰራ ከሆነ ይህ የApple ገደብ ነው። \"ዋና ፖድ\" (ለማይክሮፎን የሚጠቅም) ብቻ ይታወቃል። በApple መሳሪያዎች ላይ ያዋቅሩ: Settings → Bluetooth → AirPods → Microphone.</string>
|
||||
<string name="settings_fake_data_label">የውሸት መረጃ</string>
|
||||
<string name="settings_fake_data_description">የውሸት መረጃ ያሳዩ፣ ማለትም የሌሉ መሳሪያዎችን አስመስለው ያሳዩ።</string>
|
||||
<string name="settings_debug_label">የማረሚያ ቅንብሮች</string>
|
||||
<string name="settings_debug_description">በመተግበሪያው ላይ ያሉ ችግሮችን ለመፍታት የሚረዱ ተጨማሪ ቅንብሮች።</string>
|
||||
<string name="settings_signal_minimum_label">ዝቅተኛ የሲግናል ጥራት</string>
|
||||
<string name="settings_signal_minimum_description">አንድ መሣሪያ የእርስዎ ተደርጎ እንዲቆጠር ሊኖረው የሚገባው ዝቅተኛው የሲግናል ጥራት።</string>
|
||||
<string name="settings_autoconnect_label">ራስ-ሰር ግንኙነት</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">የግንኙነት ብቅ-ባይ አሳይ</string>
|
||||
<string name="settings_popup_connected_description">መሳሪያው ለመጀመሪያ ጊዜ ሲገናኝ ብቅ-ባይ አሳይ።</string>
|
||||
<string name="settings_popup_info_not_in_app">ፖፕ-አፕ መልዕክቶች በፕሮግራሙ ውስጥ ሳታሉ ብቻ ይታያሉ።</string>
|
||||
<string name="settings_popup_warning_manual_mode">ፖፕ-አፕ መልዕክቶች ሞኒተሩ እየሮጠ እንዲሆን ይፈልጋሉ። የሞኒተር ሁነታዎ \"ፕሮግራሙ ሲከፈት\" ተዘጋጅቷል፣ ይህም ፕሮግራሙን ሲለቁ ሞኒተሩን ያቆማል።</string>
|
||||
<string name="general_fix_it_action">አስተካክለው</string>
|
||||
<string name="settings_charged_notification_label">የተሞላ ማሳወቂያ</string>
|
||||
<string name="settings_charged_notification_description">ሁሉም ነገር ወደ ታለመው የኃይል ደረጃ ሲደርስ ማሳወቂያ አሳይ።</string>
|
||||
<string name="settings_charged_threshold_label">ታለመው የኃይል ደረጃ</string>
|
||||
<string name="settings_charged_scope_label">የሚታዩ ባትሪዎች</string>
|
||||
<string name="settings_charged_scope_pods_label">ፖድስ ብቻ</string>
|
||||
<string name="settings_charged_scope_case_label">ኬዝ ብቻ</string>
|
||||
<string name="settings_charged_scope_both_label">ፖድስ እና ኬዝ</string>
|
||||
<string name="device_settings_experimental_title">ሙከራዊ ባህሪ</string>
|
||||
<string name="device_settings_experimental_description">ይህ ባህሪ በሁሉም መሳሪያዎች ላይ ገና አልተሞከረም። ሆኖም ካልሰራ፣ እባክዎ የዲበግ ምዝግብ ማስታወሻ ጨምረው ጉዳዩን ይክፈቱ።</string>
|
||||
<string name="device_settings_experimental_action">የጉዳይ መከታተያ ክፈት</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">ዲባግ</string>
|
||||
<string name="settings_general_label">ቅንብሮች</string>
|
||||
<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="settings_acknowledgements_label">ምስጋናዎች</string>
|
||||
<string name="settings_debug_autoreports_label">ራስ-ሰር የሳንካ ሪፖርቶች</string>
|
||||
<string name="settings_debug_autoreports_description">ችግሮችን በራስ-ሰር ሪፖርት ያደርጋል፣ ለምሳሌ የመተግበሪያ ብልሽት ዝርዝሮችን እንዴት ማስተካከል እንዳለብኝ እንድገነዘብ።</string>
|
||||
<string name="settings_debug_mode_label">የማረሚያ ሁነታ</string>
|
||||
<string name="settings_debug_mode_description">ችግሮችን ለመፍታት ተጨማሪ መረጃ ያሳዩ።</string>
|
||||
<string name="settings_blescanner_unfiltered_label">ያልተጣራ የBLE መረጃ</string>
|
||||
<string name="settings_blescanner_unfiltered_description">ሁሉንም የተላለፉ የBLE መረጃዎችን ለማሳየት ማንኛውንም ማጣሪያ ከBLE ቃኚው ያስወግዱ። ለአዲስ የጆሮ ማዳመጫ አይነቶች ድጋፍ ለመጨመር ጠቃሚ ነው።</string>
|
||||
<string name="settings_compatibility_mode_label">የተኳኋኝነት ሁነታ</string>
|
||||
<string name="settings_compatibility_mode_description">ተኳኋኝነትን ለማሻሻል ማመቻቸቶችን አሰናክል። ምንም መረጃ ካላዩ ይህንን ይሞክሩ።</string>
|
||||
<string name="help_translate_label">ትርጉም</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">ይህ ዊጄት ወደ መሳሪያዎ ቀጥታ ግንኙነት (AAP) ይፈልጋል። ይህን ዊጄት ከመጠቀምዎ በፊት መሳሪያዎ ጥምር ሆኖ በአፕ ውስጥ መገናኘቱን ያረጋግጡ።</string>
|
||||
<string name="anc_widget_no_anc_support_label">ይህ መሳሪያ የጫጫታ ቁጥጥርን አይደግፍም</string>
|
||||
<string name="anc_widget_aap_not_connected_label">አልተገናኘም</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">አቅራቢያ ነው ግን አልተገናኘም</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">አቅራቢያ የለም</string>
|
||||
<string name="anc_widget_aap_connecting_label">በመገናኘት ላይ…</string>
|
||||
<string name="tile_anc_label">ጫጫታ ቁጥጥር</string>
|
||||
<string name="tile_anc_subtitle_permission_required">ፈቃድ ያስፈልጋል</string>
|
||||
<string name="tile_anc_subtitle_no_device">መሣሪያ የለም</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">ጫጫታ ቁጥጥር የለም</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">ብሉቱዝ ጠፍቷል</string>
|
||||
<string name="widget_config_screen_title">ዊጀት ውቅር</string>
|
||||
<string name="widget_configuration_title">መሣሪያ ይምረጡ</string>
|
||||
<string name="widget_configuration_description">ይህ መግብር የትኛውን የመሣሪያ መገለጫ ማሳየት እንዳለበት ይምረጡ።</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth ተሰናክሏል፣ ያንቁት ;)</string>
|
||||
<string name="overview_monitoring_active_label">መሣሪያዎችን በመከታተል ላይ</string>
|
||||
<string name="overview_monitoring_active_description">መሣሪያዎ ቅርብ እና ንቁ መሆኑን ያረጋግጡ።</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">ተገናኝቷል፣ ግን ምንም ዳታ የለም</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">መሳሪያህ ተገናኝቷል፣ ግን CAPod ከእሱ ምንም ቀጥታ ዳታ አይቀበልም። ስልክህ የተኳሃኝነት አማራጭ ሊፈልግ ይችላል — ችግር ፈቺው በራስ-ሰር ለማግኘት ሊሞክር ይችላል።</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">ችግር ፈቺን አስጀምር</string>
|
||||
<string name="overview_unmatched_devices_label">ያልተዛመዱ መሣሪያዎች</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">ተዛማጅ መገለጫ የሌለው %d መሣሪያ</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">የሚከተለው ፈቃድ ያስፈልጋል:</string>
|
||||
<string name="permission_system_alert_window_label">የስርዓት ማስጠንቀቂያ መስኮት</string>
|
||||
<string name="permission_system_alert_window_description">\"ብቅ-ባይ አሳይ\" ባህሪውን ለማስቻል CAPod በሌሎች መተግበሪያዎች ላይ እንዲሳል ፍቀድ።</string>
|
||||
<string name="settings_monitor_mode_manual_label">መተግበሪያው ሲከፈት</string>
|
||||
<string name="settings_monitor_mode_automatic_label">መሣሪያ ሲገናኝ</string>
|
||||
<string name="settings_monitor_mode_always_label">ሁልጊዜ</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">ሲታይ</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">ኬዝ ክፍት ነው</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">በጆሮ ውስጥ</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">ግልጽነት</string>
|
||||
<string name="anc_mode_adaptive">መላሚ</string>
|
||||
<string name="conversation_awareness_label">የውይይት ግንዛቤ</string>
|
||||
<string name="settings_conversation_action_label">መናገር ሲጀምሩ</string>
|
||||
<string name="settings_conversation_action_nothing_label">ምንም አታድርግ</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">ድምፅ ዝቅ አድርግ</string>
|
||||
<string name="settings_conversation_action_pause_label">ሚዲያ አቆም</string>
|
||||
<string name="settings_conversation_volume_reduction_label">የድምፅ ቅናሽ</string>
|
||||
<string name="signal_badge_ble_cd">BLE ቅኝት ንቁ ነው</string>
|
||||
<string name="signal_badge_key_irk_cd">ማንነት ተረጋግጧል</string>
|
||||
<string name="signal_badge_key_encrypted_cd">ምስጢራዊ ግንኙነት</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">ህብረ-ቀለማዊ</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">የዳሰሳ ምስል ቀለሞችን ይጠቀማል</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">ክትትል</string>
|
||||
<string name="settings_category_appearance_label">ገጽታ</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">ያልተቀመጡ ለውጦች</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">አጠቃላይ</string>
|
||||
<string name="device_settings_microphone_mode_label">ማይክሮፎን</string>
|
||||
<string name="device_settings_microphone_mode_description">የትኛው AirPod እንደ ማይክሮፎን ንእ ላይ ይወላል</string>
|
||||
<string name="device_settings_microphone_mode_description">የትኛው ኢርባድ እንደ ማይክሮፎን ጥቅም ላይ እንደሚውል</string>
|
||||
<string name="device_settings_microphone_mode_auto">ራስ-ሰር</string>
|
||||
<string name="device_settings_microphone_mode_right">ቀኝ</string>
|
||||
<string name="device_settings_microphone_mode_left">ግራ</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">የእንቅልፍ ማወቂያ</string>
|
||||
<string name="reaction_sleep_notification_title">በእንቅልፍ ማወቂያ ምክንያት ታግዷል</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s እርስዎ ተኝተዋል ብሎ ዘገበ፣ ስለዚህ ሙዚቃዎ ተቋርጧል። ይህን በመሣሪያ ቅንብሮች ውስጥ ማሰናከል ይችላሉ።</string>
|
||||
<string name="reaction_charged_channel_label">የተሞላ ማሳወቂያ</string>
|
||||
<string name="reaction_charged_notification_title">ኃይል መሙላት ተጠናቋል</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s ሙሉ በሙሉ ተሞልቷል።</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s ወደ %2$d%% ተሞልቷል።</string>
|
||||
<string name="device_settings_rename_label">ስም ቀይር</string>
|
||||
<string name="device_settings_rename_hint">የመሣሪያ ስም</string>
|
||||
<string name="device_settings_rename_confirm">ስም ቀይር</string>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<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_upgrade_required_label">يتطلب ترقية</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_close_action">إغلاق</string>
|
||||
@@ -20,46 +20,42 @@
|
||||
<string name="general_example_label">مثال: %s</string>
|
||||
<string name="upgrade_capod_label">ترقية CAPod</string>
|
||||
<string name="upgrade_capod_description">احصل على ميزات إضافية وادعم المطور.</string>
|
||||
<string name="upgrade_preamble">تم تطوير CAPod من قِبل شخص واحد. الترقية تفتح ميزات إضافية وتساعد في إبقاء التطبيق حياً.</string>
|
||||
<string name="upgrade_preamble">تم تطوير تطبيق CAPod بواسطة شخص واحد. يتيح التحديث ميزات إضافية ويساعد في استمرار عمل التطبيق.</string>
|
||||
<string name="upgrade_benefit_themes">تخصيص السمة</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">عناصر التحكم في الجهاز — الضوضاء والعصا والمزيد</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_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">عناصر التحكم في الجهاز — الضوضاء، & السيقان، & المزيد</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_hint">%s/سنة</string>
|
||||
<string name="upgrade_screen_iap_action">شراء لمرة واحدة</string>
|
||||
<string name="upgrade_screen_iap_action_hint">شراء لمرة واحدة: %s</string>
|
||||
<string name="upgrade_screen_restore_purchase_action">استعادة المُشتَرَى</string>
|
||||
<string name="upgrade_screen_iap_action">الشراء مرة واحدة</string>
|
||||
<string name="upgrade_screen_iap_action_hint">الشراء لمرة واحدة: %s</string>
|
||||
<string name="upgrade_screen_restore_purchase_action">استعادة المشتريات</string>
|
||||
<string name="upgrade_screen_restore_purchase_message">لم يتم العثور على أي مشتريات. هل تستخدم الحساب الصحيح؟</string>
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">إذا اشتريت مؤخراً، قد يستغرق Google Play لحظة للمزامنة.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">حاول مرة أخرى بعد بضع دقائق إذا لم يظهر شراؤك.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">تأكد من تسجيل دخولك بنفس حساب Google المستخدم للشراء.</string>
|
||||
<string name="settings_monitor_mode_label">وضع المراقبة</string>
|
||||
<string name="settings_monitor_mode_description">تحت أي ظروف يراقب هذا التطبيق بيانات البلوتوث.</string>
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">إذا اشتريت مؤخّرًا، فقد يستغرق الأمر بعض الوقت حتى تتم مزامنة حسابك على Google Play.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">حاول مرة أخرى بعد بضع دقائق إذا لم يظهر طلبك.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">تأكّد من تسجيل دخولك باستخدام حساب Google نفسه المستخدَم في عملية الشراء.</string>
|
||||
<string name="settings_monitor_connected_notification_label">إشعار إضافي</string>
|
||||
<string name="settings_monitor_connected_notification_description">يعرض إشعارًا إضافيًا عند توصيل جهاز. يتيح لك هذا إخفاء الإشعار الدائم \"لا توجد أجهزة\" عن طريق تعطيل قناة \"حالة الجهاز\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">الاحتفاظ بالإشعار بعد قطع الاتصال</string>
|
||||
<string name="settings_keep_notification_after_disconnect_description">الاستمرار في عرض مستويات البطارية المعروفة الأخيرة حتى بعد قطع اتصال AirPods</string>
|
||||
<string name="settings_autopause_label">إيقاف مؤقت تلقائي</string>
|
||||
<string name="settings_autopause_description">إيقاف الصوت مؤقتًا عند إزالة الجهاز من أذنك.</string>
|
||||
<string name="settings_autopause_label">إيقاف مؤقّت تلقائي</string>
|
||||
<string name="settings_autopause_description">إيقاف الصوت مؤقّتًا عند إزالة الجهاز من أذنك.</string>
|
||||
<string name="settings_autopplay_label">تشغيل تلقائي</string>
|
||||
<string name="settings_autoplay_description">بدء تشغيل الصوت عند ارتداء الجهاز.</string>
|
||||
<string name="settings_autoplay_description">استئناف الموسيقى عند ارتداء السماعات مجدّدًا، ولكن فقط إذا أوقفها الإيقاف التلقائي. الإيقافات التي شغّلتها بنفسك (الهاتف، ذراع AirPods، وضع السكون) تبقى متوقّفة.</string>
|
||||
<string name="settings_start_music_on_wear_label">تشغيل الموسيقى عند الارتداء</string>
|
||||
<string name="settings_start_music_on_wear_description">يحاول دائمًا تشغيل الموسيقى عند وضع السمّاعات، حتى بعد الإيقاف اليدوي.</string>
|
||||
<string name="settings_eardetection_info_label">ملاحظة اكتشاف الأذن</string>
|
||||
<string name="settings_eardetection_info_description">إذا كان اكتشاف الأذن يعمل لسماعة واحدة فقط، فهذا قيد من Apple. يتم اكتشاف \"السماعة الأساسية\" فقط (المستخدمة للميكروفون). قم بالتكوين على أجهزة Apple: الإعدادات → Bluetooth → AirPods → الميكروفون.</string>
|
||||
<string name="settings_fake_data_label">بيانات وهمية</string>
|
||||
<string name="settings_fake_data_description">عرض بيانات وهمية، أي محاكاة الأجهزة غير الموجودة.</string>
|
||||
<string name="settings_debug_label">إعدادات التصحيح</string>
|
||||
<string name="settings_debug_description">إعدادات إضافية للمساعدة في استكشاف المشكلات المتعلقة بالتطبيق وإصلاحها.</string>
|
||||
<string name="settings_eardetection_info_description">إذا كان اكتشاف الأذن يعمل لسماعة واحدة فقط، فهذا قيدٌ من Apple. يتم اكتشاف \"السمّاعة الأساسية\" فقط (المستخدَمة للميكروفون). اضبط على أجهزة Apple: الإعدادات → Bluetooth → AirPods → الميكروفون.</string>
|
||||
<string name="settings_signal_minimum_label">الحد الأدنى لجودة الإشارة</string>
|
||||
<string name="settings_signal_minimum_description">الحد الأدنى لجودة الإشارة التي يجب أن يتمتع بها الجهاز حتى يعتبر ملكك.</string>
|
||||
<string name="settings_signal_minimum_description">الحد الأدنى لجودة الإشارة التي يجب أن يتمتّع بها الجهاز حتى يُعدّ ملكك.</string>
|
||||
<string name="settings_autoconnect_label">اتصال تلقائي</string>
|
||||
<string name="settings_autoconnect_description">إذا لم يتصل Android تلقائيًا، فيمكننا أن نطلب منه ذلك أيضًا. سيؤدي هذا إلى ضبط إعداد وضع الشاشة على \"دائمًا\".</string>
|
||||
<string name="settings_autoconnect_description">إذا لم يتّصل Android تلقائيًا، فيمكننا أن نطلب منه ذلك أيضًا. سيؤدي هذا إلى ضبط إعداد وضع الشاشة على \"دائمًا\".</string>
|
||||
<string name="settings_autoconnect_info_android12">يعتمد الاتصال التلقائي على ميزة نظام يقيّدها Android 12 والإصدارات الأحدث. قد لا تعمل على جهازك.</string>
|
||||
<string name="settings_autoconnect_condition_label">شرط الاتصال التلقائي</string>
|
||||
<string name="settings_autoconnect_condition_description">متى يجب أن نحاول الاتصال بجهازك؟</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">إظهار نافذة منبثقة للاتصال</string>
|
||||
<string name="settings_popup_connected_description">إظهار نافذة منبثقة عند اتصال الجهاز لأول مرة.</string>
|
||||
<string name="settings_popup_info_not_in_app">تظهر النوافذ المنبثقة فقط عندما لا تكون داخل التطبيق.</string>
|
||||
<string name="settings_popup_warning_manual_mode">تتطلب النوافذ المنبثقة تشغيل المراقب. وضع المراقبة الخاص بك مضبوط على \"عند فتح التطبيق\"، مما يوقف المراقب عند مغادرة التطبيق.</string>
|
||||
<string name="general_fix_it_action">إصلاحه</string>
|
||||
<string name="settings_charged_notification_label">إشعار الشحن الكامل</string>
|
||||
<string name="settings_charged_notification_description">اعرض إشعارًا بمجرد وصول جميع الأجهزة التي يتم شحنها إلى مستوى الشحن المستهدف.</string>
|
||||
<string name="settings_charged_threshold_label">مستوى الشحن المستهدف</string>
|
||||
<string name="settings_charged_scope_label">البطاريات المُراقبة</string>
|
||||
<string name="settings_charged_scope_pods_label">السمّاعات فقط</string>
|
||||
<string name="settings_charged_scope_case_label">الحافظة فقط</string>
|
||||
<string name="settings_charged_scope_both_label">السماعات والحافظة</string>
|
||||
<string name="device_settings_experimental_title">ميزة تجريبية</string>
|
||||
<string name="device_settings_experimental_description">لم يتم اختبار هذه الميزة على جميع الأجهزة بعد. إذا كان هناك شيء لا يعمل بشكل صحيح، يرجى فتح تقرير مشكلة مع سجل تصحيح.</string>
|
||||
<string name="device_settings_experimental_action">فتح متتبع المشكلات</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">تصحيح الأخطاء</string>
|
||||
<string name="settings_general_label">الإعدادات</string>
|
||||
<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="settings_acknowledgements_label">شكر وتقدير</string>
|
||||
<string name="settings_debug_autoreports_label">تقارير الأخطاء التلقائية</string>
|
||||
<string name="settings_debug_autoreports_description">الإبلاغ تلقائيًا عن المشكلات، على سبيل المثال تفاصيل حول تعطل التطبيق حتى أتمكن من معرفة كيفية إصلاحه.</string>
|
||||
<string name="settings_debug_mode_label">وضع التصحيح</string>
|
||||
<string name="settings_debug_mode_description">عرض معلومات إضافية لاستكشاف المشكلات وإصلاحها.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">بيانات BLE غير المصفاة</string>
|
||||
<string name="settings_blescanner_unfiltered_description">إزالة أي عوامل تصفية من ماسح BLE لعرض جميع بيانات BLE المذاعة. مفيد لإضافة دعم لأنواع سماعات الرأس الجديدة.</string>
|
||||
<string name="settings_compatibility_mode_label">وضع التوافق</string>
|
||||
<string name="settings_compatibility_mode_description">تعطيل التحسينات لتحسين التوافق. جرب هذا إذا كنت لا ترى أي بيانات.</string>
|
||||
<string name="help_translate_label">الترجمة</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">يتطلب هذا الودجت اتصالاً مباشراً (AAP) بجهازك. تأكد من إقران جهازك وتوصيله في التطبيق قبل استخدام هذا الودجت.</string>
|
||||
<string name="anc_widget_no_anc_support_label">هذا الجهاز لا يدعم التحكم في الضوضاء</string>
|
||||
<string name="anc_widget_aap_not_connected_label">غير متصل</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">قريب ولكن غير متّصل</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">غير قريب</string>
|
||||
<string name="anc_widget_aap_connecting_label">جارٍ الاتصال…</string>
|
||||
<string name="tile_anc_label">التحكم في الضوضاء</string>
|
||||
<string name="tile_anc_subtitle_permission_required">الإذن مطلوب</string>
|
||||
<string name="tile_anc_subtitle_no_device">لا يوجد جهاز</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">لا يوجد تحكم في الضوضاء</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">البلوتوث مغلق</string>
|
||||
<string name="widget_config_screen_title">إعداد عنصر الواجهة</string>
|
||||
<string name="widget_configuration_title">اختيار الجهاز</string>
|
||||
<string name="widget_configuration_description">اختر ملف تعريف الجهاز الذي يجب أن تعرضه هذه الأداة.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">البلوتوث معطل، قم بتمكينه ;)</string>
|
||||
<string name="overview_monitoring_active_label">جارٍ مراقبة الأجهزة</string>
|
||||
<string name="overview_monitoring_active_description">تأكد من أن جهازك قريب ونشط.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">متصل، ولكن لا توجد بيانات</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">جهازك متصل، لكن تطبيق CAPod لا يستقبل أي بيانات مباشرة منه. قد يحتاج هاتفك إلى خيار توافق - يمكن لمُستكشف الأخطاء ومُصلحها محاولة إيجاد واحد تلقائيًّا.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">تشغيل مُستكشف الأخطاء ومُصلحها</string>
|
||||
<string name="overview_unmatched_devices_label">أجهزة غير متطابقة</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="zero">%d أجهزة بدون ملف تعريف مطابق</item>
|
||||
@@ -228,9 +237,6 @@
|
||||
<string name="permission_required_title">الإذن التالي مطلوب:</string>
|
||||
<string name="permission_system_alert_window_label">نافذة تنبيه النظام</string>
|
||||
<string name="permission_system_alert_window_description">السماح لـ CAPod بالرسم فوق التطبيقات الأخرى لتمكين ميزة \"إظهار النافذة المنبثقة\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">عند فتح التطبيق</string>
|
||||
<string name="settings_monitor_mode_automatic_label">عند اتصال الجهاز</string>
|
||||
<string name="settings_monitor_mode_always_label">دائمًا</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">عند الرؤية</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">العلبة مفتوحة</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">في الأذن</string>
|
||||
@@ -262,6 +268,11 @@
|
||||
<string name="anc_mode_transparency">الشفافية</string>
|
||||
<string name="anc_mode_adaptive">تكيفي</string>
|
||||
<string name="conversation_awareness_label">الوعي بالمحادثة</string>
|
||||
<string name="settings_conversation_action_label">عند بدء الكلام</string>
|
||||
<string name="settings_conversation_action_nothing_label">لا تفعل شيئًا</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">خفض مستوى الصوت</string>
|
||||
<string name="settings_conversation_action_pause_label">إيقاف الوسائط</string>
|
||||
<string name="settings_conversation_volume_reduction_label">تخفيض مستوى الصوت</string>
|
||||
<string name="signal_badge_ble_cd">مسح BLE نشط</string>
|
||||
<string name="signal_badge_key_irk_cd">تم التحقق من الهوية</string>
|
||||
<string name="signal_badge_key_encrypted_cd">اتصال مشفر</string>
|
||||
@@ -330,7 +341,6 @@
|
||||
<string name="ui_theme_color_amber_label">كهرماني</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">يستخدم ألوان الخلفية</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">المراقبة</string>
|
||||
<string name="settings_category_appearance_label">المظهر</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">تغييرات غير محفوظة</string>
|
||||
@@ -490,7 +500,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">عام</string>
|
||||
<string name="device_settings_microphone_mode_label">ميكروفون</string>
|
||||
<string name="device_settings_microphone_mode_description">أي AirPod يُستخدم كميكروفون</string>
|
||||
<string name="device_settings_microphone_mode_description">سمّاعة الأذن المستخدمة كميكروفون</string>
|
||||
<string name="device_settings_microphone_mode_auto">تلقائي</string>
|
||||
<string name="device_settings_microphone_mode_right">يمين</string>
|
||||
<string name="device_settings_microphone_mode_left">يسار</string>
|
||||
@@ -512,6 +522,10 @@
|
||||
<string name="reaction_sleep_channel_label">اكتشاف النوم</string>
|
||||
<string name="reaction_sleep_notification_title">تم الإيقاف المؤقت بواسطة اكتشاف النوم</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s أفاد بأنك نمت، لذا تم إيقاف موسيقاك مؤقتًا. يمكنك تعطيل هذا في إعدادات الجهاز.</string>
|
||||
<string name="reaction_charged_channel_label">إشعار الشحن الكامل</string>
|
||||
<string name="reaction_charged_notification_title">اكتمل الشحن</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s مشحون بالكامل.</string>
|
||||
<string name="reaction_charged_notification_text_partial">تم شحن %1$s إلى %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">إعادة تسمية</string>
|
||||
<string name="device_settings_rename_hint">اسم الجهاز</string>
|
||||
<string name="device_settings_rename_confirm">إعادة تسمية</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Əgər bu yaxınlarda satın almısınızsa, Google Play-in sinxronlaşması bir az vaxt ala bilər.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Alışınız görünmürsə bir neçə dəqiqədən sonra yenidən cəhd edin.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Alış üçün istifadə edilmiş eyni Google hesabı ilə daxil olduğunuzu yoxlayın.</string>
|
||||
<string name="settings_monitor_mode_label">İzləmə rejimi</string>
|
||||
<string name="settings_monitor_mode_description">Bu tətbiqin Bluetooth məlumatlarını hansı şəraitdə izlədiyi.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Əlavə bildiriş</string>
|
||||
<string name="settings_monitor_connected_notification_description">Cihaz qoşulduqda əlavə bildiriş göstərir. Bu, \"Cihaz statusu\" kanalını deaktiv etməklə daimi \"Cihaz yoxdur\" bildirişini gizlətməyə imkan verir.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Ayrıldıqdan sonra bildirişi saxla</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Avtomatik fasilə</string>
|
||||
<string name="settings_autopause_description">Cihazı qulağınızdan çıxardıqda səsi dayandırın.</string>
|
||||
<string name="settings_autopplay_label">Avtomatik oxutma</string>
|
||||
<string name="settings_autoplay_description">Cihaz taxıldıqda səs oxutmasını başladın.</string>
|
||||
<string name="settings_autoplay_description">Podlarınızı yenidən taxdıqda musiqini davam etdirin, lakin yalnız Avtomatik Fasilə onu dayandırdıqda. Özünüz dayandırdığınız fasilələr (telefon, AirPods sapı, yuxu) fasilə vəziyyətində qalır.</string>
|
||||
<string name="settings_start_music_on_wear_label">Taxdıqda musiqini başlat</string>
|
||||
<string name="settings_start_music_on_wear_description">Podlarınızı taxdıqda həmişə musiqi çalmağa çalışır, hətta əl ilə fasilə verdikdən sonra belə.</string>
|
||||
<string name="settings_eardetection_info_label">Qulaq aşkarlama qeydi</string>
|
||||
<string name="settings_eardetection_info_description">Qulaq aşkarlama yalnız bir qulaqlıq üçün işləyirsə, bu Apple məhdudiyyətidir. Yalnız \"əsas qulaqlıq\" (mikrofon üçün istifadə olunan) aşkarlanır. Apple cihazlarında tənzimləyin: Parametrlər → Bluetooth → AirPods → Mikrofon.</string>
|
||||
<string name="settings_fake_data_label">Saxta məlumat</string>
|
||||
<string name="settings_fake_data_description">Saxta məlumat göstər, yəni mövcud olmayan cihazları simulyasiya et.</string>
|
||||
<string name="settings_debug_label">Sazlama parametrləri</string>
|
||||
<string name="settings_debug_description">Tətbiqlə bağlı problemlərin aradan qaldırılmasına kömək edən əlavə parametrlər.</string>
|
||||
<string name="settings_signal_minimum_label">Minimum siqnal keyfiyyəti</string>
|
||||
<string name="settings_signal_minimum_description">Bir cihazın sizə aid hesab edilməsi üçün malik olmalı olduğu minimum siqnal keyfiyyəti.</string>
|
||||
<string name="settings_autoconnect_label">Avtomatik qoşulma</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Qoşulma pop-up-ını göstər</string>
|
||||
<string name="settings_popup_connected_description">Cihaz ilk dəfə qoşulduqda pop-up göstər.</string>
|
||||
<string name="settings_popup_info_not_in_app">Açılan pəncərələr yalnız tətbiqdə olmadığınız zaman göstərilir.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Açılan pəncərələr monitorun işləməsini tələb edir. Monitor rejiminizdə \"Tətbiq açıq olduqda\" seçilmişdir — bu, tətbiqdən çıxdığınız zaman monitoru dayandırır.</string>
|
||||
<string name="general_fix_it_action">Düzəlt</string>
|
||||
<string name="settings_charged_notification_label">Şarj bildirişi</string>
|
||||
<string name="settings_charged_notification_description">Şarj olunan hər şey hədəf şarj səviyyəsinə çatdıqda bildiriş göndərin.</string>
|
||||
<string name="settings_charged_threshold_label">Hədəf şarj səviyyəsi</string>
|
||||
<string name="settings_charged_scope_label">İzlənilən akkumulyatorlar</string>
|
||||
<string name="settings_charged_scope_pods_label">Yalnız qulaqcıqlar</string>
|
||||
<string name="settings_charged_scope_case_label">Yalnız qutu</string>
|
||||
<string name="settings_charged_scope_both_label">Qulaqcıqlar və qutu</string>
|
||||
<string name="device_settings_experimental_title">Eksperimental funksiya</string>
|
||||
<string name="device_settings_experimental_description">Bu funksiya hələ bütün cihazlarda sınaqdan keçirilməyib. Bir şey düzgün işləmirsə, debug loqu ilə problem bildirin.</string>
|
||||
<string name="device_settings_experimental_action">Problem İzləyicisini Aç</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Sazlama</string>
|
||||
<string name="settings_general_label">Parametrlər</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Sazlama rejimi</string>
|
||||
<string name="settings_debug_mode_description">Problemlərin aradan qaldırılması üçün əlavə məlumat göstərin.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Filtrlənməmiş BLE məlumatları</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Bütün yayımlanan BLE məlumatlarını göstərmək üçün BLE skanerindən hər hansı bir filtri çıxarın. Yeni qulaqlıq növləri üçün dəstək əlavə etmək üçün faydalıdır.</string>
|
||||
<string name="settings_compatibility_mode_label">Uyğunluq rejimi</string>
|
||||
<string name="settings_compatibility_mode_description">Uyğunluğu yaxşılaşdırmaq üçün optimallaşdırmaları deaktiv edin. Heç bir məlumat görmürsünüzsə, bunu sınayın.</string>
|
||||
<string name="help_translate_label">Tərcümə</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Bu vidcet cihazınızla birbaşa bağlantı (AAP) tələb edir. Bu vidceti istifadə etməzdən əvvəl cihazınızın tətbiqdə cütləşdirilmiş və qoşulu olduğuna əmin olun.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Bu cihaz səs-küy idarəetməsini dəstəkləmir</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Qoşulmayıb</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Yaxındadır, lakin qoşulmayıb</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Yaxında deyil</string>
|
||||
<string name="anc_widget_aap_connecting_label">Qoşulur…</string>
|
||||
<string name="tile_anc_label">Səs-küy Nəzarəti</string>
|
||||
<string name="tile_anc_subtitle_permission_required">İcazə tələb olunur</string>
|
||||
<string name="tile_anc_subtitle_no_device">Cihaz yoxdur</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Səs-küy nəzarəti yoxdur</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth söndürülüb</string>
|
||||
<string name="widget_config_screen_title">Vidcet Konfiqurasiyası</string>
|
||||
<string name="widget_configuration_title">Cihaz seçin</string>
|
||||
<string name="widget_configuration_description">Bu vidcetin hansı cihaz profilini göstərəcəyini seçin.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth deaktivdir, onu aktivləşdirin ;)</string>
|
||||
<string name="overview_monitoring_active_label">Cihazlar axtarılır</string>
|
||||
<string name="overview_monitoring_active_description">Cihazınızın yaxında və aktiv olduğundan əmin olun.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Qoşuldu, lakin məlumat yoxdur</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Cihazınız qoşulub, lakin CAPod ondan canlı məlumat almır. Telefonunuz uyğunluq seçiminə ehtiyac duya bilər — problem aradan qaldırıcı birini avtomatik tapmağa çalışa bilər.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Problem aradan qaldırıcını işə sal</string>
|
||||
<string name="overview_unmatched_devices_label">Uyğunlaşdırılmamış cihazlar</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">Uyğun profili olmayan %d cihaz</item>
|
||||
@@ -220,16 +229,13 @@
|
||||
<string name="permission_required_title">Aşağıdakı icazə tələb olunur:</string>
|
||||
<string name="permission_system_alert_window_label">Sistem xəbərdarlıq pəncərəsi</string>
|
||||
<string name="permission_system_alert_window_description">\"Pop-up göstər\" xüsusiyyətini mümkün etmək üçün CAPod-a digər tətbiqlərin üzərində çəkməyə icazə verin.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Tətbiq açıq olduqda</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Cihaz qoşulduqda</string>
|
||||
<string name="settings_monitor_mode_always_label">Həmişə</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Görüldüyündə</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Qutu açıqdır</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Qulaqda</string>
|
||||
<string name="pods_dual_left_label">Sol qulaqlıq</string>
|
||||
<string name="pods_dual_right_label">Sağ qulaqlıq</string>
|
||||
<string name="pods_dual_left_short_label">S</string>
|
||||
<string name="pods_dual_right_short_label">SĚ</string>
|
||||
<string name="pods_dual_right_short_label">S</string>
|
||||
<string name="pods_case_label">Qutu</string>
|
||||
<string name="battery_unavailable_label">Batareya məlumatı yoxdur</string>
|
||||
<string name="pods_case_status_open_label">Açıq</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Şəffaflıq</string>
|
||||
<string name="anc_mode_adaptive">Adaptiv</string>
|
||||
<string name="conversation_awareness_label">Söhbət Məlumatlılığı</string>
|
||||
<string name="settings_conversation_action_label">Danışmağa başladıqda</string>
|
||||
<string name="settings_conversation_action_nothing_label">Heç nə etmə</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Səsi azalt</string>
|
||||
<string name="settings_conversation_action_pause_label">Medianı dayandır</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Səs azaldılması</string>
|
||||
<string name="signal_badge_ble_cd">BLE skanı aktiv</string>
|
||||
<string name="signal_badge_key_irk_cd">Kimlik təsdiqləndi</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Şifrəli bağlantı</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Kəhrəba</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Divar kăğizi rənglərini istifadə edir</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitorinq</string>
|
||||
<string name="settings_category_appearance_label">Görünüş</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Saxlanmamış dəyişikliklər</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Ümumi</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Mikrofonun hansı AirPod tərəfindən istifadə edildiyini</string>
|
||||
<string name="device_settings_microphone_mode_description">Mikrofon kimi hansı qulaqlıq istifadə olunur</string>
|
||||
<string name="device_settings_microphone_mode_auto">Avtomatik</string>
|
||||
<string name="device_settings_microphone_mode_right">Sağ</string>
|
||||
<string name="device_settings_microphone_mode_left">Sol</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Yuxu Aşkarlaması</string>
|
||||
<string name="reaction_sleep_notification_title">Yuxu Aşkarlaması ilə dayandırıldı</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s sizin yuxuya getdiyinizi bildirdi, buna görə musiqiniz dayandırıldı. Bunu cihaz parametrlərindən söndürə bilərsiniz.</string>
|
||||
<string name="reaction_charged_channel_label">Şarj Bildirişi</string>
|
||||
<string name="reaction_charged_notification_title">Şarj tamamlandı</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s tam şarj olundu.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s %2$d%% şarj olundu.</string>
|
||||
<string name="device_settings_rename_label">Adı dəyişdirin</string>
|
||||
<string name="device_settings_rename_hint">Cihaz adı</string>
|
||||
<string name="device_settings_rename_confirm">Adı dəyişdirin</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Калі вы нядаўна зрабілі пакупку, Google Play можа спатрэбіцца некаторы час для сінхранізацыі.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Паспрабуйце яшчэ раз праз некалькі хвілін, калі ваша пакупка не з\'яўляецца.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Пераканайцеся, што вы ўвайшлі ў той самы акаўнт Google, які быў выкарыстаны для пакупкі.</string>
|
||||
<string name="settings_monitor_mode_label">Рэжым маніторынгу</string>
|
||||
<string name="settings_monitor_mode_description">Пры якіх абставінах гэта праграма сочыць за данымі Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Дадатковае апавяшчэнне</string>
|
||||
<string name="settings_monitor_connected_notification_description">Паказвае дадатковае апавяшчэнне пры падключэнні прылады. Гэта дазваляе схаваць пастаяннае апавяшчэнне \"Няма прылад\" шляхам адключэння канала \"Стан прылады\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Захаваць апавяшчэнне пасля адключэння</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Аўтаматычная паўза</string>
|
||||
<string name="settings_autopause_description">Прыпыняць аўдыя пры выманні прылады з вуха.</string>
|
||||
<string name="settings_autopplay_label">Аўтаматычнае прайграванне</string>
|
||||
<string name="settings_autoplay_description">Пачынаць прайграванне аўдыя пры надзяванні прылады.</string>
|
||||
<string name="settings_autoplay_description">Аднавіць музыку пры паўторным надзяванні навушнікаў, але толькі калі яе спыніла аўтапаўза. Паўзы, якія вы самі ўключылі (тэлефон, ножка AirPods, сон), застаюцца.</string>
|
||||
<string name="settings_start_music_on_wear_label">Пачынаць музыку пры надзяванні</string>
|
||||
<string name="settings_start_music_on_wear_description">Заўсёды спрабуе прайграць музыку, калі вы надзяваеце навушнікі, нават пасля ручной паўзы.</string>
|
||||
<string name="settings_eardetection_info_label">Заўвага пра вызначэнне вуха</string>
|
||||
<string name="settings_eardetection_info_description">Калі вызначэнне вуха працуе толькі для аднаго навушніка, гэта абмежаванне Apple. Вызначаецца толькі \"асноўны навушнік\" (выкарыстоўваецца для мікрафона). Наладзьце на прыладах Apple: Налады → Bluetooth → AirPods → Мікрафон.</string>
|
||||
<string name="settings_fake_data_label">Падробленыя даныя</string>
|
||||
<string name="settings_fake_data_description">Паказваць падробленыя даныя, г.зн. імітаваць неіснуючыя прылады.</string>
|
||||
<string name="settings_debug_label">Налады адладкі</string>
|
||||
<string name="settings_debug_description">Дадатковыя налады для ліквідацыі непаладак з праграмай.</string>
|
||||
<string name="settings_signal_minimum_label">Мінімальная якасць сігналу</string>
|
||||
<string name="settings_signal_minimum_description">Мінімальная якасць сігналу, якую павінна мець прылада, каб лічыцца вашай.</string>
|
||||
<string name="settings_autoconnect_label">Аўтаматычнае падключэнне</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Паказаць усплывальнае акно падключэння</string>
|
||||
<string name="settings_popup_connected_description">Паказваць усплывальнае акно пры першым падключэнні прылады.</string>
|
||||
<string name="settings_popup_info_not_in_app">Усплывальныя вокны паказваюцца толькі тады, калі вы не знаходзіцеся ў прыкладанні.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Усплывальныя вокны патрабуюць працы манітора. Рэжым манітора ўсталяваны на «Калі прыкладанне адкрыта», які спыняе маніторынг пры выхадзе з прыкладання.</string>
|
||||
<string name="general_fix_it_action">Выправіць</string>
|
||||
<string name="settings_charged_notification_label">Апавяшчэнне аб зарадцы</string>
|
||||
<string name="settings_charged_notification_description">Паказваць апавяшчэнне, калі ўсё, што зараджаецца, дасягне мэтавага ўзроўню зарадкі.</string>
|
||||
<string name="settings_charged_threshold_label">Мэтавы ўзровень зарадкі</string>
|
||||
<string name="settings_charged_scope_label">Адсочваемыя батарэі</string>
|
||||
<string name="settings_charged_scope_pods_label">Толькі навушнікі</string>
|
||||
<string name="settings_charged_scope_case_label">Толькі футарал</string>
|
||||
<string name="settings_charged_scope_both_label">Навушнікі і футарал</string>
|
||||
<string name="device_settings_experimental_title">Эксперыментальная функцыя</string>
|
||||
<string name="device_settings_experimental_description">Гэта функцыя яшчэ не была праверана на ўсіх прыладах. Калі нешта не працуе правільна, калі ласка, стварыце запыт з журналам адладкі.</string>
|
||||
<string name="device_settings_experimental_action">Адкрыць трэкер задач</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Адладка</string>
|
||||
<string name="settings_general_label">Налады</string>
|
||||
<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="settings_acknowledgements_label">Падзякі</string>
|
||||
<string name="settings_debug_autoreports_label">Аўтаматычныя справаздачы пра памылкі</string>
|
||||
<string name="settings_debug_autoreports_description">Аўтаматычна паведамляе пра праблемы, напрыклад, звесткі пра збой праграмы, каб я мог зразумець, як гэта выправіць.</string>
|
||||
<string name="settings_debug_mode_label">Рэжым адладкі</string>
|
||||
<string name="settings_debug_mode_description">Паказваць дадатковую інфармацыю для ліквідацыі непаладак.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Нефільтраваныя даныя BLE</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Выдаліце ўсе фільтры са сканера BLE, каб паказаць усе трансляваныя даныя BLE. Карысна для дадання падтрымкі новых тыпаў навушнікаў.</string>
|
||||
<string name="settings_compatibility_mode_label">Рэжым сумяшчальнасці</string>
|
||||
<string name="settings_compatibility_mode_description">Адключыце аптымізацыі для паляпшэння сумяшчальнасці. Паспрабуйце гэта, калі вы не бачыце ніякіх даных.</string>
|
||||
<string name="help_translate_label">Пераклад</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Гэты віджэт патрабуе прамога злучэння (AAP) з вашай прыладай. Пераканайцеся, што ваша прылада спалучана і падключана ў праграме перад выкарыстаннем гэтага віджэта.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Гэта прылада не падтрымлівае кіраванне шумам</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Не падключана</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Побач, але не падключана</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Не побач</string>
|
||||
<string name="anc_widget_aap_connecting_label">Падключэнне…</string>
|
||||
<string name="tile_anc_label">Кіраванне шумам</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Патрабуецца дазвол</string>
|
||||
<string name="tile_anc_subtitle_no_device">Няма прылады</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Няма кіравання шумам</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth выключаны</string>
|
||||
<string name="widget_config_screen_title">Канфігурацыя віджэта</string>
|
||||
<string name="widget_configuration_title">Выбраць прыладу</string>
|
||||
<string name="widget_configuration_description">Выберыце, які профіль прылады павінен адлюстроўваць гэты віджэт.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth адключаны, уключыце яго ;)</string>
|
||||
<string name="overview_monitoring_active_label">Маніторынг прылад</string>
|
||||
<string name="overview_monitoring_active_description">Пераканайцеся, што ваша прылада побач і актыўная.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Падлучана, але дадзеных няма</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Ваша прылада падлучана, але CAPod не атрымлівае ніякіх жывых дадзеных з яе. Вашаму тэлефону можа спатрэбіцца параметр сумяшчальнасці — ліквідатар непаладак можа паспрабаваць знайсці яго аўтаматычна.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Запусціць ліквідатар непаладак</string>
|
||||
<string name="overview_unmatched_devices_label">Неадпаведныя прылады</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d прылада без адпаведнага профілю</item>
|
||||
@@ -224,9 +233,6 @@
|
||||
<string name="permission_required_title">Патрабуецца наступны дазвол:</string>
|
||||
<string name="permission_system_alert_window_label">Сістэмнае акно папярэджання</string>
|
||||
<string name="permission_system_alert_window_description">Дазвольце CAPod маляваць паверх іншых праграм, каб зрабіць магчымай функцыю \"Паказаць усплывальнае акно\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">Калі праграма адкрыта</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Калі прылада падключана</string>
|
||||
<string name="settings_monitor_mode_always_label">Заўсёды</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Калі бачна</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Корпус адкрыты</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">У вуху</string>
|
||||
@@ -258,6 +264,11 @@
|
||||
<string name="anc_mode_transparency">Празрыстасць</string>
|
||||
<string name="anc_mode_adaptive">Адаптыўны</string>
|
||||
<string name="conversation_awareness_label">Усведамленне размовы</string>
|
||||
<string name="settings_conversation_action_label">Калі вы пачынаеце гаварыць</string>
|
||||
<string name="settings_conversation_action_nothing_label">Нічога не рабіць</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Зменшыць гучнасць</string>
|
||||
<string name="settings_conversation_action_pause_label">Паставіць медыя на паўзу</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Зніжэнне гучнасці</string>
|
||||
<string name="signal_badge_ble_cd">BLE-сканаванне актыўнае</string>
|
||||
<string name="signal_badge_key_irk_cd">Асоба пацверджана</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Зашыфраванае злучэнне</string>
|
||||
@@ -326,7 +337,6 @@
|
||||
<string name="ui_theme_color_amber_label">Бурштынавы</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Выкарыстоўвае колеры шпалеры</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Маніторынг</string>
|
||||
<string name="settings_category_appearance_label">Знешні выгляд</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Незахаваныя змены</string>
|
||||
@@ -480,7 +490,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Агульныя</string>
|
||||
<string name="device_settings_microphone_mode_label">Мікрафон</string>
|
||||
<string name="device_settings_microphone_mode_description">Які AirPod выкарыстоўваецца як мікрафон</string>
|
||||
<string name="device_settings_microphone_mode_description">Які навушнік выкарыстоўваецца як мікрафон</string>
|
||||
<string name="device_settings_microphone_mode_auto">Аўта</string>
|
||||
<string name="device_settings_microphone_mode_right">Правы</string>
|
||||
<string name="device_settings_microphone_mode_left">Левы</string>
|
||||
@@ -502,6 +512,10 @@
|
||||
<string name="reaction_sleep_channel_label">Выяўленне сну</string>
|
||||
<string name="reaction_sleep_notification_title">Прыпынена функцыяй выяўлення сну</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s паведаміла, што вы заснулі, таму музыка была прыпынена. Вы можаце адключыць гэта ў наладах прылады.</string>
|
||||
<string name="reaction_charged_channel_label">Апавяшчэнне аб зарадцы</string>
|
||||
<string name="reaction_charged_notification_title">Зарадка завершана</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s поўнасцю зараджаны.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s зараджаны да %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Перайменаваць</string>
|
||||
<string name="device_settings_rename_hint">Назва прылады</string>
|
||||
<string name="device_settings_rename_confirm">Перайменаваць</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Ако наскоро сте направили покупка, може да отнеме малко, докато Google Play синхронизира.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Опитайте отново след няколко минути, ако покупката ви не се появи.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Уверете се, че сте влезли с акаунта в Google, използван за покупката.</string>
|
||||
<string name="settings_monitor_mode_label">Режим на наблюдение</string>
|
||||
<string name="settings_monitor_mode_description">При какви обстоятелства това приложение следи Bluetooth данните.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Допълнително известие</string>
|
||||
<string name="settings_monitor_connected_notification_description">Показва допълнително известие, когато устройство е свързано. Това ви позволява да скриете постоянното известие \"Няма устройства\" чрез деактивиране на канала \"Състояние на устройството\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Запази известието след прекъсване</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Автоматична пауза</string>
|
||||
<string name="settings_autopause_description">Поставяне на аудио на пауза при премахване на устройството от ухото ви.</string>
|
||||
<string name="settings_autopplay_label">Автоматично възпроизвеждане</string>
|
||||
<string name="settings_autoplay_description">Стартиране на аудио възпроизвеждане, когато устройството се носи.</string>
|
||||
<string name="settings_autoplay_description">Възобновява музиката, когато отново сложите слушалките, но само ако е спряна от Автоматично пауза. Паузите, направени от вас (телефон, дръжка на AirPods, заспиване), остават.</string>
|
||||
<string name="settings_start_music_on_wear_label">Стартиране на музика при поставяне</string>
|
||||
<string name="settings_start_music_on_wear_description">Винаги се опитва да пусне музика, когато сложите слушалките, дори след ръчна пауза.</string>
|
||||
<string name="settings_eardetection_info_label">Бележка за разпознаване на ухото</string>
|
||||
<string name="settings_eardetection_info_description">Ако разпознаването на ухото работи само за една слушалка, това е ограничение на Apple. Само \"основната слушалка\" (използвана за микрофон) се разпознава. Конфигурирайте на Apple устройства: Настройки → Bluetooth → AirPods → Микрофон.</string>
|
||||
<string name="settings_fake_data_label">Фалшиви данни</string>
|
||||
<string name="settings_fake_data_description">Показване на фалшиви данни, т.е. симулиране на устройства, които не съществуват.</string>
|
||||
<string name="settings_debug_label">Настройки за отстраняване на грешки</string>
|
||||
<string name="settings_debug_description">Допълнителни настройки за подпомагане на отстраняването на проблеми с приложението.</string>
|
||||
<string name="settings_signal_minimum_label">Минимално качество на сигнала</string>
|
||||
<string name="settings_signal_minimum_description">Минималното качество на сигнала, което устройството трябва да има, за да се счита за ваше.</string>
|
||||
<string name="settings_autoconnect_label">Автоматично свързване</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Показване на изскачащ прозорец за връзка</string>
|
||||
<string name="settings_popup_connected_description">Показване на изскачащ прозорец, когато устройството се свърже за първи път.</string>
|
||||
<string name="settings_popup_info_not_in_app">Изскачащите прозорци се показват само когато не сте в приложението.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Изскачащите прозорци изискват мониторът да работи. Режимът на монитора е зададен на „Когато приложението е отворено“, което спира монитора, когато напуснете приложението.</string>
|
||||
<string name="general_fix_it_action">Поправи го</string>
|
||||
<string name="settings_charged_notification_label">Известие за зареждане</string>
|
||||
<string name="settings_charged_notification_description">Показва известие, когато всичко, което се зарежда, достигне желаното ниво на заряд.</string>
|
||||
<string name="settings_charged_threshold_label">Целево ниво на заряд</string>
|
||||
<string name="settings_charged_scope_label">Наблюдавани батерии</string>
|
||||
<string name="settings_charged_scope_pods_label">Само слушалки</string>
|
||||
<string name="settings_charged_scope_case_label">Само калъф</string>
|
||||
<string name="settings_charged_scope_both_label">Слушалки и калъф</string>
|
||||
<string name="device_settings_experimental_title">Експериментална функция</string>
|
||||
<string name="device_settings_experimental_description">Тази функция все още не е тествана на всички устройства. Ако нещо не работи правилно, моля отворете проблем с дневник за отстраняване на грешки.</string>
|
||||
<string name="device_settings_experimental_action">Отвори проследявач на проблеми</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Отстраняване на грешки</string>
|
||||
<string name="settings_general_label">Настройки</string>
|
||||
<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="settings_acknowledgements_label">Благодарности</string>
|
||||
<string name="settings_debug_autoreports_label">Автоматични доклади за грешки</string>
|
||||
<string name="settings_debug_autoreports_description">Автоматично докладва проблеми, напр. подробности за срив на приложението, за да мога да разбера как да го поправя.</string>
|
||||
<string name="settings_debug_mode_label">Режим за отстраняване на грешки</string>
|
||||
<string name="settings_debug_mode_description">Показване на допълнителна информация за отстраняване на проблеми.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Нефилтрирани BLE данни</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Премахнете всички филтри от BLE скенера, за да се покажат всички излъчвани BLE данни. Полезно за добавяне на поддръжка за нови типове слушалки.</string>
|
||||
<string name="settings_compatibility_mode_label">Режим на съвместимост</string>
|
||||
<string name="settings_compatibility_mode_description">Деактивирайте оптимизациите, за да подобрите съвместимостта. Опитайте това, ако не виждате никакви данни.</string>
|
||||
<string name="help_translate_label">Превод</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Този уиджет изисква директна връзка (AAP) с вашето устройство. Уверете се, че устройството е сдвоено и свързано в приложението преди да използвате този уиджет.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Това устройство не поддържа контрол на шума</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Не е свързано</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Наблизо, но не свързано</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Не е наблизо</string>
|
||||
<string name="anc_widget_aap_connecting_label">Свързване…</string>
|
||||
<string name="tile_anc_label">Шумов контрол</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Необходимо е разрешение</string>
|
||||
<string name="tile_anc_subtitle_no_device">Няма устройство</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Без шумов контрол</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth е изключен</string>
|
||||
<string name="widget_config_screen_title">Настройка на джаджата</string>
|
||||
<string name="widget_configuration_title">Избор на устройство</string>
|
||||
<string name="widget_configuration_description">Изберете кой профил на устройство да показва това приспособление.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth е деактивиран, активирайте го ;)</string>
|
||||
<string name="overview_monitoring_active_label">Наблюдение на устройства</string>
|
||||
<string name="overview_monitoring_active_description">Уверете се, че устройството ви е наблизо и активно.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Свързан, но без данни</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Устройството ти е свързано, но CAPod не получава данни от него в реално време. Телефонът ти може да се нуждае от опция за съвместимост — инструментът за отстраняване на неизправности може да се опита да намери такава автоматично.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Стартирай отстраняване на неизправности</string>
|
||||
<string name="overview_unmatched_devices_label">Несъвпадащи устройства</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d устройство без съвпадащ профил</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Необходимо е следното разрешение:</string>
|
||||
<string name="permission_system_alert_window_label">Системен изскачащ прозорец</string>
|
||||
<string name="permission_system_alert_window_description">Позволете на CAPod да рисува върху други приложения, за да направи функцията \"Покажи изскачащ прозорец\" възможна.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Когато приложението е отворено</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Когато устройството е свързано</string>
|
||||
<string name="settings_monitor_mode_always_label">Винаги</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Когато е видяно</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Калъфът е отворен</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">В ухото</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Прозрачност</string>
|
||||
<string name="anc_mode_adaptive">Адаптивен</string>
|
||||
<string name="conversation_awareness_label">Разговорна осъзнатост</string>
|
||||
<string name="settings_conversation_action_label">Когато започнете да говорите</string>
|
||||
<string name="settings_conversation_action_nothing_label">Без действие</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Намаляване на силата на звука</string>
|
||||
<string name="settings_conversation_action_pause_label">Пауза на медията</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Намаляване на силата на звука</string>
|
||||
<string name="signal_badge_ble_cd">BLE сканирането е активно</string>
|
||||
<string name="signal_badge_key_irk_cd">Самоличността е потвърдена</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Шифрована връзка</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Къхъленожълт</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Използва цветовете на тапета</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Мониторинг</string>
|
||||
<string name="settings_category_appearance_label">Изглед</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Незапазени промени</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Общи</string>
|
||||
<string name="device_settings_microphone_mode_label">Микрофон</string>
|
||||
<string name="device_settings_microphone_mode_description">Кой AirPod се използва като микрофон</string>
|
||||
<string name="device_settings_microphone_mode_description">Коя слушалка се използва като микрофон</string>
|
||||
<string name="device_settings_microphone_mode_auto">Автом.</string>
|
||||
<string name="device_settings_microphone_mode_right">Десен</string>
|
||||
<string name="device_settings_microphone_mode_left">Ляв</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Откриване на сън</string>
|
||||
<string name="reaction_sleep_notification_title">Поставено на пауза от Откриване на сън</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s откри, че сте заспали, затова музиката ви беше поставена на пауза. Можете да деактивирате това в настройките на устройството.</string>
|
||||
<string name="reaction_charged_channel_label">Известие за зареждане</string>
|
||||
<string name="reaction_charged_notification_title">Зареждането е завършено</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s е напълно зареден.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s е зареден до %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Преименуване</string>
|
||||
<string name="device_settings_rename_hint">Наименование на устройството</string>
|
||||
<string name="device_settings_rename_confirm">Преименуване</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">আপনি সম্প্রতি কিনে থাকলে, Google Play সিঙ্ক করতে কিছু সময় লাগতে পারে।</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">কয়েক মিনিট পরে আবার চেষ্টা করুন যদি আপনার ক্রয় না দেখায়।</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">নিশ্চিত করুন যে আপনি ক্রয়ের জন্য ব্যবহৃত একই Google অ্যাকাউন্ট দিয়ে সাইন ইন করেছেন।</string>
|
||||
<string name="settings_monitor_mode_label">মনিটর মোড</string>
|
||||
<string name="settings_monitor_mode_description">এই অ্যাপটি কোন পরিস্থিতিতে ব্লুটুথ ডেটা পর্যবেক্ষণ করবে।</string>
|
||||
<string name="settings_monitor_connected_notification_label">অতিরিক্ত বিজ্ঞপ্তি</string>
|
||||
<string name="settings_monitor_connected_notification_description">একটি ডিভাইস সংযুক্ত হলে একটি অতিরিক্ত বিজ্ঞপ্তি দেখায়। এটি আপনাকে \"ডিভাইস স্ট্যাটাস\" চ্যানেল নিষ্ক্রিয় করে স্থায়ী \"কোনো ডিভাইস নেই\" বিজ্ঞপ্তি লুকাতে দেয়।</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">সংযোগ বিচ্ছিন্নের পরে বিজ্ঞপ্তি রাখুন</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">স্বয়ংক্রিয় বিরতি</string>
|
||||
<string name="settings_autopause_description">আপনার কান থেকে ডিভাইসটি সরানোর সময় অডিওতে বিরতি দিন।</string>
|
||||
<string name="settings_autopplay_label">স্বয়ংক্রিয় প্লে</string>
|
||||
<string name="settings_autoplay_description">ডিভাইস পরিধান করার সময় অডিও প্লেব্যাক শুরু করুন।</string>
|
||||
<string name="settings_autoplay_description">আপনার পড পুনরায় পরলে সংগীত চালু করুন, তবে শুধুমাত্র যদি অটো পজ এটি থামিয়ে দিয়ে থাকে। আপনি নিজে যে পজ করেছেন (ফোন, AirPods স্টেম, স্লিপ) সেগুলো পজ থাকবে।</string>
|
||||
<string name="settings_start_music_on_wear_label">পরলে সংগীত শুরু করুন</string>
|
||||
<string name="settings_start_music_on_wear_description">আপনি পড পরলে সবসময় সংগীত চালানোর চেষ্টা করে, এমনকি ম্যানুয়াল পজের পরেও।</string>
|
||||
<string name="settings_eardetection_info_label">কান সনাক্তকরণ নোট</string>
|
||||
<string name="settings_eardetection_info_description">যদি কান সনাক্তকরণ শুধুমাত্র একটি পডের জন্য কাজ করে, এটি একটি Apple সীমাবদ্ধতা। শুধুমাত্র \"প্রাথমিক পড\" (মাইক্রোফোনের জন্য ব্যবহৃত) সনাক্ত করা হয়। Apple ডিভাইসে কনফিগার করুন: সেটিংস → Bluetooth → AirPods → মাইক্রোফোন।</string>
|
||||
<string name="settings_fake_data_label">ভুয়া ডেটা</string>
|
||||
<string name="settings_fake_data_description">ভুয়া ডেটা দেখান, অর্থাৎ, এমন ডিভাইস সিমুলেট করুন যা বিদ্যমান নেই।</string>
|
||||
<string name="settings_debug_label">ডিবাগ সেটিংস</string>
|
||||
<string name="settings_debug_description">অ্যাপের সমস্যা সমাধানে সহায়তার জন্য অতিরিক্ত সেটিংস।</string>
|
||||
<string name="settings_signal_minimum_label">ন্যূনতম সংকেত গুণমান</string>
|
||||
<string name="settings_signal_minimum_description">একটি ডিভাইসকে আপনার হিসাবে বিবেচনা করার জন্য ন্যূনতম সংকেত গুণমান থাকতে হবে।</string>
|
||||
<string name="settings_autoconnect_label">স্বয়ংক্রিয় সংযোগ</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">সংযোগ পপআপ দেখান</string>
|
||||
<string name="settings_popup_connected_description">ডিভাইস প্রথমবার সংযুক্ত হলে একটি পপআপ দেখান।</string>
|
||||
<string name="settings_popup_info_not_in_app">পপআপগুলি শুধুমাত্র তখনই দেখানো হয় যখন আপনি অ্যাপে থাকেন না।</string>
|
||||
<string name="settings_popup_warning_manual_mode">পপআপের জন্য মনিটর চালু থাকা প্রয়োজন। আপনার মনিটর মোড \"যখন অ্যাপ খোলা থাকে\" তে সেট করা আছে, যা আপনি অ্যাপ ছেড়ে গেলে মনিটর বন্ধ করে দেয়।</string>
|
||||
<string name="general_fix_it_action">ঠিক করুন</string>
|
||||
<string name="settings_charged_notification_label">চার্জ সম্পন্ন বিজ্ঞপ্তি</string>
|
||||
<string name="settings_charged_notification_description">চার্জ হচ্ছে এমন সবকিছু লক্ষ্যমাত্রা চার্জ স্তরে পৌঁছালে একটি বিজ্ঞপ্তি দেখান।</string>
|
||||
<string name="settings_charged_threshold_label">লক্ষ্যমাত্রা চার্জ স্তর</string>
|
||||
<string name="settings_charged_scope_label">পর্যবেক্ষণ করা ব্যাটারি</string>
|
||||
<string name="settings_charged_scope_pods_label">শুধুমাত্র পড</string>
|
||||
<string name="settings_charged_scope_case_label">শুধুমাত্র কেস</string>
|
||||
<string name="settings_charged_scope_both_label">পড এবং কেস</string>
|
||||
<string name="device_settings_experimental_title">পরীক্ষামূলক ফিচার</string>
|
||||
<string name="device_settings_experimental_description">এই ফিচারটি এখনো সব ডিভাইসে পরীক্ষা করা হয়নি। যদি কিছু সঠিকভাবে কাজ না করে, দয়া করে একটি ডিবাগ লগ সহ একটি ইস্যু খুলুন।</string>
|
||||
<string name="device_settings_experimental_action">ইস্যু ট্র্যাকার খুলুন</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">ডিবাগ</string>
|
||||
<string name="settings_general_label">সেটিংস</string>
|
||||
<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="settings_acknowledgements_label">স্বীকৃতি</string>
|
||||
<string name="settings_debug_autoreports_label">স্বয়ংক্রিয় বাগ রিপোর্ট</string>
|
||||
<string name="settings_debug_autoreports_description">স্বয়ংক্রিয়ভাবে সমস্যাগুলি রিপোর্ট করে, যেমন অ্যাপ ক্র্যাশের বিবরণ যাতে আমি এটি কীভাবে ঠিক করতে পারি তা বের করতে পারি।</string>
|
||||
<string name="settings_debug_mode_label">ডিবাগ মোড</string>
|
||||
<string name="settings_debug_mode_description">সমস্যা সমাধানের জন্য অতিরিক্ত তথ্য দেখান।</string>
|
||||
<string name="settings_blescanner_unfiltered_label">ফিল্টারবিহীন BLE ডেটা</string>
|
||||
<string name="settings_blescanner_unfiltered_description">সমস্ত সম্প্রচারিত BLE ডেটা দেখানোর জন্য BLE স্ক্যানার থেকে যেকোনো ফিল্টার সরান। নতুন হেডফোন প্রকারের জন্য সমর্থন যোগ করার জন্য দরকারী।</string>
|
||||
<string name="settings_compatibility_mode_label">সামঞ্জস্যতা মোড</string>
|
||||
<string name="settings_compatibility_mode_description">সামঞ্জস্যতা উন্নত করতে অপ্টিমাইজেশান নিষ্ক্রিয় করুন। যদি আপনি কোনো ডেটা দেখতে না পান তবে এটি চেষ্টা করুন।</string>
|
||||
<string name="help_translate_label">অনুবাদ</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">এই উইজেটটি আপনার ডিভাইসে একটি সরাসরি সংযোগ (AAP) প্রয়োজন। এই উইজেটটি ব্যবহার করার আগে নিশ্চিত করুন যে আপনার ডিভাইসটি অ্যাপে পেয়ার এবং সংযুক্ত আছে।</string>
|
||||
<string name="anc_widget_no_anc_support_label">এই ডিভাইসটি নয়েজ কন্ট্রোল সমর্থন করে না</string>
|
||||
<string name="anc_widget_aap_not_connected_label">সংযুক্ত নয়</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">কাছাকাছি কিন্তু সংযুক্ত নয়</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">কাছাকাছি নয়</string>
|
||||
<string name="anc_widget_aap_connecting_label">সংযুক্ত হচ্ছে…</string>
|
||||
<string name="tile_anc_label">নয়েজ কন্ট্রোল</string>
|
||||
<string name="tile_anc_subtitle_permission_required">অনুমতি প্রয়োজন</string>
|
||||
<string name="tile_anc_subtitle_no_device">কোনও ডিভাইস নেই</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">নয়েজ কন্ট্রোল নেই</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">ব্লুটুথ বন্ধ</string>
|
||||
<string name="widget_config_screen_title">উইজেট কনফিগারেশন</string>
|
||||
<string name="widget_configuration_title">ডিভাইস নির্বাচন করুন</string>
|
||||
<string name="widget_configuration_description">এই উইজেটটি কোন ডিভাইস প্রোফাইল প্রদর্শন করবে তা চয়ন করুন।</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth নিষ্ক্রিয়, এটি সক্রিয় করুন ;)</string>
|
||||
<string name="overview_monitoring_active_label">ডিভাইসের জন্য পর্যবেক্ষণ চলছে</string>
|
||||
<string name="overview_monitoring_active_description">নিশ্চিত করুন যে আপনার ডিভাইস কাছে আছে এবং সক্রিয়।</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">সংযুক্ত, কিন্তু কোনো ডেটা নেই</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">আপনার ডিভাইসটি সংযুক্ত, কিন্তু CAPod এটি থেকে কোনো লাইভ ডেটা পাচ্ছে না। আপনার ফোনের একটি সামঞ্জস্যতা বিকল্পের প্রয়োজন হতে পারে — সমস্যা সমাধানকারী স্বয়ংক্রিয়ভাবে একটি খুঁজে পেতে চেষ্টা করতে পারে।</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">সমস্যা সমাধানকারী চালান</string>
|
||||
<string name="overview_unmatched_devices_label">অমিলিত ডিভাইস</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">মিলিত প্রোফাইল ছাড়া %d ডিভাইস</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">নিম্নলিখিত অনুমতি প্রয়োজন:</string>
|
||||
<string name="permission_system_alert_window_label">সিস্টেম অ্যালার্ট উইন্ডো</string>
|
||||
<string name="permission_system_alert_window_description">\"পপআপ দেখান\" বৈশিষ্ট্যটি সম্ভব করতে CAPod-কে অন্যান্য অ্যাপের উপরে আঁকতে অনুমতি দিন।</string>
|
||||
<string name="settings_monitor_mode_manual_label">অ্যাপ খোলা থাকলে</string>
|
||||
<string name="settings_monitor_mode_automatic_label">ডিভাইস সংযুক্ত থাকলে</string>
|
||||
<string name="settings_monitor_mode_always_label">সর্বদা</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">দেখা গেলে</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">কেস খোলা</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">কানে</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">ট্রান্সপারেন্সি</string>
|
||||
<string name="anc_mode_adaptive">অ্যাডাপটিভ</string>
|
||||
<string name="conversation_awareness_label">কথোপকথন সচেতনতা</string>
|
||||
<string name="settings_conversation_action_label">যখন আপনি কথা বলতে শুরু করেন</string>
|
||||
<string name="settings_conversation_action_nothing_label">কিছু করবেন না</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">ভলিউম কমান</string>
|
||||
<string name="settings_conversation_action_pause_label">মিডিয়া পজ করুন</string>
|
||||
<string name="settings_conversation_volume_reduction_label">ভলিউম হ্রাস</string>
|
||||
<string name="signal_badge_ble_cd">BLE স্ক্যান সক্রিয়</string>
|
||||
<string name="signal_badge_key_irk_cd">পরিচয় যাচাই করা হয়েছে</string>
|
||||
<string name="signal_badge_key_encrypted_cd">এনক্রিপ্টেড সংযোগ</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">অ্যাম্বার</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">ওয়ালপেপার রঙ ব্যবহার করে</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">মনিটরিং</string>
|
||||
<string name="settings_category_appearance_label">উপস্থিতি</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">অসংরক্ষিত পরিবর্তন</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">সাধারণ</string>
|
||||
<string name="device_settings_microphone_mode_label">মাইক্রোফোন</string>
|
||||
<string name="device_settings_microphone_mode_description">কোন AirPod মাইক্রোফোন হিসেবে ব্যবহৃত হয়</string>
|
||||
<string name="device_settings_microphone_mode_description">কোন ইয়ারবাড মাইক্রোফোন হিসেবে ব্যবহার করা হয়</string>
|
||||
<string name="device_settings_microphone_mode_auto">স্বয়ংক্রিয়</string>
|
||||
<string name="device_settings_microphone_mode_right">ডান</string>
|
||||
<string name="device_settings_microphone_mode_left">বাঁ</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">ঘুম শনাক্তকরণ</string>
|
||||
<string name="reaction_sleep_notification_title">ঘুম শনাক্তকরণ দ্বারা বিরতি দেওয়া হয়েছে</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s জানিয়েছে আপনি ঘুমিয়ে পড়েছেন, তাই আপনার সঙ্গীত বিরতি দেওয়া হয়েছে। আপনি ডিভাইস সেটিংসে এটি নিষ্ক্রিয় করতে পারেন।</string>
|
||||
<string name="reaction_charged_channel_label">চার্জ সম্পন্ন বিজ্ঞপ্তি</string>
|
||||
<string name="reaction_charged_notification_title">চার্জিং সম্পন্ন</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s সম্পূর্ণ চার্জ হয়েছে।</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s %2$d%% পর্যন্ত চার্জ হয়েছে।</string>
|
||||
<string name="device_settings_rename_label">নাম বদলান</string>
|
||||
<string name="device_settings_rename_hint">ডিভাইসের নাম</string>
|
||||
<string name="device_settings_rename_confirm">নাম বদলান</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Si heu comprat alguna cosa recentment, és possible que el Google Play trigui una estona a sincronitzar-se.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Torneu-ho a provar en uns minuts si la vostra compra no apareix.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Assegureu-vos d\'haver iniciat la sessió amb el mateix compte de Google que heu utilitzat per a la compra.</string>
|
||||
<string name="settings_monitor_mode_label">Mode monitor</string>
|
||||
<string name="settings_monitor_mode_description">En quines circumstàncies aquesta aplicació supervisa les dades Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notificació addicional</string>
|
||||
<string name="settings_monitor_connected_notification_description">Mostra una notificació addicional quan hi ha un dispositiu connectat. Això permet ocultar la notificació permanent «Sense dispositius» desactivant el canal «Estat del dispositiu».</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Mantén les notificacions després de la desconnexió</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pausa automàtica</string>
|
||||
<string name="settings_autopause_description">Pausa l\'àudio quan ús tragueu el dispositiu de l\'orella.</string>
|
||||
<string name="settings_autopplay_label">Reproducció automàtica</string>
|
||||
<string name="settings_autoplay_description">Inicia la reproducció d\'àudio en utilitzar el dispositiu.</string>
|
||||
<string name="settings_autoplay_description">Reprèn la música quan torneu a posar-vos els auriculars, però només si la pausa automàtica l\'havia aturat. Les pauses que heu activat vos mateix (telèfon, pliques dels AirPods, son) romanen en pausa.</string>
|
||||
<string name="settings_start_music_on_wear_label">Inicia la música en posar-vos els auriculars</string>
|
||||
<string name="settings_start_music_on_wear_description">Sempre intenta reproduir música quan us poseu els auriculars, fins i tot després d\'una pausa manual.</string>
|
||||
<string name="settings_eardetection_info_label">Nota de detecció de l\'oïda</string>
|
||||
<string name="settings_eardetection_info_description">Si la detecció de l\'oïda només funciona per a un auricular, això és degut a una limitació d\'Apple. Només es detecta «l\'auricular principal» (utilitzat per al micròfon). Configureu-ho en dispositius Apple: Configuració → Bluetooth → AirPods → Micròfon.</string>
|
||||
<string name="settings_fake_data_label">Dades falses</string>
|
||||
<string name="settings_fake_data_description">Mostra dades falses. Per exemple: simula dispositius que no existeixen.</string>
|
||||
<string name="settings_debug_label">Configuració de depuració</string>
|
||||
<string name="settings_debug_description">Configuració addicional per ajudar a resoldre problemes amb l\'aplicació.</string>
|
||||
<string name="settings_signal_minimum_label">Qualitat de senyal mínima</string>
|
||||
<string name="settings_signal_minimum_description">La qualitat de senyal mínima que ha de tenir un dispositiu per considerar-se vostre.</string>
|
||||
<string name="settings_autoconnect_label">Connexió automàtica</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Mostra la finestra emergent de connexió</string>
|
||||
<string name="settings_popup_connected_description">Mostra una finestra emergent quan el dispositiu es connecti per primera vegada.</string>
|
||||
<string name="settings_popup_info_not_in_app">Les finestres emergents només es mostren quan no esteu a l\'aplicació.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Les finestres emergents requereixen que el monitor estigui en execució. El mode del monitor està configurat com a «quan l\'aplicació és oberta», que atura el monitor quan sortiu de l\'aplicació.</string>
|
||||
<string name="general_fix_it_action">Corregeix-ho</string>
|
||||
<string name="settings_charged_notification_label">Notificació de càrrega completa</string>
|
||||
<string name="settings_charged_notification_description">Mostra una notificació quan tots els dispositius que s\'estiguin carregant arribin al nivell de càrrega desitjat.</string>
|
||||
<string name="settings_charged_threshold_label">Nivell de càrrega objectiu</string>
|
||||
<string name="settings_charged_scope_label">Bateries monitoritzades</string>
|
||||
<string name="settings_charged_scope_pods_label">Només els auriculars</string>
|
||||
<string name="settings_charged_scope_case_label">Només la funda</string>
|
||||
<string name="settings_charged_scope_both_label">Auriculars i funda</string>
|
||||
<string name="device_settings_experimental_title">Funció experimental</string>
|
||||
<string name="device_settings_experimental_description">Aquesta funció encara no s\'ha provat a tots els dispositius. Si alguna cosa no funciona correctament, obriu una incidència amb un registre de depuració.</string>
|
||||
<string name="device_settings_experimental_action">Obre el seguidor d\'incidències</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Depuració</string>
|
||||
<string name="settings_general_label">Configuració</string>
|
||||
<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 d’altres persones.</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>
|
||||
<string name="settings_debug_mode_label">Mode depuració</string>
|
||||
<string name="settings_debug_mode_description">Mostra informació addicional per resoldre problemes.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Dades BLE sense filtrar</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Elimina els filtres de l\'escàner BLE per mostrar totes les dades BLE emeses. Útil per afegir suport a nous tipus d\'auriculars.</string>
|
||||
<string name="settings_compatibility_mode_label">Mode de compatibilitat</string>
|
||||
<string name="settings_compatibility_mode_description">Desactiva les optimitzacions per millorar la compatibilitat. Proveu això si no veieu cap dada.</string>
|
||||
<string name="help_translate_label">Traducció</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Aquest giny requereix una connexió directa (AAP) al dispositiu. Assegureu-vos que el dispositiu estigui emparellat i connectat a l\'aplicació abans d\'utilitzar aquest giny.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Aquest dispositiu no admet el control de soroll</string>
|
||||
<string name="anc_widget_aap_not_connected_label">No connectat</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">A prop però no connectat</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">No és a prop</string>
|
||||
<string name="anc_widget_aap_connecting_label">S\'està connectant…</string>
|
||||
<string name="tile_anc_label">Control de soroll</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Permís requerit</string>
|
||||
<string name="tile_anc_subtitle_no_device">Cap dispositiu</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Sense control de soroll</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth desactivat</string>
|
||||
<string name="widget_config_screen_title">Configuració del giny</string>
|
||||
<string name="widget_configuration_title">Seleccioneu un dispositiu</string>
|
||||
<string name="widget_configuration_description">Trieu quin perfil de dispositiu ha de mostrar aquest giny.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">El Bluetooth està desactivat, activeu-lo ;)</string>
|
||||
<string name="overview_monitoring_active_label">Monitorització de dispositius</string>
|
||||
<string name="overview_monitoring_active_description">Assegureu-vos que el dispositiu estigui a prop i actiu.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Connectat, però sense dades</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">El dispositiu està connectat, però el CAPod no rep dades en directe. El telèfon pot necessitar una opció de compatibilitat: el solucionador de problemes pot intentar trobar-ne una automàticament.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Executa el solucionador de problemes</string>
|
||||
<string name="overview_unmatched_devices_label">Dispositius no coincidents</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d dispositiu sense perfil coincident</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Cal el permís següent:</string>
|
||||
<string name="permission_system_alert_window_label">Finestra d\'alerta del sistema</string>
|
||||
<string name="permission_system_alert_window_description">Permet que el CAPod dibuixi sobre altres aplicacions per fer possible la característica «Mostra emergent».</string>
|
||||
<string name="settings_monitor_mode_manual_label">Quan l\'aplicació està oberta</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Quan el dispositiu està connectat</string>
|
||||
<string name="settings_monitor_mode_always_label">Sempre</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Quan es veu</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">La funda està oberta</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">A l\'orella</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparència</string>
|
||||
<string name="anc_mode_adaptive">Adaptatiu</string>
|
||||
<string name="conversation_awareness_label">Consciència de conversa</string>
|
||||
<string name="settings_conversation_action_label">Quan comenceu a parlar</string>
|
||||
<string name="settings_conversation_action_nothing_label">No fer res</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Baixa el volum</string>
|
||||
<string name="settings_conversation_action_pause_label">Posa en pausa el reproductor</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Reducció del volum</string>
|
||||
<string name="signal_badge_ble_cd">Escaneig BLE actiu</string>
|
||||
<string name="signal_badge_key_irk_cd">Identitat verificada</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Connexió xifrada</string>
|
||||
@@ -289,7 +300,7 @@
|
||||
<string name="profiles_delete_action">Elimina</string>
|
||||
<string name="profiles_basic_info_title">Informació del dispositiu</string>
|
||||
<string name="profiles_basic_info_description">Configureu el nom del dispositiu, el model i l\'emparellament Bluetooth opcional.</string>
|
||||
<string name="profiles_signal_quality_title">Qualitat de senyal mínima</string>
|
||||
<string name="profiles_signal_quality_title">Qualitat mínima del senyal</string>
|
||||
<string name="profiles_signal_quality_description">Només es detecten dispositius amb una intensitat de senyal superior a aquest llindar. Els valors més baixos augmenten el rang de detecció, però poden causar falsos positius. No configureu aquest valor massa alt: la recepció Bluetooth generalment és deficient i varia segons la distància i els obstacles.</string>
|
||||
<string name="profiles_identitykey_label">Clau d\'identitat</string>
|
||||
<string name="profilessettings_maindevice_identitykey_description">La clau de resolució d\'identitat (IRK) del dispositiu ajuda al CAPod a identificar-lo entre els dispositius propers.</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ambre</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Utilitza els colors del fons de pantalla</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitoratge</string>
|
||||
<string name="settings_category_appearance_label">Aparença</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Canvis no desats</string>
|
||||
@@ -391,7 +401,7 @@
|
||||
<string name="support_contact_send_action">Obre l\'aplicació de correu</string>
|
||||
<string name="support_contact_no_email_app">No s\'ha trobat cap aplicació de correu en aquest dispositiu.</string>
|
||||
<string name="support_contact_debuglog_delete_title">Voleu suprimir el registre de depuració?</string>
|
||||
<string name="support_contact_debuglog_delete_message">Aquest registre de depuració se suprimirà permanentment.</string>
|
||||
<string name="support_contact_debuglog_delete_message">Aquest registre de depuració se suprimrirà permanentment.</string>
|
||||
<!-- Post-share confirmation -->
|
||||
<string name="support_debuglog_sent_title">S\'ha enviat el registre?</string>
|
||||
<string name="support_debuglog_sent_message">El registre de depuració s\'ha enviat correctament? El registre s\'eliminarà.</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">General</string>
|
||||
<string name="device_settings_microphone_mode_label">Micròfon</string>
|
||||
<string name="device_settings_microphone_mode_description">Quin AirPod s\'utilitza com a micròfon</string>
|
||||
<string name="device_settings_microphone_mode_description">Quin auricular s\'utilitza com a micròfon</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automàtic</string>
|
||||
<string name="device_settings_microphone_mode_right">Dreta</string>
|
||||
<string name="device_settings_microphone_mode_left">Esquerre</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Detecció del son</string>
|
||||
<string name="reaction_sleep_notification_title">Pausat per la detecció del son</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s ha informat que us heu adormit, de manera que la vostra música s\'ha posat en pausa. Podeu desactivar-ho a la configuració del dispositiu.</string>
|
||||
<string name="reaction_charged_channel_label">Notificació de càrrega completa</string>
|
||||
<string name="reaction_charged_notification_title">Càrrega completada</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s està totalment carregat.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s s\'ha carregat fins al %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Canvia el nom</string>
|
||||
<string name="device_settings_rename_hint">Nom del dispositiu</string>
|
||||
<string name="device_settings_rename_confirm">Canvia el nom</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Pokud jste produkt zakoupili nedávno, synchronizace Google Play může chvíli trvat.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Pokud se váš nákup nezobrazí, zkuste to znovu za několik minut.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Ujistěte se, že jste přihlášeni ke stejnému účtu Google, který jste použili při nákupu.</string>
|
||||
<string name="settings_monitor_mode_label">Režim monitorování</string>
|
||||
<string name="settings_monitor_mode_description">Za jakých okolností tato aplikace monitoruje data Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Dodatečné oznámení</string>
|
||||
<string name="settings_monitor_connected_notification_description">Zobrazí dodatečné oznámení, když je zařízení připojeno. To umožňuje skrýt trvalé oznámení \"Žádné zařízení\" vypnutím kanálu \"Stav zařízení\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Uchovat oznámení po odpojení</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Autom. pozastavení</string>
|
||||
<string name="settings_autopause_description">Pozastavení zvuku při vyjmutí zařízení z ucha.</string>
|
||||
<string name="settings_autopplay_label">Autom. přehrávání</string>
|
||||
<string name="settings_autoplay_description">Spuštění přehrávání zvuku při nošení zařízení.</string>
|
||||
<string name="settings_autoplay_description">Obnoví přehrávání hudby po opětovném nasazení sluchátek, ale pouze pokud ji zastavilo Auto pozastavení. Pauzy, které jste spustili sami (telefon, ovladač AirPods, spánek), zůstanou pozastaveny.</string>
|
||||
<string name="settings_start_music_on_wear_label">Spustit hudbu při nasazení</string>
|
||||
<string name="settings_start_music_on_wear_description">Vždy se pokusí přehrát hudbu při nasazení sluchátek, i po ručním pozastavení.</string>
|
||||
<string name="settings_eardetection_info_label">Poznámka k detekci ucha</string>
|
||||
<string name="settings_eardetection_info_description">Pokud detekce sluchátek funguje pouze pro jedno sluchátko, jedná se o omezení společnosti Apple. Detekováno je pouze \"primární sluchátko\" (používané jako mikrofon). Konfigurace na zařízeních Apple: Nastavení → Bluetooth → AirPods → Mikrofon.</string>
|
||||
<string name="settings_fake_data_label">Falešná data</string>
|
||||
<string name="settings_fake_data_description">Zobrazit falešná data např. simulovat zařízení, které neexistuje.</string>
|
||||
<string name="settings_debug_label">Nastavení ladění</string>
|
||||
<string name="settings_debug_description">Další nastavení, která pomáhají řešit problémy s aplikací.</string>
|
||||
<string name="settings_signal_minimum_label">Minimální kvalita signálu</string>
|
||||
<string name="settings_signal_minimum_description">Minimální kvalita signálu, kterou musí zařízení mít, aby bylo považováno za vaše.</string>
|
||||
<string name="settings_autoconnect_label">Automatické připojení</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Zobrazit vyskakovací okno při připojení</string>
|
||||
<string name="settings_popup_connected_description">Zobrazit vyskakovací okno při prvním připojení zařízení.</string>
|
||||
<string name="settings_popup_info_not_in_app">Vyskakovací okna jsou zobrazována pouze tehdy, když nejste v aplikaci.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Vyskakovací okna vyžadují, aby monitor běžel. Váš režim monitoru je nastaven na \"Když je aplikace otevřena\", což zastaví monitor, když aplikaci opustíte.</string>
|
||||
<string name="general_fix_it_action">Opravit</string>
|
||||
<string name="settings_charged_notification_label">Oznámení o nabití</string>
|
||||
<string name="settings_charged_notification_description">Zobrazit oznámení, jakmile vše, co se nabíjí, dosáhne cílové úrovně nabití.</string>
|
||||
<string name="settings_charged_threshold_label">Cílová úroveň nabití</string>
|
||||
<string name="settings_charged_scope_label">Sledované baterie</string>
|
||||
<string name="settings_charged_scope_pods_label">Pouze sluchátka</string>
|
||||
<string name="settings_charged_scope_case_label">Pouze pouzdro</string>
|
||||
<string name="settings_charged_scope_both_label">Sluchátka a pouzdro</string>
|
||||
<string name="device_settings_experimental_title">Experimentální funkce</string>
|
||||
<string name="device_settings_experimental_description">Tato funkce ještě nebyla testována na všech zařízeních. Pokud něco nefunguje správně, nahlaste problém s logem ladění.</string>
|
||||
<string name="device_settings_experimental_action">Otevřít sledovač problémů</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Ladění</string>
|
||||
<string name="settings_general_label">Nastavení</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Režim ladění</string>
|
||||
<string name="settings_debug_mode_description">Zobrazit další informace pro řešení problémů.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Nefiltrovaná data BLE</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Aby se zobrazovala všechna vysílaná data BLE, odstraňte ze skenování BLE všechny filtry. Užitečné pro přidání podpory nových typů sluchátek.</string>
|
||||
<string name="settings_compatibility_mode_label">Režim kompatibility</string>
|
||||
<string name="settings_compatibility_mode_description">Vypnutí optimalizace pro zlepšení kompatibility. Zkuste to, pokud se vám nezobrazují žádná data.</string>
|
||||
<string name="help_translate_label">Překlad</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Tento widget vyžaduje přímé připojení (AAP) k vašemu zařízení. Před použitím tohoto widgetu se ujistěte, že je vaše zařízení spárováno a připojeno v aplikaci.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Toto zařízení nepodporuje kontrolu hluku</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Nepřipojeno</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">V dosahu, ale nepřipojeno</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Není v dosahu</string>
|
||||
<string name="anc_widget_aap_connecting_label">Připojování…</string>
|
||||
<string name="tile_anc_label">Ovládání zvuku</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Vyžadováno oprávnění</string>
|
||||
<string name="tile_anc_subtitle_no_device">Žádné zařízení</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Žádné ovládání hluku</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth je vypnuto</string>
|
||||
<string name="widget_config_screen_title">Konfigurace widgetu</string>
|
||||
<string name="widget_configuration_title">Vybrat zařízení</string>
|
||||
<string name="widget_configuration_description">Vyberte, který profil zařízení má tento widget zobrazovat.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth je vypnuto, zapněte jej ;)</string>
|
||||
<string name="overview_monitoring_active_label">Monitorování zařízení</string>
|
||||
<string name="overview_monitoring_active_description">Ujistěte se, že je vaše zařízení v dosahu a aktivní.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Připojeno, ale žádná data</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Zařízení je připojeno, ale CAPod od něj nepřijímá žádná živá data. Váš telefon může potřebovat možnost kompatibility — řešení problémů ji může zkusit najít automaticky.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Spustit řešení problémů</string>
|
||||
<string name="overview_unmatched_devices_label">Nepřiřazená zařízení</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d zařízení bez odpovídajícího profilu</item>
|
||||
@@ -224,9 +233,6 @@
|
||||
<string name="permission_required_title">Je vyžadováno následující oprávnění:</string>
|
||||
<string name="permission_system_alert_window_label">Okno systémových upozornění</string>
|
||||
<string name="permission_system_alert_window_description">Aby bylo možné použít unkci \"Zobrazit vyskakovací okno\", je nutné umožnit aplikaci CAPod vykreslení přes jiné aplikace.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Při otevření aplikace</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Při připojení zařízení</string>
|
||||
<string name="settings_monitor_mode_always_label">Vždy</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Když je vidět</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Pouzdro je otevřeno</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">V uchu</string>
|
||||
@@ -258,6 +264,11 @@
|
||||
<string name="anc_mode_transparency">Průhlednost</string>
|
||||
<string name="anc_mode_adaptive">Adaptivní</string>
|
||||
<string name="conversation_awareness_label">Uvědomění si konverzace</string>
|
||||
<string name="settings_conversation_action_label">Když začnete mluvit</string>
|
||||
<string name="settings_conversation_action_nothing_label">Nedělat nic</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Snížit hlasitost</string>
|
||||
<string name="settings_conversation_action_pause_label">Pozastavit média</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Snížení hlasitosti</string>
|
||||
<string name="signal_badge_ble_cd">BLE skenování aktivní</string>
|
||||
<string name="signal_badge_key_irk_cd">Identita ověřena</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Šifrované připojení</string>
|
||||
@@ -326,7 +337,6 @@
|
||||
<string name="ui_theme_color_amber_label">Jantarová</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Používá barvy tapety</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitorování</string>
|
||||
<string name="settings_category_appearance_label">Vzhled</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Neuložené změny</string>
|
||||
@@ -421,8 +431,8 @@
|
||||
<string name="device_settings_info_build_label">Sestavení</string>
|
||||
<string name="device_settings_info_left_serial_label">Sériové číslo levého sluchátka</string>
|
||||
<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ána</string>
|
||||
<string name="device_settings_info_right_bonded_label">Pravá spárována</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_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>
|
||||
@@ -460,48 +470,52 @@
|
||||
<string name="device_settings_press_hold_default">Výchozí</string>
|
||||
<string name="device_settings_press_hold_shorter">Kratší</string>
|
||||
<string name="device_settings_press_hold_shortest">Nejkratší</string>
|
||||
<string name="device_settings_volume_swipe_label">Přejetí pro hlasitost</string>
|
||||
<string name="device_settings_volume_swipe_label">Hlasitost přejetím</string>
|
||||
<string name="device_settings_volume_swipe_description">Přejetím upravte hlasitost</string>
|
||||
<string name="device_settings_volume_swipe_length_label">Čekací doba tažení pro hlasitost</string>
|
||||
<string name="device_settings_volume_swipe_length_description">Chcete-li zabránit nechtěným úpravám hlasitosti, zvolte, jak dlouho čekat mezi tahy.</string>
|
||||
<string name="device_settings_volume_swipe_length_label">Doba čekání hlasitosti přejetím</string>
|
||||
<string name="device_settings_volume_swipe_length_description">Chcete-li zabránit nechtěným úpravám hlasitosti, zvolte, jak dlouho čekat mezi přejetími.</string>
|
||||
<string name="device_settings_volume_swipe_length_default">Výchozí</string>
|
||||
<string name="device_settings_volume_swipe_length_longer">Delší</string>
|
||||
<string name="device_settings_volume_swipe_length_longest">Nejdelší</string>
|
||||
<string name="device_settings_end_call_mute_mic_label">Ovládání hovorů</string>
|
||||
<string name="device_settings_end_call_mute_mic_description">Zvolte, co dělá stisk stonku během hovorů</string>
|
||||
<string name="device_settings_end_call_mute_mic_option_a_title">Jednorázový stisk pro ukončení hovoru</string>
|
||||
<string name="device_settings_end_call_mute_mic_description">Vyberte, jaká akce se má spustit stisknutím tlačítka během hovoru</string>
|
||||
<string name="device_settings_end_call_mute_mic_option_a_title">Jeden stisk pro ukončení hovoru</string>
|
||||
<string name="device_settings_end_call_mute_mic_option_a_subtitle">Dvojitý stisk pro ztlumení mikrofonu</string>
|
||||
<string name="device_settings_end_call_mute_mic_option_b_title">Jednorázový stisk pro ztlumení mikrofonu</string>
|
||||
<string name="device_settings_end_call_mute_mic_option_b_title">Jeden stisk pro ztlumení mikrofonu</string>
|
||||
<string name="device_settings_end_call_mute_mic_option_b_subtitle">Dvojitý stisk pro ukončení hovoru</string>
|
||||
<string name="device_settings_open_cd">Otevřít nastavení zařízení</string>
|
||||
<string name="overview_card_missing_paired_device">Tento profil nemá spárované zařízení Bluetooth.</string>
|
||||
<string name="overview_reactions_hint_title">Reakce jsou nastaveny pro každé zařízení</string>
|
||||
<string name="overview_reactions_hint_body">Automatické přehrávání, automatické pozastavení a vyskakovací okna jsou nyní konfigurována pro každé zařízení. Klepněte na ikonu nastavení na kartě, když jsou vaše sluchátka připojena.</string>
|
||||
<string name="overview_reactions_hint_body">Automatické přehrávání, automatické pozastavení a vyskakovací okna se nyní nastavují pro každé zařízení zvlášť. Klepněte na ikonu nastavení na kartě, když máte připojená sluchátka.</string>
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Obecné</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Který AirPod je použit jako mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_auto">Auto</string>
|
||||
<string name="device_settings_microphone_mode_description">Které sluchátko je použito jako mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_auto">Autom.</string>
|
||||
<string name="device_settings_microphone_mode_right">Pravý</string>
|
||||
<string name="device_settings_microphone_mode_left">Levý</string>
|
||||
<string name="device_settings_noise_control_current_mode_label">Aktuální režim</string>
|
||||
<string name="device_settings_listening_mode_cycle_label">Cyklus dlouhého stisku</string>
|
||||
<string name="device_settings_listening_mode_cycle_description">Vyberte, které režimy jsou dostupné při držení stopky a v rychlém přepínači.</string>
|
||||
<string name="device_settings_listening_mode_cycle_description">Vyberte, které režimy budou k dispozici při podržení tlačítka a v rychlém přepínači.</string>
|
||||
<string name="device_settings_listening_mode_cycle_dialog_title">Přizpůsobit cyklus dlouhého stisku</string>
|
||||
<string name="device_settings_listening_mode_cycle_summary_helper">Používá se při držení stopky a v rychlém přepínači</string>
|
||||
<string name="device_settings_listening_mode_cycle_summary_helper_override">Používá se v rychlém přepínači. Vlastní akce dlouhého stisku stopky přepisuje cyklování AirPods.</string>
|
||||
<string name="device_settings_listening_mode_cycle_summary_helper">Používá se při držení tlačítka a v rychlém přepínači</string>
|
||||
<string name="device_settings_listening_mode_cycle_summary_helper_override">Používá se v rychlém přepínači. Vlastní akce dlouhého stisku přepínače přepisuje cyklování AirPods.</string>
|
||||
<string name="device_settings_listening_mode_cycle_minimum">Musí zůstat povoleny alespoň 2 režimy.</string>
|
||||
<string name="device_settings_listening_mode_cycle_off">Vypnuto</string>
|
||||
<string name="device_settings_listening_mode_cycle_off">Vyp.</string>
|
||||
<string name="device_settings_listening_mode_cycle_anc">Potlačení hluku</string>
|
||||
<string name="device_settings_listening_mode_cycle_transparency">Průhlednost</string>
|
||||
<string name="device_settings_listening_mode_cycle_transparency">Propustnost</string>
|
||||
<string name="device_settings_listening_mode_cycle_adaptive">Adaptivní</string>
|
||||
<string name="device_settings_allow_off_label">Povolit režim Vypnuto</string>
|
||||
<string name="device_settings_allow_off_description">Povolit Vypnuto jako volitelný režim kontroly hluku. Pokud je zakázáno, stopky při cyklování přeskočí Vypnuto.</string>
|
||||
<string name="device_settings_allow_off_description">Povolit Vypnuto jako volitelný režim kontroly hluku. Pokud je zakázáno, tlačítka při cyklování přeskočí Vypnuto.</string>
|
||||
<string name="device_settings_sleep_detection_label">Detekce spánku</string>
|
||||
<string name="device_settings_sleep_detection_description">Automaticky pozastavit zvuk, když usnete</string>
|
||||
<string name="reaction_sleep_channel_label">Detekce spánku</string>
|
||||
<string name="reaction_sleep_notification_title">Pozastaveno detekcí spánku</string>
|
||||
<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="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>
|
||||
@@ -510,7 +524,7 @@
|
||||
<string name="device_settings_rename_system_unavailable">Android nám nedovolil přejmenovat zařízení zde. Chcete-li aktualizovat název v nastavení Bluetooth, přejmenujte ho tam nebo zařízení znovu spárujte.</string>
|
||||
<string name="device_settings_rename_system_unavailable_bt_settings_action">Nastavení Bluetooth</string>
|
||||
<string name="device_settings_send_failed">Nastavení nelze použít: %1$s</string>
|
||||
<string name="device_settings_anc_off_rejected_message">Režim Vypnuto není na tomto zařízení povolen. Povolte „Povolit režim Vypnuto“ v části Kontrola hluku.</string>
|
||||
<string name="device_settings_anc_off_rejected_message">Režim Vypnuto není na tomto zařízení povolen. Povolte \"Povolit režim Vypnuto\" v části Kontrola hluku.</string>
|
||||
<string name="device_settings_category_battery_label">Baterie</string>
|
||||
<string name="device_settings_charge_cap_label">Optimalizovaný limit nabíjení</string>
|
||||
<string name="device_settings_charge_cap_description">Naučí se vaše návyky a pozastaví nabíjení přibližně na 80 %%, aby prodloužilo životnost baterie, a dobíjí sluchátka těsně před jejich použitím.</string>
|
||||
@@ -530,7 +544,7 @@
|
||||
<string name="press_controls_double_press">Dvojitý stisk</string>
|
||||
<string name="press_controls_triple_press">Trojitý stisk</string>
|
||||
<string name="press_controls_long_press">Dlouhý stisk</string>
|
||||
<string name="press_controls_long_press_anc_cycle_info">Přiřazení akce dlouhého stisku také zakáže cyklování režimů Kontroly hluku (např. Potlačení hluku, Transparentnost) stisknutím a podržením AirPods.</string>
|
||||
<string name="press_controls_long_press_anc_cycle_info">Přiřazení akce dlouhého stisku také zakáže cyklování režimů Kontroly hluku (např. Potlačení hluku, propustnost) stisknutím a podržením AirPods.</string>
|
||||
<string name="press_controls_mappings_section_title">Mapování stisku</string>
|
||||
<string name="press_controls_left">Levé</string>
|
||||
<string name="press_controls_right">Pravé</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Hvis du for nylig har købt, kan det tage et øjeblik for Google Play at synkronisere.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Prøv igen om et par minutter, hvis dit køb ikke vises.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Sørg for, at du er logget ind med den samme Google-konto, der blev brugt til købet.</string>
|
||||
<string name="settings_monitor_mode_label">Overvågningstilstand</string>
|
||||
<string name="settings_monitor_mode_description">Under hvilke omstændigheder denne app overvåger Bluetooth-data.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Ekstra notifikation</string>
|
||||
<string name="settings_monitor_connected_notification_description">Viser en ekstra notifikation, når en enhed er tilsluttet. Dette lader dig skjule den permanente \"Ingen enheder\"-notifikation ved at deaktivere \"Enhedsstatus\"-kanalen.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Behold notifikation efter afbrydelse</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Auto-pause</string>
|
||||
<string name="settings_autopause_description">Sæt lyd på pause, når du fjerner enheden fra dit øre.</string>
|
||||
<string name="settings_autopplay_label">Auto-afspilning</string>
|
||||
<string name="settings_autoplay_description">Start lydafspilning, når enheden bæres.</string>
|
||||
<string name="settings_autoplay_description">Genoptag musik, når du sætter dine pods på igen, men kun hvis Auto pause stoppede det. Pauser du selv udløste (telefon, AirPods-stift, søvn) forbliver sat på pause.</string>
|
||||
<string name="settings_start_music_on_wear_label">Start musik ved iføring</string>
|
||||
<string name="settings_start_music_on_wear_description">Forsøger altid at afspille musik, når du sætter dine pods på, selv efter en manuel pause.</string>
|
||||
<string name="settings_eardetection_info_label">Bemærkning om øredetektion</string>
|
||||
<string name="settings_eardetection_info_description">Hvis øredetektion kun virker for én pod, er dette en Apple-begrænsning. Kun den \"primære pod\" (brugt til mikrofon) detekteres. Konfigurer på Apple-enheder: Indstillinger → Bluetooth → AirPods → Mikrofon.</string>
|
||||
<string name="settings_fake_data_label">Falske data</string>
|
||||
<string name="settings_fake_data_description">Vis falske data, dvs. simuler enheder, der ikke eksisterer.</string>
|
||||
<string name="settings_debug_label">Fejlfindingsindstillinger</string>
|
||||
<string name="settings_debug_description">Yderligere indstillinger til at hjælpe med at fejlfinde problemer med appen.</string>
|
||||
<string name="settings_signal_minimum_label">Minimum signalkvalitet</string>
|
||||
<string name="settings_signal_minimum_description">Den minimum signalkvalitet, en enhed skal have for at blive betragtet som din.</string>
|
||||
<string name="settings_autoconnect_label">Auto-tilslut</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Vis forbindelses-pop op</string>
|
||||
<string name="settings_popup_connected_description">Vis en pop op, når enheden tilsluttes for første gang.</string>
|
||||
<string name="settings_popup_info_not_in_app">Pop-ups vises kun, når du ikke er i appen.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Pop-ups kræver, at monitoren kører. Din monitortilstand er indstillet til \"Når appen er åben\", som stopper monitoren, når du forlader appen.</string>
|
||||
<string name="general_fix_it_action">Ret det</string>
|
||||
<string name="settings_charged_notification_label">Opladt notifikation</string>
|
||||
<string name="settings_charged_notification_description">Vis en notifikation, når alt der oplades når det ønskede opladningsniveau.</string>
|
||||
<string name="settings_charged_threshold_label">Målopladningsniveau</string>
|
||||
<string name="settings_charged_scope_label">Overvågede batterier</string>
|
||||
<string name="settings_charged_scope_pods_label">Kun pods</string>
|
||||
<string name="settings_charged_scope_case_label">Kun etui</string>
|
||||
<string name="settings_charged_scope_both_label">Pods og etui</string>
|
||||
<string name="device_settings_experimental_title">Eksperimentel funktion</string>
|
||||
<string name="device_settings_experimental_description">Denne funktion er endnu ikke testet på alle enheder. Hvis noget ikke fungerer korrekt, bedes du åbne en sag med en fejlretningslog.</string>
|
||||
<string name="device_settings_experimental_action">Åbn fejlsporer</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Fejlfinding</string>
|
||||
<string name="settings_general_label">Indstillinger</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Fejlfindingstilstand</string>
|
||||
<string name="settings_debug_mode_description">Vis yderligere oplysninger for at fejlfinde problemer.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Ufiltrerede BLE-data</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Fjern alle filtre fra BLE-scanneren for at vise alle udsendte BLE-data. Nyttigt til at tilføje understøttelse af nye hovedtelefontyper.</string>
|
||||
<string name="settings_compatibility_mode_label">Kompatibilitetstilstand</string>
|
||||
<string name="settings_compatibility_mode_description">Deaktiver optimeringer for at forbedre kompatibiliteten. Prøv dette, hvis du ikke ser nogen data.</string>
|
||||
<string name="help_translate_label">Oversættelse</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Denne widget kræver en direkte forbindelse (AAP) til din enhed. Sørg for, at din enhed er parret og forbundet i appen, inden du bruger denne widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Denne enhed understøtter ikke støjkontrol</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Ikke forbundet</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">I nærheden, men ikke tilsluttet</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Ikke i nærheden</string>
|
||||
<string name="anc_widget_aap_connecting_label">Forbinder…</string>
|
||||
<string name="tile_anc_label">Støjstyring</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Tilladelse påkrævet</string>
|
||||
<string name="tile_anc_subtitle_no_device">Ingen enhed</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Ingen støjstyring</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth slået fra</string>
|
||||
<string name="widget_config_screen_title">Widget-konfiguration</string>
|
||||
<string name="widget_configuration_title">Vælg enhed</string>
|
||||
<string name="widget_configuration_description">Vælg hvilken enhedsprofil denne widget skal vise.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth er deaktiveret, aktivér det ;)</string>
|
||||
<string name="overview_monitoring_active_label">Overvåger for enheder</string>
|
||||
<string name="overview_monitoring_active_description">Sørg for, at din enhed er i nærheden og aktiv.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Forbundet, men ingen data</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Din enhed er forbundet, men CAPod modtager ikke live data fra den. Din telefon har måske brug for en kompatibilitetsindstilling — fejlfinderen kan prøve at finde en automatisk.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Kør fejlfinder</string>
|
||||
<string name="overview_unmatched_devices_label">Uparrede enheder</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d enhed uden matchende profil</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Følgende tilladelse er påkrævet:</string>
|
||||
<string name="permission_system_alert_window_label">Systemalarmvindue</string>
|
||||
<string name="permission_system_alert_window_description">Tillad CAPod at tegne over andre apps for at gøre funktionen \"Vis pop op\" mulig.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Når appen er åben</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Når enheden er tilsluttet</string>
|
||||
<string name="settings_monitor_mode_always_label">Altid</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Når den ses</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Etui er åbent</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">I øret</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Gennemsigtighed</string>
|
||||
<string name="anc_mode_adaptive">Adaptiv</string>
|
||||
<string name="conversation_awareness_label">Samtaleopmærksomhed</string>
|
||||
<string name="settings_conversation_action_label">Når du begynder at tale</string>
|
||||
<string name="settings_conversation_action_nothing_label">Gør intet</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Sænk lydstyrken</string>
|
||||
<string name="settings_conversation_action_pause_label">Sæt medie på pause</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Lydstyrkeregulering</string>
|
||||
<string name="signal_badge_ble_cd">BLE-scanning aktiv</string>
|
||||
<string name="signal_badge_key_irk_cd">Identitet bekræftet</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Krypteret forbindelse</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Rav</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Bruger tapetkfarver</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Overvågning</string>
|
||||
<string name="settings_category_appearance_label">Udseende</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Ikke-gemte ændringer</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Generel</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Hvilken AirPod der bruges som mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Hvilken øretelefon der bruges som mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_auto">Auto</string>
|
||||
<string name="device_settings_microphone_mode_right">Højre</string>
|
||||
<string name="device_settings_microphone_mode_left">Venstre</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Søvnregistrering</string>
|
||||
<string name="reaction_sleep_notification_title">Sat på pause af søvnregistrering</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s registrerede, at du faldt i søvn, så din musik blev sat på pause. Du kan deaktivere dette i enhedsindstillingerne.</string>
|
||||
<string name="reaction_charged_channel_label">Opladt notifikation</string>
|
||||
<string name="reaction_charged_notification_title">Opladning færdig</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s er fuldt opladet.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s er blevet opladet til %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Omdøb</string>
|
||||
<string name="device_settings_rename_hint">Enhedsnavn</string>
|
||||
<string name="device_settings_rename_confirm">Omdøb</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Wenn du kürzlich gekauft hast, kann es einen Moment dauern, bis Google Play synchronisiert.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Versuche es in wenigen Minuten erneut, falls dein Kauf nicht erscheint.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Stelle sicher, dass du mit demselben Google-Konto angemeldet bist, das für den Kauf verwendet wurde.</string>
|
||||
<string name="settings_monitor_mode_label">Überwachungsmodus</string>
|
||||
<string name="settings_monitor_mode_description">Unter welchen Umständen überwacht diese App Bluetooth-Daten.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Zusätzliche Benachrichtigung</string>
|
||||
<string name="settings_monitor_connected_notification_description">Zeigt eine zusätzliche Benachrichtigung an, wenn ein Gerät verbunden ist. Dadurch kannst du die permanente Benachrichtigung „Keine Geräte“ ausblenden, indem du den Kanal „Gerätestatus“ deaktivierst.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Benachrichtigung nach Trennung beibehalten</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Automatische Pause</string>
|
||||
<string name="settings_autopause_description">Musik pausieren, wenn der Kopfhörer vom Ohr entfernt wird.</string>
|
||||
<string name="settings_autopplay_label">Automatisches abspielen</string>
|
||||
<string name="settings_autoplay_description">Audiowiedergabe starten, wenn das Gerät getragen wird.</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_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_fake_data_label">Gefälschte Daten</string>
|
||||
<string name="settings_fake_data_description">Gefälschte Test-Daten anzeigen, d.h. nicht existierende Geräte simulieren.</string>
|
||||
<string name="settings_debug_label">Debug-Einstellungen</string>
|
||||
<string name="settings_debug_description">Zusätzliche Einstellungen zur Behebung von Problemen mit der App.</string>
|
||||
<string name="settings_signal_minimum_label">Minimale Signalqualität</string>
|
||||
<string name="settings_signal_minimum_description">Die minimale Signalqualität, die ein Gerät haben muss, damit als Deins erkannt wird.</string>
|
||||
<string name="settings_autoconnect_label">Automatisch verbinden</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Verbindungs-Popup anzeigen</string>
|
||||
<string name="settings_popup_connected_description">Zeigt ein Popup an, wenn das Gerät zum ersten Mal eine Verbindung herstellt.</string>
|
||||
<string name="settings_popup_info_not_in_app">Popups werden nur angezeigt, wenn du nicht in der App bist.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Popups erfordern, dass der Monitor läuft. Dein Monitor-Modus ist auf \"Wenn App geöffnet\" eingestellt, was den Monitor stoppt, wenn du die App verlässt.</string>
|
||||
<string name="general_fix_it_action">Beheben</string>
|
||||
<string name="settings_charged_notification_label">Aufgeladen-Benachrichtigung</string>
|
||||
<string name="settings_charged_notification_description">Zeigt eine Benachrichtigung an, sobald alles, was geladen wird, den Ziel-Ladestand erreicht.</string>
|
||||
<string name="settings_charged_threshold_label">Ziel-Ladestand</string>
|
||||
<string name="settings_charged_scope_label">Überwachte Akkus</string>
|
||||
<string name="settings_charged_scope_pods_label">Nur Pods</string>
|
||||
<string name="settings_charged_scope_case_label">Nur Case</string>
|
||||
<string name="settings_charged_scope_both_label">Pods und Case</string>
|
||||
<string name="device_settings_experimental_title">Experimentelle Funktion</string>
|
||||
<string name="device_settings_experimental_description">Diese Funktion wurde noch nicht auf allen Geräten getestet. Wenn etwas nicht korrekt funktioniert, öffne bitte ein Issue mit einem Debug-Log.</string>
|
||||
<string name="device_settings_experimental_action">Issue-Tracker öffnen</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Debug</string>
|
||||
<string name="settings_general_label">Einstellungen</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Debug-Modus</string>
|
||||
<string name="settings_debug_mode_description">Zeige zusätzliche Informationen an, um Probleme zu beheben.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Ungefilterte BLE-Daten</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Entfernt alle Filter aus dem BLE-Scanner, um alle übertragenen BLE-Daten anzuzeigen. Nützlich, um Unterstützung für neue Kopfhörertypen hinzuzufügen.</string>
|
||||
<string name="settings_compatibility_mode_label">Kompatibilitätsmodus</string>
|
||||
<string name="settings_compatibility_mode_description">Deaktiviere Optimierungen, um die Kompatibilität zu verbessern. Probier diese Option, wenn gar keine Daten angezeigt werden.</string>
|
||||
<string name="help_translate_label">Übersetzung</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Dieses Widget erfordert eine direkte Verbindung (AAP) zu deinem Gerät. Stelle sicher, dass dein Gerät in der App gekoppelt und verbunden ist, bevor du dieses Widget verwendest.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Dieses Gerät unterstützt keine Geräuschsteuerung</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Nicht verbunden</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">In der Nähe, aber nicht verbunden</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Nicht in der Nähe</string>
|
||||
<string name="anc_widget_aap_connecting_label">Verbinde…</string>
|
||||
<string name="tile_anc_label">Geräuschkontrolle</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Berechtigung erforderlich</string>
|
||||
<string name="tile_anc_subtitle_no_device">Kein Gerät</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Keine Geräuschkontrolle</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth aus</string>
|
||||
<string name="widget_config_screen_title">Widget-Konfiguration</string>
|
||||
<string name="widget_configuration_title">Gerät auswählen</string>
|
||||
<string name="widget_configuration_description">Wähle aus, welches Geräteprofil dieses Widget anzeigen soll.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth ist deaktiviert, aktiviere es ;)</string>
|
||||
<string name="overview_monitoring_active_label">Überwachung für Geräte</string>
|
||||
<string name="overview_monitoring_active_description">Stelle sicher, dass dein Gerät in der Nähe ist und aktiv ist.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Verbunden, aber keine Daten</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Dein Gerät ist verbunden, aber CAPod empfängt keine Live-Daten davon. Dein Telefon benötigt möglicherweise eine Kompatibilitätsoption – die Fehlerbehebung kann versuchen, automatisch eine zu finden.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Fehlerbehebung starten</string>
|
||||
<string name="overview_unmatched_devices_label">Nicht zugeordnete Geräte</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d Gerät ohne passendes Profil</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Die folgende Genehmigung ist erforderlich:</string>
|
||||
<string name="permission_system_alert_window_label">Systemwarnungsfenster</string>
|
||||
<string name="permission_system_alert_window_description">Erlaube CAPod, PopUp Benachrichtigungen über anderen Apps anzuzeigen.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Wenn die App geöffnet ist</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Wenn das Gerät verbunden ist</string>
|
||||
<string name="settings_monitor_mode_always_label">Immer</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Wann gesehen</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Ladecase ist offen</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Im Ohr</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparenz</string>
|
||||
<string name="anc_mode_adaptive">Adaptiv</string>
|
||||
<string name="conversation_awareness_label">Gesprächserkennung</string>
|
||||
<string name="settings_conversation_action_label">Wenn du anfängst zu sprechen</string>
|
||||
<string name="settings_conversation_action_nothing_label">Nichts tun</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Lautstärke verringern</string>
|
||||
<string name="settings_conversation_action_pause_label">Medien pausieren</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Lautstärkereduzierung</string>
|
||||
<string name="signal_badge_ble_cd">BLE-Scan aktiv</string>
|
||||
<string name="signal_badge_key_irk_cd">Identität verifiziert</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Verschlüsselte Verbindung</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Bernstein</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Verwendet Hintergrundbildfarben</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Überwachung</string>
|
||||
<string name="settings_category_appearance_label">Erscheinungsbild</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Nicht gespeicherte Änderungen</string>
|
||||
@@ -470,7 +480,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">Welcher AirPod als Mikrofon verwendet wird</string>
|
||||
<string name="device_settings_microphone_mode_description">Welches 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>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Schlaferkennung</string>
|
||||
<string name="reaction_sleep_notification_title">Pausiert durch Schlafmodus-Erkennung</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s hat erkannt, dass du eingeschlafen bist, daher wurde deine Musik pausiert. Du kannst dies in den Geräteeinstellungen deaktivieren.</string>
|
||||
<string name="reaction_charged_channel_label">Aufgeladen-Benachrichtigung</string>
|
||||
<string name="reaction_charged_notification_title">Laden abgeschlossen</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s ist vollständig geladen.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s wurde auf %2$d%% geladen.</string>
|
||||
<string name="device_settings_rename_label">Umbenennen</string>
|
||||
<string name="device_settings_rename_hint">Gerätename</string>
|
||||
<string name="device_settings_rename_confirm">Umbenennen</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Εάν έχετε αγοράσει πρόσφατα, ενδέχεται να χρειαστεί λίγος χρόνος για συγχρονισμό με το Google Play.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Δοκιμάστε ξανά σε λίγα λεπτά αν η αγορά σας δεν εμφανιστεί.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Βεβαιωθείτε ότι έχετε συνδεθεί με τον ίδιο λογαριασμό Google που χρησιμοποιήσατε για την αγορά.</string>
|
||||
<string name="settings_monitor_mode_label">Λειτουργία παρακολούθησης</string>
|
||||
<string name="settings_monitor_mode_description">Υπό ποιες συνθήκες αυτή η εφαρμογή παρακολουθεί δεδομένα Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Επιπλέον ειδοποίηση</string>
|
||||
<string name="settings_monitor_connected_notification_description">Εμφανίζει μια επιπλέον ειδοποίηση όταν μια συσκευή είναι συνδεδεμένη. Αυτό σας επιτρέπει να αποκρύψετε τη μόνιμη ειδοποίηση \"Δεν υπάρχουν συσκευές\" απενεργοποιώντας το κανάλι \"Κατάσταση συσκευής\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Διατήρηση ειδοποίησης μετά την αποσύνδεση</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Αυτόματη παύση</string>
|
||||
<string name="settings_autopause_description">Παύση ήχου κατά την αφαίρεση της συσκευής από το αυτί σας.</string>
|
||||
<string name="settings_autopplay_label">Αυτόματη αναπαραγωγή</string>
|
||||
<string name="settings_autoplay_description">Έναρξη αναπαραγωγής ήχου όταν φοριέται η συσκευή.</string>
|
||||
<string name="settings_autoplay_description">Συνέχισε τη μουσική όταν ξαναφοράς τα pods, αλλά μόνο αν την σταμάτησε η Αυτόματη παύση. Οι παύσεις που έκανες εσύ (τηλέφωνο, στέλεχος AirPods, ύπνος) παραμένουν σε παύση.</string>
|
||||
<string name="settings_start_music_on_wear_label">Εκκίνηση μουσικής κατά τη χρήση</string>
|
||||
<string name="settings_start_music_on_wear_description">Προσπαθεί πάντα να αναπαράγει μουσική όταν φοράς τα pods, ακόμα και μετά από μη αυτόματη παύση.</string>
|
||||
<string name="settings_eardetection_info_label">Σημείωση ανίχνευσης αυτιού</string>
|
||||
<string name="settings_eardetection_info_description">Εάν η ανίχνευση αυτιού λειτουργεί μόνο για ένα pod, αυτός είναι περιορισμός της Apple. Ανιχνεύεται μόνο το \"κύριο pod\" (που χρησιμοποιείται για μικρόφωνο). Ρυθμίστε σε συσκευές Apple: Ρυθμίσεις → Bluetooth → AirPods → Μικρόφωνο.</string>
|
||||
<string name="settings_fake_data_label">Ψευδή δεδομένα</string>
|
||||
<string name="settings_fake_data_description">Εμφάνιση ψευδών δεδομένων, π.χ., προσομοίωση συσκευών που δεν υπάρχουν.</string>
|
||||
<string name="settings_debug_label">Ρυθμίσεις εντοπισμού σφαλμάτων</string>
|
||||
<string name="settings_debug_description">Πρόσθετες ρυθμίσεις για την αντιμετώπιση προβλημάτων με την εφαρμογή.</string>
|
||||
<string name="settings_signal_minimum_label">Ελάχιστη ποιότητα σήματος</string>
|
||||
<string name="settings_signal_minimum_description">Η ελάχιστη ποιότητα σήματος που πρέπει να έχει μια συσκευή για να θεωρηθεί δική σας.</string>
|
||||
<string name="settings_autoconnect_label">Αυτόματη σύνδεση</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Εμφάνιση αναδυόμενου παραθύρου σύνδεσης</string>
|
||||
<string name="settings_popup_connected_description">Εμφάνιση αναδυόμενου παραθύρου όταν η συσκευή συνδέεται για πρώτη φορά.</string>
|
||||
<string name="settings_popup_info_not_in_app">Τα αναδυόμενα παράθυρα εμφανίζονται μόνο όταν δεν βρίσκεστε στην εφαρμογή.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Τα αναδυόμενα παράθυρα απαιτούν η παρακολούθηση να εκτελείται. Η λειτουργία παρακολούθησης έχει οριστεί σε «Όταν η εφαρμογή είναι ανοιχτή», η οποία διακόπτει την παρακολούθηση όταν φεύγετε από την εφαρμογή.</string>
|
||||
<string name="general_fix_it_action">Διόρθωση</string>
|
||||
<string name="settings_charged_notification_label">Ειδοποίηση φόρτισης</string>
|
||||
<string name="settings_charged_notification_description">Εμφάνιση ειδοποίησης μόλις όλα τα στοιχεία που φορτίζουν φτάσουν στο επιθυμητό επίπεδο φόρτισης.</string>
|
||||
<string name="settings_charged_threshold_label">Επιθυμητό επίπεδο φόρτισης</string>
|
||||
<string name="settings_charged_scope_label">Παρακολουθούμενες μπαταρίες</string>
|
||||
<string name="settings_charged_scope_pods_label">Μόνο ακουστικά</string>
|
||||
<string name="settings_charged_scope_case_label">Μόνο θήκη</string>
|
||||
<string name="settings_charged_scope_both_label">Ακουστικά και θήκη</string>
|
||||
<string name="device_settings_experimental_title">Πειραματική λειτουργία</string>
|
||||
<string name="device_settings_experimental_description">Αυτή η λειτουργία δεν έχει δοκιμαστεί ακόμα σε όλες τις συσκευές. Αν κάτι δεν λειτουργεί σωστά, παρακαλώ ανοίξτε ένα ζήτημα με ένα αρχείο καταγραφής εντοπισμού σφαλμάτων.</string>
|
||||
<string name="device_settings_experimental_action">Άνοιγμα Παρακολούθησης Ζητημάτων</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Αποσφαλμάτωση</string>
|
||||
<string name="settings_general_label">Ρυθμίσεις</string>
|
||||
<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="settings_acknowledgements_label">Ευχαριστίες</string>
|
||||
<string name="settings_debug_autoreports_label">Αυτόματες αναφορές σφαλμάτων</string>
|
||||
<string name="settings_debug_autoreports_description">Αναφέρει αυτόματα προβλήματα, π.χ. λεπτομέρειες σχετικά με μια κατάρρευση της εφαρμογής, ώστε να μπορώ να καταλάβω πώς να το διορθώσω.</string>
|
||||
<string name="settings_debug_mode_label">Λειτουργία εντοπισμού σφαλμάτων</string>
|
||||
<string name="settings_debug_mode_description">Εμφάνιση πρόσθετων πληροφοριών για την αντιμετώπιση προβλημάτων.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Αφιλτράριστα δεδομένα BLE</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Αφαιρέστε τυχόν φίλτρα από τον σαρωτή BLE για να εμφανιστούν όλα τα μεταδιδόμενα δεδομένα BLE. Χρήσιμο για την προσθήκη υποστήριξης για νέους τύπους ακουστικών.</string>
|
||||
<string name="settings_compatibility_mode_label">Λειτουργία συμβατότητας</string>
|
||||
<string name="settings_compatibility_mode_description">Απενεργοποιήστε τις βελτιστοποιήσεις για να βελτιώσετε τη συμβατότητα. Δοκιμάστε το εάν δεν βλέπετε δεδομένα.</string>
|
||||
<string name="help_translate_label">Μετάφραση</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Αυτό το widget απαιτεί άμεση σύνδεση (AAP) με τη συσκευή σας. Βεβαιωθείτε ότι η συσκευή σας είναι συζευγμένη και συνδεδεμένη στην εφαρμογή πριν χρησιμοποιήσετε αυτό το widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Αυτή η συσκευή δεν υποστηρίζει έλεγχο θορύβου</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Μη συνδεδεμένο</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Κοντά αλλά δεν είναι συνδεδεμένο</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Μη κοντά</string>
|
||||
<string name="anc_widget_aap_connecting_label">Σύνδεση…</string>
|
||||
<string name="tile_anc_label">Έλεγχος θορύβου</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Απαιτείται άδεια</string>
|
||||
<string name="tile_anc_subtitle_no_device">Καμία συσκευή</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Χωρίς έλεγχο θορύβου</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth απενεργοποιημένο</string>
|
||||
<string name="widget_config_screen_title">Διαμόρφωση γραφικού στοιχείου</string>
|
||||
<string name="widget_configuration_title">Επιλογή Συσκευής</string>
|
||||
<string name="widget_configuration_description">Επιλέξτε ποιο προφίλ συσκευής θα εμφανίζει αυτό το widget.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Το Bluetooth είναι απενεργοποιημένο, ενεργοποιήστε το ;)</string>
|
||||
<string name="overview_monitoring_active_label">Παρακολούθηση συσκευών</string>
|
||||
<string name="overview_monitoring_active_description">Βεβαιωθείτε ότι η συσκευή σας είναι κοντά και ενεργή.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Συνδέθηκε, αλλά χωρίς δεδομένα</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Η συσκευή σου είναι συνδεδεμένη, αλλά το CAPod δεν λαμβάνει ζωντανά δεδομένα από αυτήν. Το τηλέφωνό σου μπορεί να χρειαστεί μια επιλογή συμβατότητας — ο εντοπισμός προβλημάτων μπορεί να προσπαθήσει να βρει μία αυτόματα.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Εκτέλεση εντοπισμού προβλημάτων</string>
|
||||
<string name="overview_unmatched_devices_label">Μη αντιστοιχισμένες συσκευές</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d συσκευή χωρίς αντίστοιχο προφίλ</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Απαιτείται η ακόλουθη άδεια:</string>
|
||||
<string name="permission_system_alert_window_label">Παράθυρο ειδοποίησης συστήματος</string>
|
||||
<string name="permission_system_alert_window_description">Επιτρέψτε στο CAPod να σχεδιάζει πάνω από άλλες εφαρμογές για να καταστεί δυνατή η λειτουργία \"Εμφάνιση αναδυόμενου\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">Όταν η εφαρμογή είναι ανοιχτή</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Όταν η συσκευή είναι συνδεδεμένη</string>
|
||||
<string name="settings_monitor_mode_always_label">Πάντα</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Όταν εντοπιστεί</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Η θήκη είναι ανοιχτή</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Στο αυτί</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Διαφάνεια</string>
|
||||
<string name="anc_mode_adaptive">Προσαρμοστικό</string>
|
||||
<string name="conversation_awareness_label">Ενημερότητα Συνομιλίας</string>
|
||||
<string name="settings_conversation_action_label">Όταν αρχίζεις να μιλάς</string>
|
||||
<string name="settings_conversation_action_nothing_label">Να μην γίνει τίποτα</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Μείωση έντασης</string>
|
||||
<string name="settings_conversation_action_pause_label">Παύση μέσων</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Μείωση έντασης</string>
|
||||
<string name="signal_badge_ble_cd">Σάρωση BLE ενεργή</string>
|
||||
<string name="signal_badge_key_irk_cd">Η ταυτότητα επαληθεύτηκε</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Κρυπτογραφημένη σύνδεση</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Κεχριμπάρι</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Χρησιμοποιεί χρώματα ταπετσαρίας</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Παρακολούθηση</string>
|
||||
<string name="settings_category_appearance_label">Εμφάνιση</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Μη αποθηκευμένες αλλαγές</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Γενικά</string>
|
||||
<string name="device_settings_microphone_mode_label">Μικρόφωνο</string>
|
||||
<string name="device_settings_microphone_mode_description">Ποιο AirPod χρησιμοποιείται ως μικρόφωνο</string>
|
||||
<string name="device_settings_microphone_mode_description">Ποιο ακουστικό χρησιμοποιείται ως μικρόφωνο</string>
|
||||
<string name="device_settings_microphone_mode_auto">Αυτόματο</string>
|
||||
<string name="device_settings_microphone_mode_right">Δεξί</string>
|
||||
<string name="device_settings_microphone_mode_left">Αριστερό</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Ανίχνευση Ύπνου</string>
|
||||
<string name="reaction_sleep_notification_title">Σε παύση από την Ανίχνευση Ύπνου</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s ανέφερε ότι αποκοιμηθήκατε, οπότε η μουσική σας τέθηκε σε παύση. Μπορείτε να το απενεργοποιήσετε στις ρυθμίσεις συσκευής.</string>
|
||||
<string name="reaction_charged_channel_label">Ειδοποίηση φόρτισης</string>
|
||||
<string name="reaction_charged_notification_title">Η φόρτιση ολοκληρώθηκε</string>
|
||||
<string name="reaction_charged_notification_text_full">Το %1$s έχει φορτιστεί πλήρως.</string>
|
||||
<string name="reaction_charged_notification_text_partial">Το %1$s έχει φορτιστεί στο %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Μετονομασία</string>
|
||||
<string name="device_settings_rename_hint">Όνομα συσκευής</string>
|
||||
<string name="device_settings_rename_confirm">Μετονομασία</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Si compraste recientemente, puede demorar un momento para que Google Play sincronice.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Intentá de nuevo en unos minutos si tu compra no aparece.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Asegurate de haber iniciado sesión con la misma cuenta de Google usada para la compra.</string>
|
||||
<string name="settings_monitor_mode_label">Modo monitor</string>
|
||||
<string name="settings_monitor_mode_description">Bajo qué circunstancias esta app monitorea los datos de Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notificación extra</string>
|
||||
<string name="settings_monitor_connected_notification_description">Muestra una notificación extra cuando un dispositivo está conectado. Esto te permite ocultar la notificación permanente \"Sin dispositivos\" desactivando el canal \"Estado del dispositivo\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Mantener la notificación después de la desconexión</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pausa automática</string>
|
||||
<string name="settings_autopause_description">Pausar el audio al quitar el dispositivo de tu oreja.</string>
|
||||
<string name="settings_autopplay_label">Reproducción automática</string>
|
||||
<string name="settings_autoplay_description">Iniciar la reproducción de audio cuando se usa el dispositivo.</string>
|
||||
<string name="settings_autoplay_description">Reanuda la música al volver a ponerte los pods, pero solo si la pausa automática la detuvo. Las pausas que activaste vos mismo (teléfono, palanca de AirPods, suspensión) permanecen en pausa.</string>
|
||||
<string name="settings_start_music_on_wear_label">Iniciar música al ponérselos</string>
|
||||
<string name="settings_start_music_on_wear_description">Siempre intenta reproducir música cuando te ponés los pods, incluso después de una pausa manual.</string>
|
||||
<string name="settings_eardetection_info_label">Nota sobre la detección de oídos</string>
|
||||
<string name="settings_eardetection_info_description">Si la detección de auriculares solo funciona para un pod, se trata de una limitación de Apple. Solo se detecta el «pod principal» (utilizado para el micrófono). Configuración en dispositivos Apple: Ajustes → Bluetooth → AirPods → Micrófono.</string>
|
||||
<string name="settings_fake_data_label">Datos falsos</string>
|
||||
<string name="settings_fake_data_description">Mostrar datos falsos, es decir, simular dispositivos que no existen.</string>
|
||||
<string name="settings_debug_label">Ajustes de depuración</string>
|
||||
<string name="settings_debug_description">Ajustes adicionales para ayudar a solucionar problemas con la app.</string>
|
||||
<string name="settings_signal_minimum_label">Calidad de señal mínima</string>
|
||||
<string name="settings_signal_minimum_description">La calidad de señal mínima que un dispositivo necesita tener para ser considerado tuyo.</string>
|
||||
<string name="settings_autoconnect_label">Conexión automática</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Mostrar pop-up de conexión</string>
|
||||
<string name="settings_popup_connected_description">Mostrar un pop-up cuando el dispositivo se conecta por primera vez.</string>
|
||||
<string name="settings_popup_info_not_in_app">Los popups solo se muestran cuando no estás en la app.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Los popups requieren que el monitor esté en ejecución. El modo de monitor está configurado como \"Cuando la app está abierta\", lo que detiene el monitor cuando salís de la app.</string>
|
||||
<string name="general_fix_it_action">Corregirlo</string>
|
||||
<string name="settings_charged_notification_label">Notificación de carga completa</string>
|
||||
<string name="settings_charged_notification_description">Muestra una notificación cuando todo lo que se está cargando alcanza el nivel de carga objetivo.</string>
|
||||
<string name="settings_charged_threshold_label">Nivel de carga objetivo</string>
|
||||
<string name="settings_charged_scope_label">Baterías monitoreadas</string>
|
||||
<string name="settings_charged_scope_pods_label">Solo pods</string>
|
||||
<string name="settings_charged_scope_case_label">Solo estuche</string>
|
||||
<string name="settings_charged_scope_both_label">Pods y estuche</string>
|
||||
<string name="device_settings_experimental_title">Función experimental</string>
|
||||
<string name="device_settings_experimental_description">Esta función aún no se ha probado en todos los dispositivos. Si algo no funciona correctamente, por favor abrí un problema con un registro de depuración.</string>
|
||||
<string name="device_settings_experimental_action">Abrir seguimiento de problemas</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Depuración</string>
|
||||
<string name="settings_general_label">Ajustes</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Modo depuración</string>
|
||||
<string name="settings_debug_mode_description">Mostrar información adicional para solucionar problemas.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Datos BLE sin filtrar</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Eliminar cualquier filtro del escáner BLE para mostrar todos los datos BLE transmitidos. Útil para agregar soporte para nuevos tipos de auriculares.</string>
|
||||
<string name="settings_compatibility_mode_label">Modo de compatibilidad</string>
|
||||
<string name="settings_compatibility_mode_description">Desactivar optimizaciones para mejorar la compatibilidad. Probá esto si no estás viendo ningún dato.</string>
|
||||
<string name="help_translate_label">Traducción</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Este widget requiere una conexión directa (AAP) a tu dispositivo. Asegurate de que tu dispositivo esté emparejado y conectado en la app antes de usar este widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Este dispositivo no admite control de ruido</string>
|
||||
<string name="anc_widget_aap_not_connected_label">No conectado</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Cerca pero sin conexión</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">No está cerca</string>
|
||||
<string name="anc_widget_aap_connecting_label">Conectando…</string>
|
||||
<string name="tile_anc_label">Control de ruido</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Permiso requerido</string>
|
||||
<string name="tile_anc_subtitle_no_device">Sin dispositivo</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Sin control de ruido</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth desactivado</string>
|
||||
<string name="widget_config_screen_title">Configuración del widget</string>
|
||||
<string name="widget_configuration_title">Selecciona el dispositivo</string>
|
||||
<string name="widget_configuration_description">Elige qué perfil del dispositivo debe mostrar este complemento.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">El Bluetooth está desactivado, actívalo ;)</string>
|
||||
<string name="overview_monitoring_active_label">Monitoreo de dispositivos</string>
|
||||
<string name="overview_monitoring_active_description">Asegúrate de que tu dispositivo esté cerca y activo.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Conectado, pero sin datos</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Tu dispositivo está conectado, pero CAPod no está recibiendo datos en tiempo real. Tu teléfono puede necesitar una opción de compatibilidad: el solucionador de problemas puede intentar encontrar una automáticamente.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Ejecutar solucionador de problemas</string>
|
||||
<string name="overview_unmatched_devices_label">Dispositivos no emparejados</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d dispositivo sin perfil coincidente</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Se requiere el siguiente permiso:</string>
|
||||
<string name="permission_system_alert_window_label">Ventana emergente</string>
|
||||
<string name="permission_system_alert_window_description">Permite que CAPod se superponga a otras aplicaciones para que la función «Mostrar ventana emergente».</string>
|
||||
<string name="settings_monitor_mode_manual_label">Cuando la aplicación está abierta</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Cuando el dispositivo está conectado</string>
|
||||
<string name="settings_monitor_mode_always_label">Siempre</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Cuando está visible</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">El estuche está abierto</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">En el oído</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparencia</string>
|
||||
<string name="anc_mode_adaptive">Adaptivo</string>
|
||||
<string name="conversation_awareness_label">Conciencia de conversación</string>
|
||||
<string name="settings_conversation_action_label">Cuando empezás a hablar</string>
|
||||
<string name="settings_conversation_action_nothing_label">No hacer nada</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Bajar el volumen</string>
|
||||
<string name="settings_conversation_action_pause_label">Pausar el contenido</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Reducción de volumen</string>
|
||||
<string name="signal_badge_ble_cd">Escaneo BLE activo</string>
|
||||
<string name="signal_badge_key_irk_cd">Identidad verificada</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Conexión encriptada</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ámbar</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Usa los colores del fondo de pantalla</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitoreo</string>
|
||||
<string name="settings_category_appearance_label">Apariencia</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Cambios sin guardar</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">General</string>
|
||||
<string name="device_settings_microphone_mode_label">Micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Qué AirPod se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Qué auricular se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_auto">Auto</string>
|
||||
<string name="device_settings_microphone_mode_right">Derecho</string>
|
||||
<string name="device_settings_microphone_mode_left">Izquierdo</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Detección de sueño</string>
|
||||
<string name="reaction_sleep_notification_title">Pausado por Detección de sueño</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s informó que te quedaste dormido, por lo que tu música fue pausada. Podés desactivar esto en la configuración del dispositivo.</string>
|
||||
<string name="reaction_charged_channel_label">Notificación de carga completa</string>
|
||||
<string name="reaction_charged_notification_title">Carga completa</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s está completamente cargado.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s se ha cargado al %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Renombrar</string>
|
||||
<string name="device_settings_rename_hint">Nombre del dispositivo</string>
|
||||
<string name="device_settings_rename_confirm">Renombrar</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Si has comprado recientemente, puede tardar un momento para que Google Play sincronice.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Intenta de nuevo en unos minutos si tu compra no aparece.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Asegúrate de haber iniciado sesión con la misma cuenta de Google utilizada para la compra.</string>
|
||||
<string name="settings_monitor_mode_label">Modo monitor</string>
|
||||
<string name="settings_monitor_mode_description">Bajo qué circunstancias esta aplicación monitorea los datos de Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notificación adicional</string>
|
||||
<string name="settings_monitor_connected_notification_description">Muestra una notificación adicional cuando un dispositivo está conectado. Esto te permite ocultar la notificación permanente \"Sin dispositivos\" desactivando el canal \"Estado del dispositivo\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Mantener la notificación después de la desconexión</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pausa automática</string>
|
||||
<string name="settings_autopause_description">Pausar el audio al quitar el dispositivo de tu oreja.</string>
|
||||
<string name="settings_autopplay_label">Reproducción automática</string>
|
||||
<string name="settings_autoplay_description">Iniciar la reproducción de audio cuando se usa el dispositivo.</string>
|
||||
<string name="settings_autoplay_description">Reanuda la música al volver a usar tus pods, pero solo si Auto pausa la detuvo. Las pausas que activaste tú mismo (teléfono, varilla de AirPods, suspensión) siguen en pausa.</string>
|
||||
<string name="settings_start_music_on_wear_label">Iniciar música al ponerse</string>
|
||||
<string name="settings_start_music_on_wear_description">Siempre intenta reproducir música cuando te pones los pods, incluso después de una pausa manual.</string>
|
||||
<string name="settings_eardetection_info_label">Nota sobre la detección de oídos</string>
|
||||
<string name="settings_eardetection_info_description">Si la detección de auriculares solo funciona para un pod, se trata de una limitación de Apple. Solo se detecta el «pod principal» (utilizado para el micrófono). Configuración en dispositivos Apple: Ajustes → Bluetooth → AirPods → Micrófono.</string>
|
||||
<string name="settings_fake_data_label">Datos falsos</string>
|
||||
<string name="settings_fake_data_description">Mostrar datos falsos, es decir, simular dispositivos que no existen.</string>
|
||||
<string name="settings_debug_label">Ajustes de depuración</string>
|
||||
<string name="settings_debug_description">Ajustes adicionales para ayudar a solucionar problemas con la aplicación.</string>
|
||||
<string name="settings_signal_minimum_label">Calidad de señal mínima</string>
|
||||
<string name="settings_signal_minimum_description">La calidad de señal mínima que un dispositivo necesita tener para ser considerado tuyo.</string>
|
||||
<string name="settings_autoconnect_label">Conexión automática</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Mostrar pop-up de conexión</string>
|
||||
<string name="settings_popup_connected_description">Mostrar un pop-up cuando el dispositivo se conecta por primera vez.</string>
|
||||
<string name="settings_popup_info_not_in_app">Las ventanas emergentes solo se muestran cuando no estás en la aplicación.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Las ventanas emergentes requieren que el monitor esté en ejecución. Tu modo de monitor está configurado en \"Cuando la aplicación está abierta\", lo que detiene el monitor cuando sales de la aplicación.</string>
|
||||
<string name="general_fix_it_action">Corregir</string>
|
||||
<string name="settings_charged_notification_label">Notificación de carga completa</string>
|
||||
<string name="settings_charged_notification_description">Muestra una notificación cuando todo lo que se está cargando alcanza el nivel de carga objetivo.</string>
|
||||
<string name="settings_charged_threshold_label">Nivel de carga objetivo</string>
|
||||
<string name="settings_charged_scope_label">Baterías monitoreadas</string>
|
||||
<string name="settings_charged_scope_pods_label">Solo audífonos</string>
|
||||
<string name="settings_charged_scope_case_label">Solo estuche</string>
|
||||
<string name="settings_charged_scope_both_label">Audífonos y estuche</string>
|
||||
<string name="device_settings_experimental_title">Función experimental</string>
|
||||
<string name="device_settings_experimental_description">Esta función aún no ha sido probada en todos los dispositivos. Si algo no funciona correctamente, por favor abre un problema con un registro de depuración.</string>
|
||||
<string name="device_settings_experimental_action">Abrir rastreador de problemas</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Depuración</string>
|
||||
<string name="settings_general_label">Ajustes</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Modo depuración</string>
|
||||
<string name="settings_debug_mode_description">Mostrar información adicional para solucionar problemas.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Datos BLE sin filtrar</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Eliminar cualquier filtro del escáner BLE para mostrar todos los datos BLE transmitidos. Útil para agregar soporte para nuevos tipos de auriculares.</string>
|
||||
<string name="settings_compatibility_mode_label">Modo de compatibilidad</string>
|
||||
<string name="settings_compatibility_mode_description">Desactivar optimizaciones para mejorar la compatibilidad. Prueba esto si no estás viendo ningún dato.</string>
|
||||
<string name="help_translate_label">Traducción</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Este widget requiere una conexión directa (AAP) a tu dispositivo. Asegúrate de que tu dispositivo esté emparejado y conectado en la aplicación antes de usar este widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Este dispositivo no es compatible con el control de ruido</string>
|
||||
<string name="anc_widget_aap_not_connected_label">No conectado</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Cerca pero no conectado</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">No está cerca</string>
|
||||
<string name="anc_widget_aap_connecting_label">Conectando…</string>
|
||||
<string name="tile_anc_label">Control de ruido</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Permiso requerido</string>
|
||||
<string name="tile_anc_subtitle_no_device">Sin dispositivo</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Sin control de ruido</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth apagado</string>
|
||||
<string name="widget_config_screen_title">Configuración de widget</string>
|
||||
<string name="widget_configuration_title">Selecciona el dispositivo</string>
|
||||
<string name="widget_configuration_description">Elige qué perfil del dispositivo debe mostrar este complemento.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">El Bluetooth está desactivado, actívalo ;)</string>
|
||||
<string name="overview_monitoring_active_label">Monitoreo de dispositivos</string>
|
||||
<string name="overview_monitoring_active_description">Asegúrate de que tu dispositivo esté cerca y activo.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Conectado, pero sin datos</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Tu dispositivo está conectado, pero CAPod no está recibiendo datos en tiempo real. Es posible que tu teléfono necesite una opción de compatibilidad — el solucionador de problemas puede intentar encontrar una automáticamente.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Ejecutar solucionador de problemas</string>
|
||||
<string name="overview_unmatched_devices_label">Dispositivos no emparejados</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d dispositivo sin perfil coincidente</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Se requiere el siguiente permiso:</string>
|
||||
<string name="permission_system_alert_window_label">Ventana emergente</string>
|
||||
<string name="permission_system_alert_window_description">Permite que CAPod se superponga a otras aplicaciones para que la función «Mostrar ventana emergente».</string>
|
||||
<string name="settings_monitor_mode_manual_label">Cuando la aplicación está abierta</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Cuando el dispositivo está conectado</string>
|
||||
<string name="settings_monitor_mode_always_label">Siempre</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Cuando está visible</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">El estuche está abierto</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">En el oído</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparencia</string>
|
||||
<string name="anc_mode_adaptive">Adaptativo</string>
|
||||
<string name="conversation_awareness_label">Reconocimiento de conversación</string>
|
||||
<string name="settings_conversation_action_label">Cuando empiezas a hablar</string>
|
||||
<string name="settings_conversation_action_nothing_label">No hacer nada</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Bajar volumen</string>
|
||||
<string name="settings_conversation_action_pause_label">Pausar medios</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Reducción de volumen</string>
|
||||
<string name="signal_badge_ble_cd">Escaneo BLE activo</string>
|
||||
<string name="signal_badge_key_irk_cd">Identidad verificada</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Conexión cifrada</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ámbar</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Usa los colores del fondo de pantalla</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitoreo</string>
|
||||
<string name="settings_category_appearance_label">Apariencia</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Cambios sin guardar</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">General</string>
|
||||
<string name="device_settings_microphone_mode_label">Micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Qué AirPod se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Qué audífono se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automático</string>
|
||||
<string name="device_settings_microphone_mode_right">Derecho</string>
|
||||
<string name="device_settings_microphone_mode_left">Izquierdo</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Detección de sueño</string>
|
||||
<string name="reaction_sleep_notification_title">Pausado por detección de sueño</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s detectó que te quedaste dormido, por lo que tu música fue pausada. Puedes desactivar esto en la configuración del dispositivo.</string>
|
||||
<string name="reaction_charged_channel_label">Notificación de carga completa</string>
|
||||
<string name="reaction_charged_notification_title">Carga completada</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s está completamente cargado.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s se ha cargado al %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Renombrar</string>
|
||||
<string name="device_settings_rename_hint">Nombre del dispositivo</string>
|
||||
<string name="device_settings_rename_confirm">Renombrar</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Si has comprado recientemente, puede tardar un momento para que Google Play sincronice.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Inténtalo de nuevo en unos minutos si tu compra no aparece.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Asegúrate de haber iniciado sesión con la misma cuenta de Google utilizada para la compra.</string>
|
||||
<string name="settings_monitor_mode_label">Modo monitor</string>
|
||||
<string name="settings_monitor_mode_description">Bajo qué circunstancias esta aplicación supervisa los datos de Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notificación adicional</string>
|
||||
<string name="settings_monitor_connected_notification_description">Muestra una notificación adicional cuando un dispositivo está conectado. Esto le permite ocultar la notificación permanente \"Sin dispositivos\" deshabilitando el canal \"Estado del dispositivo\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Mantener la notificación después de la desconexión</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pausa automática</string>
|
||||
<string name="settings_autopause_description">Pausar el audio al quitar el dispositivo de tu oído.</string>
|
||||
<string name="settings_autopplay_label">Reproducción automática</string>
|
||||
<string name="settings_autoplay_description">Iniciar la reproducción de audio cuando se lleva puesto el dispositivo.</string>
|
||||
<string name="settings_autoplay_description">Reanuda la música al volver a ponerte los auriculares, pero solo si fue Auto pausa quien la detuvo. Las pausas que hiciste tú (teléfono, varilla de los AirPods, suspensión) permanecen pausadas.</string>
|
||||
<string name="settings_start_music_on_wear_label">Iniciar música al ponérselos</string>
|
||||
<string name="settings_start_music_on_wear_description">Siempre intenta reproducir música cuando te pones los auriculares, incluso después de una pausa manual.</string>
|
||||
<string name="settings_eardetection_info_label">Nota sobre la detección de oídos</string>
|
||||
<string name="settings_eardetection_info_description">Si la detección de auriculares solo funciona para un pod, se trata de una limitación de Apple. Solo se detecta el «pod principal» (utilizado para el micrófono). Configuración en dispositivos Apple: Ajustes → Bluetooth → AirPods → Micrófono.</string>
|
||||
<string name="settings_fake_data_label">Datos falsos</string>
|
||||
<string name="settings_fake_data_description">Mostrar datos falsos, es decir, simular dispositivos que no existen.</string>
|
||||
<string name="settings_debug_label">Ajustes de depuración</string>
|
||||
<string name="settings_debug_description">Ajustes adicionales para ayudar a solucionar problemas con la aplicación.</string>
|
||||
<string name="settings_signal_minimum_label">Calidad mínima de la señal</string>
|
||||
<string name="settings_signal_minimum_description">La calidad mínima de la señal que debe tener un dispositivo para que se considere tuyo.</string>
|
||||
<string name="settings_autoconnect_label">Conexión automática</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Mostrar ventana emergente de conexión</string>
|
||||
<string name="settings_popup_connected_description">Mostrar una ventana emergente cuando el dispositivo se conecta por primera vez.</string>
|
||||
<string name="settings_popup_info_not_in_app">Los popups solo se muestran cuando no estás en la aplicación.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Los popups requieren que el monitor esté en ejecución. El modo de monitor está configurado en \"Cuando la app está abierta\", lo que detiene el monitor cuando sales de la aplicación.</string>
|
||||
<string name="general_fix_it_action">Corregirlo</string>
|
||||
<string name="settings_charged_notification_label">Notificación de carga completa</string>
|
||||
<string name="settings_charged_notification_description">Mostrar una notificación cuando todo lo que se está cargando alcance el nivel de carga objetivo.</string>
|
||||
<string name="settings_charged_threshold_label">Nivel de carga objetivo</string>
|
||||
<string name="settings_charged_scope_label">Baterías supervisadas</string>
|
||||
<string name="settings_charged_scope_pods_label">Solo auriculares</string>
|
||||
<string name="settings_charged_scope_case_label">Solo funda</string>
|
||||
<string name="settings_charged_scope_both_label">Auriculares y funda</string>
|
||||
<string name="device_settings_experimental_title">Función experimental</string>
|
||||
<string name="device_settings_experimental_description">Esta función aún no se ha probado en todos los dispositivos. Si algo no funciona correctamente, por favor abre una incidencia con un registro de depuración.</string>
|
||||
<string name="device_settings_experimental_action">Abrir rastreador de incidencias</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Depuración</string>
|
||||
<string name="settings_general_label">Ajustes</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Modo de depuración</string>
|
||||
<string name="settings_debug_mode_description">Mostrar información adicional para solucionar problemas.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Datos BLE sin filtrar</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Elimina cualquier filtro del escáner BLE para mostrar todos los datos BLE transmitidos. Útil para añadir compatibilidad con nuevos tipos de auriculares.</string>
|
||||
<string name="settings_compatibility_mode_label">Modo de compatibilidad</string>
|
||||
<string name="settings_compatibility_mode_description">Desactiva las optimizaciones para mejorar la compatibilidad. Prueba esto si no ves ningún dato.</string>
|
||||
<string name="help_translate_label">Traducción</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Este widget requiere una conexión directa (AAP) con tu dispositivo. Asegúrate de que tu dispositivo esté emparejado y conectado en la app antes de usar este widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Este dispositivo no admite control de ruido</string>
|
||||
<string name="anc_widget_aap_not_connected_label">No conectado</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Cerca pero no conectado</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">No cerca</string>
|
||||
<string name="anc_widget_aap_connecting_label">Conectando…</string>
|
||||
<string name="tile_anc_label">Control de ruido</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Permiso requerido</string>
|
||||
<string name="tile_anc_subtitle_no_device">Sin dispositivo</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Sin control de ruido</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth desactivado</string>
|
||||
<string name="widget_config_screen_title">Configuración del widget</string>
|
||||
<string name="widget_configuration_title">Selecciona el dispositivo</string>
|
||||
<string name="widget_configuration_description">Elige qué perfil del dispositivo debe mostrar este complemento.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">El Bluetooth está desactivado, actívalo ;)</string>
|
||||
<string name="overview_monitoring_active_label">Monitoreo de dispositivos</string>
|
||||
<string name="overview_monitoring_active_description">Asegúrate de que tu dispositivo esté cerca y activo.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Conectado, pero sin datos</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Tu dispositivo está conectado, pero CAPod no está recibiendo datos en directo. Es posible que tu teléfono necesite una opción de compatibilidad: el solucionador de problemas puede intentar encontrar una automáticamente.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Ejecutar solucionador de problemas</string>
|
||||
<string name="overview_unmatched_devices_label">Dispositivos no emparejados</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d dispositivo sin perfil coincidente</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Se requiere el siguiente permiso:</string>
|
||||
<string name="permission_system_alert_window_label">Ventana emergente</string>
|
||||
<string name="permission_system_alert_window_description">Permite que CAPod se superponga a otras aplicaciones para que la función «Mostrar ventana emergente».</string>
|
||||
<string name="settings_monitor_mode_manual_label">Cuando la aplicación está abierta</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Cuando el dispositivo está conectado</string>
|
||||
<string name="settings_monitor_mode_always_label">Siempre</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Cuando está visible</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">El estuche está abierto</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">En el oído</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparencia</string>
|
||||
<string name="anc_mode_adaptive">Adaptativo</string>
|
||||
<string name="conversation_awareness_label">Conciencia de conversación</string>
|
||||
<string name="settings_conversation_action_label">Cuando empiezas a hablar</string>
|
||||
<string name="settings_conversation_action_nothing_label">No hacer nada</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Bajar volumen</string>
|
||||
<string name="settings_conversation_action_pause_label">Pausar multimedia</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Reducción de volumen</string>
|
||||
<string name="signal_badge_ble_cd">Escaneo BLE activo</string>
|
||||
<string name="signal_badge_key_irk_cd">Identidad verificada</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Conexión cifrada</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ámbar</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Usa colores del fondo de pantalla</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitorización</string>
|
||||
<string name="settings_category_appearance_label">Apariencia</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Cambios sin guardar</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">General</string>
|
||||
<string name="device_settings_microphone_mode_label">Micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Qué AirPod se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Qué auricular se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automático</string>
|
||||
<string name="device_settings_microphone_mode_right">Derecho</string>
|
||||
<string name="device_settings_microphone_mode_left">Izquierdo</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Detección de sueño</string>
|
||||
<string name="reaction_sleep_notification_title">Pausado por detección de sueño</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s informó que te quedaste dormido, por lo que tu música fue pausada. Puedes desactivar esto en la configuración del dispositivo.</string>
|
||||
<string name="reaction_charged_channel_label">Notificación de carga completa</string>
|
||||
<string name="reaction_charged_notification_title">Carga completada</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s está completamente cargado.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s se ha cargado al %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Renombrar</string>
|
||||
<string name="device_settings_rename_hint">Nombre del dispositivo</string>
|
||||
<string name="device_settings_rename_confirm">Renombrar</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Kui olete hiljuti ostnud, võib Google Playl kuluda ajakohastamiseks hetk.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Proovige mõne minuti pärast uuesti, kui teie ost ei ilmu.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Veenduge, et olete sisse logitud sama Google\'i kontoga, mida kasutasite ostu sooritamiseks.</string>
|
||||
<string name="settings_monitor_mode_label">Jälgimisrežiim</string>
|
||||
<string name="settings_monitor_mode_description">Mis olukordades jälgib see rakendus Bluetooth andmeid.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Lisateavitus</string>
|
||||
<string name="settings_monitor_connected_notification_description">Kuvab seadme ühendumisel lisateavituse. See peidab pidevalt nähtava „Seadmed puuduvad“ teate, selleks keelates „Seadme olek“ kanali.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Kuva teadet ka pärast eemaldamist</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Automaatne peatamine</string>
|
||||
<string name="settings_autopause_description">Seadme kõrvast eemaldamisel peata esitamine.</string>
|
||||
<string name="settings_autopplay_label">Automaatne esitamine</string>
|
||||
<string name="settings_autoplay_description">Automaatne esitamine, kui seade pannakse kõrva.</string>
|
||||
<string name="settings_autoplay_description">Jätka muusika kuulamist, kui kannad taas kõrvaklappe, kuid ainult siis, kui isekäivitunud paus selle peatas. Isepausimise (telefon, AirPodsi vars, uni) puhul jääb paus kestma.</string>
|
||||
<string name="settings_start_music_on_wear_label">Esita muusikat kõrva panemisel</string>
|
||||
<string name="settings_start_music_on_wear_description">Proovib alati esitada muusikat, kui paned kõrvaklapid pähe, isegi pärast enda käivitatud pausi.</string>
|
||||
<string name="settings_eardetection_info_label">Kõrva tuvastamisest</string>
|
||||
<string name="settings_eardetection_info_description">Kui tuvastatakse vaid üks klapp, siis seda põhjustab Apple\'i piirang. Tuvastatakse vaid peamine klapp (kasutatakse mikrofonina). Seadista Apple\'i seadmetes: Seaded (Settings) → Bluetooth → AirPods (AirPodid) → Mikrofon (Microphone).</string>
|
||||
<string name="settings_fake_data_label">Valeandmed</string>
|
||||
<string name="settings_fake_data_description">Näita valeandmeid, nt samad seadmed, mida pole olemas.</string>
|
||||
<string name="settings_debug_label">Vealogide seaded</string>
|
||||
<string name="settings_debug_description">Täiendavad seadmed rakenduse probleemide lahendamise aitamiseks.</string>
|
||||
<string name="settings_signal_minimum_label">Väikseim signaali kvaliteet</string>
|
||||
<string name="settings_signal_minimum_description">Väikseim signaali kvaliteet, mis seade peab omama, et seda peetaks teie omaks.</string>
|
||||
<string name="settings_autoconnect_label">Automaatne ühendamine</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Näita ühenduse hüpikakent</string>
|
||||
<string name="settings_popup_connected_description">Näita seadme esmaühendumisel hüpikakent.</string>
|
||||
<string name="settings_popup_info_not_in_app">Hüpikaknad kuvatakse ainult siis, kui sa pole rakenduses.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Hüpikaknad nõuavad monitori töötamist. Monitori režiim on seatud „Kui rakendus on avatud“, mis peatab monitori, kui rakendusest lahkud.</string>
|
||||
<string name="general_fix_it_action">Paranda</string>
|
||||
<string name="settings_charged_notification_label">Laetud teatis</string>
|
||||
<string name="settings_charged_notification_description">Kuva teatis, kui kõik laadimise all olevad seadmed jõuavad sihitud laetuse tasemele.</string>
|
||||
<string name="settings_charged_threshold_label">Sihitud laetuse tase</string>
|
||||
<string name="settings_charged_scope_label">Jälgitavad akud</string>
|
||||
<string name="settings_charged_scope_pods_label">Ainult kõrvaklapid</string>
|
||||
<string name="settings_charged_scope_case_label">Ainult ümbris</string>
|
||||
<string name="settings_charged_scope_both_label">Kõrvaklapid ja ümbris</string>
|
||||
<string name="device_settings_experimental_title">Katsetamisel</string>
|
||||
<string name="device_settings_experimental_description">Seda pole kõigil seadmetel veel katsetatud. Kui midagi ei toimi õigesti, avage probleem koos silumislogi failiga.</string>
|
||||
<string name="device_settings_experimental_action">Ava probleemide jälgija</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Silumine</string>
|
||||
<string name="settings_general_label">Seaded</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Veaotsing</string>
|
||||
<string name="settings_debug_mode_description">Näita vigade lahendamiseks täiendavat teavet.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Filtreerimata Bluetooth madalama energiatarbimisega andmed</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Eemalda kõik Bluetooth madalama energiatarbimisega skanneri filtrid, et näha kõiki edastatud Bluetooth madalama energiatarbimisega andmeid. Kasulik uute kõrvaklappide ühildamiseks.</string>
|
||||
<string name="settings_compatibility_mode_label">Ühilduvusrežiim</string>
|
||||
<string name="settings_compatibility_mode_description">Ühilduvuse parandamiseks keelake optimeeringud. Proovige seda, kui te ei näe mingeid andmeid.</string>
|
||||
<string name="help_translate_label">Tõlge</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">See vidin nõuab otsest ühendust (AAP) teie seadmega. Enne selle vidina kasutamist veenduge, et teie seade on rakenduses seotud ja ühendatud.</string>
|
||||
<string name="anc_widget_no_anc_support_label">See seade ei toeta mürakontrolli</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Ühendus puudub</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Lähedal, kuid pole ühendatud</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Pole lähedal</string>
|
||||
<string name="anc_widget_aap_connecting_label">Ühendamine…</string>
|
||||
<string name="tile_anc_label">Mürakontroll</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Vaja on luba</string>
|
||||
<string name="tile_anc_subtitle_no_device">Puudub seade</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Puudub mürakontroll</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth väljas</string>
|
||||
<string name="widget_config_screen_title">Vidina seadistus</string>
|
||||
<string name="widget_configuration_title">Vali seade</string>
|
||||
<string name="widget_configuration_description">Valige, millist seadmeprofiili see vidin võiks kuvada.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth on keelatud, lubage see ;)</string>
|
||||
<string name="overview_monitoring_active_label">Seadmete jälgimine</string>
|
||||
<string name="overview_monitoring_active_description">Veenduge, et seade oleks läheduses ja kasutusel.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Ühendatud, kuid andmeid pole</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Seade on ühendatud, kuid CAPod ei saa sellelt otseandmeid. Telefon vajab tõenäoliselt ühilduvuse valikut — trikijär kontrollib, kas suudab selle automaatselt leida.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Käivita trikijär</string>
|
||||
<string name="overview_unmatched_devices_label">Omavahel sobimatud seadmed</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d seade ei kattu profiiliga</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Vaja on järgmist luba:</string>
|
||||
<string name="permission_system_alert_window_label">Süsteemi teate aken</string>
|
||||
<string name="permission_system_alert_window_description">Luba CAPodil toimetada teiste rakenduste kohal, et näha hüpikaknaid.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Kui rakendus on avatud</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Kui seade on ühendatud</string>
|
||||
<string name="settings_monitor_mode_always_label">Alati</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Kui nähtaval</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Kaas on avatud</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Kõrvas</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Läbipaistvus</string>
|
||||
<string name="anc_mode_adaptive">Kohanduv</string>
|
||||
<string name="conversation_awareness_label">Vestluse tuvastamine</string>
|
||||
<string name="settings_conversation_action_label">Kui alustad rääkimist</string>
|
||||
<string name="settings_conversation_action_nothing_label">Ära tee midagi</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Vähenda helitugevust</string>
|
||||
<string name="settings_conversation_action_pause_label">Peata meedia</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Helitugevuse vähendamine</string>
|
||||
<string name="signal_badge_ble_cd">BLE skannimine aktiivne</string>
|
||||
<string name="signal_badge_key_irk_cd">Identiteet kinnitatud</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Krüpteeritud ühendus</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Merevaik</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Kasutab taustapildi värve</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Jälgimine</string>
|
||||
<string name="settings_category_appearance_label">Välimus</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Salvestamata muudatused</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Üldine</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Millist AirPodi kasutatakse mikrofonina</string>
|
||||
<string name="device_settings_microphone_mode_description">Kumba kõrvaklappi kasutatakse mikrofonina</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automaatne</string>
|
||||
<string name="device_settings_microphone_mode_right">Parem</string>
|
||||
<string name="device_settings_microphone_mode_left">Vasak</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Une tuvastamine</string>
|
||||
<string name="reaction_sleep_notification_title">Uinumise tõttu peatatud</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s teatas, et uinusite, seega muusika vaikis. Saate selle seadme seadetes keelata.</string>
|
||||
<string name="reaction_charged_channel_label">Laetud teatis</string>
|
||||
<string name="reaction_charged_notification_title">Laadimine lõpetatud</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s on täielikult laetud.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s on laetud %2$d%%-ni.</string>
|
||||
<string name="device_settings_rename_label">Nimeta ümber</string>
|
||||
<string name="device_settings_rename_hint">Seadme nimi</string>
|
||||
<string name="device_settings_rename_confirm">Nimeta ümber</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Duela gutxi erosi baduzu, baliteke Google Play-k sinkronizatzeko une bat behar izatea.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Saiatu berriro minutu batzuen buruan zure erosketa agertzen ez bada.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Ziurtatu erosketan erabilitako Google kontu berarekin saioa hasita daudela.</string>
|
||||
<string name="settings_monitor_mode_label">Monitorizazio modua</string>
|
||||
<string name="settings_monitor_mode_description">Zein egoeratan monitorizatzen ditu aplikazio honek Bluetooth datuak.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Jakinarazpen gehigarria</string>
|
||||
<string name="settings_monitor_connected_notification_description">Gailu bat konektatuta dagoenean jakinarazpen gehigarri bat erakusten du. Honek \"Gailurik ez\" jakinarazpen iraunkorra ezkutatzeko aukera ematen dizu \"Gailuaren egoera\" kanala desgaituz.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Mantendu jakinarazpena deskonektatu ondoren</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pausatze automatikoa</string>
|
||||
<string name="settings_autopause_description">Pausatu audioa gailua belarritik kentzean.</string>
|
||||
<string name="settings_autopplay_label">Erreprodukzio automatikoa</string>
|
||||
<string name="settings_autoplay_description">Hasi audio erreprodukzioa gailua jantzita dagoenean.</string>
|
||||
<string name="settings_autoplay_description">Berriro jantzita musika errepikatzen du, baina soilik Eten automatikoak gelditu bazuen. Zuk eragindako etenak (telefonoa, AirPods beso, loa) etenda geratzen dira.</string>
|
||||
<string name="settings_start_music_on_wear_label">Hasi musika jantzitakoan</string>
|
||||
<string name="settings_start_music_on_wear_description">Beti saiatzen da musika erreproduzitzen aurrikulariak jartzen dituzunean, eskuzko eten baten ondoren ere.</string>
|
||||
<string name="settings_eardetection_info_label">Belarri-detekzioaren oharra</string>
|
||||
<string name="settings_eardetection_info_description">Belarri-detekzioak pod batentzat soilik funtzionatzen badu, Apple-ren muga bat da. \"Pod nagusia\" (mikrofonorako erabiltzen dena) soilik detektatzen da. Konfiguratu Apple gailuetan: Ezarpenak → Bluetooth → AirPods → Mikrofonoa.</string>
|
||||
<string name="settings_fake_data_label">Datu faltsuak</string>
|
||||
<string name="settings_fake_data_description">Erakutsi datu faltsuak, hau da, existitzen ez diren gailuak simulatu.</string>
|
||||
<string name="settings_debug_label">Arazketa ezarpenak</string>
|
||||
<string name="settings_debug_description">Aplikazioarekin arazoak konpontzen laguntzeko ezarpen gehigarriak.</string>
|
||||
<string name="settings_signal_minimum_label">Seinalearen gutxieneko kalitatea</string>
|
||||
<string name="settings_signal_minimum_description">Gailu batek zurea dela kontsideratzeko izan behar duen seinalearen gutxieneko kalitatea.</string>
|
||||
<string name="settings_autoconnect_label">Automatikoki konektatu</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Erakutsi konexioaren pop-up-a</string>
|
||||
<string name="settings_popup_connected_description">Erakutsi pop-up bat gailua lehen aldiz konektatzen denean.</string>
|
||||
<string name="settings_popup_info_not_in_app">Leiho gainerakorrak soilik erakusten dira aplikazioan ez zaudenerako.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Leiho gainerakorrek monitorea martxan egotea behar dute. Zure monitor modua \"Aplikazioa irekita dagoenean\" ezarrita dago, eta horrek monitorea gelditzen du aplikaziotik irteten zarenean.</string>
|
||||
<string name="general_fix_it_action">Konpondu</string>
|
||||
<string name="settings_charged_notification_label">Kargatutako jakinarazpena</string>
|
||||
<string name="settings_charged_notification_description">Erakutsi jakinarazpen bat kargatzen ari den guztia helburuko karga-maila lortzen duenean.</string>
|
||||
<string name="settings_charged_threshold_label">Helburuko karga-maila</string>
|
||||
<string name="settings_charged_scope_label">Monitorizatutako bateriak</string>
|
||||
<string name="settings_charged_scope_pods_label">Podak soilik</string>
|
||||
<string name="settings_charged_scope_case_label">Kaxa soilik</string>
|
||||
<string name="settings_charged_scope_both_label">Podak eta kaxa</string>
|
||||
<string name="device_settings_experimental_title">Funtzio esperimentala</string>
|
||||
<string name="device_settings_experimental_description">Funtzio hau oraindik ez da gailu guztietan probatu. Zerbait ondo funtzionatzen ez badu, mesedez ireki arazo bat arazketa-erregistroarekin.</string>
|
||||
<string name="device_settings_experimental_action">Ireki arazo-jarraipena</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Arazketa</string>
|
||||
<string name="settings_general_label">Ezarpenak</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Arazketa modua</string>
|
||||
<string name="settings_debug_mode_description">Erakutsi informazio gehigarria arazoak konpontzeko.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Iragazi gabeko BLE datuak</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Kendu BLE eskanerreko iragazki guztiak igorritako BLE datu guztiak erakusteko. Entzungailu mota berrietarako euskarria gehitzeko erabilgarria.</string>
|
||||
<string name="settings_compatibility_mode_label">Bateragarritasun modua</string>
|
||||
<string name="settings_compatibility_mode_description">Desgaitu optimizazioak bateragarritasuna hobetzeko. Saiatu hau daturik ikusten ez baduzu.</string>
|
||||
<string name="help_translate_label">Itzulpena</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Widget honek zure gailuarekin konexio zuzen bat (AAP) behar du. Ziurtatu zure gailua parekatuta eta konektatuta dagoela aplikazioan widget hau erabili aurretik.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Gailu honek ez ditu onartzen zarata-kontrolaren funtzioak</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Konektatu gabe</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Gertu baina konektatu gabe</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Ez dago gertu</string>
|
||||
<string name="anc_widget_aap_connecting_label">Konektatzen…</string>
|
||||
<string name="tile_anc_label">Zarata kontrola</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Baimena beharrezkoa da</string>
|
||||
<string name="tile_anc_subtitle_no_device">Gailurik ez</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Ez dago zarata kontrolik</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth itzalita</string>
|
||||
<string name="widget_config_screen_title">Widget konfigurazioa</string>
|
||||
<string name="widget_configuration_title">Hautatu Gailua</string>
|
||||
<string name="widget_configuration_description">Aukeratu trepeta honek zein gailu-profil erakutsi behar duen.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth-a desgaituta dago, gaitu ezazu ;)</string>
|
||||
<string name="overview_monitoring_active_label">Gailuen jarraipena</string>
|
||||
<string name="overview_monitoring_active_description">Ziurtatu zure gailua gertuan eta aktibo dagoela.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Konektatuta, baina daturik ez</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Zure gailua konektatuta dago, baina CAPod-ek ez du zuzeneko daturik jasotzen. Baliteke zure telefonoak bateragarritasun-aukera bat behar izatea — arazo-konpontzaileak automatikoki bila dezake.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Exekutatu arazo-konpontzailea</string>
|
||||
<string name="overview_unmatched_devices_label">Bat ez datozen gailuak</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d gailu bat datorren profilik gabe</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Hurrengo baimena beharrezkoa da:</string>
|
||||
<string name="permission_system_alert_window_label">Sistemaren alerta-leihoa</string>
|
||||
<string name="permission_system_alert_window_description">Baimendu CAPod-i beste aplikazioen gainean marraztea \"Erakutsi pop-up-a\" ezaugarria posible egiteko.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Aplikazioa irekita dagoenean</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Gailua konektatuta dagoenean</string>
|
||||
<string name="settings_monitor_mode_always_label">Beti</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Ikusten denean</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Kaxa irekita dago</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Belarrian</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Gardentasuna</string>
|
||||
<string name="anc_mode_adaptive">Egokigarria</string>
|
||||
<string name="conversation_awareness_label">Elkarrizketa-kontzientzia</string>
|
||||
<string name="settings_conversation_action_label">Hitz egiten hasten zarenean</string>
|
||||
<string name="settings_conversation_action_nothing_label">Ez egin ezer</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Jaitsi bolumena</string>
|
||||
<string name="settings_conversation_action_pause_label">Pausatu multimedia</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Bolumen murrizketa</string>
|
||||
<string name="signal_badge_ble_cd">BLE eskaneatzea aktibo</string>
|
||||
<string name="signal_badge_key_irk_cd">Identitatea egiaztatuta</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Enkriptatutako konexioa</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Anbarra</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Horma-papereko koloreak erabiltzen ditu</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitorizazioa</string>
|
||||
<string name="settings_category_appearance_label">Itxura</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Gorde gabeko aldaketak</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Orokorra</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofonoa</string>
|
||||
<string name="device_settings_microphone_mode_description">Zein AirPod erabiltzen den mikrofono gisa</string>
|
||||
<string name="device_settings_microphone_mode_description">Zein entzungailu erabiltzen den mikrofono gisa</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automatikoa</string>
|
||||
<string name="device_settings_microphone_mode_right">Eskuinekoa</string>
|
||||
<string name="device_settings_microphone_mode_left">Ezkerrekoa</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Lo Detekzioa</string>
|
||||
<string name="reaction_sleep_notification_title">Lo Detekzioak Pausatuta</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s-ek jakinarazi du lo geratu zarela, eta musikak pausatu egin zen. Gailu-ezarpenetan desgaitu dezakezu.</string>
|
||||
<string name="reaction_charged_channel_label">Kargatutako jakinarazpena</string>
|
||||
<string name="reaction_charged_notification_title">Karga osatu da</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s guztiz kargatuta dago.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s %2$d%%-era kargatu da.</string>
|
||||
<string name="device_settings_rename_label">Berrizendatu</string>
|
||||
<string name="device_settings_rename_hint">Gailuaren izena</string>
|
||||
<string name="device_settings_rename_confirm">Berrizendatu</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">اگر اخیراً خرید کردهاید، ممکن است چند لحظه طول بکشد تا Google Play همگامسازی شود.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">اگر خرید شما نمایش داده نشد، چند دقیقه دیگر دوباره امتحان کنید.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">مطمئن شوید با همان حساب Google که برای خرید استفاده کردید وارد شدهاید.</string>
|
||||
<string name="settings_monitor_mode_label">حالت نظارت</string>
|
||||
<string name="settings_monitor_mode_description">این برنامه تحت چه شرایطی دادههای بلوتوث را نظارت میکند.</string>
|
||||
<string name="settings_monitor_connected_notification_label">اعلان اضافی</string>
|
||||
<string name="settings_monitor_connected_notification_description">هنگامی که یک دستگاه متصل است، یک اعلان اضافی نشان میدهد. این به شما امکان میدهد اعلان دائمی \"بدون دستگاه\" را با غیرفعال کردن کانال \"وضعیت دستگاه\" پنهان کنید.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">حفظ اعلان پس از قطع اتصال</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">مکث خودکار</string>
|
||||
<string name="settings_autopause_description">هنگام برداشتن دستگاه از گوش، صدا را متوقف کنید.</string>
|
||||
<string name="settings_autopplay_label">پخش خودکار</string>
|
||||
<string name="settings_autoplay_description">هنگام استفاده از دستگاه، پخش صدا را شروع کنید.</string>
|
||||
<string name="settings_autoplay_description">موسیقی را هنگام پوشیدن مجدد پادها از سر میگیرد، اما تنها اگر توقف خودکار آن را متوقف کرده باشد. توقفهایی که خودتان ایجاد کردید (تلفن، دکمه AirPods، خواب) همچنان متوقف میمانند.</string>
|
||||
<string name="settings_start_music_on_wear_label">شروع موسیقی هنگام پوشیدن</string>
|
||||
<string name="settings_start_music_on_wear_description">همیشه سعی میکند هنگام گذاشتن پادها موسیقی پخش کند، حتی پس از توقف دستی.</string>
|
||||
<string name="settings_eardetection_info_label">توجه درباره تشخیص گوش</string>
|
||||
<string name="settings_eardetection_info_description">اگر تشخیص گوش فقط برای یک پاد کار میکند، این محدودیت Apple است. فقط \"پاد اصلی\" (که برای میکروفون استفاده میشود) تشخیص داده میشود. در دستگاههای Apple تنظیم کنید: تنظیمات → بلوتوث → AirPods → میکروفون.</string>
|
||||
<string name="settings_fake_data_label">دادههای جعلی</string>
|
||||
<string name="settings_fake_data_description">دادههای جعلی را نشان دهید، یعنی دستگاههایی را که وجود ندارند شبیهسازی کنید.</string>
|
||||
<string name="settings_debug_label">تنظیمات اشکالزدایی</string>
|
||||
<string name="settings_debug_description">تنظیمات اضافی برای کمک به عیبیابی مشکلات برنامه.</string>
|
||||
<string name="settings_signal_minimum_label">حداقل کیفیت سیگنال</string>
|
||||
<string name="settings_signal_minimum_description">حداقل کیفیت سیگنالی که یک دستگاه باید داشته باشد تا متعلق به شما در نظر گرفته شود.</string>
|
||||
<string name="settings_autoconnect_label">اتصال خودکار</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">نمایش پاپآپ اتصال</string>
|
||||
<string name="settings_popup_connected_description">هنگام اتصال دستگاه برای اولین بار، یک پاپآپ نشان داده شود.</string>
|
||||
<string name="settings_popup_info_not_in_app">پنجرههای بازشو فقط زمانی نشان داده میشوند که در برنامه نباشید.</string>
|
||||
<string name="settings_popup_warning_manual_mode">پنجرههای بازشو نیاز دارند که مانیتور در حال اجرا باشد. حالت مانیتور شما روی «وقتی برنامه باز است» تنظیم شده که هنگام خروج از برنامه، مانیتور را متوقف میکند.</string>
|
||||
<string name="general_fix_it_action">رفع مشکل</string>
|
||||
<string name="settings_charged_notification_label">اعلان شارژ کامل</string>
|
||||
<string name="settings_charged_notification_description">پس از اینکه همه چیزهایی که در حال شارژ هستند به سطح شارژ هدف رسیدند، یک اعلان نمایش داده میشود.</string>
|
||||
<string name="settings_charged_threshold_label">سطح شارژ هدف</string>
|
||||
<string name="settings_charged_scope_label">باتریهای تحت نظارت</string>
|
||||
<string name="settings_charged_scope_pods_label">فقط پادها</string>
|
||||
<string name="settings_charged_scope_case_label">فقط کیس</string>
|
||||
<string name="settings_charged_scope_both_label">پادها و کیس</string>
|
||||
<string name="device_settings_experimental_title">ویژگی آزمایشی</string>
|
||||
<string name="device_settings_experimental_description">این ویژگی هنوز روی همه دستگاهها آزمایش نشده است. اگر چیزی درست کار نمیکند، لطفاً یک گزارش خطا با لاگ دیباگ باز کنید.</string>
|
||||
<string name="device_settings_experimental_action">باز کردن ردیاب مشکلات</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">اشکالزدایی</string>
|
||||
<string name="settings_general_label">تنظیمات</string>
|
||||
<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="settings_acknowledgements_label">قدردانیها</string>
|
||||
<string name="settings_debug_autoreports_label">گزارشهای خطای خودکار</string>
|
||||
<string name="settings_debug_autoreports_description">مشکلات را بهطور خودکار گزارش میدهد، مثلاً جزئیات مربوط به خرابی برنامه تا بتوانم نحوه رفع آن را بفهمم.</string>
|
||||
<string name="settings_debug_mode_label">حالت اشکالزدایی</string>
|
||||
<string name="settings_debug_mode_description">نمایش اطلاعات اضافی برای عیبیابی مشکلات.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">دادههای BLE فیلترنشده</string>
|
||||
<string name="settings_blescanner_unfiltered_description">هرگونه فیلتر را از اسکنر BLE حذف کنید تا تمام دادههای BLE پخششده نمایش داده شود. برای افزودن پشتیبانی از انواع جدید هدفون مفید است.</string>
|
||||
<string name="settings_compatibility_mode_label">حالت سازگاری</string>
|
||||
<string name="settings_compatibility_mode_description">بهینهسازیها را برای بهبود سازگاری غیرفعال کنید. اگر هیچ دادهای نمیبینید، این را امتحان کنید.</string>
|
||||
<string name="help_translate_label">ترجمه</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">این ویجت نیاز به اتصال مستقیم (AAP) به دستگاه شما دارد. قبل از استفاده از این ویجت، مطمئن شوید دستگاه شما در برنامه جفتشده و متصل است.</string>
|
||||
<string name="anc_widget_no_anc_support_label">این دستگاه از کنترل نویز پشتیبانی نمیکند</string>
|
||||
<string name="anc_widget_aap_not_connected_label">متصل نشده</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">در مجاورت اما متصل نیست</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">در مجاورت نیست</string>
|
||||
<string name="anc_widget_aap_connecting_label">در حال اتصال…</string>
|
||||
<string name="tile_anc_label">کنترل نویز</string>
|
||||
<string name="tile_anc_subtitle_permission_required">مجوز لازم است</string>
|
||||
<string name="tile_anc_subtitle_no_device">بدون دستگاه</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">بدون کنترل نویز</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">بلوتوث خاموش است</string>
|
||||
<string name="widget_config_screen_title">پیکربندی ابزارک</string>
|
||||
<string name="widget_configuration_title">انتخاب دستگاه</string>
|
||||
<string name="widget_configuration_description">انتخاب کنید این ویجت کدام پروفایل دستگاه را نمایش دهد.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">بلوتوث غیرفعال است، آن را فعال کنید ;)</string>
|
||||
<string name="overview_monitoring_active_label">نظارت بر دستگاهها</string>
|
||||
<string name="overview_monitoring_active_description">مطمئن شوید دستگاه شما نزدیک و فعال است.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">متصل است، اما دادهای وجود ندارد</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">دستگاه شما متصل است، اما CAPod هیچ داده زندهای از آن دریافت نمیکند. ممکن است تلفن شما به یک گزینه سازگاری نیاز داشته باشد — عیبیاب میتواند بهطور خودکار تلاش کند آن را پیدا کند.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">اجرای عیبیاب</string>
|
||||
<string name="overview_unmatched_devices_label">دستگاههای بدون تطابق</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d دستگاه بدون پروفایل منطبق</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">مجوز زیر مورد نیاز است:</string>
|
||||
<string name="permission_system_alert_window_label">پنجره هشدار سیستم</string>
|
||||
<string name="permission_system_alert_window_description">به CAPod اجازه دهید روی سایر برنامهها نمایش دهد تا ویژگی \"نمایش پاپآپ\" ممکن شود.</string>
|
||||
<string name="settings_monitor_mode_manual_label">وقتی برنامه باز است</string>
|
||||
<string name="settings_monitor_mode_automatic_label">وقتی دستگاه متصل است</string>
|
||||
<string name="settings_monitor_mode_always_label">همیشه</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">وقتی دیده شود</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">کیس باز است</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">در گوش</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">شفافیت</string>
|
||||
<string name="anc_mode_adaptive">تطبیقی</string>
|
||||
<string name="conversation_awareness_label">آگاهی مکالمه</string>
|
||||
<string name="settings_conversation_action_label">وقتی شروع به صحبت میکنید</string>
|
||||
<string name="settings_conversation_action_nothing_label">هیچ کاری نکن</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">کاهش صدا</string>
|
||||
<string name="settings_conversation_action_pause_label">توقف رسانه</string>
|
||||
<string name="settings_conversation_volume_reduction_label">میزان کاهش صدا</string>
|
||||
<string name="signal_badge_ble_cd">اسکن BLE فعال است</string>
|
||||
<string name="signal_badge_key_irk_cd">هویت تأیید شد</string>
|
||||
<string name="signal_badge_key_encrypted_cd">اتصال رمزگذاریشده</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">کهربی</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">از رنگهای تصویر پسزمینه استفاده میکند</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">پایش</string>
|
||||
<string name="settings_category_appearance_label">ظاهر</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">تغییرات ذخیرهنشده</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">عمومی</string>
|
||||
<string name="device_settings_microphone_mode_label">میکروفون</string>
|
||||
<string name="device_settings_microphone_mode_description">کدام AirPod به عنوان میکروفون استفاده میشود</string>
|
||||
<string name="device_settings_microphone_mode_description">کدام ایرباد به عنوان میکروفون استفاده میشود</string>
|
||||
<string name="device_settings_microphone_mode_auto">خودکار</string>
|
||||
<string name="device_settings_microphone_mode_right">راست</string>
|
||||
<string name="device_settings_microphone_mode_left">چپ</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">تشخیص خواب</string>
|
||||
<string name="reaction_sleep_notification_title">توسط تشخیص خواب متوقف شد</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s گزارش داد که به خواب رفتید، بنابراین موسیقی شما متوقف شد. میتوانید این را در تنظیمات دستگاه غیرفعال کنید.</string>
|
||||
<string name="reaction_charged_channel_label">اعلان شارژ کامل</string>
|
||||
<string name="reaction_charged_notification_title">شارژ کامل شد</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s کاملاً شارژ شده است.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s تا %2$d%% شارژ شده است.</string>
|
||||
<string name="device_settings_rename_label">تغییر نام</string>
|
||||
<string name="device_settings_rename_hint">نام دستگاه</string>
|
||||
<string name="device_settings_rename_confirm">تغییر نام</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Jos olet hiljattain ostanut, Google Playn synkronointi saattaa kestää hetken.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Yritä uudelleen muutaman minuutin kuluttua, jos ostoksesi ei näy.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Varmista, että olet kirjautunut sisään samalla Google-tilillä, jolla teit ostoksen.</string>
|
||||
<string name="settings_monitor_mode_label">Valvontatila</string>
|
||||
<string name="settings_monitor_mode_description">Missä olosuhteissa tämä sovellus valvoo Bluetooth-tietoja.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Lisäilmoitus</string>
|
||||
<string name="settings_monitor_connected_notification_description">Näyttää lisäilmoituksen, kun laite on yhdistetty. Tämä antaa sinun piilottaa pysyvän \"Ei laitteita\" -ilmoituksen poistamalla \"Laitteen tila\" -kanavan käytöstä.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Säilytä ilmoitus yhteyden katkaisun jälkeen</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Automaattinen tauko</string>
|
||||
<string name="settings_autopause_description">Keskeytä ääni, kun poistat laitteen korvastasi.</string>
|
||||
<string name="settings_autopplay_label">Automaattinen toisto</string>
|
||||
<string name="settings_autoplay_description">Aloita äänentoisto, kun laitetta käytetään.</string>
|
||||
<string name="settings_autoplay_description">Jatkaa musiikin toistoa, kun puet nappikuulokkeet uudelleen korviin – mutta vain jos Automaattinen tauko pysäytti sen. Itse käynnistämäsi tauot (puhelin, AirPods-varsi, uni) pysyvät tauolla.</string>
|
||||
<string name="settings_start_music_on_wear_label">Käynnistä musiikki pukiessa</string>
|
||||
<string name="settings_start_music_on_wear_description">Yrittää aina toistaa musiikkia, kun puet kuulokkeet korviin – myös manuaalisen tauon jälkeen.</string>
|
||||
<string name="settings_eardetection_info_label">Huomautus korvan tunnistuksesta</string>
|
||||
<string name="settings_eardetection_info_description">Jos korvan tunnistus toimii vain yhdelle kuulokkeelle, tämä on Applen rajoitus. Vain \"ensisijainen kuuloke\" (jota käytetään mikrofonina) tunnistetaan. Määritä Apple-laitteissa: Asetukset → Bluetooth → AirPods → Mikrofoni.</string>
|
||||
<string name="settings_fake_data_label">Valetiedot</string>
|
||||
<string name="settings_fake_data_description">Näytä valetietoja, eli simuloi laitteita, joita ei ole olemassa.</string>
|
||||
<string name="settings_debug_label">Virheenkorjausasetukset</string>
|
||||
<string name="settings_debug_description">Lisäasetukset sovelluksen ongelmien vianmääritykseen.</string>
|
||||
<string name="settings_signal_minimum_label">Signaalin vähimmäislaatu</string>
|
||||
<string name="settings_signal_minimum_description">Signaalin vähimmäislaatu, joka laitteella on oltava, jotta se katsotaan omaksesi.</string>
|
||||
<string name="settings_autoconnect_label">Automaattinen yhdistäminen</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Näytä yhteyden ponnahdusikkuna</string>
|
||||
<string name="settings_popup_connected_description">Näytä ponnahdusikkuna, kun laite yhdistetään ensimmäisen kerran.</string>
|
||||
<string name="settings_popup_info_not_in_app">Ponnahdusikkunat näytetään vain, kun et ole sovelluksessa.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Ponnahdusikkunat vaativat monitorin toimiakseen. Monitorintilaksesi on asetettu \"Kun sovellus on auki\", joka pysäyttää monitorin, kun poistut sovelluksesta.</string>
|
||||
<string name="general_fix_it_action">Korjaa se</string>
|
||||
<string name="settings_charged_notification_label">Latausilmoitus</string>
|
||||
<string name="settings_charged_notification_description">Näytä ilmoitus, kun kaikki ladattavat laitteet saavuttavat tavoitelataustason.</string>
|
||||
<string name="settings_charged_threshold_label">Tavoitelataustaso</string>
|
||||
<string name="settings_charged_scope_label">Seurattavat akut</string>
|
||||
<string name="settings_charged_scope_pods_label">Vain nappikuulokkeet</string>
|
||||
<string name="settings_charged_scope_case_label">Vain kotelo</string>
|
||||
<string name="settings_charged_scope_both_label">Nappikuulokkeet ja kotelo</string>
|
||||
<string name="device_settings_experimental_title">Kokeellinen ominaisuus</string>
|
||||
<string name="device_settings_experimental_description">Tätä ominaisuutta ei ole vielä testattu kaikilla laitteilla. Jos jokin ei toimi oikein, avaa ongelmaraportti virheenkorjauslokilla.</string>
|
||||
<string name="device_settings_experimental_action">Avaa ongelmaseuranta</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Virheenjäljitys</string>
|
||||
<string name="settings_general_label">Asetukset</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Virheenkorjaustila</string>
|
||||
<string name="settings_debug_mode_description">Näytä lisätietoja ongelmien vianmääritykseen.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Suodattamattomat BLE-tiedot</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Poista kaikki suodattimet BLE-skannerista näyttääksesi kaikki lähetetyt BLE-tiedot. Hyödyllinen uusien kuuloketyyppien tuen lisäämiseen.</string>
|
||||
<string name="settings_compatibility_mode_label">Yhteensopivuustila</string>
|
||||
<string name="settings_compatibility_mode_description">Poista optimoinnit käytöstä yhteensopivuuden parantamiseksi. Kokeile tätä, jos et näe mitään tietoja.</string>
|
||||
<string name="help_translate_label">Käännös</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Tämä widget vaatii suoran yhteyden (AAP) laitteeseesi. Varmista, että laitteesi on pariliitetty ja yhdistetty sovelluksessa ennen tämän widgetin käyttöä.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Tämä laite ei tue melunhallintaa</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Ei yhdistetty</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Lähellä, mutta ei yhdistetty</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Ei lähellä</string>
|
||||
<string name="anc_widget_aap_connecting_label">Yhdistetään…</string>
|
||||
<string name="tile_anc_label">Melunhallinta</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Lupa vaaditaan</string>
|
||||
<string name="tile_anc_subtitle_no_device">Ei laitetta</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Ei melunhallintaa</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth pois</string>
|
||||
<string name="widget_config_screen_title">Widgetin määritys</string>
|
||||
<string name="widget_configuration_title">Valitse laite</string>
|
||||
<string name="widget_configuration_description">Valitse, minkä laiteprofiilin tämä widget näyttää.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth on pois käytöstä, ota se käyttöön ;)</string>
|
||||
<string name="overview_monitoring_active_label">Etsitään laitteita</string>
|
||||
<string name="overview_monitoring_active_description">Varmista, että laitteesi on lähellä ja aktiivinen.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Yhdistetty, mutta ei dataa</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Laitteesi on yhdistetty, mutta CAPod ei vastaanota siitä reaaliaikaista dataa. Puhelimesi saattaa tarvita yhteensopivuusasetuksen — vianmääritys voi yrittää löytää sellaisen automaattisesti.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Käynnistä vianmääritys</string>
|
||||
<string name="overview_unmatched_devices_label">Vastaavuudetta olevat laitteet</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d laite ilman vastaavaa profiilia</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Seuraava lupa vaaditaan:</string>
|
||||
<string name="permission_system_alert_window_label">Järjestelmän hälytysikkuna</string>
|
||||
<string name="permission_system_alert_window_description">Salli CAPod-sovelluksen piirtää muiden sovellusten päälle mahdollistaakseen \"Näytä ponnahdusikkuna\" -ominaisuuden.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Kun sovellus on auki</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Kun laite on yhdistetty</string>
|
||||
<string name="settings_monitor_mode_always_label">Aina</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Kun havaitaan</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Kotelo on auki</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Korvassa</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Läpikuuluvuus</string>
|
||||
<string name="anc_mode_adaptive">Mukautuva</string>
|
||||
<string name="conversation_awareness_label">Keskustelutietoisuus</string>
|
||||
<string name="settings_conversation_action_label">Kun aloitat puhumisen</string>
|
||||
<string name="settings_conversation_action_nothing_label">Älä tee mitään</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Laske äänenvoimakkuutta</string>
|
||||
<string name="settings_conversation_action_pause_label">Keskeytä media</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Äänenvoimakkuuden vähennys</string>
|
||||
<string name="signal_badge_ble_cd">BLE-skannaus aktiivinen</string>
|
||||
<string name="signal_badge_key_irk_cd">Henkilöllisyys vahvistettu</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Salattu yhteys</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ambra</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Käyttää taustakuvan värejä</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Valvonta</string>
|
||||
<string name="settings_category_appearance_label">Ulkoasu</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Tallentamattomat muutokset</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Yleiset</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofoni</string>
|
||||
<string name="device_settings_microphone_mode_description">Kumpi AirPod toimii mikrofonina</string>
|
||||
<string name="device_settings_microphone_mode_description">Mikä kuuloke toimii mikrofonina</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automaattinen</string>
|
||||
<string name="device_settings_microphone_mode_right">Oikea</string>
|
||||
<string name="device_settings_microphone_mode_left">Vasen</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Unitunnistus</string>
|
||||
<string name="reaction_sleep_notification_title">Unitunnistus keskeytti toiston</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s havaitsi sinun nukahtaneen, joten musiikkisi keskeytettiin. Voit poistaa tämän käytöstä laiteasetuksista.</string>
|
||||
<string name="reaction_charged_channel_label">Latausilmoitus</string>
|
||||
<string name="reaction_charged_notification_title">Lataus valmis</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s on täyteen ladattu.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s on ladattu %2$d%%:iin.</string>
|
||||
<string name="device_settings_rename_label">Nimeä uudelleen</string>
|
||||
<string name="device_settings_rename_hint">Laitteen nimi</string>
|
||||
<string name="device_settings_rename_confirm">Nimeä uudelleen</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Kung nabili mo kamakailan, maaaring tumagal ng ilang sandali para ma-sync ng Google Play.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Subukang muli pagkatapos ng ilang minuto kung hindi lumabas ang iyong pagbili.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Tiyaking naka-sign in ka sa parehong Google account na ginamit sa pagbili.</string>
|
||||
<string name="settings_monitor_mode_label">Mode ng monitor</string>
|
||||
<string name="settings_monitor_mode_description">Sa ilalim ng anong mga pangyayari mino-monitor ng app na ito ang data ng Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Karagdagang notipikasyon</string>
|
||||
<string name="settings_monitor_connected_notification_description">Nagpapakita ng karagdagang notipikasyon kapag konektado ang isang device. Pinapayagan ka nitong itago ang permanenteng \"Walang mga device\" na notipikasyon sa pamamagitan ng pag-disable sa \"Katayuan ng device\" na channel.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Panatilihin ang notipikasyon pagkatapos mag-disconnect</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Auto pause</string>
|
||||
<string name="settings_autopause_description">I-pause ang audio kapag inaalis ang device sa iyong tainga.</string>
|
||||
<string name="settings_autopplay_label">Auto play</string>
|
||||
<string name="settings_autoplay_description">Simulan ang pag-playback ng audio kapag suot ang device.</string>
|
||||
<string name="settings_autoplay_description">I-resume ang musika kapag suot mo ulit ang iyong pods, pero tanging kung ang Auto pause ang huminto nito. Ang mga pause na ikaw mismo ang nag-trigger (telepono, AirPods stem, tulog) ay nananatiling naka-pause.</string>
|
||||
<string name="settings_start_music_on_wear_label">Simulan ang musika kapag suot</string>
|
||||
<string name="settings_start_music_on_wear_description">Palaging sinusubukang i-play ang musika kapag nilagay mo ang iyong pods, kahit pagkatapos ng manu-manong pause.</string>
|
||||
<string name="settings_eardetection_info_label">Paalala sa ear detection</string>
|
||||
<string name="settings_eardetection_info_description">Kung ang ear detection ay gumagana lang para sa isang pod, ito ay limitasyon ng Apple. Tanging ang \"pangunahing pod\" (ginagamit para sa mikropono) lang ang nadedetect. I-configure sa mga Apple device: Settings → Bluetooth → AirPods → Microphone.</string>
|
||||
<string name="settings_fake_data_label">Pekeng data</string>
|
||||
<string name="settings_fake_data_description">Magpakita ng pekeng data, ibig sabihin, gayahin ang mga device na wala.</string>
|
||||
<string name="settings_debug_label">Mga setting ng debug</string>
|
||||
<string name="settings_debug_description">Mga karagdagang setting para makatulong sa pag-troubleshoot ng mga isyu sa app.</string>
|
||||
<string name="settings_signal_minimum_label">Minimum na kalidad ng signal</string>
|
||||
<string name="settings_signal_minimum_description">Ang minimum na kalidad ng signal na kailangan ng isang device para maituring na sa iyo.</string>
|
||||
<string name="settings_autoconnect_label">Auto connect</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Ipakita ang connection popup</string>
|
||||
<string name="settings_popup_connected_description">Magpakita ng popup kapag unang kumonekta ang device.</string>
|
||||
<string name="settings_popup_info_not_in_app">Ang mga popup ay ipinapakita lamang kapag wala ka sa app.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Ang mga popup ay nangangailangan ng monitor na tumatakbo. Ang iyong monitor mode ay nakatakda sa \"When app is open\", na nagpapatigil ng monitor kapag umalis ka sa app.</string>
|
||||
<string name="general_fix_it_action">Ayusin ito</string>
|
||||
<string name="settings_charged_notification_label">Notipikasyon ng pagkakarga</string>
|
||||
<string name="settings_charged_notification_description">Magpakita ng notipikasyon kapag ang lahat ng nagcha-charge ay umabot na sa target na antas ng karga.</string>
|
||||
<string name="settings_charged_threshold_label">Target na antas ng karga</string>
|
||||
<string name="settings_charged_scope_label">Mga sinusubaybaygang baterya</string>
|
||||
<string name="settings_charged_scope_pods_label">Mga pod lamang</string>
|
||||
<string name="settings_charged_scope_case_label">Case lamang</string>
|
||||
<string name="settings_charged_scope_both_label">Mga pod at case</string>
|
||||
<string name="device_settings_experimental_title">Pang-eksperimentong tampok</string>
|
||||
<string name="device_settings_experimental_description">Ang tampok na ito ay hindi pa nasubok sa lahat ng device. Kung may hindi gumagana nang tama, mangyaring magbukas ng isyu na may debug log.</string>
|
||||
<string name="device_settings_experimental_action">Buksan ang Issue Tracker</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Debug</string>
|
||||
<string name="settings_general_label">Mga Setting</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Debug mode</string>
|
||||
<string name="settings_debug_mode_description">Magpakita ng karagdagang impormasyon para sa pag-troubleshoot ng mga isyu.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Hindi na-filter na BLE data</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Alisin ang anumang mga filter mula sa BLE scanner para ipakita ang lahat ng na-broadcast na BLE data. Kapaki-pakinabang para magdagdag ng suporta para sa mga bagong uri ng headphone.</string>
|
||||
<string name="settings_compatibility_mode_label">Compatibility mode</string>
|
||||
<string name="settings_compatibility_mode_description">I-disable ang mga optimization para mapabuti ang compatibility. Subukan ito kung wala kang nakikitang data.</string>
|
||||
<string name="help_translate_label">Pagsasalin</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Ang widget na ito ay nangangailangan ng direktang koneksyon (AAP) sa iyong device. Siguraduhing ang iyong device ay naka-pair at nakakonekta sa app bago gamitin ang widget na ito.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Hindi sinusuportahan ng device na ito ang noise control</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Hindi nakakonekta</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Malapit ngunit hindi nakakonekta</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Hindi malapit</string>
|
||||
<string name="anc_widget_aap_connecting_label">Kumukonekta…</string>
|
||||
<string name="tile_anc_label">Kontrol ng Ingay</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Kailangan ng pahintulot</string>
|
||||
<string name="tile_anc_subtitle_no_device">Walang device</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Walang kontrol ng ingay</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth ay naka-off</string>
|
||||
<string name="widget_config_screen_title">Pagsasaayos ng Widget</string>
|
||||
<string name="widget_configuration_title">Pumili ng Device</string>
|
||||
<string name="widget_configuration_description">Piliin kung aling device profile ang ipapakita ng widget na ito.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Naka-disable ang Bluetooth, i-enable ito ;)</string>
|
||||
<string name="overview_monitoring_active_label">Nagmo-monitor ng mga device</string>
|
||||
<string name="overview_monitoring_active_description">Siguraduhing malapit at aktibo ang iyong device.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Nakakonekta, ngunit walang data</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Nakakonekta ang iyong device, ngunit hindi nakakatanggap ng live data ang CAPod mula rito. Maaaring kailanganin ng iyong telepono ng opsyon sa compatibility — maaaring subukang hanapin ito nang awtomatiko ng troubleshooter.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Patakbuhin ang troubleshooter</string>
|
||||
<string name="overview_unmatched_devices_label">Mga hindi nakatugmang device</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d device na walang katugmang profile</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Kailangan ang sumusunod na pahintulot:</string>
|
||||
<string name="permission_system_alert_window_label">System Alert Window</string>
|
||||
<string name="permission_system_alert_window_description">Payagan ang CAPod na mag-draw sa ibabaw ng ibang mga app para maging posible ang feature na \"Ipakita ang PopUp\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">Kapag bukas ang app</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Kapag konektado ang device</string>
|
||||
<string name="settings_monitor_mode_always_label">Palagi</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Kapag nakita</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Bukas ang case</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Nasa tainga</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparency</string>
|
||||
<string name="anc_mode_adaptive">Adaptive</string>
|
||||
<string name="conversation_awareness_label">Conversation Awareness</string>
|
||||
<string name="settings_conversation_action_label">Kapag nagsimula kang magsalita</string>
|
||||
<string name="settings_conversation_action_nothing_label">Huwag gumawa ng anuman</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Ibaba ang volume</string>
|
||||
<string name="settings_conversation_action_pause_label">I-pause ang media</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Pagbabawas ng volume</string>
|
||||
<string name="signal_badge_ble_cd">Aktibo ang BLE scan</string>
|
||||
<string name="signal_badge_key_irk_cd">Na-verify ang pagkakakilanlan</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Naka-encrypt na koneksyon</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Amber</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Gumagamit ng mga kulay ng wallpaper</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Pagmo-monitor</string>
|
||||
<string name="settings_category_appearance_label">Hitsura</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Mga Hindi Na-save na Pagbabago</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Pangkalahatan</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikropono</string>
|
||||
<string name="device_settings_microphone_mode_description">Aling AirPod ang ginagamit bilang mikropono</string>
|
||||
<string name="device_settings_microphone_mode_description">Kung aling earbud ang ginagamit bilang mikropono</string>
|
||||
<string name="device_settings_microphone_mode_auto">Auto</string>
|
||||
<string name="device_settings_microphone_mode_right">Kanan</string>
|
||||
<string name="device_settings_microphone_mode_left">Kaliwa</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Pagtuklas ng Pagtulog</string>
|
||||
<string name="reaction_sleep_notification_title">Naka-pause ng Pagtuklas ng Pagtulog</string>
|
||||
<string name="reaction_sleep_notification_text">Naiulat ng %1$s na nakatulog ka, kaya na-pause ang iyong musika. Maaari mong i-disable ito sa mga setting ng device.</string>
|
||||
<string name="reaction_charged_channel_label">Notipikasyon ng Pagkakarga</string>
|
||||
<string name="reaction_charged_notification_title">Kumpleto na ang pag-charge</string>
|
||||
<string name="reaction_charged_notification_text_full">Ganap na na-charge ang %1$s.</string>
|
||||
<string name="reaction_charged_notification_text_partial">Na-charge na ang %1$s sa %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Palitan ang Pangalan</string>
|
||||
<string name="device_settings_rename_hint">Pangalan ng device</string>
|
||||
<string name="device_settings_rename_confirm">Palitan ang Pangalan</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Si l’achat est récent, Google Play peut mettre du temps à synchroniser.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Si votre achat n’apparaît pas, réessayez dans quelques minutes.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Le même compte Google doit être utilisé pour l’achat et la connexion.</string>
|
||||
<string name="settings_monitor_mode_label">Mode surveillance</string>
|
||||
<string name="settings_monitor_mode_description">Les circonstances de surveillance des données Bluetooth par l’appli.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notification supplémentaire</string>
|
||||
<string name="settings_monitor_connected_notification_description">Affiche une notification supplémentaire si un appareil est connecté. Cela vous permet de masquer la notification permanente « Aucun appareil » en désactivant le canal « État de l’appareil ».</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Garder la notification après déconnexion</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pause automatique</string>
|
||||
<string name="settings_autopause_description">Mettre le son en pause si vous ôtez l’appareil de votre oreille.</string>
|
||||
<string name="settings_autopplay_label">Lecture automatique</string>
|
||||
<string name="settings_autoplay_description">Démarrer la lecture du son quand l’appareil est porté.</string>
|
||||
<string name="settings_autoplay_description">Reprend la musique quand tu remets tes pods, mais seulement si la pause automatique l’a arrêtée. Les pauses que tu as déclenchées toi-même (téléphone, tige AirPods, veille) restent en pause.</string>
|
||||
<string name="settings_start_music_on_wear_label">Lancer la musique au port</string>
|
||||
<string name="settings_start_music_on_wear_description">Essaie toujours de lancer la musique quand tu mets tes pods, même après une pause manuelle.</string>
|
||||
<string name="settings_eardetection_info_label">Note de détection de l’oreille</string>
|
||||
<string name="settings_eardetection_info_description">Si la détection de l’oreille ne fonctionne que pour un seul AirPod, cela est causé par une limitation d’Apple. Seul « l’AirPod principal » (utilisé pour le microphone) est détecté. Configurez dans les appareils Apple : Paramètres → Bluetooth → AirPods → Microphone.</string>
|
||||
<string name="settings_fake_data_label">Fausses données</string>
|
||||
<string name="settings_fake_data_description">Afficher les fausses données, c.-à-d. simuler les appareils qui n’existent pas.</string>
|
||||
<string name="settings_debug_label">Paramètres de débogage</string>
|
||||
<string name="settings_debug_description">Autres paramètres pour aider à diagnostiquer les problèmes de l’appli.</string>
|
||||
<string name="settings_signal_minimum_label">Qualité minimale du signal</string>
|
||||
<string name="settings_signal_minimum_description">Qualité minimale du signal qu’un appareil doit émettre pour être considéré le vôtre.</string>
|
||||
<string name="settings_autoconnect_label">Connexion automatique</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Afficher une fenêtre de connection</string>
|
||||
<string name="settings_popup_connected_description">Afficher une fenêtre contextuelle quand l’appareil se connecte pour la première fois.</string>
|
||||
<string name="settings_popup_info_not_in_app">Les popups ne sont affichées que lorsque vous n\'êtes pas dans l\'application.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Les popups nécessitent que le moniteur soit en cours d\'exécution. Votre mode moniteur est réglé sur « Quand l\'application est ouverte », ce qui arrête le moniteur lorsque vous quittez l\'application.</string>
|
||||
<string name="general_fix_it_action">Corriger</string>
|
||||
<string name="settings_charged_notification_label">Notification de charge complète</string>
|
||||
<string name="settings_charged_notification_description">Afficher une notification une fois que tout ce qui est en charge atteint le niveau de charge cible.</string>
|
||||
<string name="settings_charged_threshold_label">Niveau de charge cible</string>
|
||||
<string name="settings_charged_scope_label">Batteries surveillées</string>
|
||||
<string name="settings_charged_scope_pods_label">Écouteurs uniquement</string>
|
||||
<string name="settings_charged_scope_case_label">Boîtier uniquement</string>
|
||||
<string name="settings_charged_scope_both_label">Écouteurs et boîtier</string>
|
||||
<string name="device_settings_experimental_title">Fonctionnalité expérimentale</string>
|
||||
<string name="device_settings_experimental_description">Cette fonctionnalité n\'a pas encore été testée sur tous les appareils. Si quelque chose ne fonctionne pas correctement, veuillez ouvrir un ticket avec un journal de débogage.</string>
|
||||
<string name="device_settings_experimental_action">Ouvrir le suivi des problèmes</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Débogage</string>
|
||||
<string name="settings_general_label">Paramètres</string>
|
||||
<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 l’aperçu les appareils à proximité qui ne correspondent à aucun de vos profils, p. ex. les AirPods d’autres personnes.</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 d’un plantage de l’appli, afin que je puisse essayer de les corriger.</string>
|
||||
<string name="settings_debug_mode_label">Mode de débogage</string>
|
||||
<string name="settings_debug_mode_description">Afficher des renseignements supplémentaires pour dépanner les problèmes.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Données BÉB non filtrées</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Supprimer tous les filtres de l’analyseur BÉB pour afficher toutes les données BÉB diffusées. Utile pour prendre en charge les nouveaux types de casques-micros.</string>
|
||||
<string name="settings_compatibility_mode_label">Mode de compatibilité</string>
|
||||
<string name="settings_compatibility_mode_description">Désactiver les optimisations pour améliorer la compatibilité. À essayer si vous ne voyez aucune donnée.</string>
|
||||
<string name="help_translate_label">Traduction</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Ce widget nécessite une connexion directe (AAP) à votre appareil. Assurez-vous que votre appareil est couplé et connecté dans l\'appli avant d\'utiliser ce widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Cet appareil ne prend pas en charge le contrôle du bruit</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Non connecté</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">À proximité mais non connecté</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Pas à proximité</string>
|
||||
<string name="anc_widget_aap_connecting_label">Connexion en cours…</string>
|
||||
<string name="tile_anc_label">Contrôle du bruit</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Une autorisation est nécessaire</string>
|
||||
<string name="tile_anc_subtitle_no_device">Aucun appareil</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Pas de contrôle du bruit</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth désactivé</string>
|
||||
<string name="widget_config_screen_title">Configuration du widget</string>
|
||||
<string name="widget_configuration_title">Choisir un appareil</string>
|
||||
<string name="widget_configuration_description">Choisissez le profil d’appareil que ce widget doit afficher.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth est désactivé, activez-le ;)</string>
|
||||
<string name="overview_monitoring_active_label">Surveillance des appareils</string>
|
||||
<string name="overview_monitoring_active_description">Assurez-vous que votre appareil est proche et actif.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Connecté, mais aucune donnée</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Votre appareil est connecté, mais CAPod ne reçoit aucune donnée en direct de sa part. Votre téléphone a peut-être besoin d’une option de compatibilité — le dépannage peut essayer d’en trouver une automatiquement.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Lancer le dépannage</string>
|
||||
<string name="overview_unmatched_devices_label">Appareils sans correspondance</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d appareil sans profil correspondant</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">L’autorisation suivante est nécessaire :</string>
|
||||
<string name="permission_system_alert_window_label">Fenêtre d’alerte système</string>
|
||||
<string name="permission_system_alert_window_description">Permettre à CAPod d’apparaître par-dessus les autres applis pour rendre la fonction « Afficher une fenêtre contextuelle » possible.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Si l’appli est ouverte</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Si l’appareil est connecté</string>
|
||||
<string name="settings_monitor_mode_always_label">Toujours</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">S’il est visible</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Si l’étui est ouvert</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">S’il est porté</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparence</string>
|
||||
<string name="anc_mode_adaptive">Adaptatif</string>
|
||||
<string name="conversation_awareness_label">Sensibilisation aux conversations</string>
|
||||
<string name="settings_conversation_action_label">Quand tu commences à parler</string>
|
||||
<string name="settings_conversation_action_nothing_label">Ne rien faire</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Baisser le volume</string>
|
||||
<string name="settings_conversation_action_pause_label">Mettre le média en pause</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Réduction du volume</string>
|
||||
<string name="signal_badge_ble_cd">Analyse BLE active</string>
|
||||
<string name="signal_badge_key_irk_cd">Identité vérifiée</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Connexion chiffrée</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ambre</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Utilise les couleurs du fond d’écran</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Surveillance</string>
|
||||
<string name="settings_category_appearance_label">Apparence</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Des changements ne sont pas enregistrés</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Général</string>
|
||||
<string name="device_settings_microphone_mode_label">Microphone</string>
|
||||
<string name="device_settings_microphone_mode_description">Quel AirPod est utilisé comme microphone</string>
|
||||
<string name="device_settings_microphone_mode_description">Quel écouteur est utilisé comme microphone</string>
|
||||
<string name="device_settings_microphone_mode_auto">Auto</string>
|
||||
<string name="device_settings_microphone_mode_right">Droite</string>
|
||||
<string name="device_settings_microphone_mode_left">Gauche</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Détection du sommeil</string>
|
||||
<string name="reaction_sleep_notification_title">Mis en pause par la détection du sommeil</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s a détecté que vous vous êtes endormi, votre musique a donc été mise en pause. Vous pouvez désactiver cela dans les paramètres de l\'appareil.</string>
|
||||
<string name="reaction_charged_channel_label">Notification de charge</string>
|
||||
<string name="reaction_charged_notification_title">Charge terminée</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s est complètement chargé.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s a été chargé à %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Renommer</string>
|
||||
<string name="device_settings_rename_hint">Nom de l’appareil</string>
|
||||
<string name="device_settings_rename_confirm">Renommer</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Se compraches recentemente, pode tardar un momento en que Google Play sincronice.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Téntao de novo en poucos minutos se a túa compra non aparece.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Asegúrate de ter iniciado sesión coa mesma conta de Google usada para a compra.</string>
|
||||
<string name="settings_monitor_mode_label">Modo de supervisión</string>
|
||||
<string name="settings_monitor_mode_description">Baixo que circunstancias esta aplicación supervisa os datos Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notificación adicional</string>
|
||||
<string name="settings_monitor_connected_notification_description">Mostra unha notificación adicional cando un dispositivo está conectado. Isto permíteche ocultar a notificación permanente de \"Sen dispositivos\" desactivando a canle \"Estado do dispositivo\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Manter notificación tras desconexión</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pausa automática</string>
|
||||
<string name="settings_autopause_description">Pausar o son ao quitar o dispositivo da orella.</string>
|
||||
<string name="settings_autopplay_label">Reprodución automática</string>
|
||||
<string name="settings_autoplay_description">Iniciar a reprodución de son cando se leva posto o dispositivo.</string>
|
||||
<string name="settings_autoplay_description">Retoma a música ao poñer os auriculares de novo, pero só se a pausa automática a detivo. As pausas que activaches ti mesmo (teléfono, botón dos AirPods, suspensión) permanecen en pausa.</string>
|
||||
<string name="settings_start_music_on_wear_label">Iniciar música ao poñer</string>
|
||||
<string name="settings_start_music_on_wear_description">Sempre intenta reproducir música cando pon os auriculares, incluso despois dunha pausa manual.</string>
|
||||
<string name="settings_eardetection_info_label">Nota sobre detección no oído</string>
|
||||
<string name="settings_eardetection_info_description">Se a detección no oído só funciona para un auricular, esta é unha limitación de Apple. Só se detecta o \"auricular principal\" (usado para o micrófono). Configúrao en dispositivos Apple: Axustes → Bluetooth → AirPods → Micrófono.</string>
|
||||
<string name="settings_fake_data_label">Datos falsos</string>
|
||||
<string name="settings_fake_data_description">Mostrar datos falsos, é dicir, simular dispositivos que non existen.</string>
|
||||
<string name="settings_debug_label">Configuración de depuración</string>
|
||||
<string name="settings_debug_description">Configuración adicional para axudar a solucionar problemas coa aplicación.</string>
|
||||
<string name="settings_signal_minimum_label">Calidade mínima do sinal</string>
|
||||
<string name="settings_signal_minimum_description">A calidade mínima do sinal que un dispositivo necesita ter para ser considerado teu.</string>
|
||||
<string name="settings_autoconnect_label">Conexión automática</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Mostrar ventá emerxente de conexión</string>
|
||||
<string name="settings_popup_connected_description">Mostrar unha ventá emerxente cando o dispositivo se conecta por primeira vez.</string>
|
||||
<string name="settings_popup_info_not_in_app">As ventás emerxentes só se mostran cando non estás na aplicación.</string>
|
||||
<string name="settings_popup_warning_manual_mode">As ventás emerxentes requiren que o monitor estea en execución. O teu modo de monitor está configurado como \"Cando a aplicación está aberta\", o que detén o monitor cando saes da aplicación.</string>
|
||||
<string name="general_fix_it_action">Arranxalo</string>
|
||||
<string name="settings_charged_notification_label">Notificación de carga completa</string>
|
||||
<string name="settings_charged_notification_description">Mostra unha notificación cando todo o que está cargando alcanza o nivel de carga obxectivo.</string>
|
||||
<string name="settings_charged_threshold_label">Nivel de carga obxectivo</string>
|
||||
<string name="settings_charged_scope_label">Baterías monitorizadas</string>
|
||||
<string name="settings_charged_scope_pods_label">Só os auriculares</string>
|
||||
<string name="settings_charged_scope_case_label">Só o estoxo</string>
|
||||
<string name="settings_charged_scope_both_label">Auriculares e estoxo</string>
|
||||
<string name="device_settings_experimental_title">Función experimental</string>
|
||||
<string name="device_settings_experimental_description">Esta función aínda non foi probada en todos os dispositivos. Se algo non funciona correctamente, abre un problema co rexistro de depuración.</string>
|
||||
<string name="device_settings_experimental_action">Abrir o rastreador de problemas</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Depuración</string>
|
||||
<string name="settings_general_label">Configuración</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Modo de depuración</string>
|
||||
<string name="settings_debug_mode_description">Mostrar información adicional para solucionar problemas.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Datos BLE sen filtrar</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Elimina calquera filtro do escáner BLE para mostrar todos os datos BLE emitidos. Útil para engadir soporte para novos tipos de auriculares.</string>
|
||||
<string name="settings_compatibility_mode_label">Modo de compatibilidade</string>
|
||||
<string name="settings_compatibility_mode_description">Desactivar as optimizacións para mellorar a compatibilidade. Proba isto se non ves ningún dato.</string>
|
||||
<string name="help_translate_label">Tradución</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Este widget require unha conexión directa (AAP) ao teu dispositivo. Asegúrate de que o dispositivo está emparellado e conectado na aplicación antes de usar este widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Este dispositivo non admite o control de ruído</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Non conectado</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Preto pero non conectado</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Non preto</string>
|
||||
<string name="anc_widget_aap_connecting_label">Conectando…</string>
|
||||
<string name="tile_anc_label">Control de ruído</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Permiso necesario</string>
|
||||
<string name="tile_anc_subtitle_no_device">Sen dispositivo</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Sen control de ruído</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth desactivado</string>
|
||||
<string name="widget_config_screen_title">Configuración do widget</string>
|
||||
<string name="widget_configuration_title">Seleccionar dispositivo</string>
|
||||
<string name="widget_configuration_description">Escolle que perfil de dispositivo debe mostrar este widget.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">O Bluetooth está desactivado, actívao ;)</string>
|
||||
<string name="overview_monitoring_active_label">Supervisando dispositivos</string>
|
||||
<string name="overview_monitoring_active_description">Asegúrate de que o teu dispositivo estea preto e activo.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Conectado, pero sen datos</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">O teu dispositivo está conectado, pero CAPod non está a recibir datos en tempo real del. O teu teléfono pode necesitar unha opción de compatibilidade — o solucionador de problemas pode tentar atopala automaticamente.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Executar o solucionador de problemas</string>
|
||||
<string name="overview_unmatched_devices_label">Dispositivos sen perfil coincidente</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d dispositivo sen perfil coincidente</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">É necesario o seguinte permiso:</string>
|
||||
<string name="permission_system_alert_window_label">Xanela de alerta do sistema</string>
|
||||
<string name="permission_system_alert_window_description">Permitir que CAPod debuxe sobre outras aplicacións para facer posible a función \"Mostrar PopUp\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">Cando a aplicación está aberta</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Cando o dispositivo está conectado</string>
|
||||
<string name="settings_monitor_mode_always_label">Sempre</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Cando se ve</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">A funda está aberta</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">No oído</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Transparencia</string>
|
||||
<string name="anc_mode_adaptive">Adaptativo</string>
|
||||
<string name="conversation_awareness_label">Consciencia da conversa</string>
|
||||
<string name="settings_conversation_action_label">Cando comezas a falar</string>
|
||||
<string name="settings_conversation_action_nothing_label">Non facer nada</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Baixar volume</string>
|
||||
<string name="settings_conversation_action_pause_label">Pausar multimedia</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Redución de volume</string>
|
||||
<string name="signal_badge_ble_cd">Exploración BLE activa</string>
|
||||
<string name="signal_badge_key_irk_cd">Identidade verificada</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Conexión cifrada</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ámbar</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Usa as cores do fondo de pantalla</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitorización</string>
|
||||
<string name="settings_category_appearance_label">Aparencia</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Cambios sen gardar</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Xeral</string>
|
||||
<string name="device_settings_microphone_mode_label">Micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Que AirPod se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_description">Cal auricular se usa como micrófono</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automático</string>
|
||||
<string name="device_settings_microphone_mode_right">Dereito</string>
|
||||
<string name="device_settings_microphone_mode_left">Esquerdo</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Detección de sono</string>
|
||||
<string name="reaction_sleep_notification_title">Pausado pola detección de sono</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s detectou que te durmiches, polo que a túa música foi pausada. Podes desactivar isto na configuración do dispositivo.</string>
|
||||
<string name="reaction_charged_channel_label">Notificación de carga</string>
|
||||
<string name="reaction_charged_notification_title">Carga completa</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s está completamente cargado.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s foi cargado ata o %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Renomear</string>
|
||||
<string name="device_settings_rename_hint">Nome do dispositivo</string>
|
||||
<string name="device_settings_rename_confirm">Renomear</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">यदि आपने हाल ही में खरीदारी की है, तो Google Play को सिंक करने में कुछ समय लग सकता है।</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">यदि आपकी खरीद दिखाई नहीं दे रही है, तो कुछ मिनट बाद पुनः प्रयास करें।</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">सुनिश्चित करें कि आप उसी Google खाते से साइन इन हैं जिससे खरीदारी की गई थी।</string>
|
||||
<string name="settings_monitor_mode_label">मॉनिटर मोड</string>
|
||||
<string name="settings_monitor_mode_description">यह ऐप किन परिस्थितियों में ब्लूटूथ डेटा की निगरानी करता है।</string>
|
||||
<string name="settings_monitor_connected_notification_label">अतिरिक्त सूचना</string>
|
||||
<string name="settings_monitor_connected_notification_description">जब कोई डिवाइस कनेक्ट होता है तो एक अतिरिक्त सूचना दिखाता है। यह आपको \"डिवाइस स्थिति\" चैनल को अक्षम करके स्थायी \"कोई डिवाइस नहीं\" सूचना को छिपाने की अनुमति देता है।</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">डिस्कनेक्ट होने के बाद सूचना बनाए रखें</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">ऑटो-पॉज़</string>
|
||||
<string name="settings_autopause_description">डिवाइस को अपने कान से हटाने पर ऑडियो रोकें।</string>
|
||||
<string name="settings_autopplay_label">ऑटो-प्ले</string>
|
||||
<string name="settings_autoplay_description">डिवाइस पहनने पर ऑडियो प्लेबैक शुरू करें।</string>
|
||||
<string name="settings_autoplay_description">अपने पॉड्स फिर पहनने पर संगीत फिर से शुरू करें, लेकिन केवल तभी जब ऑटो पॉज़ ने इसे रोका हो। आपके द्वारा स्वयं किए गए पॉज़ (फ़ोन, AirPods स्टेम, स्लीप) पॉज़ रहेंगे।</string>
|
||||
<string name="settings_start_music_on_wear_label">पहनने पर संगीत शुरू करें</string>
|
||||
<string name="settings_start_music_on_wear_description">जब आप अपने पॉड्स पहनते हैं तो हमेशा संगीत चलाने की कोशिश करता है, मैन्युअल पॉज़ के बाद भी।</string>
|
||||
<string name="settings_eardetection_info_label">कान का पता लगाने की जानकारी</string>
|
||||
<string name="settings_eardetection_info_description">यदि कान का पता लगाना केवल एक पॉड के लिए काम करता है, तो यह Apple की सीमा है। केवल \"प्राथमिक पॉड\" (माइक्रोफ़ोन के लिए उपयोग किया जाने वाला) का पता लगाया जाता है। Apple डिवाइस पर कॉन्फ़िगर करें: सेटिंग्स → Bluetooth → AirPods → माइक्रोफ़ोन।</string>
|
||||
<string name="settings_fake_data_label">नकली डेटा</string>
|
||||
<string name="settings_fake_data_description">नकली डेटा दिखाएं, यानी उन डिवाइसों का अनुकरण करें जो मौजूद नहीं हैं।</string>
|
||||
<string name="settings_debug_label">डीबग सेटिंग्स</string>
|
||||
<string name="settings_debug_description">ऐप के साथ समस्याओं के निवारण में मदद करने के लिए अतिरिक्त सेटिंग्स।</string>
|
||||
<string name="settings_signal_minimum_label">न्यूनतम सिग्नल गुणवत्ता</string>
|
||||
<string name="settings_signal_minimum_description">न्यूनतम सिग्नल गुणवत्ता जो किसी डिवाइस के पास आपकी मानी जाने के लिए होनी चाहिए।</string>
|
||||
<string name="settings_autoconnect_label">ऑटो-कनेक्ट</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">कनेक्शन पॉपअप दिखाएं</string>
|
||||
<string name="settings_popup_connected_description">डिवाइस पहली बार कनेक्ट होने पर एक पॉपअप दिखाएं।</string>
|
||||
<string name="settings_popup_info_not_in_app">पॉपअप केवल तब दिखाए जाते हैं जब आप ऐप में नहीं होते।</string>
|
||||
<string name="settings_popup_warning_manual_mode">पॉपअप के लिए मॉनिटर का चलना आवश्यक है। आपका मॉनिटर मोड \"जब ऐप खुला हो\" पर सेट है, जो ऐप छोड़ने पर मॉनिटर बंद कर देता है।</string>
|
||||
<string name="general_fix_it_action">इसे ठीक करें</string>
|
||||
<string name="settings_charged_notification_label">चार्ज सूचना</string>
|
||||
<string name="settings_charged_notification_description">एक बार जब चार्ज हो रही हर चीज़ लक्ष्य चार्ज स्तर तक पहुँच जाए, तब एक सूचना दिखाएँ।</string>
|
||||
<string name="settings_charged_threshold_label">लक्ष्य चार्ज स्तर</string>
|
||||
<string name="settings_charged_scope_label">निगरानी की जा रही बैटरियाँ</string>
|
||||
<string name="settings_charged_scope_pods_label">केवल पॉड्स</string>
|
||||
<string name="settings_charged_scope_case_label">केवल केस</string>
|
||||
<string name="settings_charged_scope_both_label">पॉड्स और केस</string>
|
||||
<string name="device_settings_experimental_title">प्रयोगात्मक फ़ीचर</string>
|
||||
<string name="device_settings_experimental_description">इस फ़ीचर को अभी सभी डिवाइस पर नहीं परखा गया है। यदि कुछ सही तरह से काम नहीं कर रहा, तो कृपया डीबग लॉग के साथ एक समस्या खोलें।</string>
|
||||
<string name="device_settings_experimental_action">इश्यू ट्रैकर खोलें</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">डीबग</string>
|
||||
<string name="settings_general_label">सेटिंग्स</string>
|
||||
<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="settings_acknowledgements_label">स्वीकृतियाँ</string>
|
||||
<string name="settings_debug_autoreports_label">स्वचालित बग रिपोर्ट</string>
|
||||
<string name="settings_debug_autoreports_description">समस्याओं की स्वचालित रूप से रिपोर्ट करता है, जैसे ऐप क्रैश पर विवरण ताकि मैं यह पता लगा सकूं कि इसे कैसे ठीक किया जाए।</string>
|
||||
<string name="settings_debug_mode_label">डीबग मोड</string>
|
||||
<string name="settings_debug_mode_description">समस्याओं के निवारण के लिए अतिरिक्त जानकारी दिखाएं।</string>
|
||||
<string name="settings_blescanner_unfiltered_label">अनफ़िल्टर्ड BLE डेटा</string>
|
||||
<string name="settings_blescanner_unfiltered_description">सभी प्रसारित BLE डेटा दिखाने के लिए BLE स्कैनर से किसी भी फ़िल्टर को हटा दें। नए हेडफ़ोन प्रकारों के लिए समर्थन जोड़ने के लिए उपयोगी है।</string>
|
||||
<string name="settings_compatibility_mode_label">संगतता मोड</string>
|
||||
<string name="settings_compatibility_mode_description">संगतता में सुधार के लिए अनुकूलन अक्षम करें। यदि आप कोई डेटा नहीं देख रहे हैं तो इसे आज़माएँ।</string>
|
||||
<string name="help_translate_label">अनुवाद</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">इस विजेट को आपके डिवाइस से सीधे कनेक्शन (AAP) की आवश्यकता है। इस विजेट का उपयोग करने से पहले सुनिश्चित करें कि आपका डिवाइस ऐप में पेयर और कनेक्ट है।</string>
|
||||
<string name="anc_widget_no_anc_support_label">यह डिवाइस शोर नियंत्रण का समर्थन नहीं करता</string>
|
||||
<string name="anc_widget_aap_not_connected_label">कनेक्ट नहीं है</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">पास में है लेकिन कनेक्ट नहीं है</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">पास में नहीं है</string>
|
||||
<string name="anc_widget_aap_connecting_label">कनेक्ट हो रहा है…</string>
|
||||
<string name="tile_anc_label">शोर नियंत्रण</string>
|
||||
<string name="tile_anc_subtitle_permission_required">अनुमति आवश्यक है</string>
|
||||
<string name="tile_anc_subtitle_no_device">उपकरण नहीं</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">कोई शोर नियंत्रण नहीं</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">ब्लूटूथ बंद है</string>
|
||||
<string name="widget_config_screen_title">विजेट कॉन्फ़िगरेशन</string>
|
||||
<string name="widget_configuration_title">डिवाइस चुनें</string>
|
||||
<string name="widget_configuration_description">चुनें कि यह विजेट कौन सा डिवाइस प्रोफ़ाइल प्रदर्शित करेगा।</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth अक्षम है, इसे सक्षम करें ;)</string>
|
||||
<string name="overview_monitoring_active_label">डिवाइसों की निगरानी हो रही है</string>
|
||||
<string name="overview_monitoring_active_description">सुनिश्चित करें कि आपका डिवाइस पास में और सक्रिय है।</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">कनेक्टेड, लेकिन कोई डेटा नहीं</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">आपका डिवाइस कनेक्टेड है, लेकिन CAPod उससे कोई लाइव डेटा प्राप्त नहीं कर रहा। आपके फ़ोन को एक संगतता विकल्प की ज़रूरत हो सकती है — समस्या निवारक इसे स्वचालित रूप से खोजने की कोशिश कर सकता है।</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">समस्या निवारक चलाएँ</string>
|
||||
<string name="overview_unmatched_devices_label">बेमेल डिवाइस</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d डिवाइस बिना मेल खाते प्रोफ़ाइल के</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">निम्नलिखित अनुमति आवश्यक है:</string>
|
||||
<string name="permission_system_alert_window_label">सिस्टम अलर्ट विंडो</string>
|
||||
<string name="permission_system_alert_window_description">CAPod को अन्य ऐप्स के ऊपर ड्रॉ करने की अनुमति दें ताकि \"पॉपअप दिखाएं\" सुविधा संभव हो सके।</string>
|
||||
<string name="settings_monitor_mode_manual_label">जब ऐप खुला हो</string>
|
||||
<string name="settings_monitor_mode_automatic_label">जब डिवाइस कनेक्ट हो</string>
|
||||
<string name="settings_monitor_mode_always_label">हमेशा</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">जब देखा जाए</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">केस खुला है</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">कान में</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">पारदर्शिता</string>
|
||||
<string name="anc_mode_adaptive">अनुकूली</string>
|
||||
<string name="conversation_awareness_label">बातचीत जागरूकता</string>
|
||||
<string name="settings_conversation_action_label">जब आप बोलना शुरू करते हैं</string>
|
||||
<string name="settings_conversation_action_nothing_label">कुछ न करें</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">वॉल्यूम कम करें</string>
|
||||
<string name="settings_conversation_action_pause_label">मीडिया पॉज़ करें</string>
|
||||
<string name="settings_conversation_volume_reduction_label">वॉल्यूम कमी</string>
|
||||
<string name="signal_badge_ble_cd">BLE स्कैन सक्रिय</string>
|
||||
<string name="signal_badge_key_irk_cd">पहचान सत्यापित</string>
|
||||
<string name="signal_badge_key_encrypted_cd">एन्क्रिप्टेड कनेक्शन</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">एम्बर</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">वॉलपेपर रंगों का उपयोग करता है</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">मॉनिटरिंग</string>
|
||||
<string name="settings_category_appearance_label">दिखावट</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">असहेजे परिवर्तन</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">सामान्य</string>
|
||||
<string name="device_settings_microphone_mode_label">माइक्रोफ़ोन</string>
|
||||
<string name="device_settings_microphone_mode_description">कौन सा AirPod माइक्रोफ़ोन के रूप में उपयोग होता है</string>
|
||||
<string name="device_settings_microphone_mode_description">कौन सा ईयरबड माइक्रोफ़ोन के रूप में उपयोग किया जाता है</string>
|
||||
<string name="device_settings_microphone_mode_auto">स्वतः</string>
|
||||
<string name="device_settings_microphone_mode_right">दायाँ</string>
|
||||
<string name="device_settings_microphone_mode_left">बायाँ</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">नींद का पता लगाना</string>
|
||||
<string name="reaction_sleep_notification_title">नींद का पता लगाने से रोका गया</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s ने बताया कि आप सो गए, इसलिए आपका संगीत रोक दिया गया। आप इसे डिवाइस सेटिंग्स में बंद कर सकते हैं।</string>
|
||||
<string name="reaction_charged_channel_label">चार्ज सूचना</string>
|
||||
<string name="reaction_charged_notification_title">चार्जिंग पूर्ण</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s पूरी तरह चार्ज हो गया है।</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s को %2$d%% तक चार्ज किया जा चुका है।</string>
|
||||
<string name="device_settings_rename_label">नाम बदलें</string>
|
||||
<string name="device_settings_rename_hint">डिवाइस का नाम</string>
|
||||
<string name="device_settings_rename_confirm">नाम बदलें</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Ako ste nedavno kupili, može potrajati trenutak dok Google Play sinkronizira.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Pokušajte ponovo za nekoliko minuta ako vaša kupnja nije prikazana.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Provjerite jeste li prijavljeni s istim Google računom koji ste koristili za kupnju.</string>
|
||||
<string name="settings_monitor_mode_label">Način praćenja</string>
|
||||
<string name="settings_monitor_mode_description">Pod kojim okolnostima ova aplikacija prati Bluetooth podatke.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Dodatna obavijest</string>
|
||||
<string name="settings_monitor_connected_notification_description">Prikazuje dodatnu obavijest kada je uređaj povezan. To vam omogućuje da sakrijete trajnu obavijest \"Nema uređaja\" onemogućavanjem kanala \"Status uređaja\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Zadrži obavijest nakon prekida veze</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Automatska pauza</string>
|
||||
<string name="settings_autopause_description">Pauzirajte zvuk kada uklonite uređaj iz uha.</string>
|
||||
<string name="settings_autopplay_label">Automatska reprodukcija</string>
|
||||
<string name="settings_autoplay_description">Pokrenite reprodukciju zvuka kada se uređaj nosi.</string>
|
||||
<string name="settings_autoplay_description">Nastavi glazbu kad opet staviš slušalice, ali samo ako ju je zaustavio Auto pauza. Pauze koje si sam pokrenuo (telefon, AirPods drška, spavanje) ostaju pauzirane.</string>
|
||||
<string name="settings_start_music_on_wear_label">Pokreni glazbu pri nošenju</string>
|
||||
<string name="settings_start_music_on_wear_description">Uvijek pokušava reproducirati glazbu kad staviš slušalice, čak i nakon ručne pauze.</string>
|
||||
<string name="settings_eardetection_info_label">Napomena o detekciji uha</string>
|
||||
<string name="settings_eardetection_info_description">Ako detekcija uha radi samo za jednu slušalicu, to je ograničenje Applea. Samo \"primarna slušalica\" (korištena za mikrofon) se detektira. Konfigurirajte na Apple uređajima: Postavke → Bluetooth → AirPods → Mikrofon.</string>
|
||||
<string name="settings_fake_data_label">Lažni podaci</string>
|
||||
<string name="settings_fake_data_description">Prikaži lažne podatke, tj. simuliraj uređaje koji ne postoje.</string>
|
||||
<string name="settings_debug_label">Postavke za ispravljanje pogrešaka</string>
|
||||
<string name="settings_debug_description">Dodatne postavke za pomoć pri rješavanju problema s aplikacijom.</string>
|
||||
<string name="settings_signal_minimum_label">Minimalna kvaliteta signala</string>
|
||||
<string name="settings_signal_minimum_description">Minimalna kvaliteta signala koju uređaj mora imati da bi se smatrao vašim.</string>
|
||||
<string name="settings_autoconnect_label">Automatsko povezivanje</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Prikaži skočni prozor veze</string>
|
||||
<string name="settings_popup_connected_description">Prikaži skočni prozor kada se uređaj prvi put poveže.</string>
|
||||
<string name="settings_popup_info_not_in_app">Skočni prozori prikazuju se samo kada niste u aplikaciji.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Skočni prozori zahtijevaju pokrenuti monitor. Vaš način rada monitora postavljen je na \"Kada je aplikacija otvorena\", što zaustavlja monitor kada napustite aplikaciju.</string>
|
||||
<string name="general_fix_it_action">Popravi</string>
|
||||
<string name="settings_charged_notification_label">Obavijest o napunjenosti</string>
|
||||
<string name="settings_charged_notification_description">Prikaži obavijest kada sve što se puni dostigne ciljanu razinu napunjenosti.</string>
|
||||
<string name="settings_charged_threshold_label">Ciljana razina napunjenosti</string>
|
||||
<string name="settings_charged_scope_label">Praćene baterije</string>
|
||||
<string name="settings_charged_scope_pods_label">Samo slušalice</string>
|
||||
<string name="settings_charged_scope_case_label">Samo kutija</string>
|
||||
<string name="settings_charged_scope_both_label">Slušalice i kutija</string>
|
||||
<string name="device_settings_experimental_title">Eksperimentalna značajka</string>
|
||||
<string name="device_settings_experimental_description">Ova značajka još nije testirana na svim uređajima. Ako nešto ne radi ispravno, molimo otvorite prijavu s debug zapisnikom.</string>
|
||||
<string name="device_settings_experimental_action">Otvori praćenje problema</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Ispravljanje pogrešaka</string>
|
||||
<string name="settings_general_label">Postavke</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Način za ispravljanje pogrešaka</string>
|
||||
<string name="settings_debug_mode_description">Prikaži dodatne informacije za rješavanje problema.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Nefiltrirani BLE podaci</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Uklonite sve filtre iz BLE skenera kako biste prikazali sve emitirane BLE podatke. Korisno za dodavanje podrške za nove vrste slušalica.</string>
|
||||
<string name="settings_compatibility_mode_label">Način kompatibilnosti</string>
|
||||
<string name="settings_compatibility_mode_description">Onemogućite optimizacije kako biste poboljšali kompatibilnost. Pokušajte ovo ako ne vidite nikakve podatke.</string>
|
||||
<string name="help_translate_label">Prijevod</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Ovaj widget zahtijeva izravnu vezu (AAP) s vašim uređajem. Provjerite je li vaš uređaj uparen i povezan u aplikaciji prije korištenja ovog widgeta.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Ovaj uređaj ne podržava kontrolu buke</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Nije povezano</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">U blizini, ali nije povezano</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Nije u blizini</string>
|
||||
<string name="anc_widget_aap_connecting_label">Povezivanje…</string>
|
||||
<string name="tile_anc_label">Kontrola buke</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Potrebna dozvola</string>
|
||||
<string name="tile_anc_subtitle_no_device">Nema uređaja</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Nema kontrole buke</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth isključen</string>
|
||||
<string name="widget_config_screen_title">Konfiguracija widgeta</string>
|
||||
<string name="widget_configuration_title">Odaberi uređaj</string>
|
||||
<string name="widget_configuration_description">Odaberite koji profil uređaja ovaj widget treba prikazivati.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth je onemogućen, omogućite ga ;)</string>
|
||||
<string name="overview_monitoring_active_label">Praćenje uređaja</string>
|
||||
<string name="overview_monitoring_active_description">Provjerite je li vaš uređaj u blizini i aktivan.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Spojeno, ali nema podataka</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Tvoj uređaj je spojen, ali CAPod ne prima žive podatke od njega. Tvoj telefon možda treba opciju kompatibilnosti — alat za rješavanje problema može pokušati pronaći jednu automatski.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Pokreni rješavanje problema</string>
|
||||
<string name="overview_unmatched_devices_label">Nepodudarni uređaji</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d uređaj bez odgovarajućeg profila</item>
|
||||
@@ -222,9 +231,6 @@
|
||||
<string name="permission_required_title">Sljedeća dozvola je potrebna:</string>
|
||||
<string name="permission_system_alert_window_label">Prozor sistemskog upozorenja</string>
|
||||
<string name="permission_system_alert_window_description">Dopustite CAPod-u crtanje preko drugih aplikacija kako bi omogućili značajku \"Prikaži skočni prozor\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">Kada je aplikacija otvorena</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Kada je uređaj povezan</string>
|
||||
<string name="settings_monitor_mode_always_label">Uvijek</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Kada je viđen</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Kućište je otvoreno</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">U uhu</string>
|
||||
@@ -256,6 +262,11 @@
|
||||
<string name="anc_mode_transparency">Transparentnost</string>
|
||||
<string name="anc_mode_adaptive">Prilagodljivo</string>
|
||||
<string name="conversation_awareness_label">Svjesnost razgovora</string>
|
||||
<string name="settings_conversation_action_label">Kad počneš govoriti</string>
|
||||
<string name="settings_conversation_action_nothing_label">Ne radi ništa</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Smanji glasnoću</string>
|
||||
<string name="settings_conversation_action_pause_label">Pauziraj medij</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Smanjenje glasnoće</string>
|
||||
<string name="signal_badge_ble_cd">BLE skeniranje aktivno</string>
|
||||
<string name="signal_badge_key_irk_cd">Identitet potvrđen</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Šifrirana veza</string>
|
||||
@@ -324,7 +335,6 @@
|
||||
<string name="ui_theme_color_amber_label">Jantar</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Koristi boje pozadine</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Praćenje</string>
|
||||
<string name="settings_category_appearance_label">Izgled</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Nespremljene promjene</string>
|
||||
@@ -475,7 +485,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Općenito</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Koji se AirPod koristi kao mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Koja slušalica se koristi kao mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automatski</string>
|
||||
<string name="device_settings_microphone_mode_right">Desni</string>
|
||||
<string name="device_settings_microphone_mode_left">Lijevi</string>
|
||||
@@ -497,6 +507,10 @@
|
||||
<string name="reaction_sleep_channel_label">Otkrivanje spavanja</string>
|
||||
<string name="reaction_sleep_notification_title">Pauzirano otkrivanjem spavanja</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s je prijavio da ste zaspali, pa je vaša glazba pauzirana. Možete to onemogućiti u postavkama uređaja.</string>
|
||||
<string name="reaction_charged_channel_label">Obavijest o napunjenosti</string>
|
||||
<string name="reaction_charged_notification_title">Punjenje završeno</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s je potpuno napunjen.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s je napunjen na %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Preimenuj</string>
|
||||
<string name="device_settings_rename_hint">Naziv uređaja</string>
|
||||
<string name="device_settings_rename_confirm">Preimenuj</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Ha nemrég vásároltál, a Google Play-nek egy pillanatra szüksége lehet a szinkronizáláshoz.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Próbálkozz újra néhány perc múlva, ha a vásárlás nem jelenik meg.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Győződj meg róla, hogy ugyanazzal a Google-fiókkal vagy bejelentkezve, amellyel a vásárlást végezted.</string>
|
||||
<string name="settings_monitor_mode_label">Monitor mód</string>
|
||||
<string name="settings_monitor_mode_description">Milyen körülmények között figyelje az alkalmazás a Bluetooth-adatokat.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Extra értesítés</string>
|
||||
<string name="settings_monitor_connected_notification_description">Extra értesítést jelenít meg, amikor egy eszköz csatlakozik. Ez lehetővé teszi az állandó \"Nincs eszköz\" értesítés elrejtését az \"Eszköz állapota\" csatorna letiltásával.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Értesítés megőrzése lecsatlakozás után</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Automatikus szünet</string>
|
||||
<string name="settings_autopause_description">Zene leállítása, amikor kiveszi a füléből az eszközt.</string>
|
||||
<string name="settings_autopplay_label">Automatikus lejátszás</string>
|
||||
<string name="settings_autoplay_description">Hanglejátszás elindítása, ha az eszköz viselve van.</string>
|
||||
<string name="settings_autoplay_description">Folytatja a zenét, ha újra felveszed a fülhallgatót, de csak akkor, ha az Auto szünet állította le. A te magad által kiváltott szünetek (telefon, AirPods szár, alvás) szüneteltetett állapotban maradnak.</string>
|
||||
<string name="settings_start_music_on_wear_label">Zene indítása felvételkor</string>
|
||||
<string name="settings_start_music_on_wear_description">Mindig megpróbál zenét lejátszani, ha felveszed a fülhallgatót, még kézi szünet után is.</string>
|
||||
<string name="settings_eardetection_info_label">Fülészlelési megjegyzés</string>
|
||||
<string name="settings_eardetection_info_description">Ha a fülészlelés csak az egyik fülhallgatóval működik, ez egy Apple korlátozás. Csak az \"elsődleges fülhallgató\" (mikrofonhoz használt) érzékelhető. Beállítás Apple eszközökön: Beállítások → Bluetooth → AirPods → Mikrofon.</string>
|
||||
<string name="settings_fake_data_label">Hamis adat</string>
|
||||
<string name="settings_fake_data_description">Hamis adatok megjelenítése, azaz olyan eszköz szimulálása, amely nem létezik.</string>
|
||||
<string name="settings_debug_label">Hibakeresési beállítások</string>
|
||||
<string name="settings_debug_description">További beállítások az alkalmazással kapcsolatos problémák megoldásához.</string>
|
||||
<string name="settings_signal_minimum_label">Minimális jelerősség</string>
|
||||
<string name="settings_signal_minimum_description">Vegye figyelembe a minimális jelerősséget, amelyre egy eszköznek szüksége van.</string>
|
||||
<string name="settings_autoconnect_label">Automatikus csatlakozás</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Előugró ablak csatlakozáskor</string>
|
||||
<string name="settings_popup_connected_description">Előugró ablak megjelenítése, amikor az eszköz először csatlakozik.</string>
|
||||
<string name="settings_popup_info_not_in_app">Az előugró ablakok csak akkor jelennek meg, ha nem tartózkodik az alkalmazásban.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Az előugró ablakokhoz futnia kell a monitornak. A monitor módja „Amikor az alkalmazás nyitva van”, ami leállítja a monitort, amikor elhagyja az alkalmazást.</string>
|
||||
<string name="general_fix_it_action">Javítás</string>
|
||||
<string name="settings_charged_notification_label">Feltöltési értesítés</string>
|
||||
<string name="settings_charged_notification_description">Értesítés küldése, ha minden töltés alatt lévő eszköz eléri a céltöltési szintet.</string>
|
||||
<string name="settings_charged_threshold_label">Céltöltési szint</string>
|
||||
<string name="settings_charged_scope_label">Figyelt akkumulátorok</string>
|
||||
<string name="settings_charged_scope_pods_label">Csak fülhallgatók</string>
|
||||
<string name="settings_charged_scope_case_label">Csak tok</string>
|
||||
<string name="settings_charged_scope_both_label">Fülhallgatók és tok</string>
|
||||
<string name="device_settings_experimental_title">Kísérleti funkció</string>
|
||||
<string name="device_settings_experimental_description">Ezt a funkciót még nem tesztelték minden eszközön. Ha valami nem működik megfelelően, kérjük, nyiss egy hibajegyet hibakeresési naplóval.</string>
|
||||
<string name="device_settings_experimental_action">Hibajegykövetős megnyitása</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Hibakeresés</string>
|
||||
<string name="settings_general_label">Beállítások</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Hibakeresési mód</string>
|
||||
<string name="settings_debug_mode_description">További információk megjelenítése a problémák hibaelhárításához.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Nem szűrt BLE adatok</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Minden szűrő eltávolítása a BLE szkennerből, hogy az összes adat megjelenjen. Hasznos új fejhallgató típusok támogatásához.</string>
|
||||
<string name="settings_compatibility_mode_label">Kompatibilitási mód</string>
|
||||
<string name="settings_compatibility_mode_description">A kompatibilitás javítása érdekében letiltja az optimalizálást. Próbálja ki ezt, ha nem lát semmilyen adatot.</string>
|
||||
<string name="help_translate_label">Fordítás</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Ez a widget közvetlen kapcsolatot (AAP) igényel az eszközödhöz. Győződj meg róla, hogy az eszköz párosítva és csatlakoztatva van az alkalmazásban a widget használata előtt.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Ez az eszköz nem támogatja a zajvezérlést</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Nincs csatlakoztatva</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">A közelben van, de nincs csatlakoztatva</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Nincs a közelben</string>
|
||||
<string name="anc_widget_aap_connecting_label">Csatlakozás…</string>
|
||||
<string name="tile_anc_label">Zajszabályozás</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Engedély szükséges</string>
|
||||
<string name="tile_anc_subtitle_no_device">Nincs eszköz</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Nincs zajszabályozás</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth kikapcsolva</string>
|
||||
<string name="widget_config_screen_title">Widget konfiguráció</string>
|
||||
<string name="widget_configuration_title">Eszköz kiválasztása</string>
|
||||
<string name="widget_configuration_description">Válassza ki, melyik eszközprofilt jelenítse meg ez a widget.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">A Bluetooth le van tiltva, engedélyezze ;)</string>
|
||||
<string name="overview_monitoring_active_label">Eszközök figyelése</string>
|
||||
<string name="overview_monitoring_active_description">Győződjön meg róla, hogy az eszköze a közelben van és aktív.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Csatlakoztatva, de nincs adat</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Az eszköz csatlakoztatva van, de a CAPod nem kap élő adatot tőle. Előfordulhat, hogy a telefonodnak kompatibilitási beállításra van szüksége – a hibaelőárító automatikusan megpróbálhat egyet találni.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Hibaelőárító futtatása</string>
|
||||
<string name="overview_unmatched_devices_label">Nem párosított eszközök</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d eszköz párosított profil nélkül</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">A következő engedély szükséges:</string>
|
||||
<string name="permission_system_alert_window_label">Rendszer figyelmeztető ablak</string>
|
||||
<string name="permission_system_alert_window_description">Engedélyezze a CAPod-nak, hogy más alkalmazások fölé rajzoljon az \"Előugró ablak megjelenítése\" funkció lehetővé tételéhez.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Amikor az alkalmazás nyitva van</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Amikor az eszköz csatlakozik</string>
|
||||
<string name="settings_monitor_mode_always_label">Mindig</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Amikor észlelhető</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">A tok nyitva van</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Fülben</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Átlátszóság</string>
|
||||
<string name="anc_mode_adaptive">Adaptív</string>
|
||||
<string name="conversation_awareness_label">Társalgásérzékelés</string>
|
||||
<string name="settings_conversation_action_label">Amikor elkezdesz beszélni</string>
|
||||
<string name="settings_conversation_action_nothing_label">Ne csinálj semmit</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Hangerő csökkentése</string>
|
||||
<string name="settings_conversation_action_pause_label">Média szüneteltetése</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Hangerő-csökkentés</string>
|
||||
<string name="signal_badge_ble_cd">BLE szkennelés aktív</string>
|
||||
<string name="signal_badge_key_irk_cd">Azonosság ellenőrizve</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Titkosított kapcsolat</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Borostyán</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Háttérkép színeit használja</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Figyelés</string>
|
||||
<string name="settings_category_appearance_label">Megjelenés</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Nem mentett változtatások</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Általános</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Melyik AirPod-ot használja mikrofenként</string>
|
||||
<string name="device_settings_microphone_mode_description">Melyik fülhallgató van mikrofonként használva</string>
|
||||
<string name="device_settings_microphone_mode_auto">Automatikus</string>
|
||||
<string name="device_settings_microphone_mode_right">Jobb</string>
|
||||
<string name="device_settings_microphone_mode_left">Bal</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Alvásérzékelés</string>
|
||||
<string name="reaction_sleep_notification_title">Az alvásérzékelés által szüneteltetve</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s jelezte, hogy elaludtál, ezért a zene szünetelt. Ezt a készülék beállításaiban kapcsolhatod ki.</string>
|
||||
<string name="reaction_charged_channel_label">Feltöltési értesítés</string>
|
||||
<string name="reaction_charged_notification_title">Töltés kész</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s teljesen feltöltődött.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s feltöltődött %2$d%%-ra.</string>
|
||||
<string name="device_settings_rename_label">Átnevézés</string>
|
||||
<string name="device_settings_rename_hint">Eszköz neve</string>
|
||||
<string name="device_settings_rename_confirm">Átnevézés</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Եթե վերջերս գնել եք, Google Play-ի համաժամացմանը կարող է մի փոքր ժամանակ պահանջվել:</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Մի քանի րոպե հետո կրկին փորձեք, եթե ձեր գնումը չի երևում:</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Համոզվեք, որ մուտք եք գործել գնման համար օգտագործված Google հաշվով:</string>
|
||||
<string name="settings_monitor_mode_label">Մոնիթորինգի ռեժիմ</string>
|
||||
<string name="settings_monitor_mode_description">Ինչ պայմաններում է այս հավելվածը վերահսկում Bluetooth-ի տվյալները։</string>
|
||||
<string name="settings_monitor_connected_notification_label">Լրացուցիչ ծանուցում</string>
|
||||
<string name="settings_monitor_connected_notification_description">Ցուցադրում է լրացուցիչ ծանուցում, երբ սարքը միացված է։ Սա թույլ է տալիս թաքցնել մշտական \"Սարքեր չկան\" ծանուցումը՝ անջատելով \"Սարքի կարգավիճակը\" ալիքը։</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Պահել ծանուցումը անջատումից հետո</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Ինքնադադար</string>
|
||||
<string name="settings_autopause_description">Դադարեցրեք ձայնը, երբ սարքը հանում եք ականջից։</string>
|
||||
<string name="settings_autopplay_label">Ինքնանվագարկում</string>
|
||||
<string name="settings_autoplay_description">Սկսեք ձայնի նվագարկումը, երբ սարքը կրում եք։</string>
|
||||
<string name="settings_autoplay_description">Վերսկսել երաժշտությունը, երբ կրկին կրում եք ականջակալները, բայց միայն եթե Ավտոդադարը դադարեցրել է այն: Ձեր ձեռքով կատարած դադարները (հեռախոս, AirPods ոտք, քուն) մնում են դադարած:</string>
|
||||
<string name="settings_start_music_on_wear_label">Երաժշտություն սկսել կրելիս</string>
|
||||
<string name="settings_start_music_on_wear_description">Միշտ փորձում է նվագել երաժշտություն, երբ կկրեք ականջակալները, նույնիսկ ձեռքով դադարից հետո:</string>
|
||||
<string name="settings_eardetection_info_label">Ականջում հայտնաբերման նշում</string>
|
||||
<string name="settings_eardetection_info_description">Եթե ականջում հայտնաբերումը աշխատում է միայն մեկ ականջակալի համար, սա Apple-ի սահմանափակում է։ Միայն \"հիմնական ականջակալը\" (որն օգտագործվում է բարձրախոսի համար) է հայտնաբերվում։ Կարգավորեք Apple սարքերում՝ Կարգավորումներ → Bluetooth → AirPods → Բարձրախոս։</string>
|
||||
<string name="settings_fake_data_label">Կեղծ տվյալներ</string>
|
||||
<string name="settings_fake_data_description">Ցուցադրել կեղծ տվյալներ, այսինքն՝ գոյություն չունեցող սարքերի նմանակում։</string>
|
||||
<string name="settings_debug_label">Կարգաբերման կարգավորումներ</string>
|
||||
<string name="settings_debug_description">Լրացուցիչ կարգավորումներ՝ հավելվածի հետ կապված խնդիրների լուծմանն օգնելու համար։</string>
|
||||
<string name="settings_signal_minimum_label">Ազդանշանի նվազագույն որակ</string>
|
||||
<string name="settings_signal_minimum_description">Ազդանշանի նվազագույն որակը, որը պետք է ունենա սարքը՝ ձերը համարվելու համար։</string>
|
||||
<string name="settings_autoconnect_label">Ինքնամիացում</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Ցուցադրել միացման բացվող պատուհանը</string>
|
||||
<string name="settings_popup_connected_description">Ցուցադրել բացվող պատուհան, երբ սարքն առաջին անգամ է միանում։</string>
|
||||
<string name="settings_popup_info_not_in_app">Ելնող պատուհանները ցուցադրվում են միայն այն ժամանակ, երբ դուք հավելվածում չեք:</string>
|
||||
<string name="settings_popup_warning_manual_mode">Ելնող պատուհանները պահանջում են, որ մոնիտորը աշխատի: Մոնիտորի ռեժիմն սահմանված է «Հավելվածը բաց է», ինչը կանգնեցնում է մոնիտորը, երբ դուք լքում եք հավելվածը:</string>
|
||||
<string name="general_fix_it_action">Շտկել</string>
|
||||
<string name="settings_charged_notification_label">Լիցքավորման ծանուցում</string>
|
||||
<string name="settings_charged_notification_description">Ծանուցում ցուցադրել, երբ լիցքավորվող բոլոր սարքերը հասնեն թիրախային լիցքի մակարդակին:</string>
|
||||
<string name="settings_charged_threshold_label">Թիրախային լիցքի մակարդակ</string>
|
||||
<string name="settings_charged_scope_label">Վերահսկվող մարտկոցներ</string>
|
||||
<string name="settings_charged_scope_pods_label">Միայն pods-երը</string>
|
||||
<string name="settings_charged_scope_case_label">Միայն պատյանը</string>
|
||||
<string name="settings_charged_scope_both_label">Pods-երն ու պատյանը</string>
|
||||
<string name="device_settings_experimental_title">Փորձնական հատկություն</string>
|
||||
<string name="device_settings_experimental_description">Այս հատկությունը դեռ չի փորձարկվել բոլոր սարքերում: Եթե ինչ-որ բան ճիշտ չի աշխատում, խնդրում ենք բացել հարց՝ կազմաբերման մատյանով:</string>
|
||||
<string name="device_settings_experimental_action">Բացել հարցերի հետևիչը</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Կարգաբանություն</string>
|
||||
<string name="settings_general_label">Կարգավորումներ</string>
|
||||
<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="settings_acknowledgements_label">Շնորհակալագրեր</string>
|
||||
<string name="settings_debug_autoreports_label">Սխալների ավտոմատ հաշվետվություններ</string>
|
||||
<string name="settings_debug_autoreports_description">Ավտոմատ կերպով հաղորդում է խնդիրների մասին, օրինակ՝ հավելվածի խափանման մանրամասները, որպեսզի կարողանամ պարզել, թե ինչպես այն շտկել։</string>
|
||||
<string name="settings_debug_mode_label">Կարգաբերման ռեժիմ</string>
|
||||
<string name="settings_debug_mode_description">Ցուցադրել լրացուցիչ տեղեկատվություն՝ խնդիրները լուծելու համար։</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Չզտված BLE տվյալներ</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Հեռացրեք ցանկացած զտիչ BLE սկաներից՝ ցուցադրելու բոլոր հեռարձակվող BLE տվյալները։ Օգտակար է նոր տեսակի ականջակալների աջակցություն ավելացնելու համար։</string>
|
||||
<string name="settings_compatibility_mode_label">Համատեղելիության ռեժիմ</string>
|
||||
<string name="settings_compatibility_mode_description">Անջատեք օպտիմիզացումները՝ համատեղելիությունը բարելավելու համար։ Փորձեք սա, եթե տվյալներ չեք տեսնում։</string>
|
||||
<string name="help_translate_label">Թարգմանություն</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Այս վիջեթը պահանջում է ուղղակի կապ (AAP) ձեր սարքի հետ: Համոզվեք, որ ձեր սարքը զուգակցված և կապակցված է հավելվածում, նախքան այս վիջեթն օգտագործելը:</string>
|
||||
<string name="anc_widget_no_anc_support_label">Այս սարքը չի աջակցում աղմուկի վերահսկմանը</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Կապ չկա</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Մոտակայքում է, բայց կապված չէ</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Մոտակայքում չէ</string>
|
||||
<string name="anc_widget_aap_connecting_label">Կապակցվում է…</string>
|
||||
<string name="tile_anc_label">Աղմուկի կառավարում</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Թույլտվություն պահանջվում է</string>
|
||||
<string name="tile_anc_subtitle_no_device">Սարք չկա</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Աղմուկի կառավարում չկա</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth-ն անջատված է</string>
|
||||
<string name="widget_config_screen_title">Վիջեթի կարգավորում</string>
|
||||
<string name="widget_configuration_title">Ընտրել սարք</string>
|
||||
<string name="widget_configuration_description">Ընտրեք, թե որ սարքի պրոֆիլը պետք է ցուցադրի այս վիջեթը։</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth-ը անջատված է, միացրեք այն ;)</string>
|
||||
<string name="overview_monitoring_active_label">Սարքերի մոնիթորինգ</string>
|
||||
<string name="overview_monitoring_active_description">Համոզվեք, որ ձեր սարքը մոտակայքում է և ակտիվ է։</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Միացված է, բայց տվյալներ չկան</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Ձեր սարքը միացված է, սակայն CAPod-ը կենդանի տվյալներ չի ստանում դրանից: Ձեր հեռախոսը կարող է կոմպատիբիլության տարբերակի կարիք ունենալ — խնդիրների լուծիչը կարող է ավտոմատ կերպով գտնել համապատասխանը:</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Գործարկել խնդիրների լուծիչը</string>
|
||||
<string name="overview_unmatched_devices_label">Չհամապատասխանող սարքեր</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d սարք՝ առանց համապատասխան պրոֆիլի</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Հետևյալ թույլտվությունն անհրաժեշտ է՝</string>
|
||||
<string name="permission_system_alert_window_label">Համակարգային զգուշացման պատուհան</string>
|
||||
<string name="permission_system_alert_window_description">Թույլ տվեք CAPod-ին նկարել այլ հավելվածների վերևում՝ \"Ցուցադրել բացվող պատուհան\" գործառույթը հնարավոր դարձնելու համար։</string>
|
||||
<string name="settings_monitor_mode_manual_label">Երբ հավելվածը բաց է</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Երբ սարքը միացված է</string>
|
||||
<string name="settings_monitor_mode_always_label">Միշտ</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Երբ տեսնվում է</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Պատյանը բաց է</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Ականջում է</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Թափանցիկություն</string>
|
||||
<string name="anc_mode_adaptive">Հարմարվող</string>
|
||||
<string name="conversation_awareness_label">Խոսակցության ճանաչում</string>
|
||||
<string name="settings_conversation_action_label">Երբ սկսել եք խոսել</string>
|
||||
<string name="settings_conversation_action_nothing_label">Ոչինչ չանել</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Ձայնն իջեցնել</string>
|
||||
<string name="settings_conversation_action_pause_label">Դադարեցնել մեդիան</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Ձայնի կրճատում</string>
|
||||
<string name="signal_badge_ble_cd">BLE սկանն ակտիվ է</string>
|
||||
<string name="signal_badge_key_irk_cd">Ինքնությունը հաստատված է</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Կոդավորված կապ</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ծիրանագույն</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Պատկերի գույները</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Մոնիտորինգ</string>
|
||||
<string name="settings_category_appearance_label">Արտաքին տեսք</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Չպահպանված փոփոխություններ</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Ընդհանուր</string>
|
||||
<string name="device_settings_microphone_mode_label">Խոսափող</string>
|
||||
<string name="device_settings_microphone_mode_description">Որ AirPod-ն ոգտագործվում ե որպես խոսափոն</string>
|
||||
<string name="device_settings_microphone_mode_description">Որ ականջակալն օգտագործվում է որպես խոսափող</string>
|
||||
<string name="device_settings_microphone_mode_auto">Ավտոմատ</string>
|
||||
<string name="device_settings_microphone_mode_right">Աջ</string>
|
||||
<string name="device_settings_microphone_mode_left">Ձախ</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Քնի հայտնաբերում</string>
|
||||
<string name="reaction_sleep_notification_title">Դադարեցված Քնի հայտնաբերման կողմից</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s-ը հայտնել է, որ դուք քնեցիք, ուստի ձեր երաժշտությունը դադարեցվեց։ Դուք կարող եք անջատել սա սարքի կարգավորումներում։</string>
|
||||
<string name="reaction_charged_channel_label">Լիցքավորման ծանուցում</string>
|
||||
<string name="reaction_charged_notification_title">Լիցքավորումն ավարտված է</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s-ն ամբողջությամբ լիցքավորված է:</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s-ն լիցքավորվել է %2$d%%-ի:</string>
|
||||
<string name="device_settings_rename_label">Փոխել անունը</string>
|
||||
<string name="device_settings_rename_hint">Սառքի անուն</string>
|
||||
<string name="device_settings_rename_confirm">Փոխել անունը</string>
|
||||
@@ -515,16 +529,29 @@
|
||||
<!-- Press Controls -->
|
||||
<string name="press_controls_title">Սեղմումների կառավարում</string>
|
||||
<string name="press_controls_nav_description">Կարգավորել սեղմումների վարքը և կապել սեղմումները գործողություններին</string>
|
||||
<string name="press_controls_description">Կարգավորեք ինչպես են հայտնաբերվում սեղմումները, ընտրեք զանգի կոճակի վարքը և կարգավորեք սեղմումները գործողությունների։ Կարգավորված գործողությունները վերաբերում են միայն այս սարքին և թույլ են տալիս հավելվածին ընդհատել սեղմումը՝ AirPods-ի փոխարեն:</string>
|
||||
<string name="press_controls_single_press">Մեկ սեղմում</string>
|
||||
<string name="press_controls_double_press">Կրկնակի սեղմում</string>
|
||||
<string name="press_controls_triple_press">Եռակի սեղմում</string>
|
||||
<string name="press_controls_long_press">Երկար սեղմում</string>
|
||||
<string name="press_controls_long_press_anc_cycle_info">Երկար սեղմման գործողություն նշանակելը նաև անջատում է Noise Control ռեժիմների (օրինակ՝ Noise Cancellation, Transparency) ցիկլային փոփոխությունը AirPods-ի վրա սեղմելով և պահելով:</string>
|
||||
<string name="press_controls_mappings_section_title">Սեղմումների կապակցություն</string>
|
||||
<string name="press_controls_left">Ձախ</string>
|
||||
<string name="press_controls_right">Աջ</string>
|
||||
<string name="press_action_none">Լռելյայն</string>
|
||||
<string name="press_action_no_action">Գործություն չկա</string>
|
||||
<string name="press_action_play_pause">Նվագարկել/դադարել</string>
|
||||
<string name="press_action_next_track">Հաջորդ երգ</string>
|
||||
<string name="press_action_previous_track">Նախորդ երգ</string>
|
||||
<string name="press_action_volume_up">Ձայն բարձրացնել</string>
|
||||
<string name="press_action_volume_down">Ձայն իջեցնել</string>
|
||||
<string name="press_action_mute_toggle">Անձայնացնել երաժշտությունը</string>
|
||||
<string name="press_action_stop">Կանգնել</string>
|
||||
<string name="press_action_fast_forward">Արագ առաջ</string>
|
||||
<string name="press_action_rewind">Հետ փռոձ</string>
|
||||
<string name="press_action_cycle_anc">Աղմուկի կառավարումի շրջան</string>
|
||||
<string name="press_action_toggle_anc_transparency">Փոխանջատել թափանցիկությունը</string>
|
||||
<string name="press_controls_reset_label">Վերականգնել կանխադրվածները</string>
|
||||
<string name="press_controls_reset_confirm_message">Վերականգնե՞լ բոլոր սեղմումների կարգավորումները կանխադրվածների:</string>
|
||||
<string name="device_settings_noise_control_open_press_controls_action">Բացել Սեղմումների կառավարումը</string>
|
||||
</resources>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Jika Anda baru saja melakukan pembelian, mungkin butuh beberapa saat agar Google Play dapat menyinkronkan.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Coba lagi dalam beberapa menit jika pembelian Anda belum muncul.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Pastikan Anda masuk dengan akun Google yang sama yang digunakan untuk pembelian.</string>
|
||||
<string name="settings_monitor_mode_label">Mode pantau</string>
|
||||
<string name="settings_monitor_mode_description">Dalam kondisi apa aplikasi ini memantau data Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notifikasi tambahan</string>
|
||||
<string name="settings_monitor_connected_notification_description">Menampilkan notifikasi tambahan ketika perangkat terhubung. Ini memungkinkan Anda menyembunyikan notifikasi permanen \"Tidak ada perangkat\" dengan menonaktifkan saluran \"Status perangkat\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Pertahankan notifikasi setelah terputus</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Jeda otomatis</string>
|
||||
<string name="settings_autopause_description">Jeda audio saat melepas perangkat dari telinga Anda.</string>
|
||||
<string name="settings_autopplay_label">Putar otomatis</string>
|
||||
<string name="settings_autoplay_description">Mulai pemutaran audio saat perangkat dipakai.</string>
|
||||
<string name="settings_autoplay_description">Lanjutkan musik saat memakai pods lagi, tetapi hanya jika Auto jeda yang menghentikannya. Jeda yang kamu picu sendiri (telepon, batang AirPods, tidur) tetap dijeda.</string>
|
||||
<string name="settings_start_music_on_wear_label">Mulai musik saat dipakai</string>
|
||||
<string name="settings_start_music_on_wear_description">Selalu mencoba memutar musik saat kamu memakai pods, bahkan setelah jeda manual.</string>
|
||||
<string name="settings_eardetection_info_label">Catatan deteksi telinga</string>
|
||||
<string name="settings_eardetection_info_description">Jika deteksi telinga hanya berfungsi untuk satu pod, ini adalah batasan Apple. Hanya \"pod utama\" (digunakan untuk mikrofon) yang terdeteksi. Konfigurasikan di perangkat Apple: Pengaturan → Bluetooth → AirPods → Mikrofon.</string>
|
||||
<string name="settings_fake_data_label">Data palsu</string>
|
||||
<string name="settings_fake_data_description">Tampilkan data palsu, yaitu, simulasikan perangkat yang tidak ada.</string>
|
||||
<string name="settings_debug_label">Pengaturan debug</string>
|
||||
<string name="settings_debug_description">Pengaturan tambahan untuk membantu memecahkan masalah dengan aplikasi.</string>
|
||||
<string name="settings_signal_minimum_label">Kualitas sinyal minimum</string>
|
||||
<string name="settings_signal_minimum_description">Kualitas sinyal minimum yang harus dimiliki perangkat agar dianggap milik Anda.</string>
|
||||
<string name="settings_autoconnect_label">Sambungkan otomatis</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Tampilkan pop-up koneksi</string>
|
||||
<string name="settings_popup_connected_description">Tampilkan pop-up saat perangkat pertama kali terhubung.</string>
|
||||
<string name="settings_popup_info_not_in_app">Popup hanya ditampilkan saat Anda tidak berada di aplikasi.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Popup memerlukan monitor yang sedang berjalan. Mode monitor Anda diatur ke \"Saat aplikasi terbuka\", yang menghentikan monitor saat Anda meninggalkan aplikasi.</string>
|
||||
<string name="general_fix_it_action">Perbaiki</string>
|
||||
<string name="settings_charged_notification_label">Pemberitahuan terisi</string>
|
||||
<string name="settings_charged_notification_description">Tampilkan pemberitahuan setelah semua yang sedang diisi daya mencapai level pengisian target.</string>
|
||||
<string name="settings_charged_threshold_label">Level pengisian target</string>
|
||||
<string name="settings_charged_scope_label">Baterai yang dipantau</string>
|
||||
<string name="settings_charged_scope_pods_label">Pod saja</string>
|
||||
<string name="settings_charged_scope_case_label">Casing saja</string>
|
||||
<string name="settings_charged_scope_both_label">Pod dan casing</string>
|
||||
<string name="device_settings_experimental_title">Fitur eksperimental</string>
|
||||
<string name="device_settings_experimental_description">Fitur ini belum diuji di semua perangkat. Jika ada yang tidak berfungsi dengan benar, silakan buka laporan masalah beserta log debug.</string>
|
||||
<string name="device_settings_experimental_action">Buka Pelacak Masalah</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Debug</string>
|
||||
<string name="settings_general_label">Pengaturan</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Mode debug</string>
|
||||
<string name="settings_debug_mode_description">Tampilkan informasi tambahan untuk memecahkan masalah.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Data BLE yang tidak difilter</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Hapus semua filter dari pemindai BLE untuk menampilkan semua data BLE yang disiarkan. Berguna untuk menambahkan dukungan untuk jenis headphone baru.</string>
|
||||
<string name="settings_compatibility_mode_label">Mode kompatibilitas</string>
|
||||
<string name="settings_compatibility_mode_description">Nonaktifkan pengoptimalan untuk meningkatkan kompatibilitas. Coba ini jika Anda tidak melihat data apa pun.</string>
|
||||
<string name="help_translate_label">Terjemahan</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Widget ini memerlukan koneksi langsung (AAP) ke perangkat Anda. Pastikan perangkat Anda sudah dipasangkan dan terhubung di aplikasi sebelum menggunakan widget ini.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Perangkat ini tidak mendukung kontrol kebisingan</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Tidak terhubung</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Di dekat tetapi tidak terhubung</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Tidak di dekat</string>
|
||||
<string name="anc_widget_aap_connecting_label">Menghubungkan…</string>
|
||||
<string name="tile_anc_label">Kontrol Kebisingan</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Izin diperlukan</string>
|
||||
<string name="tile_anc_subtitle_no_device">Tidak ada perangkat</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Tidak ada kontrol kebisingan</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth mati</string>
|
||||
<string name="widget_config_screen_title">Konfigurasi Widget</string>
|
||||
<string name="widget_configuration_title">Pilih Perangkat</string>
|
||||
<string name="widget_configuration_description">Pilih profil perangkat mana yang akan ditampilkan widget ini.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth dinonaktifkan, aktifkan ;)</string>
|
||||
<string name="overview_monitoring_active_label">Memantau perangkat</string>
|
||||
<string name="overview_monitoring_active_description">Pastikan perangkat Anda berada di dekat dan aktif.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Terhubung, tetapi tidak ada data</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Perangkat terhubung, tetapi CAPod tidak menerima data langsung darinya. Ponsel mungkin memerlukan opsi kompatibilitas — pemecah masalah dapat mencoba menemukannya secara otomatis.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Jalankan pemecah masalah</string>
|
||||
<string name="overview_unmatched_devices_label">Perangkat tidak cocok</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="other">%d perangkat tanpa profil yang cocok</item>
|
||||
@@ -218,9 +227,6 @@
|
||||
<string name="permission_required_title">Izin berikut diperlukan:</string>
|
||||
<string name="permission_system_alert_window_label">Jendela Peringatan Sistem</string>
|
||||
<string name="permission_system_alert_window_description">Izinkan CAPod menggambar di atas aplikasi lain untuk memungkinkan fitur \"Tampilkan PopUp\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">Saat aplikasi terbuka</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Saat perangkat terhubung</string>
|
||||
<string name="settings_monitor_mode_always_label">Selalu</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Saat terlihat</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Casing terbuka</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Di telinga</string>
|
||||
@@ -252,6 +258,11 @@
|
||||
<string name="anc_mode_transparency">Transparansi</string>
|
||||
<string name="anc_mode_adaptive">Adaptif</string>
|
||||
<string name="conversation_awareness_label">Kesadaran Percakapan</string>
|
||||
<string name="settings_conversation_action_label">Saat kamu mulai berbicara</string>
|
||||
<string name="settings_conversation_action_nothing_label">Tidak melakukan apa-apa</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Kurangi volume</string>
|
||||
<string name="settings_conversation_action_pause_label">Jeda media</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Pengurangan volume</string>
|
||||
<string name="signal_badge_ble_cd">Pemindaian BLE aktif</string>
|
||||
<string name="signal_badge_key_irk_cd">Identitas terverifikasi</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Koneksi terenkripsi</string>
|
||||
@@ -320,7 +331,6 @@
|
||||
<string name="ui_theme_color_amber_label">Kuning keemasan</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Menggunakan warna wallpaper</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Pemantauan</string>
|
||||
<string name="settings_category_appearance_label">Tampilan</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Perubahan Belum Disimpan</string>
|
||||
@@ -465,7 +475,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Umum</string>
|
||||
<string name="device_settings_microphone_mode_label">Mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">AirPod mana yang digunakan sebagai mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_description">Earbud mana yang digunakan sebagai mikrofon</string>
|
||||
<string name="device_settings_microphone_mode_auto">Otomatis</string>
|
||||
<string name="device_settings_microphone_mode_right">Kanan</string>
|
||||
<string name="device_settings_microphone_mode_left">Kiri</string>
|
||||
@@ -487,6 +497,10 @@
|
||||
<string name="reaction_sleep_channel_label">Deteksi Tidur</string>
|
||||
<string name="reaction_sleep_notification_title">Dijeda oleh Deteksi Tidur</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s mendeteksi Anda tertidur, sehingga musik Anda dijeda. Anda dapat menonaktifkan ini di pengaturan perangkat.</string>
|
||||
<string name="reaction_charged_channel_label">Pemberitahuan Terisi</string>
|
||||
<string name="reaction_charged_notification_title">Pengisian selesai</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s sudah terisi penuh.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s telah diisi daya hingga %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Ganti Nama</string>
|
||||
<string name="device_settings_rename_hint">Nama perangkat</string>
|
||||
<string name="device_settings_rename_confirm">Ganti Nama</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Ef þú keyptir nýlega gæti það tekið smá tíma fyrir Google Play að samstilla.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Reyndu aftur eftir nokkrar mínútur ef kaupin þín birtast ekki.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Gakktu úr skugga um að þú sért skráður inn með sama Google-reikninginn sem notaður var við kaupin.</string>
|
||||
<string name="settings_monitor_mode_label">Vöktunarstilling</string>
|
||||
<string name="settings_monitor_mode_description">Undir hvaða kringumstæðum þetta forrit fylgist með Bluetooth gögnum.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Auka tilkynning</string>
|
||||
<string name="settings_monitor_connected_notification_description">Sýnir auka tilkynningu þegar tæki er tengt. Þetta gerir þér kleift að fela varanlega \"Engin tæki\" tilkynninguna með því að slökkva á \"Staða tækis\" rásinni.</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Halda tilkynningu eftir aftengi</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Sjálfvirk hlé</string>
|
||||
<string name="settings_autopause_description">Gera hlé á hljóði þegar tækið er tekið úr eyranu.</string>
|
||||
<string name="settings_autopplay_label">Sjálfvirk spilun</string>
|
||||
<string name="settings_autoplay_description">Hefja hljóðspilun þegar tækið er notað.</string>
|
||||
<string name="settings_autoplay_description">Halda áfram tónlist þegar þú setur hlustunartækið á aftur, en aðeins ef Sjálfvirk hlé stöðvaði hana. Hlé sem þú settir sjálfur/sjálf (sími, AirPods stingill, svefn) haldast í bið.</string>
|
||||
<string name="settings_start_music_on_wear_label">Byrja tónlist þegar þú setur á</string>
|
||||
<string name="settings_start_music_on_wear_description">Reynir alltaf að spila tónlist þegar þú setur hlustunartækið á, jafnvel eftir handvirkt hlé.</string>
|
||||
<string name="settings_eardetection_info_label">Athugasemd um eyraskynjun</string>
|
||||
<string name="settings_eardetection_info_description">Ef eyraskynjun virkar aðeins fyrir annað heyrnartólið er þetta takmörkun frá Apple. Aðeins \"aðalheyrnartólið\" (notað fyrir hljóðnema) er skynjað. Stilla á Apple tækjum: Stillingar → Bluetooth → AirPods → Hljóðnemi.</string>
|
||||
<string name="settings_fake_data_label">Fölsuð gögn</string>
|
||||
<string name="settings_fake_data_description">Sýna fölsuð gögn, þ.e.a.s. herma eftir tækjum sem eru ekki til.</string>
|
||||
<string name="settings_debug_label">Villuleitarstillingar</string>
|
||||
<string name="settings_debug_description">Viðbótarstillingar til að hjálpa til við að leysa vandamál með forritið.</string>
|
||||
<string name="settings_signal_minimum_label">Lágmarksgæði merkis</string>
|
||||
<string name="settings_signal_minimum_description">Lágmarksgæði merkis sem tæki þarf að hafa til að teljast þitt.</string>
|
||||
<string name="settings_autoconnect_label">Sjálfvirk tenging</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Sýna upplýsingaglugga tengingar</string>
|
||||
<string name="settings_popup_connected_description">Sýna upplýsingaglugga þegar tækið tengist í fyrsta skipti.</string>
|
||||
<string name="settings_popup_info_not_in_app">Sprettigluggar eru aðeins sýndir þegar þú ert ekki í forritinu.</string>
|
||||
<string name="settings_popup_warning_manual_mode">Sprettigluggar krefjast þess að vaktarinn sé í gangi. Vaktagreiningin þín er stillt á \"Þegar forrit er opið\", sem stöðvar vaktarann þegar þú yfirgefur forritið.</string>
|
||||
<string name="general_fix_it_action">Laga það</string>
|
||||
<string name="settings_charged_notification_label">Hlaðin tilkynning</string>
|
||||
<string name="settings_charged_notification_description">Sýna tilkynningu þegar allt sem er að hlaðast nær markhlutfall hleðslu.</string>
|
||||
<string name="settings_charged_threshold_label">Markhlutfall hleðslu</string>
|
||||
<string name="settings_charged_scope_label">Fylgst með rafhlöðum</string>
|
||||
<string name="settings_charged_scope_pods_label">Aðeins heyrnartæki</string>
|
||||
<string name="settings_charged_scope_case_label">Aðeins hulstur</string>
|
||||
<string name="settings_charged_scope_both_label">Heyrnartæki og hulstur</string>
|
||||
<string name="device_settings_experimental_title">Tilraunaeiginleiki</string>
|
||||
<string name="device_settings_experimental_description">Þessi eiginleiki hefur ekki verið prófaður á öllum tækjum enn. Ef eitthvað virkar ekki rétt skaltu opna málskrá með kembikladda.</string>
|
||||
<string name="device_settings_experimental_action">Opna málaskráreftirlit</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Kembing</string>
|
||||
<string name="settings_general_label">Stillingar</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Villuleitarstilling</string>
|
||||
<string name="settings_debug_mode_description">Sýna viðbótarupplýsingar til að leysa vandamál.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Ósíað BLE gögn</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Fjarlægðu allar síur úr BLE skannanum til að sýna öll send BLE gögn. Gagnlegt til að bæta við stuðningi fyrir nýjar gerðir heyrnartóla.</string>
|
||||
<string name="settings_compatibility_mode_label">Samhæfisstilling</string>
|
||||
<string name="settings_compatibility_mode_description">Slökkva á hagræðingum til að bæta samhæfni. Prófaðu þetta ef þú sérð engin gögn.</string>
|
||||
<string name="help_translate_label">Þýðing</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Þetta græja krefst beinnar tengingar (AAP) við tækið þitt. Gakktu úr skugga um að tækið þitt sé parað og tengt í forritinu áður en þú notar þetta græja.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Þetta tæki styður ekki hljóðstýringu</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Ekki tengt</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Í nánd en ekki tengt</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Ekki í nánd</string>
|
||||
<string name="anc_widget_aap_connecting_label">Tengist…</string>
|
||||
<string name="tile_anc_label">Hljóðstýring</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Heimild nauðsynleg</string>
|
||||
<string name="tile_anc_subtitle_no_device">Ekkert tæki</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Engin hljóðstýring</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth slökkt</string>
|
||||
<string name="widget_config_screen_title">Skilgreining viðbætar</string>
|
||||
<string name="widget_configuration_title">Velja tæki</string>
|
||||
<string name="widget_configuration_description">Veldu hvaða tækjasnið þessi græja á að sýna.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Bluetooth er óvirkt, virkjaðu það ;)</string>
|
||||
<string name="overview_monitoring_active_label">Leitar að tækjum</string>
|
||||
<string name="overview_monitoring_active_description">Gakktu úr skugga um að tækið þitt sé nálægt og virkt.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Tengt, en engar gögn</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Tækið þitt er tengt, en CAPod er ekki að taka við lifandi gögnum frá því. Síminn þinn þarf kannski samhæfnismöguleika — villuleitin getur reynt að finna hann sjálfkrafa.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Keyra villuleit</string>
|
||||
<string name="overview_unmatched_devices_label">Ópöruð tæki</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d tæki án samsvarandi sniðs</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">Eftirfarandi heimild er nauðsynleg:</string>
|
||||
<string name="permission_system_alert_window_label">Kerfisviðvörunargluggi</string>
|
||||
<string name="permission_system_alert_window_description">Leyfðu CAPod að teikna yfir önnur forrit til að gera eiginleikann \"Sýna upplýsingaglugga\" mögulegan.</string>
|
||||
<string name="settings_monitor_mode_manual_label">Þegar forrit er opið</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Þegar tæki er tengt</string>
|
||||
<string name="settings_monitor_mode_always_label">Alltaf</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Þegar séð</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Hulstur opið</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Í eyra</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Gegnsæi</string>
|
||||
<string name="anc_mode_adaptive">Aðlægt</string>
|
||||
<string name="conversation_awareness_label">Samtalsvitund</string>
|
||||
<string name="settings_conversation_action_label">Þegar þú byrjar að tala</string>
|
||||
<string name="settings_conversation_action_nothing_label">Gera ekki neitt</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Lækka hljóðstyrk</string>
|
||||
<string name="settings_conversation_action_pause_label">Gera hlé á efni</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Hljóðstyrksmækkun</string>
|
||||
<string name="signal_badge_ble_cd">BLE skönnun virk</string>
|
||||
<string name="signal_badge_key_irk_cd">Auðkenni staðfest</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Dulkóðuð tenging</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Gulrauður</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Notar liti bakgrunnsmyndar</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Eftirlit</string>
|
||||
<string name="settings_category_appearance_label">Utsýni</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Óvistaðar breytingar</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Álmennt</string>
|
||||
<string name="device_settings_microphone_mode_label">Hljóðnemi</string>
|
||||
<string name="device_settings_microphone_mode_description">Hvaðan AirPod er notast sem hátalari</string>
|
||||
<string name="device_settings_microphone_mode_description">Hvaða hlustunartæki er notað sem hljóðnemi</string>
|
||||
<string name="device_settings_microphone_mode_auto">Sjálfvirkt</string>
|
||||
<string name="device_settings_microphone_mode_right">Hægri</string>
|
||||
<string name="device_settings_microphone_mode_left">Vinstri</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Svefngreining</string>
|
||||
<string name="reaction_sleep_notification_title">Gert hlé af svefngreiningu</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s greindi að þú sefur, þannig var tónlistin sett í hlé. Þú getur slökkt á þessu í tækjastillingum.</string>
|
||||
<string name="reaction_charged_channel_label">Hlaðin tilkynning</string>
|
||||
<string name="reaction_charged_notification_title">Hleðslu lokið</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s er fullhlaðið.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s hefur verið hlaðið í %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Endurskíra</string>
|
||||
<string name="device_settings_rename_hint">Nafn tækis</string>
|
||||
<string name="device_settings_rename_confirm">Endurskíra</string>
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
<string name="upgrade_screen_restore_troubleshooting_msg">Se hai acquistato di recente, potrebbe volerci un momento affinché Google Play si sincronizzi.</string>
|
||||
<string name="upgrade_screen_restore_sync_patience_hint">Riprova tra qualche minuto se il tuo acquisto non appare.</string>
|
||||
<string name="upgrade_screen_restore_multiaccount_hint">Assicurati di essere connesso con lo stesso account Google utilizzato per l\'acquisto.</string>
|
||||
<string name="settings_monitor_mode_label">Modalità di monitoraggio</string>
|
||||
<string name="settings_monitor_mode_description">In quali circostanze questa applicazione monitora i dati Bluetooth.</string>
|
||||
<string name="settings_monitor_connected_notification_label">Notifica aggiuntiva</string>
|
||||
<string name="settings_monitor_connected_notification_description">Mostra una notifica aggiuntiva quando un dispositivo è connesso. Ciò consente di nascondere la notifica permanente \"Nessun dispositivo\" disabilitando il canale \"Stato del dispositivo\".</string>
|
||||
<string name="settings_keep_notification_after_disconnect_label">Mantieni notifica dopo la disconnessione</string>
|
||||
@@ -49,13 +47,11 @@
|
||||
<string name="settings_autopause_label">Pausa automatica</string>
|
||||
<string name="settings_autopause_description">Metti in pausa l\'audio quando rimuovi il dispositivo dall\'orecchio.</string>
|
||||
<string name="settings_autopplay_label">Riproduzione automatica</string>
|
||||
<string name="settings_autoplay_description">Avvia la riproduzione audio quando il dispositivo è indossato.</string>
|
||||
<string name="settings_autoplay_description">Riprendi la musica quando indossi di nuovo i pod, ma solo se l\'ha fermata la pausa automatica. Le pause che hai attivato tu stesso (telefono, stelo AirPods, sleep) rimangono in pausa.</string>
|
||||
<string name="settings_start_music_on_wear_label">Avvia musica all\'indossaggio</string>
|
||||
<string name="settings_start_music_on_wear_description">Cerca sempre di riprodurre musica quando indossi i pod, anche dopo una pausa manuale.</string>
|
||||
<string name="settings_eardetection_info_label">Nota sul rilevamento dell\'orecchio</string>
|
||||
<string name="settings_eardetection_info_description">Se il rilevamento dell\'orecchio funziona solo per un auricolare, è una limitazione di Apple. Solo il \"pod principale\" (usato per il microfono) viene rilevato. Configura su dispositivi Apple: Impostazioni → Bluetooth → AirPods → Microfono.</string>
|
||||
<string name="settings_fake_data_label">Dati falsi</string>
|
||||
<string name="settings_fake_data_description">Mostra dati falsi, ad es. simula dispositivi inesistenti.</string>
|
||||
<string name="settings_debug_label">Impostazioni di debug</string>
|
||||
<string name="settings_debug_description">Impostazioni aggiuntive per aiutare a risolvere i problemi con l\'app.</string>
|
||||
<string name="settings_signal_minimum_label">Qualità minima del segnale</string>
|
||||
<string name="settings_signal_minimum_description">La qualità minima del segnale che un dispositivo deve avere per essere considerato tuo.</string>
|
||||
<string name="settings_autoconnect_label">Connessione automatica</string>
|
||||
@@ -80,8 +76,13 @@
|
||||
<string name="settings_popup_connected_label">Mostra pop-up connessione</string>
|
||||
<string name="settings_popup_connected_description">Mostra un pop-up quando il dispositivo si connette per la prima volta.</string>
|
||||
<string name="settings_popup_info_not_in_app">I popup vengono mostrati solo quando non sei nell\'app.</string>
|
||||
<string name="settings_popup_warning_manual_mode">I popup richiedono che il monitor sia in esecuzione. La modalità monitor è impostata su \"Quando l\'app è aperta\", che interrompe il monitor quando esci dall\'app.</string>
|
||||
<string name="general_fix_it_action">Correggi</string>
|
||||
<string name="settings_charged_notification_label">Notifica di ricarica completata</string>
|
||||
<string name="settings_charged_notification_description">Mostra una notifica quando tutto ciò che è in carica raggiunge il livello di carica target.</string>
|
||||
<string name="settings_charged_threshold_label">Livello di carica target</string>
|
||||
<string name="settings_charged_scope_label">Batterie monitorate</string>
|
||||
<string name="settings_charged_scope_pods_label">Solo auricolari</string>
|
||||
<string name="settings_charged_scope_case_label">Solo custodia</string>
|
||||
<string name="settings_charged_scope_both_label">Auricolari e custodia</string>
|
||||
<string name="device_settings_experimental_title">Funzione sperimentale</string>
|
||||
<string name="device_settings_experimental_description">Questa funzione non è stata ancora testata su tutti i dispositivi. Se qualcosa non funziona correttamente, apri una segnalazione con un log di debug.</string>
|
||||
<string name="device_settings_experimental_action">Apri il tracker dei problemi</string>
|
||||
@@ -119,13 +120,11 @@
|
||||
<string name="settings_category_debug_label">Debug</string>
|
||||
<string name="settings_general_label">Impostazioni</string>
|
||||
<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="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>
|
||||
<string name="settings_debug_mode_label">Modalità debug</string>
|
||||
<string name="settings_debug_mode_description">Mostra informazioni aggiuntive per risolvere i problemi.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Dati BLE non filtrati</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Rimuovi eventuali filtri dallo scanner BLE per mostrare tutti i dati BLE trasmessi. Utile per aggiungere il supporto per nuovi tipi di cuffie.</string>
|
||||
<string name="settings_compatibility_mode_label">Modalità compatibilità</string>
|
||||
<string name="settings_compatibility_mode_description">Disabilita le ottimizzazioni per migliorare la compatibilità. Prova questa opzione se non vedi alcun dato.</string>
|
||||
<string name="help_translate_label">Traduzione</string>
|
||||
@@ -137,7 +136,14 @@
|
||||
<string name="anc_widget_config_aap_required_hint">Questo widget richiede una connessione diretta (AAP) al tuo dispositivo. Assicurati che il dispositivo sia associato e connesso nell\'app prima di utilizzare questo widget.</string>
|
||||
<string name="anc_widget_no_anc_support_label">Questo dispositivo non supporta il controllo del rumore</string>
|
||||
<string name="anc_widget_aap_not_connected_label">Non connesso</string>
|
||||
<string name="anc_widget_aap_not_connected_nearby_description">Nelle vicinanze ma non connesso</string>
|
||||
<string name="anc_widget_aap_not_connected_not_nearby_description">Non nelle vicinanze</string>
|
||||
<string name="anc_widget_aap_connecting_label">Connessione in corso…</string>
|
||||
<string name="tile_anc_label">Controllo rumore</string>
|
||||
<string name="tile_anc_subtitle_permission_required">Permesso richiesto</string>
|
||||
<string name="tile_anc_subtitle_no_device">Nessun dispositivo</string>
|
||||
<string name="tile_anc_subtitle_no_anc_support">Nessun controllo rumore</string>
|
||||
<string name="tile_anc_subtitle_bluetooth_off">Bluetooth disattivato</string>
|
||||
<string name="widget_config_screen_title">Configurazione widget</string>
|
||||
<string name="widget_configuration_title">Seleziona dispositivo</string>
|
||||
<string name="widget_configuration_description">Scegli quale profilo dispositivo questo widget dovrebbe visualizzare.</string>
|
||||
@@ -196,6 +202,9 @@
|
||||
<string name="overview_bluetooth_disabled_description">Il Bluetooth è disabilitato, abilitalo ;)</string>
|
||||
<string name="overview_monitoring_active_label">Monitoraggio dispositivi in corso</string>
|
||||
<string name="overview_monitoring_active_description">Assicurati che il tuo dispositivo sia vicino e attivo.</string>
|
||||
<string name="overview_troubleshoot_suggestion_label">Connesso, ma nessun dato</string>
|
||||
<string name="overview_troubleshoot_suggestion_description">Il tuo dispositivo è connesso, ma CAPod non riceve dati live da esso. Il tuo telefono potrebbe aver bisogno di un\'opzione di compatibilità — lo strumento di risoluzione problemi può provare a trovarne una automaticamente.</string>
|
||||
<string name="overview_troubleshoot_suggestion_action">Avvia risoluzione problemi</string>
|
||||
<string name="overview_unmatched_devices_label">Dispositivi non corrispondenti</string>
|
||||
<plurals name="overview_unmatched_devices_count">
|
||||
<item quantity="one">%d dispositivo senza profilo corrispondente</item>
|
||||
@@ -220,9 +229,6 @@
|
||||
<string name="permission_required_title">È richiesto il seguente permesso:</string>
|
||||
<string name="permission_system_alert_window_label">Finestra di avviso di sistema</string>
|
||||
<string name="permission_system_alert_window_description">Consenti a CAPod di disegnare sopra altre app per rendere possibile la funzionalità \"Mostra pop-up\".</string>
|
||||
<string name="settings_monitor_mode_manual_label">Quando l\'app è aperta</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Quando il dispositivo è connesso</string>
|
||||
<string name="settings_monitor_mode_always_label">Sempre</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Quando rilevato</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Custodia aperta</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">Nell\'orecchio</string>
|
||||
@@ -254,6 +260,11 @@
|
||||
<string name="anc_mode_transparency">Trasparenza</string>
|
||||
<string name="anc_mode_adaptive">Adattiva</string>
|
||||
<string name="conversation_awareness_label">Conversation Awareness</string>
|
||||
<string name="settings_conversation_action_label">Quando inizi a parlare</string>
|
||||
<string name="settings_conversation_action_nothing_label">Non fare nulla</string>
|
||||
<string name="settings_conversation_action_lower_volume_label">Abbassa il volume</string>
|
||||
<string name="settings_conversation_action_pause_label">Metti in pausa i media</string>
|
||||
<string name="settings_conversation_volume_reduction_label">Riduzione del volume</string>
|
||||
<string name="signal_badge_ble_cd">Scansione BLE attiva</string>
|
||||
<string name="signal_badge_key_irk_cd">Identità verificata</string>
|
||||
<string name="signal_badge_key_encrypted_cd">Connessione crittografata</string>
|
||||
@@ -322,7 +333,6 @@
|
||||
<string name="ui_theme_color_amber_label">Ambra</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Utilizza i colori dello sfondo</string>
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_monitoring_label">Monitoraggio</string>
|
||||
<string name="settings_category_appearance_label">Aspetto</string>
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Modifiche non salvate</string>
|
||||
@@ -470,7 +480,7 @@
|
||||
<!-- New device settings -->
|
||||
<string name="device_settings_category_general_label">Generale</string>
|
||||
<string name="device_settings_microphone_mode_label">Microfono</string>
|
||||
<string name="device_settings_microphone_mode_description">Quale AirPod viene usato come microfono</string>
|
||||
<string name="device_settings_microphone_mode_description">Quale auricolare viene usato come microfono</string>
|
||||
<string name="device_settings_microphone_mode_auto">Auto</string>
|
||||
<string name="device_settings_microphone_mode_right">Destra</string>
|
||||
<string name="device_settings_microphone_mode_left">Sinistra</string>
|
||||
@@ -492,6 +502,10 @@
|
||||
<string name="reaction_sleep_channel_label">Rilevamento del sonno</string>
|
||||
<string name="reaction_sleep_notification_title">In pausa per Rilevamento del sonno</string>
|
||||
<string name="reaction_sleep_notification_text">%1$s ha rilevato che ti sei addormentato/a, quindi la musica è stata messa in pausa. Puoi disabilitarlo nelle impostazioni del dispositivo.</string>
|
||||
<string name="reaction_charged_channel_label">Notifica di ricarica</string>
|
||||
<string name="reaction_charged_notification_title">Ricarica completata</string>
|
||||
<string name="reaction_charged_notification_text_full">%1$s è completamente carico.</string>
|
||||
<string name="reaction_charged_notification_text_partial">%1$s è stato caricato al %2$d%%.</string>
|
||||
<string name="device_settings_rename_label">Rinomina</string>
|
||||
<string name="device_settings_rename_hint">Nome dispositivo</string>
|
||||
<string name="device_settings_rename_confirm">Rinomina</string>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user