Compare commits

..
10 Commits
Author SHA1 Message Date
darken 5860bbffb6 Release: 3.0.3-rc0 2025-10-29 15:52:54 +01:00
darken da53d68190 Update translations 2025-10-29 15:52:04 +01:00
darken b397bf190c Update translations 2025-10-29 15:52:04 +01:00
darken 15d664c361 feat: Make connectedDevices flow more robust
Adds retry and catch logic to handle failures gracefully.
2025-10-29 15:51:48 +01:00
darken d2f24d8b95 fix: Improve BluetoothManager stability
Enhances robustness by adding extensive error handling around broadcast receiver registration, profile event processing, and service disconnections to prevent crashes. Also ensures device flow is cleared when Bluetooth is disabled.
2025-10-29 15:51:48 +01:00
darken b5436659b6 refactor: Make connectedDevices a hot StateFlow
Convert `connectedDevices()` from a function returning a cold Flow to a property that is a hot `StateFlow`. This simplifies call sites and improves efficiency by sharing the underlying subscription.
2025-10-29 15:51:48 +01:00
darken 1cfc4611cb Improve Bluetooth device connection monitoring
Switched from monitoring generic ACL events to specific Headset profile state changes for more reliable device connection and disconnection detection.

This fixes a race condition where CAPod thinks no device is connected because we triggered too early, before "connectedDevices" on the HEADSET profile contains our target device.

This fixes #313
2025-10-29 15:14:45 +01:00
darken bda0a20743 Release: 3.0.2-rc0 2025-10-11 08:02:51 +02:00
darken 09bad90c0c feat: Add and update translations for Dutch and Mexican Spanish 2025-10-11 07:50:32 +02:00
darken 34856607f8 Fix crash on start due to R8
Closes #341
2025-10-11 07:45:42 +02:00
17 changed files with 361 additions and 79 deletions
+1 -1
View File
@@ -1 +1 @@
3.0.1-rc0 30001000
3.0.3-rc0 30003000
+1 -22
View File
@@ -1,23 +1,2 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class eu.darken.capod.BuildConfig { *; }
-dontobfuscate
+1
View File
@@ -2,4 +2,5 @@
<resources>
<string name="upgrades_gplay_unavailable_error">Služby Google Play nie sú k dispozícii.</string>
<string name="upgrades_no_purchases_found_check_account">Nenašli sa žiadne nákupy. Používate správny účet?</string>
<string name="upgrades_gplay_billing_error_label">Chyba Google Play</string>
</resources>
@@ -2,4 +2,8 @@
<resources>
<string name="upgrades_gplay_unavailable_error">Google Play 服務無法使用。</string>
<string name="upgrades_no_purchases_found_check_account">未找到訂單,確定帳戶無誤?</string>
<string name="upgrades_gplay_billing_error_label">Google Play 錯誤</string>
<string name="upgrades_gplay_billing_error_description">Google Play 發生錯誤。請稍後再試或重新啟動手機。\n\n錯誤:%s</string>
<string name="upgrades_gplay_billing_result_error_label">Google Play 結帳錯誤</string>
<string name="upgrades_gplay_billing_result_error_description">向 Google Play 請求購買明細時發生錯誤。請清除 Google Play 快取並重新啟動手機。\n\n錯誤:%s</string>
</resources>
@@ -12,8 +12,8 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.HandlerThread
import android.os.ParcelUuid
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.coroutine.AppScope
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.Bugs
import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
@@ -21,27 +21,38 @@ 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
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.flow.setupCommonEventHandlers
import eu.darken.capod.pods.core.apple.protocol.ContinuityProtocol
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.retryWhen
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.IOException
import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BluetoothManager2 @Inject constructor(
private val manager: BluetoothManager,
@ApplicationContext private val context: Context,
@AppScope private val appScope: CoroutineScope,
private val dispatcherProvider: DispatcherProvider,
@ApplicationContext private val context: Context,
private val manager: BluetoothManager,
) {
val adapter: BluetoothAdapter?
@@ -89,7 +100,7 @@ class BluetoothManager2 @Inject constructor(
override fun onServiceDisconnected(profile: Int) {
log(TAG, WARN) { "onServiceDisconnected(profile=$profile)" }
close(IOException("BluetoothProfile service disconnected (profile=$profile)"))
close() // Close gracefully without exception to prevent crash
}
}, profile)
@@ -103,59 +114,117 @@ class BluetoothManager2 @Inject constructor(
}
private fun monitorDevicesForProfile(
private fun monitorProfile(
profile: Int = BluetoothProfile.HEADSET
): Flow<Set<BluetoothDevice>> = getBluetoothProfile(profile).flatMapLatest { bluetoothProfile ->
callbackFlow {
log(TAG, VERBOSE) { "connectedDevices(profile=$profile) starting" }
trySend(bluetoothProfile.connectedDevices)
log(TAG, VERBOSE) { "monitorProfile(): for profile=$profile starting" }
try {
trySend(bluetoothProfile.connectedDevices)
} catch (e: Exception) {
log(TAG, ERROR) { "monitorProfile(): Error querying initial connected devices: $e" }
close(e)
return@callbackFlow
}
val filter = IntentFilter().apply {
addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
}
val handlerThread = HandlerThread("BluetoothEventReceiver").apply {
start()
}
val handlerThread = HandlerThread("BluetoothEventReceiver").apply { start() }
val handler = Handler(handlerThread.looper)
val receiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
log(TAG, VERBOSE) { "Bluetooth event (intent=$intent, extras=${intent.extras})" }
val action = intent.action
if (action == null) {
log(TAG, ERROR) { "Bluetooth event without action, how did we get this?" }
log(TAG, VERBOSE) { "monitorProfile(): Bluetooth event (intent=$intent, extras=${intent.extras})" }
if (intent.action == null) {
log(TAG, ERROR) { "monitorProfile(): Bluetooth event without action?" }
return
}
val device = intent.getParcelableExtra<BluetoothDevice?>(BluetoothDevice.EXTRA_DEVICE)
if (device == null) {
log(TAG, ERROR) { "Connection event is missing EXTRA_DEVICE: ${intent.extras}" }
log(TAG, ERROR) { "monitorProfile(): Event is missing EXTRA_DEVICE" }
return
}
this@callbackFlow.launch {
val currentDevices = bluetoothProfile.connectedDevices
if (intent.action != BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) {
log(TAG, WARN) { "Unknown action: ${intent.action}" }
return@launch
}
when (action) {
BluetoothDevice.ACTION_ACL_CONNECTED -> {
log(TAG) { "Adding $device to current devices $currentDevices" }
trySend(currentDevices.plus(device))
// Profile connection changed - query actual state from proxy
val statePrevious = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)
log(TAG) { "monitorProfile(): HEADSET profile state changed for $device - previous: $statePrevious" }
val stateNow = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)
log(TAG) { "monitorProfile(): HEADSET profile state changed for $device - now: $stateNow" }
val currentDevices = try {
bluetoothProfile.connectedDevices
} catch (e: Exception) {
log(TAG, ERROR) { "monitorProfile(): Error handling profile event: $e" }
// Log but continue - don't kill the whole Flow for one bad event
emptySet()
}.toMutableSet()
log(TAG) { "monitorProfile(): currentDevices: $currentDevices" }
when (stateNow) {
BluetoothProfile.STATE_CONNECTING -> {
log(TAG) { "monitorProfile(): Currently connecting $device" }
}
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
log(TAG) { "Removing $device from current devices $currentDevices" }
trySend(currentDevices.minus(device))
BluetoothProfile.STATE_CONNECTED -> {
log(TAG) { "monitorProfile(): Device has connected $device" }
if (!currentDevices.contains(device)) {
log(
TAG,
VERBOSE
) { "monitorProfile(): $device not in proxy yet, adding manually" }
currentDevices.add(device)
}
trySend(currentDevices)
}
BluetoothProfile.STATE_DISCONNECTING -> {
log(TAG) { "monitorProfile(): Currently DISconnecting $device" }
}
BluetoothProfile.STATE_DISCONNECTED -> {
log(TAG) { "monitorProfile(): Device has disconnected $device" }
if (currentDevices.contains(device)) {
log(
TAG,
VERBOSE
) { "monitorProfile(): $device still in proxy, removing manually" }
currentDevices.remove(device)
}
trySend(currentDevices)
}
}
}
}
}
context.registerReceiver(receiver, filter, null, handler)
try {
context.registerReceiver(receiver, filter, null, handler)
} catch (e: Exception) {
log(TAG, ERROR) { "monitorProfile(): Failed to register receiver: $e" }
close(e)
return@callbackFlow
}
awaitClose {
log(TAG, VERBOSE) { "connectedDevices(profile=$profile) closed." }
context.unregisterReceiver(receiver)
log(TAG, VERBOSE) { "monitorProfile(): profile=$profile closed." }
try {
context.unregisterReceiver(receiver)
} catch (e: Exception) {
log(TAG, ERROR) { "monitorProfile(): Error unregistering receiver: $e" }
} finally {
handlerThread.quitSafely()
}
}
}
}
@@ -163,10 +232,11 @@ class BluetoothManager2 @Inject constructor(
private val seenDevicesLock = Mutex()
private val seenDevicesCache = mutableMapOf<String, Instant>()
fun connectedDevices(
featureFilter: Set<ParcelUuid> = ContinuityProtocol.BLE_FEATURE_UUIDS
): Flow<List<BluetoothDevice2>> = isBluetoothEnabled
.flatMapLatest { monitorDevicesForProfile(BluetoothProfile.HEADSET) }
val connectedDevices: Flow<List<BluetoothDevice2>> = isBluetoothEnabled
.flatMapLatest { enabled ->
if (enabled) monitorProfile(BluetoothProfile.HEADSET)
else flowOf(emptySet()) // Return empty when Bluetooth is off
}
.map { devices ->
val currentAddresses = devices.map { it.address }
@@ -177,7 +247,9 @@ class BluetoothManager2 @Inject constructor(
}
devices
.filter { device -> featureFilter.any { feature -> device.hasFeature(feature) } }
.filter { device ->
ContinuityProtocol.BLE_FEATURE_UUIDS.any { feature -> device.hasFeature(feature) }
}
.map { device ->
BluetoothDevice2(
internal = device,
@@ -189,6 +261,30 @@ class BluetoothManager2 @Inject constructor(
)
}
}
.retryWhen { cause, attempt ->
log(TAG, WARN) { "connectedDevices Flow failed (attempt ${attempt + 1}): $cause" }
if (attempt < 3) {
delay(1000 * (attempt + 1)) // 1s, 2s, 3s exponential backoff
true // Retry
} else {
false // Give up after 3 attempts
}
}
.catch { e ->
log(TAG, ERROR) { "connectedDevices Flow failed after retries: $e" }
emit(emptyList()) // Emit empty list and complete gracefully
}
.distinctUntilChanged()
.setupCommonEventHandlers(TAG) { "connectedDevices" }
.stateIn(
scope = appScope + dispatcherProvider.IO,
started = SharingStarted.WhileSubscribed(
stopTimeoutMillis = 5_000L,
replayExpirationMillis = 0L,
),
initialValue = null
)
.filterNotNull()
fun bondedDevices(): Flow<Set<BluetoothDevice2>> = flow {
val rawDevices = adapter?.bondedDevices ?: throw IllegalStateException("Bluetooth adapter unavailable")
@@ -90,7 +90,7 @@ class OverviewFragmentVM @Inject constructor(
val shouldStartMonitor = when (generalSettings.monitorMode.value) {
MonitorMode.MANUAL -> false
MonitorMode.AUTOMATIC -> bluetoothManager.connectedDevices().first().isNotEmpty()
MonitorMode.AUTOMATIC -> bluetoothManager.connectedDevices.first().isNotEmpty()
MonitorMode.ALWAYS -> true
}
if (shouldStartMonitor) {
@@ -148,7 +148,7 @@ class MonitorWorker @AssistedInject constructor(
combine(
generalSettings.monitorMode.flow,
profilesRepo.profiles,
bluetoothManager.connectedDevices(),
bluetoothManager.connectedDevices,
) { monitorMode, profiles, connectedDevices ->
listOf(monitorMode, profiles, connectedDevices)
}
@@ -16,7 +16,6 @@ import eu.darken.capod.profiles.core.DeviceProfilesRepo
import eu.darken.capod.reaction.core.ReactionSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
@@ -39,7 +38,7 @@ class AutoConnect @Inject constructor(
.flatMapLatest { isAutoConnectEnabled ->
if (isAutoConnectEnabled) {
combine(
bluetoothManager.connectedDevices().distinctUntilChanged(),
bluetoothManager.connectedDevices,
podMonitor.primaryDevice().filterNotNull().distinctUntilChangedBy { it.rawDataHex },
) { connectedDevices, mainDevice ->
connectedDevices to mainDevice
@@ -35,7 +35,7 @@ class PlayPause @Inject constructor(
reactionSettings.autoPause.flow,
reactionSettings.onePodMode.flow,
) { play, pause, _ -> play || pause }
.flatMapLatest { if (it) bluetoothManager.connectedDevices() else emptyFlow() }
.flatMapLatest { if (it) bluetoothManager.connectedDevices else emptyFlow() }
.flatMapLatest {
if (it.isEmpty()) {
log(TAG) { "No known devices connected." }
@@ -16,7 +16,6 @@ import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.reaction.core.ReactionSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
@@ -116,7 +115,7 @@ class PopUpReaction @Inject constructor(
if (!isEnabled) return@flatMapLatest emptyFlow()
combine(
bluetoothManager.connectedDevices().distinctUntilChanged(),
bluetoothManager.connectedDevices,
podMonitor.primaryDevice().distinctUntilChangedBy { it?.rawDataHex },
) { devices, broadcast ->
log(TAG) { "$broadcast $devices " }
+2 -2
View File
@@ -76,7 +76,7 @@
<string name="settings_support_description">Si necesitas ayuda.</string>
<string name="issue_tracker_label">Seguidor de problemas</string>
<string name="issue_tracker_description">Un seguidor público de problemas para reportes de errores y solicitudes de funciones (solo en inglés).</string>
<string name="discord_label">En español</string>
<string name="discord_label">Discord</string>
<string name="discord_description">Un lugar para pasar el rato y hacer preguntas.</string>
<string name="changelog_label">Registro de cambios</string>
<string name="settings_label">Ajustes</string>
@@ -98,7 +98,7 @@
<string name="help_translate_label">Traducción</string>
<string name="help_translate_description">Ayuda a traducir esta aplicación a tu idioma favorito.</string>
<string name="translators_thanks_title">Traductores</string>
<string name="translators_thanks_description">español</string>
<string name="translators_thanks_description">Jaime Muñoz(jmmartin_5@outlook.com)</string>
<string name="widget_description">Un widget que muestra el último estado conocido del dispositivo.</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>
+73 -5
View File
@@ -28,6 +28,8 @@
<string name="settings_autopause_description">기기를 귀에서 빼면 오디오를 일시 중지합니다.</string>
<string name="settings_autopplay_label">자동 재생</string>
<string name="settings_autoplay_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>
@@ -35,7 +37,7 @@
<string name="settings_signal_minimum_label">최소 신호 품질</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_condition_label">자동 연결 조건</string>
<string name="settings_autoconnect_condition_description">언제 기기에 연결을 시도해야 할까요?</string>
<string name="settings_devices_label">장치</string>
@@ -49,7 +51,7 @@
<string name="settings_compat_offloaded_filtering_disabled_summary">데이터 필터링을 시스템에 위임하지 말고 대신 모든 데이터를 가져와 앱 내에서 필터링합니다.</string>
<string name="settings_compat_offloaded_batching_disabled_title">하드웨어 일괄 처리 비활성화</string>
<string name="settings_compat_offloaded_batching_disabled_summary">수집된 BLE 데이터를 저희에게 전달하기 전에 시스템이 그룹화하도록 허용하지 마십시오.</string>
<string name="settings_onepod_mode_label">단일 Pod 모드</string>
<string name="settings_onepod_mode_label">단일 유닛 모드</string>
<string name="settings_onepod_mode_description">양쪽 기기를 모두 착용할 필요 없이 한쪽만 착용해도 앱이 반응합니다.</string>
<string name="settings_popup_caseopen_label">케이스 팝업 표시</string>
<string name="settings_popup_caseopen_description">기기 케이스를 열면 팝업을 표시합니다(실험적).</string>
@@ -98,6 +100,10 @@
<string name="translators_thanks_title">번역가</string>
<string name="translators_thanks_description">윤지호(annyeong1alt@gmail.com)</string>
<string name="widget_description">마지막으로 알려진 기기 상태를 표시하는 위젯입니다.</string>
<string name="widget_configuration_title">기기 선택</string>
<string name="widget_configuration_description">이 위젯에 표시할 기기 프로필을 선택해 주세요.</string>
<string name="common_feature_requires_pro_msg">이 기능을 사용하기 위해 CAPod Pro가 필요합니다.</string>
<string name="widget_no_data_label">데이터 없음</string>
<string name="settings_compat_indirectcallback_title">간접 데이터 전달</string>
<string name="settings_compat_indirectcallback_summary">시스템에서 BLE 데이터를 수신하는 대체 방법을 사용합니다(콜백 대신 브로드캐스트).</string>
<string name="troubleshooter_title">문제 해결사</string>
@@ -130,15 +136,27 @@
<string name="overview_nomaindevice_label">설정된 장치가 없음</string>
<string name="overview_nomaindevice_description">배터리 잔량 감시와 추가 기능을 활성화하기 위해 당신의 장치를 설정하십시오.</string>
<string name="overview_bluetooth_disabled_label">Bluetooth가 꺼져 있습니다</string>
<string name="overview_monitoring_active_label">장치 감시중</string>
<string name="overview_bluetooth_disabled_description">블루투스를 켜야 연결할 수 있어요 ;)</string>
<string name="overview_monitoring_active_label">기기 모니터링 중</string>
<string name="overview_monitoring_active_description">기기가 켜진 상태에서 가까이 위치해 있는지 확인해 주세요.</string>
<string name="overview_unmatched_devices_label">일치하지 않는 기기</string>
<plurals name="overview_unmatched_devices_count">
<item quantity="other">%d프로필과 일치하지 않는 기기</item>
</plurals>
<string name="permission_bluetooth_connect_label">블루투스 연결</string>
<string name="permission_bluetooth_connect_description">이 앱은 페어링된 기기와 상호작용하고 연결을 시작하기 위해 \"Bluetooth 연결\"권한을 필요로 합니다.</string>
<string name="permission_bluetooth_connect_description">이 앱은 페어링 된 기기와 상호작용하고 연결을 시작하기 위해 \"Bluetooth 연결\"권한을 필요로 합니다.</string>
<string name="permission_bluetooth_scan_label">Bluetooth 검색</string>
<string name="permission_bluetooth_scan_description">\"Bluetooth 검색\" 권한은 앱이 당신의 AirPods같은 주변의 기기를 찾고 Bluetooth 데이터를 받도록 합니다.</string>
<string name="permission_bluetooth_label">Bluetooth</string>
<string name="permission_bluetooth_description">이 앱은 페어링 된 기기에 접근하기 위해 블루투스 권한을 필요로 합니다.</string>
<string name="permission_access_fine_location_label">정밀 위치 접근</string>
<string name="permission_access_fine_location_description">CAPod는 BLE 정보를 받기 위해 정밀 위치 접근 권한을 사용합니다. 앱은 블루투스 헤드폰의 BLE 기술을 사용해 기기의 상태에 접근합니다. CAPod는 이 권한으로 사용자의 위치를 확인하지 않습니다.</string>
<string name="permission_background_location_label">백그라운드 위치 접근</string>
<string name="permission_background_location_description">CAPod는 팝업 보기나 자동 연결과 같은 기능을 앱이 꺼져있는 상태에서도 사용할 수 있게 하기 위해 백그라운드 위치 접근 권한을 사용합니다. 백그라운드 위치 접근 권한은 백그라운드에서도 BLE 정보를 받을 수 있게 합니다. CAPod는 이 권한으로 사용자의 위치를 확인하지 않습니다.</string>
<string name="permission_ignore_battery_optimizations_label">배터리 사용량 최적화 비활성화</string>
<string name="permission_ignore_battery_optimizations_description">배터리 사용량 최적화는 앱이 백그라운드에서 Bluetooth 신호를 지속적으로 받지 못하게 합니다.</string>
<string name="permission_required_title">다음과 같은 권한이 필요합니다:</string>
<string name="permission_system_alert_window_label">시스템 알림창</string>
<string name="permission_system_alert_window_description">\"팝업 표시\" 기능을 사용하려면 다른 앱 위에 그리기를 허용해야 합니다.</string>
<string name="settings_scanner_mode_lowpower_label">저전력</string>
<string name="settings_scanner_mode_balanced_label">균형잡힌</string>
@@ -148,12 +166,57 @@
<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>
<string name="settings_reaction_autoconnect_inear_label">귀에 착용했을 </string>
<string name="pods_dual_left_label">왼쪽 유닛</string>
<string name="pods_dual_right_label">오른쪽 유닛</string>
<string name="pods_case_label">케이스</string>
<string name="pods_case_status_open_label">열림</string>
<string name="pods_case_status_closed_label">닫힘</string>
<string name="pods_connection_state_disconnected_label">연결되지 않음</string>
<string name="pods_connection_state_idle_label">대기 모드</string>
<string name="pods_connection_state_music_label">음악 모드</string>
<string name="pods_connection_state_call_label">통화 모드</string>
<string name="pods_connection_state_ringing_label">수신 중</string>
<string name="pods_connection_state_hanging_up_label">끊는 중</string>
<string name="pods_connection_state_unknown_label">연결 상태 알 수 없음</string>
<string name="pods_unknown_raw_data_label">Raw data</string>
<string name="pods_unknown_label">알 수 없는 기기</string>
<string name="pods_unknown_contact_dev">알 수 없는 기기이나 비슷한 형식으로 정보를 보내는 기기인 것 같습니다. 제게 알려주시면 앱에 해당 기기 지원을 추가해 드릴게요 :)</string>
<string name="pods_none_label_short">기기 없음</string>
<string name="pods_charging_label">충전 중</string>
<string name="pods_inear_label">착용 중</string>
<string name="pods_microphone_label">마이크</string>
<string name="pods_yours">내 기기</string>
<string name="headset_being_worn_label">착용 중</string>
<string name="headset_not_being_worn_label">착용하지 않음</string>
<string name="pods_case_unknown_state">알 수 없는 상태</string>
<string name="last_seen_x">마지막 업데이트: %s</string>
<string name="first_seen_x">처음 업데이트: %s</string>
<string name="permission_post_notifications_label">알림 표시</string>
<string name="permission_post_notifications_description">"CAPod이 현재 연결 상태 등 AirPods에 대한 알림을 표시할 수 있도록 허용합니다."</string>
<!-- Device profiles -->
<string name="profiles_empty_title">기기 프로필이 설정되지 않음</string>
<string name="profiles_empty_description">여러 기기를 설정하고 우선순위를 지정하기 위해 기기 프로필을 생성하세요.</string>
<string name="profiles_add_action">프로필 추가</string>
<string name="profiles_create_title">프로필 생성</string>
<string name="profiles_name_label">프로필 이름</string>
<string name="profiles_name_default">나의 헤드폰</string>
<string name="profiles_model_label">기기 모델</string>
<string name="profiles_paired_device_label">페어링 된 기기</string>
<string name="profiles_paired_device_none">없음</string>
<string name="profiles_paired_device_none_description">선택된 기기가 없습니다.</string>
<string name="profiles_save_action">프로필 저장</string>
<string name="profiles_drag_handle_description">드래그하여 순서 변경</string>
<string name="profiles_delete_title">프로필 삭제</string>
<string name="profiles_delete_message">프로필을 삭제하시겠습니까?
이 작업은 취소할 수 없습니다.</string>
<string name="profiles_delete_action">삭제</string>
<string name="profiles_basic_info_title">기기 정보</string>
<string name="profiles_basic_info_description">기기의 이름, 모델 및 선택적 블루투스 페어링을 설정하세요.</string>
<string name="profiles_signal_quality_title">최소 신호 품질</string>
<string name="profiles_signal_quality_description">이 수치 이상의 신호 세기를 가진 기기만이 감지 됩니다. 낮은 값은 감지 범위를 넓히지만, 오탐을 일으킬 수 있습니다. 너무 높은 값은 블루투스 수신이 거리와 장애물에 민감하기에 권장하지 않습니다.</string>
<string name="profiles_identitykey_label">ID 키</string>
<string name="profilessettings_maindevice_identitykey_description">기기의 ID 확인 키(IRK)로, CAPod가 주변 기기 중에서 해당 기기를 식별하는 데 도움이 됩니다.</string>
<string name="profiles_maindevice_identitykey_explanation">AirPods은 개인정보 보호를 위해 자주 Bluetooth 주소를 변경합니다. IRK는 앱이 당신의 기기를 인식하는데 도움이 됩니다. MacBook을 잠깐 사용해야 합니다.</string>
<string name="profiles_maindevice_encryptionkey_label">암호화 키</string>
<string name="profiles_maindevice_encryptionkey_description">당신의 기기의 암호화 키. 앱이 구체적인 상태 정보를 불러올 수 있게 합니다.</string>
@@ -163,4 +226,9 @@
<string name="profiles_priority_hint">프로필 순서는 우선순위를 결정합니다. 프로필을 드래그하여 순서를 변경하세요.
리스트의 위에 있을수록 여러 장치가 일치할때 우선순위가 높습니다.</string>
<!-- Unsaved changes dialog -->
<string name="general_unsaved_changes_title">저장되지 않은 변경 사항</string>
<string name="general_unsaved_changes_message">저장되지 않은 변경 사항이 있습니다. 어떻게 하시겠습니까?</string>
<string name="general_save_and_exit_action">저장 &amp; 종료</string>
<string name="general_discard_action">무시</string>
<string name="general_keep_editing_action">계속 수정하기</string>
</resources>
+21
View File
@@ -28,6 +28,8 @@
<string name="settings_autopause_description">Pauzeer de muziek wanneer u het apparaat van uw oor haalt.</string>
<string name="settings_autopplay_label">Automatisch afspelen</string>
<string name="settings_autoplay_description">Start audioweergave wanneer het apparaat wordt gedragen.</string>
<string name="settings_eardetection_info_label">Ear detectie notitie</string>
<string name="settings_eardetection_info_description">Als oordetectie slechts voor één pod werkt, is dit een beperking van Apple. Alleen de \"primaire pod\" (gebruikt voor de microfoon) wordt gedetecteerd. Configureer op Apple-apparaten: Instellingen → Bluetooth → AirPods → Microfoon.</string>
<string name="settings_fake_data_label">Nep gegevens</string>
<string name="settings_fake_data_description">Toon nepgegevens, d.w.z. simuleer apparaten die niet bestaan.</string>
<string name="settings_debug_label">Foutopsporingsinstellingen</string>
@@ -98,6 +100,10 @@
<string name="translators_thanks_title">Vertalers</string>
<string name="translators_thanks_description">darken</string>
<string name="widget_description">Een widget dat de laatst gekende apparaatstatus toont.</string>
<string name="widget_configuration_title">Selecteer Apparaat</string>
<string name="widget_configuration_description">Kies welk apparaatprofiel deze widget moet weergeven.</string>
<string name="common_feature_requires_pro_msg">Voor deze functie is CAPod Pro vereist.</string>
<string name="widget_no_data_label">Geen data</string>
<string name="settings_compat_indirectcallback_title">Onrechtstreekse data aflevering</string>
<string name="settings_compat_indirectcallback_summary">Gebruik een alternatieve methode om BLE data van het systeem (broadcast inplaats van callback).</string>
<string name="troubleshooter_title">Probleemoplossing</string>
@@ -134,6 +140,10 @@
<string name="overview_monitoring_active_label">Monitoring voor apparaten</string>
<string name="overview_monitoring_active_description">Zorg ervoor dat je apparaat in de buurt is en actief is.</string>
<string name="overview_unmatched_devices_label">Niet overeenkomende apparaten</string>
<plurals name="overview_unmatched_devices_count">
<item quantity="one">%d apparaat zonder overeenkomend profiel</item>
<item quantity="other">%d apparaten zonder overeenkomend profiel</item>
</plurals>
<string name="permission_bluetooth_connect_label">Bluetooth-verbinding</string>
<string name="permission_bluetooth_connect_description">Deze app heeft de toestemming voor \'Bluetooth verbinden\' nodig om met gekoppelde apparaten te communiceren en verbindingen tot stand te brengen.</string>
<string name="permission_bluetooth_scan_label">Bluetooth-scannen</string>
@@ -159,6 +169,17 @@
<string name="settings_reaction_autoconnect_caseopen_label">Zaak is geopend</string>
<string name="settings_reaction_autoconnect_inear_label">In oor</string>
<string name="pods_dual_left_label">Linker pod</string>
<string name="pods_dual_right_label">Rechter pod</string>
<string name="pods_case_label">Geval</string>
<string name="pods_case_status_open_label">Open</string>
<string name="pods_case_status_closed_label">Gesloten</string>
<string name="pods_connection_state_disconnected_label">Niet verbonden met een apparaat</string>
<string name="pods_connection_state_idle_label">Aangesloten op een apparaat, maar inactief</string>
<string name="pods_connection_state_music_label">In muziekmodus</string>
<string name="pods_connection_state_call_label">In oproepmodus</string>
<string name="pods_connection_state_ringing_label">Rinkelen</string>
<string name="pods_connection_state_hanging_up_label">Ophangen</string>
<string name="pods_connection_state_unknown_label">Onbekende verbindingsstatus</string>
<!-- Device profiles -->
<string name="profiles_maindevice_encryptionkey_explanation">AirPods sturen een statusbericht, waarvan een deel versleuteld is. De encryptiesleutel stelt CAPod in staat het volledige bericht te ontsleutelen. Je hebt hiervoor eenmalige toegang tot een MacBook nodig.</string>
<string name="profiles_key_invalid_format">Ongeldige sleutelindeling</string>
+115
View File
@@ -11,6 +11,8 @@
<string name="general_save_action">儲存</string>
<string name="general_guide_action">指南</string>
<string name="general_continue_action">繼續</string>
<string name="general_show_action">顯示</string>
<string name="general_hide_action">隱藏</string>
<string name="general_example_label">例如:%s</string>
<string name="upgrade_capod_label">升級 CAPod</string>
<string name="upgrade_capod_description">取得附加功能並支持開發人員。</string>
@@ -18,12 +20,16 @@
<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>
<string name="settings_keep_notification_after_disconnect_description">即使 AirPods 斷線後仍顯示上次的電量</string>
<string name="settings_scanner_mode_label">掃描模式</string>
<string name="settings_scanner_mode_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_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>
@@ -34,6 +40,8 @@
<string name="settings_autoconnect_description">如果 Android 不會自動連線,我們也可以要求它。這將把監視模式設定為「一律」。</string>
<string name="settings_autoconnect_condition_label">自動連線條件</string>
<string name="settings_autoconnect_condition_description">何時連線到您的裝置?</string>
<string name="settings_devices_label">裝置</string>
<string name="settings_devices_description">管理你的裝置。</string>
<string name="settings_reaction_label">反應</string>
<string name="settings_reaction_description">對事件和行為作出反應。</string>
<string name="settings_category_yourdevice_label">您的裝置</string>
@@ -68,6 +76,7 @@
<string name="settings_support_description">如果您需要協助。</string>
<string name="issue_tracker_label">問題追蹤器</string>
<string name="issue_tracker_description">一個用於錯誤回報和功能需求的公用問題追蹤器 (僅英文)。</string>
<string name="discord_label">Discord</string>
<string name="discord_description">一個可以在其中閒逛並提出問題的地方。</string>
<string name="changelog_label">變更記錄</string>
<string name="settings_label">設定</string>
@@ -91,9 +100,14 @@
<string name="translators_thanks_title">翻譯人員</string>
<string name="translators_thanks_description">人工知能</string>
<string name="widget_description">顯示最後已知裝置狀態的小工具。</string>
<string name="widget_configuration_title">選擇裝置</string>
<string name="widget_configuration_description">選擇此小工具要顯示的裝置設定檔。</string>
<string name="common_feature_requires_pro_msg">此功能需 CAPod Pro。</string>
<string name="widget_no_data_label">無資料</string>
<string name="settings_compat_indirectcallback_title">間接資料傳遞</string>
<string name="settings_compat_indirectcallback_summary">使用替代方法從系統中接收低功耗藍牙資料 (廣播而非回撥)。</string>
<string name="troubleshooter_title">疑難排解員</string>
<string name="troubleshooter_summary">診斷並修復藍牙連線問題。</string>
<string name="troubleshooter_ble_intro_title">低功耗藍牙廣播</string>
<string name="troubleshooter_ble_intro_body1">AirPods (和類似的耳機) 使用一種叫做「廣告」的低功耗藍牙技術廣播狀態資訊。部分手機不能正確實作這項技術。CAPod 可以嘗試透過不同的相容性選項來修正這個問題,直到收到資料。在耳機上開始播放音樂,並把它們放在靠近手機的地方,然後啟動這個處理程序。</string>
<string name="troubleshooter_ble_intro_start_action">啟動疑難排解</string>
@@ -112,6 +126,107 @@
<string name="onboarding_body3">CAPod 沒有廣告,也不會收集您的資料。</string>
<string name="onboarding_body4">您可以升級到 CAPod Pro 以獲得額外功能並支援開發。</string>
<!-- Strings from app-common -->
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">不適用</string>
<string name="general_error_label">錯誤</string>
<string name="general_grant_permission_action">授予權限</string>
<string name="general_manage_devices_action">設備管理</string>
<string name="overview_nomaindevice_label">尚未設定裝置</string>
<string name="overview_nomaindevice_description">設定你的裝置以開始監測電量並啟用其他功能。</string>
<string name="overview_bluetooth_disabled_label">藍牙已停用</string>
<string name="overview_bluetooth_disabled_description">藍牙已停用,請啟用它 ;)</string>
<string name="overview_monitoring_active_label">正在監測裝置</string>
<string name="overview_monitoring_active_description">請確保你的裝置在附近且處於啟用狀態。</string>
<string name="overview_unmatched_devices_label">未配對的裝置</string>
<plurals name="overview_unmatched_devices_count">
<item quantity="other">%d 個裝置沒有相符的設定檔</item>
</plurals>
<string name="permission_bluetooth_connect_label">藍牙連線</string>
<string name="permission_bluetooth_connect_description">此應用程式需要「藍牙連線」權限,才能與已配對的裝置互動並建立連線。</string>
<string name="permission_bluetooth_scan_label">藍牙掃描中</string>
<string name="permission_bluetooth_scan_description">「藍牙掃描」權限可讓此應用程式探索並接收來自附近裝置的藍牙資料,例如你的 AirPods。</string>
<string name="permission_bluetooth_label">藍牙</string>
<string name="permission_bluetooth_description">這個應用程式需要「藍牙」權限與已配對裝置連線。</string>
<string name="permission_access_fine_location_label">存取精確位置</string>
<string name="permission_access_fine_location_description">CAPod 使用「精確位置」權限以接收低功耗藍牙資料。您的耳機使用低功耗藍牙技術以廣播其狀態。這個應用程式不會使用藍牙資料來確定您的位置。</string>
<string name="permission_background_location_label">背景位置存取</string>
<string name="permission_background_location_description">CAPods 在應用程式關閉時使用「背景位置存取」來啟用諸如「顯示彈出式視窗」和「自動連線」等功能。背景位置存取允許這個應用程式在背景接收低功耗藍牙資料。這個應用程式不會使用藍牙資料來確定您的位置。</string>
<string name="permission_ignore_battery_optimizations_label">停用電池效能最佳化</string>
<string name="permission_ignore_battery_optimizations_description">電池效能最佳化使這個應用程式在背景時無法可靠地接收藍牙資料。</string>
<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_scanner_mode_lowpower_label">低功耗</string>
<string name="settings_scanner_mode_balanced_label">平衡</string>
<string name="settings_scanner_mode_lowlatency_label">低延遲</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>
<string name="pods_dual_left_label">左耳</string>
<string name="pods_dual_right_label">右耳</string>
<string name="pods_case_label">充電盒</string>
<string name="pods_case_status_open_label">已開啟</string>
<string name="pods_case_status_closed_label">已關閉</string>
<string name="pods_connection_state_disconnected_label">未連線到裝置</string>
<string name="pods_connection_state_idle_label">已連線到裝置,閒置中</string>
<string name="pods_connection_state_music_label">音樂模式</string>
<string name="pods_connection_state_call_label">呼叫模式</string>
<string name="pods_connection_state_ringing_label">響鈴中</string>
<string name="pods_connection_state_hanging_up_label">正在掛斷</string>
<string name="pods_connection_state_unknown_label">未知連線狀態</string>
<string name="pods_unknown_raw_data_label">原始資料</string>
<string name="pods_unknown_label">未知裝置</string>
<string name="pods_unknown_contact_dev">這是一個未知裝置,但可以使用相似的訊息格式。聯絡我以為此裝置新增相關支援 :)</string>
<string name="pods_none_label_short">無裝置</string>
<string name="pods_charging_label">充電中</string>
<string name="pods_inear_label">在耳中</string>
<string name="pods_microphone_label">麥克風</string>
<string name="pods_yours">您的</string>
<string name="headset_being_worn_label">正被配戴</string>
<string name="headset_not_being_worn_label">未配戴</string>
<string name="pods_case_unknown_state">未知狀態</string>
<string name="last_seen_x">最後連線:%s</string>
<string name="first_seen_x">首次連線:%s</string>
<string name="permission_post_notifications_label">顯示通知</string>
<string name="permission_post_notifications_description">"允許 CAPod 顯示關於您 AirPods 的通知,例如在連線時顯示它們的目前狀態。"</string>
<!-- Device profiles -->
<string name="profiles_empty_title">尚未設定任何裝置設定檔</string>
<string name="profiles_empty_description">建立裝置設定檔,以自訂設定與優先順序來管理多個裝置。</string>
<string name="profiles_add_action">新增設定檔</string>
<string name="profiles_create_title">建立設定檔</string>
<string name="profiles_name_label">設定檔名稱</string>
<string name="profiles_name_default">我的耳機</string>
<string name="profiles_model_label">設備型號</string>
<string name="profiles_paired_device_label">已配對裝置</string>
<string name="profiles_paired_device_none"></string>
<string name="profiles_paired_device_none_description">尚未選擇裝置</string>
<string name="profiles_save_action">儲存設定檔</string>
<string name="profiles_drag_handle_description">拖曳重新排序</string>
<string name="profiles_delete_title">刪除設定檔</string>
<string name="profiles_delete_message">是否確定要刪除此設定檔?此操作無法撤銷。</string>
<string name="profiles_delete_action">刪除</string>
<string name="profiles_basic_info_title">設備資訊</string>
<string name="profiles_basic_info_description">設定你的裝置名稱、型號,以及可選的藍牙配對。</string>
<string name="profiles_signal_quality_title">最低訊號品質</string>
<string name="profiles_signal_quality_description">僅偵測訊號強度高於此閾值的裝置。數值越低可增加偵測範圍,但可能導致誤判。請勿將此值設得太高——藍牙接收通常較弱,且會受距離與障礙物影響。</string>
<string name="profiles_identitykey_label">身份金鑰</string>
<string name="profilessettings_maindevice_identitykey_description">您裝置的身份解析金鑰 (IRK),協助 CAPod 在附近的裝置中識別它。</string>
<string name="profiles_maindevice_identitykey_explanation">為保護隱私,AirPods 會經常更改其藍牙位址。IRK 可協助 CAPod 識別您的裝置。您需要一次性存取 MacBook。</string>
<string name="profiles_maindevice_encryptionkey_label">加密金鑰</string>
<string name="profiles_maindevice_encryptionkey_description">您裝置的加密金鑰,允許 CAPod 檢索詳細的狀態資訊。</string>
<string name="profiles_maindevice_encryptionkey_explanation">AirPods 會傳送狀態訊息,其中一部分已加密。加密金鑰允許 CAPod 解密完整的訊息。您需要一次性存取 MacBook。</string>
<string name="profiles_key_invalid_format">無效的金鑰格式</string>
<string name="profiles_key_expected_format">預期格式:%1$s</string>
<string name="profiles_priority_hint">設定檔的順序決定優先權。拖曳設定檔以重新排序——當多個裝置符合時,列表中較上方的設定檔將具有較高優先權。</string>
<!-- Unsaved changes dialog -->
<string name="general_unsaved_changes_title">修改未儲存</string>
<string name="general_unsaved_changes_message">您有未儲存的變更。您想如何處理?</string>
<string name="general_save_and_exit_action">儲存並退出</string>
<string name="general_discard_action">捨棄</string>
<string name="general_keep_editing_action">繼續編輯</string>
</resources>
@@ -15,4 +15,4 @@ CAPod no tiene publicidad. Algunas características requieren ser compradas dent
Los AirPods y Beats más populares son soportados.
Si su dispositivo es similar a AirPods pero aún no es compatible, envíeme un correo.
¿Tienes una buena idea para una nueva característica? ¡Háznoslo saber!
¿Tienes una buena idea para una nueva característica? ¡Contáctanos!
@@ -2,17 +2,17 @@ CAPod 是一個能提供 AirPods 相關功能的應用程式。
功能:
* 顯示耳機和充電盒電力
* 顯示耳機和充電盒充電狀態
* 顯示有關連線、麥克風、充電盒的附加資訊
* 可以接收並顯示附近的所有裝置。
* 耳朵偵測,自動播放/暫停。
* 自動連線手機和 AirPods。
* 開啟充電盒時顯示彈出式視窗
CAPod 是無廣告的應用程式。 一些功能需要應用程式內購。 。
CAPod 是無廣告的應用程式。 一些功能需要應用程式內購。 。 一些功能需要應用程式內購。
支援大部分流行的 AirPods 和 Beats 裝置。
如果您的裝置和 AirPods 相似但並不支援,請寄給我一封郵件。
有新功能的好點子? 與我溝通一下吧!
有新功能的好點子? 與我溝通一下吧!
+1 -1
View File
@@ -1,7 +1,7 @@
### Updated by release.sh ###
project.versioning.major=3
project.versioning.minor=0
project.versioning.patch=1
project.versioning.patch=3
project.versioning.build=0
project.versioning.type=rc
#############################