mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
647300fd3a | ||
|
|
3eaf99390e | ||
|
|
0ee7915e31 | ||
|
|
10c880a800 | ||
|
|
b1bf658ff3 | ||
|
|
77dc13d9ba | ||
|
|
21b6cfc013 | ||
|
|
5d953ee23e | ||
|
|
42f2f5bfee | ||
|
|
6708901394 | ||
|
|
788038beb7 | ||
|
|
18ac05ed80 |
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
+46
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
-17
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
-12
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 d’installation</string>
|
||||
<string name="settings_support_installid_desc">Les relevés automatiques d’erreur sont anonymes. Partagez votre ID d’installation au cas où le développeur aurait besoin de trouver votre relevé d’erreur.</string>
|
||||
<string name="settings_support_label">Assistance</string>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
|
||||
#############################
|
||||
|
||||
Reference in New Issue
Block a user