From 3991f35a1b4984934faad61b8b3f46b89312d856 Mon Sep 17 00:00:00 2001 From: SAGIRIxr Date: Tue, 20 Oct 2026 16:53:21 +0800 Subject: [PATCH 1/3] android: don't clip the EQ band labels in transparency settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The slider took fillMaxWidth(0.9f) of whatever the value label left over, so the trailing band label only got the remainder and was cut off in scripts wider than Latin (e.g. 频段 1). Let the slider take the remaining space with weight(1f) instead. --- .../presentation/screens/apple/TransparencySettingsScreen.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/TransparencySettingsScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/TransparencySettingsScreen.kt index 3296ae6ea..70752c80e 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/TransparencySettingsScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/TransparencySettingsScreen.kt @@ -295,8 +295,11 @@ fun TransparencySettingsScreen( eq.value = newEQ }, valueRange = 0f..100f, + // weight, not fillMaxWidth(0.9f): the band label after the slider + // only got the leftover width and was clipped in wider scripts modifier = Modifier - .fillMaxWidth(0.9f) + .weight(1f) + .padding(horizontal = 8.dp) .height(36.dp), colors = SliderDefaults.colors( thumbColor = thumbColor, From 77af067ccdc2ae747964a0316c9ac519a61cbe46 Mon Sep 17 00:00:00 2001 From: SAGIRIxr Date: Tue, 20 Oct 2026 16:53:21 +0800 Subject: [PATCH 2/3] android(i18n): complete zh-rCN/zh-rTW and route hard-coded UI text through resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fills the remaining zh-rCN (60) and zh-rTW (162) strings, fixes the ear_detection typo (人耳 -> 入耳), and marks the font family names and the GMS font cert arrays translatable=false. Moves user-visible literals in Kotlin onto string resources: the QS tile and notification listening-mode labels, the widget configuration screen, head tracking labels and gesture feedback, hearing aid confirmation, recording button, Grant all, and the contentDescriptions read by TalkBack. The ongoing notification's one-letter battery labels now come from resources as well; English output is unchanged. --- ...NoiseControlWidgetConfigurationActivity.kt | 8 +- .../components/apple/BatteryView.kt | 2 +- .../components/apple/PressAndHoldSettings.kt | 4 +- .../primitives/StyledConfirmationDialog.kt | 6 +- .../components/primitives/StyledInputField.kt | 4 +- .../components/primitives/StyledScaffold.kt | 4 +- .../presentation/screens/DeviceListScreen.kt | 4 +- .../screens/apple/HeadTrackingScreen.kt | 21 +- .../screens/apple/HearingAidScreen.kt | 8 +- .../screens/apple/HeartRateScreen.kt | 8 +- .../screens/apple/RecordingScreen.kt | 4 +- .../screens/onboarding/OnboardingScreen.kt | 2 +- .../screens/onboarding/PermissionsPage.kt | 16 +- .../screens/settings/AppSettingsScreen.kt | 4 +- .../screens/settings/IslandSettingsScreen.kt | 2 +- .../utils/BatteryTextGenerator.kt | 4 +- .../librepods/services/AirPodsQSService.kt | 12 +- .../librepods/services/LibrePodsService.kt | 17 +- .../src/main/res/values-zh-rCN/strings.xml | 101 ++++++++- .../src/main/res/values-zh-rTW/strings.xml | 201 ++++++++++++++++++ android/app/src/main/res/values/arrays.xml | 4 +- android/app/src/main/res/values/strings.xml | 36 +++- 22 files changed, 411 insertions(+), 61 deletions(-) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/activities/NoiseControlWidgetConfigurationActivity.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/activities/NoiseControlWidgetConfigurationActivity.kt index f7448ad9d..2d6c1360d 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/activities/NoiseControlWidgetConfigurationActivity.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/activities/NoiseControlWidgetConfigurationActivity.kt @@ -143,7 +143,7 @@ class NoiseControlWidgetConfigurationActivity: ComponentActivity() { ) } ?: run { Text( - text = "Service not available. Please ensure LibrePods is running and try again.", + text = stringResource(R.string.widget_service_not_available), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurface ) @@ -175,7 +175,7 @@ private fun WidgetDevicePickerContent( Spacer(modifier = Modifier.size(topPadding)) if (devices.isEmpty()) { Text( - text = "No devices found. Please ensure a compatible device is paired with your phone and try again.", + text = stringResource(R.string.widget_no_devices_found), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurface ) @@ -184,7 +184,7 @@ private fun WidgetDevicePickerContent( onClick = onCancelClicked ) { Text( - text = "Cancel", + text = stringResource(R.string.cancel), style = MaterialTheme.typography.bodyMedium, ) } @@ -228,7 +228,7 @@ private fun WidgetDevicePickerContent( enabled = pickedDevice != null ) { Text( - text = "Done", + text = stringResource(R.string.done), style = MaterialTheme.typography.bodyMedium, ) } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/BatteryView.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/BatteryView.kt index 61a303f70..9ede1cc10 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/BatteryView.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/BatteryView.kt @@ -77,7 +77,7 @@ fun BatteryView( ) { Image( bitmap = ImageBitmap.imageResource(primaryImageRes), - contentDescription = "Headset", + contentDescription = stringResource(R.string.headset), modifier = Modifier.fillMaxWidth() ) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/PressAndHoldSettings.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/PressAndHoldSettings.kt index 1a78f2e66..06cb0c24d 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/PressAndHoldSettings.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/apple/PressAndHoldSettings.kt @@ -34,13 +34,13 @@ fun PressAndHoldSettings( ) { val leftActionText = when (leftAction) { StemAction.CYCLE_listening_mode_MODES -> stringResource(R.string.listening_mode) - StemAction.DIGITAL_ASSISTANT -> "Digital Assistant" + StemAction.DIGITAL_ASSISTANT -> stringResource(R.string.digital_assistant) else -> "INVALID!!" } val rightActionText = when (rightAction) { StemAction.CYCLE_listening_mode_MODES -> stringResource(R.string.listening_mode) - StemAction.DIGITAL_ASSISTANT -> "Digital Assistant" + StemAction.DIGITAL_ASSISTANT -> stringResource(R.string.digital_assistant) else -> "INVALID!!" } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledConfirmationDialog.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledConfirmationDialog.kt index 954929317..7de386052 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledConfirmationDialog.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledConfirmationDialog.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties @@ -57,6 +58,7 @@ import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.blur import com.kyant.backdrop.effects.lens import com.kyant.backdrop.effects.vibrancy +import me.kavishdevar.librepods.R import me.kavishdevar.librepods.presentation.design.DesignSystem import me.kavishdevar.librepods.presentation.design.LocalDesignSystem @@ -66,8 +68,8 @@ fun StyledConfirmationDialog( showDialog: MutableState, title: String, message: String, - confirmText: String = "Enable", - dismissText: String = "Cancel", + confirmText: String = stringResource(R.string.enable), + dismissText: String = stringResource(R.string.cancel), onConfirm: () -> Unit, onDismiss: () -> Unit = { showDialog.value = false }, backdrop: LayerBackdrop, diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledInputField.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledInputField.kt index 15dcd7c44..fbc0f26c6 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledInputField.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledInputField.kt @@ -35,8 +35,10 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import me.kavishdevar.librepods.R import me.kavishdevar.librepods.presentation.icons.LocalIcons import me.kavishdevar.librepods.presentation.design.DesignSystem import me.kavishdevar.librepods.presentation.design.LocalDesignSystem @@ -147,7 +149,7 @@ fun StyledInputField( ) { Icon( imageVector = LocalIcons.current.CloseCircle, - contentDescription = "Clear text", + contentDescription = stringResource(R.string.clear_text), tint = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f), modifier = Modifier.size(16.dp) ) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledScaffold.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledScaffold.kt index 549f7ebea..8b0a48171 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledScaffold.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/components/primitives/StyledScaffold.kt @@ -72,6 +72,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalWindowInfo +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp @@ -84,6 +85,7 @@ import dev.chrisbanes.haze.HazeTint import dev.chrisbanes.haze.hazeEffect import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.rememberHazeState +import me.kavishdevar.librepods.R import me.kavishdevar.librepods.presentation.design.DesignSystem import me.kavishdevar.librepods.presentation.design.LocalDesignSystem import me.kavishdevar.librepods.presentation.icons.LocalIcons @@ -298,7 +300,7 @@ fun StyledScaffold( ) { Icon( imageVector = LocalIcons.current.ArrowBack, - contentDescription = "Back", + contentDescription = stringResource(R.string.back), modifier = Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onBackground ) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/DeviceListScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/DeviceListScreen.kt index 7ea5db103..9c18af8c6 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/DeviceListScreen.kt @@ -109,7 +109,7 @@ fun DeviceListScreen( ) { Icon( imageVector = Icons.Outlined.Settings, - contentDescription = "settings", + contentDescription = stringResource(R.string.settings), modifier = Modifier.size(IconButtonDefaults.mediumIconSize), ) } @@ -120,7 +120,7 @@ fun DeviceListScreen( ) { Icon( imageVector = LocalIcons.current.Settings, - contentDescription = "Settings", + contentDescription = stringResource(R.string.settings), tint = MaterialTheme.colorScheme.onBackground ) } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeadTrackingScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeadTrackingScreen.kt index 1a559abb4..646caad78 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeadTrackingScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeadTrackingScreen.kt @@ -137,7 +137,7 @@ fun HeadTrackingScreen( ) { Icon( imageVector = if (state.headTrackingState == BuddyState.ACTIVE) MaterialIcons.Pause else Icons.Default.PlayArrow, - contentDescription = "Play/Pause", + contentDescription = stringResource(R.string.play_pause), modifier = Modifier.size(IconButtonDefaults.mediumIconSize), ) } @@ -149,7 +149,7 @@ fun HeadTrackingScreen( ) { Icon( imageVector = if (state.headTrackingState == BuddyState.ACTIVE) LocalIcons.current.Pause else LocalIcons.current.Play, - contentDescription = "Play/Pause", + contentDescription = stringResource(R.string.play_pause), modifier = Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onBackground ) @@ -192,7 +192,7 @@ fun HeadTrackingScreen( } StyledToggle( - label = "Head Gestures", + label = stringResource(R.string.head_gestures), checked = settings.headGesturesEnabled, onCheckedChange = { viewModel.setHeadGesturesEnabled(it) }, enabled = uiState.isPremium || settings.headGesturesEnabled, @@ -202,7 +202,7 @@ fun HeadTrackingScreen( Spacer(modifier = Modifier.height(16.dp)) Text( - "Velocity", + stringResource(R.string.velocity), style = MaterialTheme.typography.labelSmallEmphasized, modifier = Modifier.padding(vertical = 8.dp) ) @@ -222,11 +222,13 @@ fun HeadTrackingScreen( } } val gestureTextValue = stringResource(R.string.shake_your_head_or_nod) + val yesDetectedText = stringResource(R.string.head_gesture_yes_detected) + val noDetectedText = stringResource(R.string.head_gesture_no_detected) StyledButton( onClick = { gestureText = gestureTextValue coroutineScope.launch { - viewModel.detectHeadGestures { gestureText = if (it) "\"Yes\" gesture detected." else "\"No\" gesture detected." } + viewModel.detectHeadGestures { gestureText = if (it) yesDetectedText else noDetectedText } } }, backdrop = backdrop, @@ -236,7 +238,7 @@ fun HeadTrackingScreen( materialButtonStyle = MaterialButtonStyle.Outlined ) { Text( - "Test Head Gestures", + stringResource(R.string.test_head_gestures), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.primary ) @@ -338,6 +340,9 @@ private fun Plot() { .padding(16.dp) ) { val onBackground = MaterialTheme.colorScheme.onBackground + // the Canvas draw scope is not composable, so resolve the legend labels here + val horizontalLabel = stringResource(R.string.horizontal) + val verticalLabel = stringResource(R.string.vertical) Canvas( modifier = Modifier.fillMaxSize() ) { @@ -420,7 +425,7 @@ private fun Plot() { textSize = 12.sp.toPx() textAlign = Paint.Align.LEFT } - drawText("Horizontal", width - 140.dp.toPx(), textOffsetY, paint) + drawText(horizontalLabel, width - 140.dp.toPx(), textOffsetY, paint) } drawCircle(verticalColor, 5.dp.toPx(), Offset(width - 70.dp.toPx(), legendY)) @@ -430,7 +435,7 @@ private fun Plot() { textSize = 12.sp.toPx() textAlign = Paint.Align.LEFT } - drawText("Vertical", width - 60.dp.toPx(), textOffsetY, paint) + drawText(verticalLabel, width - 60.dp.toPx(), textOffsetY, paint) } } } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HearingAidScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HearingAidScreen.kt index 0f3b2c15a..36f72638a 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HearingAidScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HearingAidScreen.kt @@ -190,10 +190,10 @@ fun HearingAidScreen( StyledConfirmationDialog( showDialog = showDialog, - title = "Enable Hearing Aid", - message = "Enabling Hearing Aid will disable Headphone Accommodation and Customized Transparency Mode.", - confirmText = "Enable", - dismissText = "Cancel", + title = stringResource(R.string.enable_hearing_aid), + message = stringResource(R.string.enable_hearing_aid_description), + confirmText = stringResource(R.string.enable), + dismissText = stringResource(R.string.cancel), onConfirm = { showDialog.value = false val enrolled = state.controlStates[ControlCommandIdentifier.HEARING_AID]?.getOrNull(0) == 0x01.toByte() diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeartRateScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeartRateScreen.kt index 86e545628..a13ea0ee4 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeartRateScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/HeartRateScreen.kt @@ -122,7 +122,7 @@ fun HeartRateScreen( ) { Icon( imageVector = it, - contentDescription = "Start/Stop", + contentDescription = stringResource(R.string.start_stop), modifier = Modifier.size(IconButtonDefaults.mediumIconSize), ) } @@ -137,7 +137,7 @@ fun HeartRateScreen( ) { Icon( imageVector = it, - contentDescription = "Start/Stop", + contentDescription = stringResource(R.string.start_stop), modifier = Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onBackground ) @@ -255,7 +255,7 @@ fun HeartRateScreen( ) { Icon( imageVector = LocalIcons.current.VitalSigns, - contentDescription = "vital signs", + contentDescription = stringResource(R.string.heart_rate), modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.onPrimaryContainer ) @@ -285,7 +285,7 @@ fun HeartRateScreen( ) { Icon( imageVector = LocalIcons.current.VitalSigns, - contentDescription = "vital signs", + contentDescription = stringResource(R.string.heart_rate), modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.onSurface ) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/RecordingScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/RecordingScreen.kt index e4fd0fdd7..de0af292b 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/RecordingScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/apple/RecordingScreen.kt @@ -254,8 +254,8 @@ fun RecordingScreen( ) { Text( text = if (state.recordingState.isRecording) { - "Stop Recording" - } else "Start Recording", + stringResource(R.string.stop_recording) + } else stringResource(R.string.start_recording), style = MaterialTheme.typography.labelMedium ) } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/OnboardingScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/OnboardingScreen.kt index f13574f98..c54625b67 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/OnboardingScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/OnboardingScreen.kt @@ -168,7 +168,7 @@ fun OnboardingScreen( ) { Icon( Icons.AutoMirrored.Default.ArrowForward, - contentDescription = "forward", + contentDescription = stringResource(R.string.next), modifier = Modifier.size(IconButtonDefaults.largeIconSize), ) } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/PermissionsPage.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/PermissionsPage.kt index 93a29dc75..6b530ff47 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/PermissionsPage.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/onboarding/PermissionsPage.kt @@ -178,7 +178,7 @@ fun PermissionsPage( ) { Icon( imageVector = MaterialIcons.Bluetooth, - contentDescription = "bluetooth", + contentDescription = stringResource(R.string.bluetooth), modifier = Modifier.size(24.dp), tint = animatedBluetoothIconColor ) @@ -221,7 +221,7 @@ fun PermissionsPage( ) { Icon( imageVector = MaterialIcons.Notifications, - contentDescription = "notifications", + contentDescription = stringResource(R.string.notifications), modifier = Modifier.size(24.dp), tint = animatedNotificationsIconColor ) @@ -251,7 +251,7 @@ fun PermissionsPage( ) { Icon( imageVector = MaterialIcons.Call, - contentDescription = "call", + contentDescription = stringResource(R.string.phone), modifier = Modifier.size(24.dp), tint = animatedPhoneIconColor ) @@ -285,7 +285,7 @@ fun PermissionsPage( ) { Icon( imageVector = MaterialIcons.VitalSigns, - contentDescription = "vital signs", + contentDescription = stringResource(R.string.heart_rate), modifier = Modifier.size(24.dp), tint = animatedHealthConnectIconColor ) @@ -323,7 +323,7 @@ fun PermissionsPage( ) { Icon( imageVector = MaterialIcons.Overlay, - contentDescription = "overlay", + contentDescription = stringResource(R.string.permission_overlay), modifier = Modifier.size(24.dp), tint = animatedOverlayIconColor ) @@ -346,7 +346,7 @@ fun PermissionsPage( ) { Icon( Icons.AutoMirrored.Default.ArrowBack, - contentDescription = "backward", + contentDescription = stringResource(R.string.back), modifier = Modifier.size(IconButtonDefaults.mediumIconSize), ) } @@ -365,7 +365,7 @@ fun PermissionsPage( enabled = !bluetoothPermissionsState.allPermissionsGranted || !notificationPermissionState.status.isGranted || !phonePermissionState.allPermissionsGranted || !canDrawOverlays.value ) { Text( - text = "Grant all", + text = stringResource(R.string.grant_all), style = MaterialTheme.typography.labelMedium ) } @@ -381,7 +381,7 @@ fun PermissionsPage( ) { Icon( Icons.AutoMirrored.Default.ArrowForward, - contentDescription = "forward", + contentDescription = stringResource(R.string.next), modifier = Modifier.size(IconButtonDefaults.mediumIconSize), ) } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppSettingsScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppSettingsScreen.kt index 914f405f1..bd16764e7 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppSettingsScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppSettingsScreen.kt @@ -458,7 +458,7 @@ fun AppSettingsScreen( ) { Icon( imageVector = LocalIcons.current.Close, - contentDescription = "Close", + contentDescription = stringResource(R.string.close), tint = MaterialTheme.colorScheme.onBackground ) } @@ -502,7 +502,7 @@ fun AppSettingsScreen( ) { Icon( imageVector = LocalIcons.current.Send, - contentDescription = "Send", + contentDescription = stringResource(R.string.send), tint = if (subjectState.text.isNotEmpty() && descriptionState.text.isNotEmpty()) Color.White else Color.Gray ) } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/IslandSettingsScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/IslandSettingsScreen.kt index 669a04e8d..9ab123692 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/IslandSettingsScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/IslandSettingsScreen.kt @@ -232,7 +232,7 @@ fun IslandSettingsScreen( verticalArrangement = Arrangement.spacedBy(16.dp) ) { StyledList( - title = "Compact" + title = stringResource(R.string.compact) ) { styledListItem( contentText = stringResource(R.string.size_and_position), diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/utils/BatteryTextGenerator.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/utils/BatteryTextGenerator.kt index 163fe6651..cd6326af6 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/utils/BatteryTextGenerator.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/utils/BatteryTextGenerator.kt @@ -1,6 +1,8 @@ package me.kavishdevar.librepods.presentation.utils import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import me.kavishdevar.librepods.R import me.kavishdevar.librepods.core.devices.AirPodsSpec import me.kavishdevar.librepods.core.devices.Battery import me.kavishdevar.librepods.core.devices.BatteryComponent @@ -93,7 +95,7 @@ fun createAirPodsBatteryRichText( } headsetIconName + statusIcon + " ${headsetLevel}%" } else { - "No battery info available" + stringResource(R.string.no_battery_info) } } ) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/services/AirPodsQSService.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/services/AirPodsQSService.kt index a5777a57c..f3723ebff 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/services/AirPodsQSService.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/services/AirPodsQSService.kt @@ -171,7 +171,7 @@ class AirPodsQSService : TileService() { } else { tile.state = Tile.STATE_UNAVAILABLE tile.label = "AirPods" - tile.subtitle = "Disconnected" + tile.subtitle = getString(R.string.disconnected) tile.icon = Icon.createWithResource(this, R.drawable.ic_airpods) } @@ -210,11 +210,11 @@ class AirPodsQSService : TileService() { private fun getModeLabel(mode: Int): String { return when (mode) { - NoiseControlMode.OFF.ordinal + 1 -> "Off" - NoiseControlMode.TRANSPARENCY.ordinal + 1 -> "Transparency" - NoiseControlMode.ADAPTIVE.ordinal + 1 -> "Adaptive" - NoiseControlMode.NOISE_CANCELLATION.ordinal + 1 -> "Noise Cancellation" - else -> "Unknown" + NoiseControlMode.OFF.ordinal + 1 -> getString(R.string.off) + NoiseControlMode.TRANSPARENCY.ordinal + 1 -> getString(R.string.transparency) + NoiseControlMode.ADAPTIVE.ordinal + 1 -> getString(R.string.adaptive) + NoiseControlMode.NOISE_CANCELLATION.ordinal + 1 -> getString(R.string.noise_cancellation) + else -> getString(R.string.unknown) } } diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/services/LibrePodsService.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/services/LibrePodsService.kt index b0d496eb6..ce8d344b8 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/services/LibrePodsService.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/services/LibrePodsService.kt @@ -459,6 +459,15 @@ class LibrePodsService: Service() { } } + private fun batteryShortLabel(component: BatteryComponent): String = getString( + when (component) { + BatteryComponent.LEFT -> R.string.battery_short_left + BatteryComponent.RIGHT -> R.string.battery_short_right + BatteryComponent.CASE -> R.string.battery_short_case + BatteryComponent.HEADSET -> R.string.battery_short_headset + } + ) + private fun getDeviceFromBleMac(bleMac: MacAddress): Device<*, *, *>? { var deviceMac: MacAddress? = devices.value[bleMac]?.macAddress @@ -1051,14 +1060,14 @@ class LibrePodsService: Service() { NotificationCompat.Builder(this, "device_${device.macAddress}") .setSmallIcon(R.drawable.ic_airpods) .setContentTitle(device.metadata.value.name) - .setContentText(device.state.value.battery.joinToString(" ") { "${it.component.name[0]}: ${it.level}%" }) + .setContentText(device.state.value.battery.joinToString(" ") { "${batteryShortLabel(it.component)}: ${it.level}%" }) .setContentIntent(pendingIntent) .setCategory(Notification.CATEGORY_STATUS) .setPriority(NotificationCompat.PRIORITY_LOW) .setOngoing(true) .addAction( R.drawable.ic_transparency, // icon never shows up?? (tested on AOSP ROMs) - "Transparency", + getString(R.string.transparency), PendingIntent.getService( this, notificationId + 3, @@ -1072,7 +1081,7 @@ class LibrePodsService: Service() { ) .addAction( R.drawable.ic_adaptive, - "Adaptive", + getString(R.string.adaptive), PendingIntent.getService( this, notificationId + 4, @@ -1086,7 +1095,7 @@ class LibrePodsService: Service() { ) .addAction( R.drawable.ic_noise_cancellation, - "Noise Cancellation", + getString(R.string.noise_cancellation), PendingIntent.getService( this, notificationId + 2, diff --git a/android/app/src/main/res/values-zh-rCN/strings.xml b/android/app/src/main/res/values-zh-rCN/strings.xml index 7ee107bd7..c524d38a7 100644 --- a/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/android/app/src/main/res/values-zh-rCN/strings.xml @@ -55,7 +55,7 @@ 开发者 打开 AirPods 设置 管理 AirPods 功能与偏好 - 自动人耳检测 + 自动入耳检测 自动播放 自动暂停 故障排查 @@ -337,10 +337,107 @@ 已接听来电 正在等待… 服务运行中 - 没什么用的通知,点它即可关闭。 + 没什么用的通知,点击即可关闭。 前台服务状态 你的心率已超过设定阈值。(当前:%d bpm) 心率过高 此提醒目前不会考虑运动或体力活动的影响,仅供参考,不构成医疗建议。 提醒已关闭,直到重新连接或重新开启心率监测为止。 + 测试 + + 由于 Android 蓝牙协议栈的限制,许多设备暂不受支持。 + \n在这些设备上,需要 Root 权限和 Xposed 框架才能使用完整功能。 + \n\n较新的 Android 版本已解决这一限制。以下设备配置可以直接运行本应用: + \n• 运行 Android 16 三月更新及更高版本,并已安装最新 Play 系统更新的 Google Pixel® + \n• 运行 Android 17 Beta 3 及更高版本的 Google Pixel® + \n• 运行 OxygenOS 16 或更高版本的一加设备 + \n• 运行 ColorOS 16 或更高版本的 OPPO 设备 + \n\n详情请参阅项目文档。 + + 不以颜色区分 + 字体 + 字体 + 字体缩放 + 字体缩放 + 灵动岛设置 + 相机控制和可交互的灵动岛弹窗 + 大小与位置 + 按住 + 自定义 + 打开 %s + 点按以展开 + 点按以打开 %s + 展开 + 保持显示紧凑式灵动岛 + 灵动岛显示在状态栏时,需要无障碍服务才能与其交互。 + 无障碍服务 + 可用 + 不可用 + 电池样式 + 不显示 + 圆环 + 文字 + 两者 + 隐藏灵动岛 + 显示灵动岛 + 边框宽度 + 圆角半径 + 水平偏移 + 垂直偏移 + 高度 + 宽度 + 聆听模式控制 + 长按以打开 %s + 字号 + 默认样式 + 展开式 + 紧凑式 + 不透明度 + 单色 + 图标 + 图片 + 取消 + 重置应用设置 + 所有应用设置(外观、交互、辅助功能、灵动岛等)都将被重置。 + 重置灵动岛设置 + 通话中可能无法通过 AirPods 上的控制将麦克风静音或取消静音。这是 Android 的已知问题,LibrePods 无法修复。 + 已跳过兼容性检查。你确定你的设备兼容吗? + 你的设备目前不兼容。请更新设备后继续。 + 由于 Android 的限制,需要较新版本的 Android 才能使用 LibrePods。 + \n• Google Pixel 设备需要 Android 16 三月更新或更高版本,并安装最新的 Google Play 系统更新 + \n• 一加和 OPPO 设备需要 Android 16 + \n• 其他所有设备需要 Android 17(OneUI 9、MagicOS 11、Nothing OS 5.0 等) + + 低 + 中 + 高 + 完成 + 启用 + 关闭 + 发送 + 清除文本 + 耳机 + 未连接 + 未知 + 速度 + 测试头部动作 + 水平 + 垂直 + 启用助听 + 启用助听后,耳机调节和自定义通透模式将被停用。 + 开始录音 + 停止录音 + 全部授予 + 暂无电池信息 + 服务不可用。请确认 LibrePods 正在运行,然后重试。 + 未找到设备。请确认已有兼容的设备与手机配对,然后重试。 + 左 + 右 + 盒 + 耳机 + 检测到点头。 + 检测到摇头。 + 播放/暂停 + 开始/停止 + 下一步 diff --git a/android/app/src/main/res/values-zh-rTW/strings.xml b/android/app/src/main/res/values-zh-rTW/strings.xml index 807c7c30e..dfe055ec7 100644 --- a/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/android/app/src/main/res/values-zh-rTW/strings.xml @@ -240,4 +240,205 @@ 你確定你的裝置原生支援或已啟用 Xposed 模組嗎? 不支援 您的應用程式已更新為 Google Play 版本,進階功能將在 %1$d 天後停用。請從您的原始安裝來源更新以繼續使用 FOSS 版本,或等待此期限結束後直接購買升級;如果您已是支持者,請點按此處申請促銷代碼。 + 測試 + 彈出視窗動畫 + 底部彈出視窗 + 連接 AirPods 時,在底部顯示 iOS 風格的彈出視窗。 + 動態島彈出視窗 + 在頂部以動態島樣式顯示連線與接管事件的彈出視窗。 + + 由於 Android 藍牙堆疊的限制,許多裝置目前尚不支援。 + \n在這些裝置上,需要 Root 權限與 Xposed 框架才能使用完整功能。 + \n\n較新的 Android 版本已解決此限制。以下裝置設定可以原生執行本應用程式: + \n• 執行 Android 16 三月更新或更新版本,並已安裝最新 Play 系統更新的 Google Pixel® + \n• 執行 Android 17 Beta 3 或以上版本的 Google Pixel® + \n• 執行 OxygenOS 16 或更新版本的 OnePlus 裝置 + \n• 執行 ColorOS 16 或更新版本的 OPPO 裝置 + \n\n詳情請參閱專案說明文件。 + + (價格由你決定) + + 由於平台限制,此裝置可能不受支援,需要使用 Xposed 框架。請勾選下方的核取方塊並輸入 OK 以繼續。 + + 輸入「%s」以繼續 + 繼續 + 我已閱讀相容性需求。 + 裝置資訊 + 建置 ID + 製造商 + 免費功能 + 進階功能 + 長按啟動語音助理 + 按住 AirPods Pro 耳機柄時啟動語音助理。 + 無法使用自訂功能。請至少連接 AirPods 一次後再使用。 + Xposed 可用 + 已在 Xposed 中啟用應用程式 + 主旨 + 描述你遇到的問題 + 最佳化充電上限 + AirPods 可以學習你的每日使用習慣,決定何時充電至最佳化上限、何時允許充滿電。此上限會依據你的每日使用情況調整,長期下來可延長電池壽命。\n此設定對不支援的 AirPods 或韌體版本較舊的 AirPods 可能無效。 + 請更新你的裝置,或在 Xposed 中啟用 LibrePods 並重新開啟應用程式以繼續。 + 自訂 + 建議 + 外觀 + Material 3 Expressive + LibrePods 現已支援以 Material 3 Expressive 為基礎的全新外觀,包括更新的字體排版與自適應配色。\n你可以在應用程式設定中切換回 Apple 外觀。 + 僅適用於最新的 AirPods Beta 韌體。安裝 Beta 韌體需要一台執行 OS 27 版本的 Apple 裝置。 + 新功能 + 正在重新連線 + 輕觸以重新連線 + 權限 + 隱私權政策 + 我同意 + 錄音 + 錄音檔 + 使用 AirPods 的高品質麥克風錄製音訊。 + 心率 + 跟隨系統 + 淺色 + 深色 + 設計風格 + Apple + Apple + 正在連線 + 裝置 + 設定小工具 + 歡迎使用 + 直接向開發者尋求協助。回覆可能需要一些時間,這個專案只有我一個人在做 :) + 向社群尋求協助。 + 在 GitHub 儲存庫中建立 issue。僅在你要提出功能建議,或確定發現 bug 時使用。開 issue 之前請先向社群詢問。 + 無法開啟 Discord 邀請連結 + 無法開啟 GitHub 連結 + 掃描模式 + BLE 設定 + 除非你清楚自己在做什麼,否則請勿變更這些設定! + 低功耗 + 耗電量最少 + 平衡 + 在掃描頻率與耗電量之間取得良好平衡 + 低延遲 + 掃描頻率最高 + 啟用偵錯模式 + 大於 0 的值會啟用批次回報。 + 回報延遲(毫秒) + 間隔 + 兩次心率量測之間的間隔 + 沒有心率資料 + 1 秒 + 1 分鐘 + 將心率寫入「健康資料同步」 + 允許應用程式將心率資料寫入 Android 的「健康資料同步」平台。 + 必要權限 + 藍牙 + 與 AirPods 通訊所必需。 + 選用權限 + 通知 + 顯示電池狀態、心率過高警示(支援的型號)等。 + 使用頭部手勢接聽或拒接來電。 + 顯示在其他應用程式上層 + 當 AirPods 在附近或音訊切換到 AirPods 時顯示彈出視窗。 + 在任意位置滑動即可返回 + 使用裝置支援的最高更新率 + 顯示額外的資訊與設定。 + 互動 + 心率監測 + 心率超過閾值時傳送通知。會增加耗電量。 + 心率警示 + 警示閾值 + 已拒接來電 + 已接聽來電 + 正在等待… + 服務執行中 + 沒什麼用的通知,點一下即可關閉。 + 前景服務狀態 + 你的心率已超過設定的閾值。(目前:%d bpm) + 心率過高 + 此警示目前不會考慮運動或體能活動的影響,僅供參考,不構成醫療建議。 + 警示已停用,直到重新連線或重新啟用心率監測為止。 + 不以顏色區分 + 字型 + 字型 + 字型縮放 + 字型縮放 + 動態島設定 + 相機控制與可互動的動態島彈出視窗 + 大小與位置 + 按住 + 自訂 + 開啟 %s + 輕觸以展開 + 輕觸以開啟 %s + 展開 + 持續顯示精簡式動態島 + 動態島顯示在狀態列時,需要無障礙服務才能與其互動。 + 無障礙服務 + 可用 + 無法使用 + 電池樣式 + 不顯示 + 圓環 + 文字 + 兩者 + 隱藏動態島 + 顯示動態島 + 邊框寬度 + 圓角半徑 + 水平偏移 + 垂直偏移 + 高度 + 寬度 + 聽覺模式控制 + 長按以開啟 %s + 字型大小 + 預設樣式 + 展開式 + 精簡式 + 不透明度 + 單色 + 圖示 + 圖片 + 取消 + 重設應用程式設定 + 所有應用程式設定(外觀、互動、輔助使用、動態島等)都將重設。 + 重設動態島設定 + 通話中可能無法使用 AirPods 上的控制將麥克風靜音或取消靜音。這是 Android 的已知問題,LibrePods 無法修正。 + 已略過相容性檢查。你確定你的裝置相容嗎? + 你的裝置目前不相容。請更新裝置以繼續。 + 由於 Android 的限制,需要較新版本的 Android 才能使用 LibrePods。 + \n• Google Pixel 裝置需要 Android 16 三月更新或更新版本,並安裝最新的 Google Play 系統更新 + \n• OnePlus 與 OPPO 裝置需要 Android 16 + \n• 其他所有裝置需要 Android 17(OneUI 9、MagicOS 11、Nothing OS 5.0 等) + + 低 + 中 + 高 + 完成 + 啟用 + 關閉 + 傳送 + 清除文字 + 耳機 + 未連線 + 未知 + 速度 + 測試頭部手勢 + 水平 + 垂直 + 啟用助聽器 + 啟用助聽器後,耳機調節和自訂通透模式將會停用。 + 開始錄音 + 停止錄音 + 全部授權 + 沒有可用的電池資訊 + 服務無法使用。請確認 LibrePods 正在執行,然後再試一次。 + 找不到裝置。請確認已有相容的裝置與手機配對,然後再試一次。 + 左 + 右 + 盒 + 耳機 + 偵測到點頭。 + 偵測到搖頭。 + 播放/暫停 + 開始/停止 + 下一步 diff --git a/android/app/src/main/res/values/arrays.xml b/android/app/src/main/res/values/arrays.xml index dc69d9b43..8e4d6472f 100644 --- a/android/app/src/main/res/values/arrays.xml +++ b/android/app/src/main/res/values/arrays.xml @@ -4,12 +4,12 @@ @array/com_google_android_gms_fonts_certs_dev @array/com_google_android_gms_fonts_certs_prod - + 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 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 971b6d6d1..c52fffdae 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -357,9 +357,9 @@ Differentiate without color Font Font - Inter - Roboto Flex (Expressive) - Roboto Flex + Inter + Roboto Flex (Expressive) + Roboto Flex Font scale Font scale Island settings @@ -414,4 +414,34 @@ Low Mid High + Done + Enable + Close + Send + Clear text + Headset + Disconnected + Unknown + Velocity + Test Head Gestures + Horizontal + Vertical + Enable Hearing Aid + Enabling Hearing Aid will disable Headphone Accommodation and Customized Transparency Mode. + Start Recording + Stop Recording + Grant all + No battery info available + Service not available. Please ensure LibrePods is running and try again. + No devices found. Please ensure a compatible device is paired with your phone and try again. + + L + R + C + H + \"Yes\" gesture detected. + \"No\" gesture detected. + Play/Pause + Start/Stop + Next From 6d96cc0888d94e92a3049162f999b92faaf70859 Mon Sep 17 00:00:00 2001 From: SAGIRIxr Date: Tue, 27 Oct 2026 23:06:26 +0800 Subject: [PATCH 3/3] android(i18n): translate the strings added since, extract the glass effect toggles Covers the 46 strings that came in with theming, the proximity card and the connection rules, so zh-rCN and zh-rTW are at 472/472 again. Drops this branch's no_battery_info in favour of the battery_not_available that landed upstream for the same text, marks material_symbols translatable=false, and moves the hard-coded labels in the app accessibility screen's Glass effects list (the TODO there) onto resources. --- .../AppAccessibilitySettingsScreen.kt | 9 ++-- .../src/main/res/values-zh-rCN/strings.xml | 49 +++++++++++++++++++ .../src/main/res/values-zh-rTW/strings.xml | 49 +++++++++++++++++++ android/app/src/main/res/values/strings.xml | 6 ++- 4 files changed, 107 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppAccessibilitySettingsScreen.kt b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppAccessibilitySettingsScreen.kt index 5dd43eec4..b2f92585b 100644 --- a/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppAccessibilitySettingsScreen.kt +++ b/android/app/src/main/kotlin/me/kavishdevar/librepods/presentation/screens/settings/AppAccessibilitySettingsScreen.kt @@ -90,20 +90,19 @@ fun AppAccessibilitySettingsScreen( ) } - // TODO: extract strings - StyledList(title = "Glass effects") { + StyledList(title = stringResource(R.string.glass_effects)) { styledToggle( - label = "Reduce transparency", + label = stringResource(R.string.reduce_transparency), checked = reduceTransparency, onCheckedChange = onReduceTransparencyChanged ) styledToggle( - label = "Increase contrast", + label = stringResource(R.string.increase_contrast), checked = increaseContrast, onCheckedChange = onIncreaseContrastChanged ) styledToggle( - label = "Show borders", + label = stringResource(R.string.show_borders), checked = showBorders, onCheckedChange = onShowBordersChanged ) diff --git a/android/app/src/main/res/values-zh-rCN/strings.xml b/android/app/src/main/res/values-zh-rCN/strings.xml index c5c02c37e..793cc57af 100644 --- a/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/android/app/src/main/res/values-zh-rCN/strings.xml @@ -439,4 +439,53 @@ 播放/暂停 开始/停止 下一步 + 调整 AirPods 播放声音效果的提示声音量。当前的声音比推荐的默认音量更响。 + 应用颜色 + 壁纸 + 图标 + 模糊样式 + 超薄 + 薄 + 常规 + 厚 + 超厚 + 顶栏使用渐进式模糊 + 颜色 + 模糊与玻璃效果 + 模糊顶栏后方的内容 + 纯黑背景 + 调色板样式 + 暂无电池信息 + 未连接 + 色相 + 饱和度 + 亮度 + 对比度 + 连接 + 显示卡片 + 隐藏卡片 + 播放循环视频 + 靠近弹窗 + 在手机附近打开充电盒时显示弹窗。 + 显示连接按钮 + 悬浮窗 + 灵动岛弹窗 + 佩戴 AirPods 时显示。你可以选择在佩戴期间一直显示,或在几秒后隐藏。 + 更多设置 + 自定义悬浮窗的大小、位置、外观和行为。 + 未连接其他设备时自动连接 + 启用后,当 AirPods 没有连接其他设备时,应用会自动连接 AirPods。 + 通话规则 + 选择何时自动将通话音频切换到 AirPods。 + 媒体音频规则 + 选择何时自动将音乐、视频等媒体音频切换到 AirPods。 + 未连接任何设备 + 已连接其他设备,但处于空闲 + 已连接正在播放媒体的其他设备 + 配置自动连接 + 两只 AirPods 都在充电盒内时才显示 + 玻璃效果 + 降低透明度 + 增强对比度 + 显示边框 diff --git a/android/app/src/main/res/values-zh-rTW/strings.xml b/android/app/src/main/res/values-zh-rTW/strings.xml index 08681e606..d9ae383e6 100644 --- a/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/android/app/src/main/res/values-zh-rTW/strings.xml @@ -440,4 +440,53 @@ 播放/暫停 開始/停止 下一步 + 調整 AirPods 播放音效的提示音音量。目前的聲音比建議的預設音量更大聲。 + 應用程式顏色 + 桌布 + 圖示 + 模糊樣式 + 超薄 + 薄 + 標準 + 厚 + 超厚 + 頂部列使用漸進式模糊 + 顏色 + 模糊與玻璃效果 + 模糊頂部列後方的內容 + 純黑背景 + 調色盤樣式 + 沒有可用的電池資訊 + 未連線 + 色相 + 飽和度 + 亮度 + 對比 + 連線 + 顯示卡片 + 隱藏卡片 + 播放循環影片 + 靠近彈出視窗 + 在手機附近打開充電盒時顯示彈出視窗。 + 顯示連線按鈕 + 浮動視窗 + 動態島彈出視窗 + 配戴 AirPods 時顯示。你可以選擇在配戴期間持續顯示,或在幾秒後隱藏。 + 更多設定 + 自訂浮動視窗的大小、位置、外觀與行為。 + 未連接其他裝置時自動連線 + 啟用後,當 AirPods 沒有連接其他裝置時,應用程式會自動連接 AirPods。 + 通話規則 + 選擇何時自動將通話音訊切換到 AirPods。 + 媒體音訊規則 + 選擇何時自動將音樂、影片等媒體音訊切換到 AirPods。 + 未連接任何裝置 + 已連接其他裝置,但處於閒置 + 已連接正在播放媒體的其他裝置 + 設定自動連線 + 兩只 AirPods 都在充電盒內時才顯示 + 玻璃效果 + 降低透明度 + 增加對比 + 顯示邊框 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b2fe5fef1..70222565e 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -418,7 +418,7 @@ App color Wallpaper Icons - Material Symbols + Material Symbols Blur style Ultra thin Thin @@ -489,4 +489,8 @@ Play/Pause Start/Stop Next + Glass effects + Reduce transparency + Increase contrast + Show borders