Compare commits

...
12 Commits
Author SHA1 Message Date
darken 647300fd3a Release: 2.15.0-rc0 2024-09-23 15:05:48 +02:00
darken 3eaf99390e Record debug logs without foreground service
Some overhaul of log recording to get rid of the `FOREGROUND_SERVICE_SPECIAL_USE` which is a headache with Google
2024-09-23 15:05:11 +02:00
darken 0ee7915e31 Fix WorkManager init crash
```
Caused by java.lang.IllegalStateException: WorkManager is not initialized properly.  You have explicitly disabled WorkManagerInitializer in your manifest, have not manually called WorkManager#initialize at this point, and your Application does not implement Configuration.Provider.
  at androidx.work.impl.WorkManagerImpl.getInstance (WorkManagerImpl.java)
  at androidx.work.impl.foreground.SystemForegroundDispatcher.<init> (SystemForegroundDispatcher.java)
  at androidx.work.impl.foreground.SystemForegroundService.initializeDispatcher (SystemForegroundService.java)
  at androidx.work.impl.foreground.SystemForegroundService.onCreate (SystemForegroundService.java)
  at android.app.ActivityThread.handleCreateService (ActivityThread.java:4726)
```
2024-09-23 14:12:25 +02:00
darken 10c880a800 Do staged rollouts via fastlane 2024-09-23 13:53:18 +02:00
Matthias Urhahn b1bf658ff3 Update README.md 2024-09-23 13:51:09 +02:00
Matthias Urhahn 77dc13d9ba Merge pull request #228 from d4rken-org/airpods-gen4-ANC
Add support for AirPods (Gen 4 with ANC)
2024-09-23 13:41:21 +02:00
darken 21b6cfc013 Add support for AirPods (Gen 4 with ANC)
Closes #226
2024-09-23 13:33:12 +02:00
Matthias Urhahn 5d953ee23e Merge pull request #227 from d4rken-org/airpods-gen4
Add support for AirPods (Gen 4 - no ANC)
2024-09-23 13:32:30 +02:00
darken 42f2f5bfee Add support for AirPods (Gen 4)
Closes #225
2024-09-23 13:08:35 +02:00
darken 6708901394 Improve BLE scanning
The default "balanced" mode was reporting data too infrequently.
2024-07-26 08:48:21 +02:00
darken 788038beb7 Release: 2.14.1-rc0 2024-07-12 21:47:39 +02:00
darken 18ac05ed80 Fix crashes when entering settings
Likely from #209
2024-07-12 21:47:04 +02:00
58 changed files with 392 additions and 283 deletions
+2
View File
@@ -26,6 +26,8 @@ Currently supported models:
* AirPods 1. Generation
* AirPods 2. Generation
* AirPods 3. Generation
* AirPods 4. Generation
* AirPods 4. Generation with ANC
* AirPods Pro 1. Generation
* AirPods Pro 2. Generation
* AirPods Pro 2. Generation (USB-C)
+1 -1
View File
@@ -1 +1 @@
2.14.0-rc0 21400000
2.15.0-rc0 21500000
@@ -9,13 +9,19 @@ import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.Intent
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.debug.logging.Logging.Priority.*
import eu.darken.capod.common.debug.logging.Logging.Priority.DEBUG
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.notifications.PendingIntentCompat
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -132,12 +138,12 @@ class BleScanner @Inject constructor(
ScannerMode.LOW_POWER -> {
setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
setMatchMode(ScanSettings.MATCH_MODE_STICKY)
setNumOfMatches(ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT)
setNumOfMatches(ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT)
}
ScannerMode.BALANCED -> {
setScanMode(ScanSettings.SCAN_MODE_BALANCED)
setMatchMode(ScanSettings.MATCH_MODE_STICKY)
setNumOfMatches(ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT)
setNumOfMatches(ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT)
}
ScannerMode.LOW_LATENCY -> {
setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
@@ -83,6 +83,14 @@ interface PodDevice {
"AirPods (Gen 3)",
R.drawable.devic_airpods_gen2_both,
),
@Json(name = "airpods.gen4") AIRPODS_GEN4(
"AirPods (Gen 4)",
R.drawable.devic_airpods_gen2_both,
),
@Json(name = "airpods.gen4.anc") AIRPODS_GEN4_ANC(
"AirPods (Gen 4 ANC)",
R.drawable.devic_airpods_gen2_both,
),
@Json(name = "airpods.pro") AIRPODS_PRO(
"AirPods Pro",
R.drawable.devic_airpods_pro2_both
@@ -5,9 +5,28 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoSet
import eu.darken.capod.pods.core.apple.airpods.*
import eu.darken.capod.pods.core.apple.beats.*
import eu.darken.capod.pods.core.apple.misc.*
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen1
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen2
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen3
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen4
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen4Anc
import eu.darken.capod.pods.core.apple.airpods.AirPodsMax
import eu.darken.capod.pods.core.apple.airpods.AirPodsPro
import eu.darken.capod.pods.core.apple.airpods.AirPodsPro2
import eu.darken.capod.pods.core.apple.airpods.AirPodsPro2Usbc
import eu.darken.capod.pods.core.apple.beats.BeatsFitPro
import eu.darken.capod.pods.core.apple.beats.BeatsFlex
import eu.darken.capod.pods.core.apple.beats.BeatsSolo3
import eu.darken.capod.pods.core.apple.beats.BeatsStudio3
import eu.darken.capod.pods.core.apple.beats.BeatsX
import eu.darken.capod.pods.core.apple.beats.PowerBeats3
import eu.darken.capod.pods.core.apple.beats.PowerBeats4
import eu.darken.capod.pods.core.apple.beats.PowerBeatsPro
import eu.darken.capod.pods.core.apple.misc.FakeAirPodsGen1
import eu.darken.capod.pods.core.apple.misc.FakeAirPodsGen2
import eu.darken.capod.pods.core.apple.misc.FakeAirPodsGen3
import eu.darken.capod.pods.core.apple.misc.FakeAirPodsPro
import eu.darken.capod.pods.core.apple.misc.FakeAirPodsPro2
@InstallIn(SingletonComponent::class)
@Module
@@ -16,6 +35,8 @@ abstract class AppleFactoryModule {
@Binds @IntoSet abstract fun airPodsGen1(factory: AirPodsGen1.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsGen2(factory: AirPodsGen2.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsGen3(factory: AirPodsGen3.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsGen4(factory: AirPodsGen4.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsGen4Anc(factory: AirPodsGen4Anc.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsPro(factory: AirPodsPro.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsPro2(factory: AirPodsPro2.Factory): ApplePodsFactory<out ApplePods>
@@ -0,0 +1,70 @@
package eu.darken.capod.pods.core.apple.airpods
import eu.darken.capod.common.bluetooth.BleScanResult
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.ApplePods
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.DualApplePodsFactory
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
import java.time.Instant
import javax.inject.Inject
data class AirPodsGen4(
override val identifier: PodDevice.Id = PodDevice.Id(),
override val seenLastAt: Instant = Instant.now(),
override val seenFirstAt: Instant = Instant.now(),
override val seenCounter: Int = 1,
override val scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val reliability: Float = PodDevice.BASE_CONFIDENCE,
private val rssiAverage: Int? = null,
private val cachedBatteryPercentage: Float? = null,
private val cachedCaseState: DualApplePods.LidState? = null
) : DualApplePods, HasStateDetectionAirPods {
override val model: PodDevice.Model = PodDevice.Model.AIRPODS_GEN4
override val batteryCasePercent: Float?
get() = super.batteryCasePercent ?: cachedBatteryPercentage
override val caseLidState: DualApplePods.LidState
get() = cachedCaseState ?: super.caseLidState
override val rssi: Int
get() = rssiAverage ?: super<DualApplePods>.rssi
class Factory @Inject constructor() : DualApplePodsFactory(TAG) {
override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run {
getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH
}
override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods {
var basic = AirPodsGen4(scanResult = scanResult, proximityMessage = message)
val result = searchHistory(basic)
if (result != null) basic = basic.copy(identifier = result.id)
updateHistory(basic)
if (result == null) return basic
return basic.copy(
identifier = result.id,
seenFirstAt = result.seenFirstAt,
seenLastAt = scanResult.receivedAt,
seenCounter = result.seenCounter,
reliability = result.reliability,
cachedBatteryPercentage = result.getLatestCaseBattery(),
rssiAverage = result.rssiSmoothed(basic.rssi),
cachedCaseState = result.getLatestCaseLidState(basic)
)
}
}
companion object {
private val DEVICE_CODE = 0x1920.toUShort()
private val TAG = logTag("PodDevice", "Apple", "AirPods", "Gen4")
}
}
@@ -0,0 +1,70 @@
package eu.darken.capod.pods.core.apple.airpods
import eu.darken.capod.common.bluetooth.BleScanResult
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.ApplePods
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.DualApplePodsFactory
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
import java.time.Instant
import javax.inject.Inject
data class AirPodsGen4Anc(
override val identifier: PodDevice.Id = PodDevice.Id(),
override val seenLastAt: Instant = Instant.now(),
override val seenFirstAt: Instant = Instant.now(),
override val seenCounter: Int = 1,
override val scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val reliability: Float = PodDevice.BASE_CONFIDENCE,
private val rssiAverage: Int? = null,
private val cachedBatteryPercentage: Float? = null,
private val cachedCaseState: DualApplePods.LidState? = null
) : DualApplePods, HasStateDetectionAirPods {
override val model: PodDevice.Model = PodDevice.Model.AIRPODS_GEN4_ANC
override val batteryCasePercent: Float?
get() = super.batteryCasePercent ?: cachedBatteryPercentage
override val caseLidState: DualApplePods.LidState
get() = cachedCaseState ?: super.caseLidState
override val rssi: Int
get() = rssiAverage ?: super<DualApplePods>.rssi
class Factory @Inject constructor() : DualApplePodsFactory(TAG) {
override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run {
getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH
}
override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods {
var basic = AirPodsGen4Anc(scanResult = scanResult, proximityMessage = message)
val result = searchHistory(basic)
if (result != null) basic = basic.copy(identifier = result.id)
updateHistory(basic)
if (result == null) return basic
return basic.copy(
identifier = result.id,
seenFirstAt = result.seenFirstAt,
seenLastAt = scanResult.receivedAt,
seenCounter = result.seenCounter,
reliability = result.reliability,
cachedBatteryPercentage = result.getLatestCaseBattery(),
rssiAverage = result.rssiSmoothed(basic.rssi),
cachedCaseState = result.getLatestCaseLidState(basic)
)
}
}
companion object {
private val DEVICE_CODE = 0x1B20.toUShort()
private val TAG = logTag("PodDevice", "Apple", "AirPods", "Gen4-ANC")
}
}
@@ -0,0 +1,46 @@
package eu.darken.capod.pods.core.apple.airpods
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.HasAppleColor
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
class AirPodsGen4AncTest : BaseAirPodsTest() {
@Test
fun `AirPods Gen4 with ANC via log from #226`() = runTest {
create<AirPodsGen4Anc>("07 19 01 1B 20 0B 9A 8F 10 00 04 43 DF EC 1D D3 F1 C3 F4 A1 9B 29 26 B9 E7 3A A0") {
rawPrefix shouldBe 0x01.toUByte()
rawDeviceModel shouldBe 0x1B20.toUShort()
rawStatus shouldBe 0x0b.toUByte()
rawPodsBattery shouldBe 0x9A.toUByte()
rawFlags shouldBe 0x8.toUShort()
rawCaseBattery shouldBe 0xF.toUShort()
rawCaseLidState shouldBe 0x10.toUByte()
rawDeviceColor shouldBe 0x00.toUByte()
rawSuffix shouldBe 0x04.toUByte()
batteryLeftPodPercent shouldBe 0.9f
batteryRightPodPercent shouldBe 1.0f
isCaseCharging shouldBe false
isLeftPodCharging shouldBe false
isRightPodCharging shouldBe false
isLeftPodInEar shouldBe true
isRightPodInEar shouldBe true
batteryCasePercent shouldBe null
caseLidState shouldBe DualApplePods.LidState.UNKNOWN
state shouldBe HasStateDetectionAirPods.ConnectionState.IDLE
podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name
model shouldBe PodDevice.Model.AIRPODS_GEN4_ANC
}
}
}
@@ -0,0 +1,46 @@
package eu.darken.capod.pods.core.apple.airpods
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.HasAppleColor
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
class AirPodsGen4Test : BaseAirPodsTest() {
@Test
fun `AirPods Gen4 via log from #225`() = runTest {
create<AirPodsGen4>("07 19 01 19 20 2B 33 8F 11 00 04 59 D4 57 20 0F 1C 13 38 B2 00 74 E9 DD 70 D7 A5") {
rawPrefix shouldBe 0x01.toUByte()
rawDeviceModel shouldBe 0x1920.toUShort()
rawStatus shouldBe 0x2B.toUByte()
rawPodsBattery shouldBe 0x33.toUByte()
rawFlags shouldBe 0x8.toUShort()
rawCaseBattery shouldBe 0xF.toUShort()
rawCaseLidState shouldBe 0x11.toUByte()
rawDeviceColor shouldBe 0x00.toUByte()
rawSuffix shouldBe 0x04.toUByte()
batteryLeftPodPercent shouldBe 0.3f
batteryRightPodPercent shouldBe 0.3f
isCaseCharging shouldBe false
isLeftPodCharging shouldBe false
isRightPodCharging shouldBe false
isLeftPodInEar shouldBe true
isRightPodInEar shouldBe true
batteryCasePercent shouldBe null
caseLidState shouldBe DualApplePods.LidState.UNKNOWN
state shouldBe HasStateDetectionAirPods.ConnectionState.IDLE
podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name
model shouldBe PodDevice.Model.AIRPODS_GEN4
}
}
}
+9 -10
View File
@@ -10,7 +10,6 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-feature
android:name="android.hardware.bluetooth_le"
@@ -77,14 +76,6 @@
android:name=".common.debug.recording.ui.RecorderActivity"
android:theme="@style/AppThemeFloating" />
<service
android:name=".common.debug.recording.core.RecorderService"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Users can record a debug log to help developers with bugs. The debug log recording is controlled via notifications and hosted in a foreground service." />
</service>
<!-- Worker stuff-->
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
@@ -94,7 +85,15 @@
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
</application>
</manifest>
@@ -14,9 +14,12 @@ import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.debug.recording.ui.RecorderActivity
import eu.darken.capod.common.flow.DynamicStateFlow
import eu.darken.capod.common.startServiceCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus
import java.io.File
import javax.inject.Inject
@@ -55,8 +58,6 @@ class RecorderModule @Inject constructor(
newRecorder.start(createRecordingFilePath())
triggerFile.createNewFile()
context.startServiceCompat(Intent(context, RecorderService::class.java))
log(TAG, INFO) { "Build.Fingerprint: ${Build.FINGERPRINT}" }
log(TAG, INFO) { "BuildConfig.Versions: ${BuildConfigWrap.VERSION_DESCRIPTION_LONG}" }
@@ -1,115 +0,0 @@
package eu.darken.capod.common.debug.recording.core
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.BuildConfigWrap
import eu.darken.capod.common.coroutine.DispatcherProvider
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.common.uix.Service2
import eu.darken.capod.main.ui.MainActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
class RecorderService : Service2() {
private lateinit var builder: NotificationCompat.Builder
@Inject lateinit var recorderModule: RecorderModule
@Inject lateinit var notificationManager: NotificationManager
@Inject lateinit var dispatcherProvider: DispatcherProvider
private val recorderScope by lazy {
CoroutineScope(SupervisorJob() + dispatcherProvider.IO)
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
NOTIF_CHANID_DEBUG,
getString(R.string.debug_notification_channel_label),
NotificationManager.IMPORTANCE_MIN
)
notificationManager.createNotificationChannel(channel)
}
val openIntent = Intent(this, MainActivity::class.java)
val openPi = PendingIntent.getActivity(
this,
0,
openIntent,
PendingIntentCompat.FLAG_IMMUTABLE
)
val stopIntent = Intent(this, RecorderService::class.java)
stopIntent.action = STOP_ACTION
val stopPi = PendingIntent.getService(
this,
0,
stopIntent,
PendingIntentCompat.FLAG_IMMUTABLE
)
builder = NotificationCompat.Builder(this, NOTIF_CHANID_DEBUG)
.setChannelId(NOTIF_CHANID_DEBUG)
.setContentIntent(openPi)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSmallIcon(R.drawable.ic_baseline_bug_report_24)
.setContentText("Idle")
.setContentTitle(getString(eu.darken.capod.common.R.string.app_name))
.addAction(NotificationCompat.Action.Builder(0, getString(R.string.general_done_action), stopPi).build())
startForeground(NOTIFICATION_ID, builder.build())
recorderModule.state
.onEach {
if (it.isRecording) {
builder.setContentText("Recording debug log: ${it.currentLogPath?.path}")
notificationManager.notify(NOTIFICATION_ID, builder.build())
} else {
stopForeground(true)
stopSelf()
}
}
.launchIn(recorderScope)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
log(TAG) { "onStartCommand(intent=$intent, flags=$flags, startId=$startId" }
if (intent?.action == STOP_ACTION) {
recorderScope.launch {
recorderModule.stopRecorder()
}
}
return START_STICKY
}
override fun onDestroy() {
recorderScope.coroutineContext.cancel()
super.onDestroy()
}
companion object {
private val TAG = logTag("Debug", "Log", "Recorder", "Service")
private val NOTIF_CHANID_DEBUG = "${BuildConfigWrap.APPLICATION_ID}.notification.channel.debug"
private const val STOP_ACTION = "STOP_SERVICE"
private const val NOTIFICATION_ID = 53
}
}
@@ -95,7 +95,7 @@ class RecorderActivityVM @Inject constructor(
}
val chooserIntent = Intent.createChooser(intent, context.getString(R.string.debug_debuglog_file_label))
val chooserIntent = Intent.createChooser(intent, context.getString(R.string.support_debuglog_label))
shareEvent.postValue(chooserIntent)
}
@@ -0,0 +1,24 @@
package eu.darken.capod.common.debug.recording.ui
import android.content.Context
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.darken.capod.R
import eu.darken.capod.common.PrivacyPolicy
import eu.darken.capod.common.WebpageTool
class RecorderConsentDialog(
private val context: Context,
private val webpageTool: WebpageTool
) {
fun showDialog(onStartRecord: () -> Unit) {
MaterialAlertDialogBuilder(context).apply {
setTitle(R.string.support_debuglog_label)
setMessage(R.string.settings_debuglog_explanation)
setPositiveButton(R.string.debug_debuglog_record_action) { _, _ -> onStartRecord() }
setNegativeButton(R.string.general_cancel_action) { _, _ -> }
setNeutralButton(R.string.settings_privacy_policy_label) { _, _ ->
webpageTool.open(PrivacyPolicy.URL)
}
}.show()
}
}
@@ -1,14 +1,10 @@
package eu.darken.capod.main.ui.settings.general.debug
import android.os.Bundle
import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.debug.DebugSettings
import eu.darken.capod.common.observe2
import eu.darken.capod.common.uix.PreferenceFragment3
import javax.inject.Inject
@@ -25,17 +21,4 @@ class DebugSettingsFragment : PreferenceFragment3() {
override val preferenceFile: Int = R.xml.preferences_debug
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val logPref = findPreference<Preference>("debug.log.record")!!
vm.state.observe2(this) {
logPref.summary = it.currentLogPath?.path
}
logPref.setOnPreferenceClickListener {
vm.toggleRecorder()
true
}
super.onViewCreated(view, savedInstanceState)
}
}
@@ -6,11 +6,9 @@ import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.DebugSettings
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.debug.recording.core.RecorderModule
import eu.darken.capod.common.uix.ViewModel3
import eu.darken.capod.main.core.GeneralSettings
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
@@ -18,13 +16,10 @@ import javax.inject.Inject
class DebugSettingsFragmentVM @Inject constructor(
private val handle: SavedStateHandle,
dispatcherProvider: DispatcherProvider,
private val recorderModule: RecorderModule,
private val generalSettings: GeneralSettings,
private val debugSettings: DebugSettings,
) : ViewModel3(dispatcherProvider) {
val state = recorderModule.state.asLiveData2()
init {
debugSettings.showUnfiltered.flow
.distinctUntilChanged()
@@ -37,13 +32,6 @@ class DebugSettingsFragmentVM @Inject constructor(
.launchInViewModel()
}
fun toggleRecorder() = launch {
if (recorderModule.state.first().isRecording) {
recorderModule.stopRecorder()
} else {
recorderModule.startRecorder()
}
}
companion object {
private val TAG = logTag("Settings", "Debug", "VM")
@@ -5,10 +5,10 @@ import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.ClipboardHelper
import eu.darken.capod.common.WebpageTool
import eu.darken.capod.common.debug.recording.ui.RecorderConsentDialog
import eu.darken.capod.common.observe2
import eu.darken.capod.common.uix.PreferenceFragment3
import eu.darken.capod.main.core.GeneralSettings
@@ -25,28 +25,36 @@ class SupportFragment : PreferenceFragment3() {
override val settings: GeneralSettings by lazy { generalSettings }
@Inject lateinit var clipboardHelper: ClipboardHelper
@Inject lateinit var webpageTool: WebpageTool
private val installIdPref by lazy { findPreference<Preference>("support.installid")!! }
override fun onPreferencesCreated() {
installIdPref.setOnPreferenceClickListener {
vm.copyInstallID()
true
}
super.onPreferencesCreated()
}
private val debugLogPref by lazy { findPreference<Preference>("support.debuglog")!! }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.clipboardEvent.observe2(this) { installId ->
Snackbar.make(requireView(), installId, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.general_copy_action) {
clipboardHelper.copyToClipboard(installId)
}
.show()
}
vm.recorderState.observe2(this) { state ->
debugLogPref.setIcon(
if (state.isRecording) R.drawable.ic_cancel
else R.drawable.ic_baseline_bug_report_24
)
debugLogPref.setTitle(
if (state.isRecording) R.string.debug_debuglog_stop_action
else R.string.debug_debuglog_record_action
)
debugLogPref.summary = when {
state.isRecording -> state.currentLogPath?.path
else -> getString(R.string.debug_debuglog_record_action)
}
debugLogPref.setOnPreferenceClickListener {
if (state.isRecording) {
vm.stopDebugLog()
} else {
RecorderConsentDialog(requireContext(), webpageTool).showDialog {
vm.startDebugLog()
}
}
true
}
}
super.onViewCreated(view, savedInstanceState)
}
}
@@ -1,23 +1,27 @@
package eu.darken.capod.main.ui.settings.support
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.darken.capod.common.InstallId
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.recording.core.RecorderModule
import eu.darken.capod.common.uix.ViewModel3
import javax.inject.Inject
@HiltViewModel
class SupportFragmentVM @Inject constructor(
private val handle: SavedStateHandle,
private val dispatcherProvider: DispatcherProvider,
private val installId: InstallId,
private val recorderModule: RecorderModule,
) : ViewModel3(dispatcherProvider) {
val clipboardEvent = SingleLiveEvent<String>()
val recorderState = recorderModule.state.asLiveData2()
fun copyInstallID() = launch {
clipboardEvent.postValue(installId.id)
fun startDebugLog() = launch {
log(TAG) { "startDebugLog()" }
recorderModule.startRecorder()
}
fun stopDebugLog() = launch {
log(TAG) { "stopDebugLog()" }
recorderModule.stopRecorder()
}
}
@@ -5,9 +5,9 @@ import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import androidx.lifecycle.asLiveData
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.TwoStatePreference
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.bluetooth.BluetoothDevice2
@@ -51,17 +51,17 @@ class ReactionSettingsFragment : PreferenceFragment3() {
override fun onPreferenceTreeClick(preference: Preference): Boolean {
if (preference.key == reactionSettings.autoPlay.key && !isPro) {
preference as CheckBoxPreference
preference as TwoStatePreference
upgradeRepo.launchBillingFlow(requireActivity())
preference.isChecked = false
return true
} else if (preference.key == reactionSettings.autoPause.key && !isPro) {
preference as CheckBoxPreference
preference as TwoStatePreference
upgradeRepo.launchBillingFlow(requireActivity())
preference.isChecked = false
return true
} else if (preference.key == reactionSettings.autoConnect.key) {
preference as CheckBoxPreference
preference as TwoStatePreference
if (!isPro) {
upgradeRepo.launchBillingFlow(requireActivity())
@@ -79,12 +79,12 @@ class ReactionSettingsFragment : PreferenceFragment3() {
return true
}
} else if (preference.key == reactionSettings.showPopUpOnCaseOpen.key && !isPro) {
preference as CheckBoxPreference
preference as TwoStatePreference
upgradeRepo.launchBillingFlow(requireActivity())
preference.isChecked = false
return true
} else if (preference.key == reactionSettings.showPopUpOnConnection.key && !isPro) {
preference as CheckBoxPreference
preference as TwoStatePreference
upgradeRepo.launchBillingFlow(requireActivity())
preference.isChecked = false
return true
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z" />
</vector>
-2
View File
@@ -44,8 +44,6 @@
<string name="debug_debuglog_size_label">الحجم</string>
<string name="debug_debuglog_size_compressed_label">الحجم المضغوط</string>
<string name="debug_notification_channel_label">اشعارات التصحيح</string>
<string name="debug_debuglog_file_label">ملف مسجل</string>
<string name="debug_debuglog_record_action">ملف التصحيح مسجل</string>
<string name="settings_support_installid_label">مُعرف التثبيت</string>
<string name="settings_support_installid_desc">تقارير الأخطاء والمشاكل التلقائية تكون مجهول المصدر. شارك مُعرف التثبيت الخاص بك إذا احتاج المطور إلى العثور على الأخطاء والمشاكل الخاصة بك.</string>
<string name="settings_support_label">الدعم</string>
-2
View File
@@ -40,8 +40,6 @@
<string name="debug_debuglog_size_label">Həcm</string>
<string name="debug_debuglog_size_compressed_label">Sıxışdırılmış həcm</string>
<string name="debug_notification_channel_label">Sazlama bildirişləri</string>
<string name="debug_debuglog_file_label">Yazılmış jurnal faylı</string>
<string name="debug_debuglog_record_action">Sazlama jurnalına yaz</string>
<string name="settings_support_installid_label">Quraşdırma kimliyi</string>
<string name="settings_support_installid_desc">Avtomatik xəta hesabatları anonimdir. Tərtibatçının xəta hesabatlarınızı tapmasına ehtiyacı varsa quraşdırma kimliyinizi paylaşın.</string>
<string name="settings_support_label">Dəstək</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Памер</string>
<string name="debug_debuglog_size_compressed_label">Сціснуты памер</string>
<string name="debug_notification_channel_label">Апавяшчэнні адладкі</string>
<string name="debug_debuglog_file_label">Файл журнала</string>
<string name="debug_debuglog_record_action">Запісаць лог аладкі ў журнал</string>
<string name="settings_support_installid_label">ID інсталяцыі</string>
<string name="settings_support_installid_desc">Аўтаматычныя справаздачы пра памылкі з\'яўляюцца ананімнымі. Падзяліцеся вашым ID інсталяцыі, калі распрацоўшчыку спатрэбіцца доступ да вашых справаздач пра памылкі.</string>
<string name="settings_support_label">Падтрымка</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Mida</string>
<string name="debug_debuglog_size_compressed_label">Mida comprimida</string>
<string name="debug_notification_channel_label">Notificacions de depuració</string>
<string name="debug_debuglog_file_label">Fitxer de registre gravat</string>
<string name="debug_debuglog_record_action">Grava el registre de depuració</string>
<string name="settings_support_installid_label">ID d\'instal·lació</string>
<string name="settings_support_installid_desc">Els informes d\'error automàtics són anònims. Compartiu el vostre ID d\'instal·lació si el desenvolupador necessita trobar els vostres informes d\'error.</string>
<string name="settings_support_label">Suport</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Velikost</string>
<string name="debug_debuglog_size_compressed_label">Komprimovaná velikost</string>
<string name="debug_notification_channel_label">Oznámení o ladění</string>
<string name="debug_debuglog_file_label">Zaznamenaný soubor protokolu</string>
<string name="debug_debuglog_record_action">Zaznamenat protokol ladění</string>
<string name="settings_support_installid_label">ID instalace</string>
<string name="settings_support_installid_desc">Automatická hlášení chyb jsou anonymní. Sdílíte pouze své ID instalace, pokud vývojář potřebuje najít vaše chybová hlášení.</string>
<string name="settings_support_label">Podpora</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Größe</string>
<string name="debug_debuglog_size_compressed_label">Komprimierte Größe</string>
<string name="debug_notification_channel_label">Debug Benachrichtigungen</string>
<string name="debug_debuglog_file_label">Aufgenommene Log-Datei</string>
<string name="debug_debuglog_record_action">Debug Log aufnehmen</string>
<string name="settings_support_installid_label">Installations-ID</string>
<string name="settings_support_installid_desc">Automatische Fehlermeldungen sind anonym. Teilen Sie Ihre Installations- ID dem Entwickler mit, wenn er Ihre Fehlerberichte finden muss.</string>
<string name="settings_support_label">Unterstützung</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Μέγεθος</string>
<string name="debug_debuglog_size_compressed_label">Συμπιεσμένο μέγεθος</string>
<string name="debug_notification_channel_label">Ειδοποιήσεις εντοπισμού σφαλμάτων</string>
<string name="debug_debuglog_file_label">Εγγεγραμμένο αρχείο καταγραφής</string>
<string name="debug_debuglog_record_action">Εγγραφή καταγραφής εντοπισμού σφαλμάτων</string>
<string name="settings_support_installid_label">Αναγνωριστικό εγκατάστασης</string>
<string name="settings_support_installid_desc">Οι αυτόματες αναφορές σφαλμάτων είναι ανώνυμες. Κοινοποιήστε το αναγνωριστικό εγκατάστασης εάν ο προγραμματιστής πρέπει να βρει τις αναφορές σας σφαλμάτων.</string>
<string name="settings_support_label">Υποστήριξη</string>
@@ -11,6 +11,4 @@
<string name="debug_debuglog_size_label">Tamaño</string>
<string name="debug_debuglog_size_compressed_label">Tamaño comprimido</string>
<string name="debug_notification_channel_label">Depurar notificaciones</string>
<string name="debug_debuglog_file_label">Archivo de registro guardado</string>
<string name="debug_debuglog_record_action">Grabar registro de depuracion</string>
</resources>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Tamaño</string>
<string name="debug_debuglog_size_compressed_label">Tamaño comprimido</string>
<string name="debug_notification_channel_label">Notificaciones de la depuración</string>
<string name="debug_debuglog_file_label">Registro guardado</string>
<string name="debug_debuglog_record_action">Registro de depuración</string>
<string name="settings_support_installid_label">ID de la instalación</string>
<string name="settings_support_installid_desc">Los informes automáticos de errores son anónimos. Comparte tu ID de la instalación si el desarrollador necesita encontrar tus informes de errores.</string>
<string name="settings_support_label">Ayuda</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Taille</string>
<string name="debug_debuglog_size_compressed_label">Taille compressée</string>
<string name="debug_notification_channel_label">Notifications de débogage</string>
<string name="debug_debuglog_file_label">Ficher journal enregistré</string>
<string name="debug_debuglog_record_action">Enregistrer le journal de débogage</string>
<string name="settings_support_installid_label">ID dinstallation</string>
<string name="settings_support_installid_desc">Les relevés automatiques derreur sont anonymes. Partagez votre ID dinstallation au cas où le développeur aurait besoin de trouver votre relevé derreur.</string>
<string name="settings_support_label">Assistance</string>
-2
View File
@@ -44,8 +44,6 @@
<string name="debug_debuglog_size_label">Méret</string>
<string name="debug_debuglog_size_compressed_label">Tömörített_méret</string>
<string name="debug_notification_channel_label">Hibakeresési_értesítések</string>
<string name="debug_debuglog_file_label">Rögzített_naplófájl</string>
<string name="debug_debuglog_record_action">Hibakeresési_napló_rögzítése</string>
<string name="settings_support_installid_label">Azonosító</string>
<string name="settings_support_installid_desc">Az automatikus hibajelentések névtelenek. Ossza meg azonosítóját, hogy a fejlesztő megtalálja a hibajelentéseket.</string>
<string name="settings_support_label">Támogatás</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Ukuran</string>
<string name="debug_debuglog_size_compressed_label">Ukuran terkompresi</string>
<string name="debug_notification_channel_label">Notifikasi debug</string>
<string name="debug_debuglog_file_label">File log yang direkam</string>
<string name="debug_debuglog_record_action">Rekam log debug</string>
<string name="settings_support_installid_label">ID pemasangan</string>
<string name="settings_support_installid_desc">Laporan kesalahan otomatis bersifat anonim. Bagikan ID pemasangan anda jika pengembang perlu menemukan laporan kesalahan anda.</string>
<string name="settings_support_label">Dukungan</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Dimensione</string>
<string name="debug_debuglog_size_compressed_label">Dimensione compressa</string>
<string name="debug_notification_channel_label">Notifiche di Debug</string>
<string name="debug_debuglog_file_label">File di log registrati</string>
<string name="debug_debuglog_record_action">Log di debug</string>
<string name="settings_support_installid_label">ID d\'installazione</string>
<string name="settings_support_installid_desc">Le segnalazioni automatiche degli errori sono anonime. Condividi il tuo ID d\'installazione con lo sviluppatore se necessita di trovare la tua segnalazione all\'interno del registro segnalazioni.</string>
<string name="settings_support_label">Supporto</string>
-2
View File
@@ -50,8 +50,6 @@
<string name="debug_debuglog_size_label">מידה</string>
<string name="debug_debuglog_size_compressed_label">גודל דחיסה</string>
<string name="debug_notification_channel_label">ניפוי באגים</string>
<string name="debug_debuglog_file_label">קובץ לוג מוקלט</string>
<string name="debug_debuglog_record_action">הקלט יומן ניפוי באגים</string>
<string name="settings_support_installid_label">מזהה התקנה</string>
<string name="settings_support_installid_desc">דוחות שגיאה אוטומטיים הם אנונימיים. שתף את מזהה ההתקנה שלך אם המפתח צריך למצוא את דוחות השגיאות שלך.</string>
<string name="settings_support_label">תמיכה</string>
-2
View File
@@ -48,8 +48,6 @@
<string name="debug_debuglog_size_label">サイズ</string>
<string name="debug_debuglog_size_compressed_label">圧縮サイズ</string>
<string name="debug_notification_channel_label">デバッグ通知</string>
<string name="debug_debuglog_file_label">記録されたログファイル</string>
<string name="debug_debuglog_record_action">デバッグログを記録する</string>
<string name="settings_support_installid_label">インストールID</string>
<string name="settings_support_installid_desc">自動エラーレポートは匿名です。開発者があなたのエラーレポートを見つける必要がある場合は、インストールIDを共有してください。</string>
<string name="settings_support_label">サポート</string>
-2
View File
@@ -44,8 +44,6 @@
<string name="debug_debuglog_size_label">크기</string>
<string name="debug_debuglog_size_compressed_label">압축된 크기</string>
<string name="debug_notification_channel_label">디버그 알림</string>
<string name="debug_debuglog_file_label">기록된 로그 파일</string>
<string name="debug_debuglog_record_action">기록된 디버그 로그</string>
<string name="settings_support_installid_label">설치 ID</string>
<string name="settings_support_installid_desc">자동 오류 보고는 익명성이 보장됩니다. 개발자가 오류 보고서를 필요로 할 때 설치 ID를 알려주세요.</string>
<string name="settings_support_label">지원</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Saiz</string>
<string name="debug_debuglog_size_compressed_label">Saiz termampat</string>
<string name="debug_notification_channel_label">Notifikasi nyahpepijat</string>
<string name="debug_debuglog_file_label">Fail log yang direkodkan</string>
<string name="debug_debuglog_record_action">Rekod log nyahpepijat</string>
<string name="settings_support_installid_label">Pasang ID</string>
<string name="settings_support_installid_desc">Laporan ralat automatik adalah tanpa nama. Kongsi ID pemasangan anda jika pembangun perlu mencari laporan ralat anda.</string>
<string name="settings_support_label">Sokongan</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Grootte</string>
<string name="debug_debuglog_size_compressed_label">Gecomprimeerde grootte</string>
<string name="debug_notification_channel_label">Foutopsporingsmeldingen</string>
<string name="debug_debuglog_file_label">Opgenomen logbestand</string>
<string name="debug_debuglog_record_action">Foutopsporingslogboek opnemen</string>
<string name="settings_support_installid_label">Installatie-ID</string>
<string name="settings_support_installid_desc">Automatische foutmeldingen zijn anoniem. Deel uw installatie-ID als de ontwikkelaar uw foutrapporten moet vinden.</string>
<string name="settings_support_label">Ondersteuning</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Størrelse</string>
<string name="debug_debuglog_size_compressed_label">Komprimert størrelse</string>
<string name="debug_notification_channel_label">Feilsøkingsvarslinger</string>
<string name="debug_debuglog_file_label">Registrert loggfil</string>
<string name="debug_debuglog_record_action">Registrer feilsøkingslogg</string>
<string name="settings_support_installid_label">Installasjons-ID</string>
<string name="settings_support_installid_desc">Automatiske feilrapporter er anonyme. Del installasjons-ID-en din hvis utvikleren skal finne feilrapportene dine.</string>
<string name="settings_support_label">Brukerstøtte</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Rozmiar</string>
<string name="debug_debuglog_size_compressed_label">Rozmiar skompresowanego</string>
<string name="debug_notification_channel_label">Powiadomienia debugowania</string>
<string name="debug_debuglog_file_label">Nagrany plik logowania</string>
<string name="debug_debuglog_record_action">Nagraj plik logowania</string>
<string name="settings_support_installid_label">ID instalacji</string>
<string name="settings_support_installid_desc">Automatyczne raporty błędów są anonimowe. Udostępni swoje ID instalacji, aby autor mógł znaleźć twój raport błędu.</string>
<string name="settings_support_label">Wsparcie</string>
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Tamanho</string>
<string name="debug_debuglog_size_compressed_label">Tamanho compactado</string>
<string name="debug_notification_channel_label">Notificações de depuração</string>
<string name="debug_debuglog_file_label">Arquivo de registro gravado</string>
<string name="debug_debuglog_record_action">Gravar registro de depuração</string>
<string name="settings_support_installid_label">ID de instalação</string>
<string name="settings_support_installid_desc">Os relatórios automáticos de erros são anônimos. Compartilhe seu ID de instalação se o desenvolvedor precisar encontrar seus relatórios de erros.</string>
<string name="settings_support_label">Suporte</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Tamanho</string>
<string name="debug_debuglog_size_compressed_label">Tamanho comprimido</string>
<string name="debug_notification_channel_label">Notificações de debug</string>
<string name="debug_debuglog_file_label">Ficheiro de registo gravado</string>
<string name="debug_debuglog_record_action">Gravar ficheiro de registo</string>
<string name="settings_support_installid_label">ID de instalação</string>
<string name="settings_support_installid_desc">Os relatórios de erros automáticos são anónimos. Compartilhe o seu ID de instalação se o programador precisar encontrar os seus relatórios de erro.</string>
<string name="settings_support_label">Suporte</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Dimensiune</string>
<string name="debug_debuglog_size_compressed_label">Dimensiune comprimată</string>
<string name="debug_notification_channel_label">Notificări de depanare</string>
<string name="debug_debuglog_file_label">Fișier jurnal înregistrat</string>
<string name="debug_debuglog_record_action">Înregistrați jurnalul de depanare</string>
<string name="settings_support_installid_label">ID de instalare</string>
<string name="settings_support_installid_desc">Rapoartele automate de eroare sunt anonime. Partajați ID-ul de instalare dacă dezvoltatorul trebuie să vă găsească rapoartele de eroare.</string>
<string name="settings_support_label">Suport</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Размер</string>
<string name="debug_debuglog_size_compressed_label">Сжатый размер</string>
<string name="debug_notification_channel_label">Уведомления отладки</string>
<string name="debug_debuglog_file_label">Записанный файл лога</string>
<string name="debug_debuglog_record_action">Записать лог отладки</string>
<string name="settings_support_installid_label">ID установки</string>
<string name="settings_support_installid_desc">Автоматические отчёты об ошибках анонимны. Поделитесь Вашим ID установки, если разработчику нужен доступ к Вашим отчётам об ошибках.</string>
<string name="settings_support_label">Поддержка</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Veľkosť</string>
<string name="debug_debuglog_size_compressed_label">Komprimovaná veľkosť</string>
<string name="debug_notification_channel_label">Oznámenia o ladení</string>
<string name="debug_debuglog_file_label">Zaznamenaný súbor protokolu</string>
<string name="debug_debuglog_record_action">Zaznamenať protokol ladenia</string>
<string name="settings_support_installid_label">ID inštalácie</string>
<string name="settings_support_installid_desc">Automatické chybové hlásenia sú anonymné. Zdieľajte svoje ID inštalácie, ak vývojár potrebuje nájsť vaše chybové hlásenia.</string>
<string name="settings_support_label">Podpora</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Boyut</string>
<string name="debug_debuglog_size_compressed_label">Sıkıştırılmış boyut</string>
<string name="debug_notification_channel_label">Hata ayıklama bildirimleri</string>
<string name="debug_debuglog_file_label">Kayıtlı günlük dosyası</string>
<string name="debug_debuglog_record_action">Hata ayıklama günlüğünü kaydet</string>
<string name="settings_support_installid_label">Kurulum Kimliği</string>
<string name="settings_support_installid_desc">Otomatik hata raporları anonimdir. Geliştiricinin hata raporlarınızı bulması gerekiyorsa kurulum kimliğinizi paylaşabilirsiniz.</string>
<string name="settings_support_label">Destek</string>
-2
View File
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">Розмір</string>
<string name="debug_debuglog_size_compressed_label">Стиснутий розмір</string>
<string name="debug_notification_channel_label">Повідомлення зневадження</string>
<string name="debug_debuglog_file_label">Журнал зневадження</string>
<string name="debug_debuglog_record_action">Зберегти журнал зневадження</string>
<string name="settings_support_installid_label">ID інсталяції</string>
<string name="settings_support_installid_desc">Автоматичні звіти про помилки є анонімними. Поділіться своїм ID інсталяції, якщо розробнику потрібно знайти ваші звіти про помилки.</string>
<string name="settings_support_label">Підтримка</string>
-2
View File
@@ -47,8 +47,6 @@
<string name="debug_debuglog_size_label">Kích thước</string>
<string name="debug_debuglog_size_compressed_label">Kích thước nén</string>
<string name="debug_notification_channel_label">Thông báo gỡ lỗi</string>
<string name="debug_debuglog_file_label">Tệp nhật ký đã ghi</string>
<string name="debug_debuglog_record_action">Ghi nhật ký gỡ lỗi</string>
<string name="settings_support_installid_label">ID Cài đặt</string>
<string name="settings_support_installid_desc">Các báo cáo lỗi tự động được ẩn danh. Chia sẻ ID cài đặt của bạn nếu nhà phát triển cần tìm các báo cáo lỗi của bạn.</string>
<string name="settings_support_label">Hỗ trợ</string>
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">大小</string>
<string name="debug_debuglog_size_compressed_label">压缩后大小</string>
<string name="debug_notification_channel_label">调试通知</string>
<string name="debug_debuglog_file_label">记录的日志文件</string>
<string name="debug_debuglog_record_action">记录调试日志</string>
<string name="settings_support_installid_label">安装 ID</string>
<string name="settings_support_installid_desc">自动发送的错误报告是匿名的。如果需要,给开发者发送您的安装 ID 以便找出您的错误报告。</string>
<string name="settings_support_label">用户支持</string>
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">大小</string>
<string name="debug_debuglog_size_compressed_label">壓縮後大小</string>
<string name="debug_notification_channel_label">偵錯通知</string>
<string name="debug_debuglog_file_label">已記錄的記錄檔</string>
<string name="debug_debuglog_record_action">記錄偵錯記錄</string>
<string name="settings_support_installid_label">安裝 ID</string>
<string name="settings_support_installid_desc">自動傳送的錯誤報告是匿名的。如果開發人員需要找出您的錯誤報告將分享你的安裝 ID。</string>
<string name="settings_support_label">支援</string>
@@ -53,8 +53,6 @@
<string name="debug_debuglog_size_label">大小</string>
<string name="debug_debuglog_size_compressed_label">壓縮後大小</string>
<string name="debug_notification_channel_label">偵錯通知</string>
<string name="debug_debuglog_file_label">已記錄的記錄檔</string>
<string name="debug_debuglog_record_action">記錄偵錯記錄</string>
<string name="settings_support_installid_label">安裝 ID</string>
<string name="settings_support_installid_desc">自動傳送的錯誤報告是匿名的。如果開發人員需要找出您的錯誤報告將分享你的安裝 ID。</string>
<string name="settings_support_label">支援</string>
+7 -2
View File
@@ -1,6 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="general_share_action">Share</string>
<string name="general_done_action">Done</string>
<string name="general_cancel_action">Cancel</string>
<string name="general_copy_action">Copy</string>
<string name="general_thank_you_label">Thank you</string>
<string name="general_upgrade_action">Upgrade</string>
@@ -53,15 +54,19 @@
<string name="settings_popup_connected_label">Show connection popup</string>
<string name="settings_popup_connected_description">Show a popup when the device connects for the first time.</string>
<string name="notification_channel_device_status_label">Device status</string>
<string name="support_debuglog_label">Debug log</string>
<string name="support_debuglog_desc">Record everything the app is doing into a text file that you can share.</string>
<string name="debug_debuglog_size_label">Size</string>
<string name="debug_debuglog_size_compressed_label">Compressed size</string>
<string name="debug_notification_channel_label">Debug notifications</string>
<string name="debug_debuglog_file_label">Recorded log file</string>
<string name="debug_debuglog_recording_progress">Recording debug log</string>
<string name="debug_debuglog_record_action">Record debug log</string>
<string name="debug_debuglog_stop_action">Stop recording</string>
<string name="debug_debuglog_sensitive_information_message">The generated file contains sensitive information (e.g. Bluetooth device details). Only share it with trusted parties.</string>
<string name="settings_debuglog_explanation">This feature records everything the app is doing into a shareable file. The generated file contains sensitive information (e.g. Bluetooth device details). Only share it with trusted parties (e.g. a developer that is troubleshooting an issue).</string>
<string name="settings_support_installid_label">Install ID</string>
<string name="settings_support_installid_desc">Automatic error reports are anonymous. Share your install ID if the developer needs to find your error reports.</string>
@@ -1,11 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:icon="@drawable/ic_baseline_text_snippet_24"
android:key="debug.log.record"
android:title="@string/debug_debuglog_record_action" />
<eu.darken.capod.common.preferences.MaterialSwitchPreference
android:icon="@drawable/ic_baseline_bug_report_24"
android:key="debug.mode.enabled"
+4 -12
View File
@@ -18,19 +18,11 @@
android:data="https://github.com/d4rken-org/capod/issues" />
</eu.darken.capod.common.preferences.IntentPreference>
<!-- Hide Install-ID and bug reporting settings, but keep for possible use in the future.
<PreferenceCategory app:title="@string/settings_category_other_label">
<Preference
android:icon="@drawable/ic_id_onsurface"
android:key="support.installid"
android:summary="@string/settings_support_installid_desc"
android:title="@string/settings_support_installid_label" />
<CheckBoxPreference
android:icon="@drawable/ic_spider_thread_onsurface"
android:key="debug.bugreport.automatic.enabled"
android:summary="@string/settings_debug_autoreports_description"
android:title="@string/settings_debug_autoreports_label" />
android:icon="@drawable/ic_baseline_bug_report_24"
android:key="support.debuglog"
android:summary="@string/support_debuglog_desc"
android:title="@string/support_debuglog_label" />
</PreferenceCategory>
-->
</PreferenceScreen>
+4
View File
@@ -25,6 +25,7 @@ platform :android do
sh "bash ./remove_unsupported_languages.sh"
supply(
track: 'beta',
rollout: '0.10',
package_name: 'eu.darken.capod',
skip_upload_changelogs: 'false',
skip_upload_apk: 'true',
@@ -42,6 +43,7 @@ platform :android do
sh "bash ./remove_unsupported_languages.sh"
supply(
track: 'wear:beta',
rollout: '0.10',
package_name: 'eu.darken.capod',
skip_upload_changelogs: 'false',
skip_upload_apk: 'true',
@@ -59,6 +61,7 @@ platform :android do
sh "bash ./remove_unsupported_languages.sh"
supply(
track: 'beta',
rollout: '0.10',
package_name: 'eu.darken.capod',
skip_upload_changelogs: 'false',
skip_upload_apk: 'true',
@@ -76,6 +79,7 @@ platform :android do
sh "bash ./remove_unsupported_languages.sh"
supply(
track: 'wear:beta',
rollout: '0.10',
package_name: 'eu.darken.capod',
skip_upload_changelogs: 'false',
skip_upload_apk: 'true',
@@ -0,0 +1,2 @@
Bugfixes, performance improvements and maybe new features.
¯\_(ツ)_/¯
@@ -0,0 +1,2 @@
Bugfixes, performance improvements and maybe new features.
¯\_(ツ)_/¯
+1 -1
View File
@@ -1,6 +1,6 @@
### Updated by release.sh ###
project.versioning.major=2
project.versioning.minor=14
project.versioning.minor=15
project.versioning.patch=0
project.versioning.build=0
#############################