mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Compare commits
2
Commits
ed66731207
...
43740d8b2e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43740d8b2e | ||
|
|
83849efe80 |
@@ -193,4 +193,15 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
||||||
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||||
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
|
||||||
|
# The Play flavor is obfuscated; the bundle embeds this mapping, but keep it reachable
|
||||||
|
# for retracing user-submitted logs without going through Play Console.
|
||||||
|
- name: Archive R8 mapping
|
||||||
|
if: always() && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
||||||
|
with:
|
||||||
|
name: r8-mapping-${{ github.ref_name }}
|
||||||
|
path: app/build/outputs/mapping/gplay*/mapping.txt
|
||||||
|
if-no-files-found: warn
|
||||||
|
overwrite: true
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ android {
|
|||||||
includeInApk = false
|
includeInApk = false
|
||||||
includeInBundle = false
|
includeInBundle = false
|
||||||
}
|
}
|
||||||
|
proguardFiles("proguard-rules-foss.pro")
|
||||||
}
|
}
|
||||||
create("gplay") {
|
create("gplay") {
|
||||||
dimension = "version"
|
dimension = "version"
|
||||||
signingConfig = signingConfigs["releaseGplay"]
|
signingConfig = signingConfigs["releaseGplay"]
|
||||||
|
proguardFiles("proguard-rules-gplay.pro")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# The FOSS build is open source and ships on GitHub/F-Droid; readable stack traces are worth more
|
||||||
|
# than smaller identifiers there. Only the Google Play flavor obfuscates (proguard-rules-gplay.pro).
|
||||||
|
-dontobfuscate
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Google Play scores the obfuscation share of every uploaded bundle and restricts listings that
|
||||||
|
# stay below its threshold, so the Play flavor obfuscates. The FOSS flavor does not
|
||||||
|
# (proguard-rules-foss.pro).
|
||||||
|
|
||||||
|
# Keep stack traces retraceable with the mapping file that the bundle embeds.
|
||||||
|
-keepattributes SourceFile,LineNumberTable
|
||||||
|
-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
# AAP session logs identify settings and commands by their simple class name
|
||||||
|
# (AapSessionEngine, AapAncController, AapOutboundController). Names only; unused classes are
|
||||||
|
# still removed.
|
||||||
|
-keepnames class eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
||||||
|
-keepnames class * extends eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
||||||
|
-keepnames class eu.darken.capod.pods.core.apple.aap.protocol.AapCommand
|
||||||
|
-keepnames class * extends eu.darken.capod.pods.core.apple.aap.protocol.AapCommand
|
||||||
|
|
||||||
|
# ViewModel1 derives its log tag from the subclass name (VM:OverviewViewModel).
|
||||||
|
-keepnames class * extends eu.darken.capod.common.uix.ViewModel1
|
||||||
|
|
||||||
|
# Error dialogs and log summaries show exceptions by class name (LocalizedError, asLogSummary).
|
||||||
|
-keepnames class * extends java.lang.Throwable
|
||||||
Vendored
-1
@@ -1,5 +1,4 @@
|
|||||||
-keep class eu.darken.capod.BuildConfig { *; }
|
-keep class eu.darken.capod.BuildConfig { *; }
|
||||||
-dontobfuscate
|
|
||||||
|
|
||||||
# work-runtime 2.7.1 (pulled by Glance) uses Class.newInstance() reflection throughout.
|
# work-runtime 2.7.1 (pulled by Glance) uses Class.newInstance() reflection throughout.
|
||||||
# R8 full mode strips no-arg constructors not reachable by static analysis.
|
# R8 full mode strips no-arg constructors not reachable by static analysis.
|
||||||
|
|||||||
+3
-1
@@ -10,6 +10,7 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
|
|||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
import eu.darken.capod.common.error.HasLocalizedError
|
import eu.darken.capod.common.error.HasLocalizedError
|
||||||
import eu.darken.capod.common.error.LocalizedError
|
import eu.darken.capod.common.error.LocalizedError
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
class GplayServiceUnavailableException(cause: Throwable) :
|
class GplayServiceUnavailableException(cause: Throwable) :
|
||||||
BillingException("Google Play services are unavailable.", cause), HasLocalizedError {
|
BillingException("Google Play services are unavailable.", cause), HasLocalizedError {
|
||||||
@@ -48,10 +49,11 @@ class GplayServiceUnavailableException(cause: Throwable) :
|
|||||||
)
|
)
|
||||||
|
|
||||||
private fun onLaunchFailed(e: Exception) {
|
private fun onLaunchFailed(e: Exception) {
|
||||||
log(ERROR) { "Can't launch settings intent for Google Play: $e" }
|
log(TAG, ERROR) { "Can't launch settings intent for Google Play: $e" }
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val GPLAY_PKG = "com.android.vending"
|
private const val GPLAY_PKG = "com.android.vending"
|
||||||
|
private val TAG = logTag("Upgrade", "Gplay", "ServiceUnavailable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
fun BroadcastReceiver.PendingResult.finish2(): Boolean = try {
|
fun BroadcastReceiver.PendingResult.finish2(): Boolean = try {
|
||||||
finish()
|
finish()
|
||||||
true
|
true
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
log { "BroadcastReceiver.PendingResult.finish() failed: $e" }
|
log(TAG) { "BroadcastReceiver.PendingResult.finish() failed: $e" }
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val TAG = logTag("Common", "BroadcastReceiver")
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
|
|||||||
import eu.darken.capod.common.debug.logging.asLog
|
import eu.darken.capod.common.debug.logging.asLog
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
@Reusable
|
@Reusable
|
||||||
class WebpageTool @Inject constructor(
|
class WebpageTool @Inject constructor(
|
||||||
@@ -25,9 +26,12 @@ class WebpageTool @Inject constructor(
|
|||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log(ERROR) { "Failed to launch: ${e.asLog()}" }
|
log(TAG, ERROR) { "Failed to launch: ${e.asLog()}" }
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = logTag("WebpageTool")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package eu.darken.capod.common.bluetooth
|
|||||||
|
|
||||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
suspend fun Collection<BleScanResult>.onlyNewAndUnique(): List<BleScanResult> = this
|
suspend fun Collection<BleScanResult>.onlyNewAndUnique(): List<BleScanResult> = this
|
||||||
.groupBy { it.address }
|
.groupBy { it.address }
|
||||||
@@ -10,7 +11,9 @@ suspend fun Collection<BleScanResult>.onlyNewAndUnique(): List<BleScanResult> =
|
|||||||
// For each address we only want the newest result, upstream may batch data
|
// For each address we only want the newest result, upstream may batch data
|
||||||
val newest = sameAdrDevs.maxByOrNull { it.generatedAtNanos }!!
|
val newest = sameAdrDevs.maxByOrNull { it.generatedAtNanos }!!
|
||||||
sameAdrDevs.minus(newest).let {
|
sameAdrDevs.minus(newest).let {
|
||||||
if (it.isNotEmpty()) log(VERBOSE) { "Discarding stale results: ${it.logSummary()}" }
|
if (it.isNotEmpty()) log(TAG, VERBOSE) { "Discarding stale results: ${it.logSummary()}" }
|
||||||
}
|
}
|
||||||
newest
|
newest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val TAG = logTag("Bluetooth", "Scanner")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.bluetooth.le.ScanResult
|
|||||||
import android.os.ParcelUuid
|
import android.os.ParcelUuid
|
||||||
import eu.darken.capod.common.debug.logging.asLog
|
import eu.darken.capod.common.debug.logging.asLog
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
fun BluetoothDevice.hasFeature(uuid: ParcelUuid): Boolean {
|
fun BluetoothDevice.hasFeature(uuid: ParcelUuid): Boolean {
|
||||||
return uuids?.contains(uuid) ?: false
|
return uuids?.contains(uuid) ?: false
|
||||||
@@ -21,6 +22,8 @@ fun BluetoothDevice.hasFeature(uuid: ParcelUuid): Boolean {
|
|||||||
fun ScanFilter.matchesSafe(scanResult: ScanResult): Boolean = try {
|
fun ScanFilter.matchesSafe(scanResult: ScanResult): Boolean = try {
|
||||||
matches(scanResult)
|
matches(scanResult)
|
||||||
} catch (e: NullPointerException) {
|
} catch (e: NullPointerException) {
|
||||||
log { "AOSP error: ${e.asLog()}" }
|
log(TAG) { "AOSP error: ${e.asLog()}" }
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val TAG = logTag("Bluetooth", "Extensions")
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
|||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
class DataStoreValue<T>(
|
class DataStoreValue<T>(
|
||||||
private val dataStore: DataStore<Preferences>,
|
private val dataStore: DataStore<Preferences>,
|
||||||
@@ -45,10 +46,14 @@ class DataStoreValue<T>(
|
|||||||
prefs[key as Preferences.Key<Any>] = raw
|
prefs[key as Preferences.Key<Any>] = raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log(VERBOSE) { "DataStoreValue($keyName) updated from $old to $new" }
|
log(TAG, VERBOSE) { "DataStoreValue($keyName) updated from $old to $new" }
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return Updated(old as T, new as T)
|
return Updated(old as T, new as T)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = logTag("DataStore", "Value")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T> DataStoreValue<T>.value(): T = flow.first()
|
suspend fun <T> DataStoreValue<T>.value(): T = flow.first()
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ object Logging {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val TAG = logTag("Logging")
|
||||||
|
|
||||||
private val internalLoggers = mutableListOf<Logger>()
|
private val internalLoggers = mutableListOf<Logger>()
|
||||||
|
|
||||||
val loggers: List<Logger>
|
val loggers: List<Logger>
|
||||||
@@ -46,11 +48,11 @@ object Logging {
|
|||||||
|
|
||||||
fun install(logger: Logger) {
|
fun install(logger: Logger) {
|
||||||
synchronized(internalLoggers) { internalLoggers.add(logger) }
|
synchronized(internalLoggers) { internalLoggers.add(logger) }
|
||||||
log { "Was installed $logger" }
|
log(TAG) { "Was installed $logger" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(logger: Logger) {
|
fun remove(logger: Logger) {
|
||||||
log { "Removing: $logger" }
|
log(TAG) { "Removing: $logger" }
|
||||||
synchronized(internalLoggers) { internalLoggers.remove(logger) }
|
synchronized(internalLoggers) { internalLoggers.remove(logger) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,26 +79,11 @@ object Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clearAll() {
|
fun clearAll() {
|
||||||
log { "Clearing all loggers" }
|
log(TAG) { "Clearing all loggers" }
|
||||||
synchronized(internalLoggers) { internalLoggers.clear() }
|
synchronized(internalLoggers) { internalLoggers.clear() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun Any.log(
|
|
||||||
priority: Logging.Priority = Logging.Priority.DEBUG,
|
|
||||||
metaData: Map<String, Any>? = null,
|
|
||||||
message: () -> String,
|
|
||||||
) {
|
|
||||||
if (Logging.hasReceivers) {
|
|
||||||
Logging.logInternal(
|
|
||||||
tag = "CAP:${logTagViaCallSite()}",
|
|
||||||
priority = priority,
|
|
||||||
metaData = metaData,
|
|
||||||
message = message(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun log(
|
inline fun log(
|
||||||
tag: String,
|
tag: String,
|
||||||
priority: Logging.Priority = Logging.Priority.DEBUG,
|
priority: Logging.Priority = Logging.Priority.DEBUG,
|
||||||
@@ -135,15 +122,3 @@ fun Throwable.asLogSummary(): String {
|
|||||||
|
|
||||||
private fun Throwable.safeMessage(): String? = runCatching { message }.getOrNull()
|
private fun Throwable.safeMessage(): String? = runCatching { message }.getOrNull()
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal fun Any.logTagViaCallSite(): String {
|
|
||||||
val javaClass = this::class.java
|
|
||||||
val fullClassName = javaClass.name
|
|
||||||
val outerClassName = fullClassName.substringBefore('$')
|
|
||||||
val simplerOuterClassName = outerClassName.substringAfterLast('.')
|
|
||||||
return if (simplerOuterClassName.isEmpty()) {
|
|
||||||
fullClassName
|
|
||||||
} else {
|
|
||||||
simplerOuterClassName.removeSuffix("Kt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.lifecycle.Observer
|
|||||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
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.log
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
|
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
|
||||||
@@ -46,7 +47,7 @@ class SingleLiveEvent<T> : MutableLiveData<T>() {
|
|||||||
@MainThread
|
@MainThread
|
||||||
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
|
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
|
||||||
if (hasActiveObservers()) {
|
if (hasActiveObservers()) {
|
||||||
log(WARN) { "Multiple observers registered but only one will be notified of changes." }
|
log(TAG, WARN) { "Multiple observers registered but only one will be notified of changes." }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observe the internal MutableLiveData
|
// Observe the internal MutableLiveData
|
||||||
@@ -73,4 +74,8 @@ class SingleLiveEvent<T> : MutableLiveData<T>() {
|
|||||||
fun call() {
|
fun call() {
|
||||||
value = null
|
value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = logTag("SingleLiveEvent")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ abstract class ViewModel2(
|
|||||||
|
|
||||||
if (this is ErrorEventSource) {
|
if (this is ErrorEventSource) {
|
||||||
return CoroutineExceptionHandler { _, ex ->
|
return CoroutineExceptionHandler { _, ex ->
|
||||||
log(WARN) { "Error during launch: ${ex.asLog()}" }
|
log(TAG, WARN) { "Error during launch: ${ex.asLog()}" }
|
||||||
errorEvents.postValue(ex)
|
errorEvents.postValue(ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class BluetoothEventReceiver : BroadcastReceiver() {
|
|||||||
log(TAG, WARN) { "Event without Bluetooth device association." }
|
log(TAG, WARN) { "Event without Bluetooth device association." }
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log { "Event related to $bluetoothDevice" }
|
log(TAG) { "Event related to $bluetoothDevice" }
|
||||||
}
|
}
|
||||||
val supportedFeatures = try {
|
val supportedFeatures = try {
|
||||||
ContinuityProtocol.BLE_FEATURE_UUIDS.filter { bluetoothDevice.hasFeature(it) }
|
ContinuityProtocol.BLE_FEATURE_UUIDS.filter { bluetoothDevice.hasFeature(it) }
|
||||||
@@ -47,7 +47,7 @@ class BluetoothEventReceiver : BroadcastReceiver() {
|
|||||||
log(TAG) { "Device has no features we support." }
|
log(TAG) { "Device has no features we support." }
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log { "Device has the following we features we support $supportedFeatures" }
|
log(TAG) { "Device has the following we features we support $supportedFeatures" }
|
||||||
}
|
}
|
||||||
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import eu.darken.capod.pods.core.apple.ble.BATTERY_RESOLUTION_DECILE
|
|||||||
import eu.darken.capod.pods.core.apple.ble.BATTERY_RESOLUTION_PERCENT
|
import eu.darken.capod.pods.core.apple.ble.BATTERY_RESOLUTION_PERCENT
|
||||||
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot
|
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot
|
||||||
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot.Pod
|
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot.Pod
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot, HasEarDetectionDual, HasCase,
|
interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot, HasEarDetectionDual, HasCase,
|
||||||
HasDualMicrophone, HasAppleColor {
|
HasDualMicrophone, HasAppleColor {
|
||||||
@@ -38,7 +39,7 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
|
|||||||
return when (value) {
|
return when (value) {
|
||||||
15 -> null
|
15 -> null
|
||||||
else -> if (value > 10) {
|
else -> if (value > 10) {
|
||||||
log { "Left pod: Above 100% battery: $value" }
|
log(TAG) { "Left pod: Above 100% battery: $value" }
|
||||||
1.0f
|
1.0f
|
||||||
} else {
|
} else {
|
||||||
(value / 10f)
|
(value / 10f)
|
||||||
@@ -59,7 +60,7 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
|
|||||||
return when (value) {
|
return when (value) {
|
||||||
15 -> null
|
15 -> null
|
||||||
else -> if (value > 10) {
|
else -> if (value > 10) {
|
||||||
log { "Right pod: Above 100% battery: $value" }
|
log(TAG) { "Right pod: Above 100% battery: $value" }
|
||||||
1.0f
|
1.0f
|
||||||
} else {
|
} else {
|
||||||
value / 10f
|
value / 10f
|
||||||
@@ -131,7 +132,7 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
|
|||||||
return when (val value = pubCaseBattery.toInt()) {
|
return when (val value = pubCaseBattery.toInt()) {
|
||||||
15 -> null
|
15 -> null
|
||||||
else -> if (value > 10) {
|
else -> if (value > 10) {
|
||||||
log { "Case: Above 100% battery: $value" }
|
log(TAG) { "Case: Above 100% battery: $value" }
|
||||||
1.0f
|
1.0f
|
||||||
} else {
|
} else {
|
||||||
value / 10f
|
value / 10f
|
||||||
@@ -206,3 +207,5 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val TAG = logTag("Pod", "Apple", "DualApplePods")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package eu.darken.capod.pods.core.apple.ble.devices
|
|||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
import eu.darken.capod.common.lowerNibble
|
import eu.darken.capod.common.lowerNibble
|
||||||
import eu.darken.capod.pods.core.apple.ble.SingleBlePodSnapshot
|
import eu.darken.capod.pods.core.apple.ble.SingleBlePodSnapshot
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Devices that only present a single charge level, e.g. most Beats devices
|
* Devices that only present a single charge level, e.g. most Beats devices
|
||||||
@@ -17,11 +18,13 @@ interface SingleApplePods : ApplePods, SingleBlePodSnapshot, HasAppleColor {
|
|||||||
return when (val value = pubPodsBattery.lowerNibble.toInt()) {
|
return when (val value = pubPodsBattery.lowerNibble.toInt()) {
|
||||||
15 -> null
|
15 -> null
|
||||||
else -> if (value > 10) {
|
else -> if (value > 10) {
|
||||||
log { "Headset above 100% battery: $value" }
|
log(TAG) { "Headset above 100% battery: $value" }
|
||||||
1.0f
|
1.0f
|
||||||
} else {
|
} else {
|
||||||
(value / 10f)
|
(value / 10f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val TAG = logTag("Pod", "Apple", "SingleApplePods")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ object ProximityPairing {
|
|||||||
class Decoder @Inject constructor() {
|
class Decoder @Inject constructor() {
|
||||||
fun decode(message: ContinuityProtocol.Message): ProximityMessage? {
|
fun decode(message: ContinuityProtocol.Message): ProximityMessage? {
|
||||||
if (message.type != CONTINUITY_PROTOCOL_MESSAGE_TYPE_PROXIMITY_PAIRING) {
|
if (message.type != CONTINUITY_PROTOCOL_MESSAGE_TYPE_PROXIMITY_PAIRING) {
|
||||||
log { "Not a proximity pairing message: $this" }
|
log(TAG) { "Not a proximity pairing message: $message" }
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class DeviceProfilesRepo @Inject constructor(
|
|||||||
if (addFirst) add(0, profile) else add(profile)
|
if (addFirst) add(0, profile) else add(profile)
|
||||||
}.toList()
|
}.toList()
|
||||||
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
||||||
log(VERBOSE) { "Added device profile: ${profile.label}" }
|
log(TAG, VERBOSE) { "Added device profile: ${profile.label}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateProfile(profile: DeviceProfile) = mutex.withLock {
|
suspend fun updateProfile(profile: DeviceProfile) = mutex.withLock {
|
||||||
@@ -152,7 +152,7 @@ class DeviceProfilesRepo @Inject constructor(
|
|||||||
if (it.id == profile.id) profile else it
|
if (it.id == profile.id) profile else it
|
||||||
}
|
}
|
||||||
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
||||||
log(VERBOSE) { "Updated device profile: ${profile.label}" }
|
log(TAG, VERBOSE) { "Updated device profile: ${profile.label}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,14 +176,14 @@ class DeviceProfilesRepo @Inject constructor(
|
|||||||
checkAddressUniqueness(updated, otherProfiles)
|
checkAddressUniqueness(updated, otherProfiles)
|
||||||
val updatedProfiles = currentContainer.profiles.map { if (it.id == id) updated else it }
|
val updatedProfiles = currentContainer.profiles.map { if (it.id == id) updated else it }
|
||||||
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
||||||
log(VERBOSE) { "Updated apple device profile: ${updated.label}" }
|
log(TAG, VERBOSE) { "Updated apple device profile: ${updated.label}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun removeProfile(profileId: ProfileId) = mutex.withLock {
|
suspend fun removeProfile(profileId: ProfileId) = mutex.withLock {
|
||||||
val currentContainer = settings.profiles.valueBlocking
|
val currentContainer = settings.profiles.valueBlocking
|
||||||
val updatedProfiles = currentContainer.profiles.filter { it.id != profileId }
|
val updatedProfiles = currentContainer.profiles.filter { it.id != profileId }
|
||||||
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
|
||||||
log(VERBOSE) { "Removed device profile with ID: $profileId" }
|
log(TAG, VERBOSE) { "Removed device profile with ID: $profileId" }
|
||||||
deviceStateCache.delete(profileId)
|
deviceStateCache.delete(profileId)
|
||||||
batteryDrainStore.delete(profileId)
|
batteryDrainStore.delete(profileId)
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ class DeviceProfilesRepo @Inject constructor(
|
|||||||
val byId = current.associateBy { it.id }
|
val byId = current.associateBy { it.id }
|
||||||
val reordered = orderedIds.map { byId.getValue(it) }
|
val reordered = orderedIds.map { byId.getValue(it) }
|
||||||
settings.profiles.valueBlocking = DeviceProfilesContainer(reordered)
|
settings.profiles.valueBlocking = DeviceProfilesContainer(reordered)
|
||||||
log(VERBOSE) { "Reordered ${reordered.size} device profiles by ID" }
|
log(TAG, VERBOSE) { "Reordered ${reordered.size} device profiles by ID" }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clear() = mutex.withLock {
|
suspend fun clear() = mutex.withLock {
|
||||||
|
|||||||
Reference in New Issue
Block a user