diff --git a/app/src/main/java/eu/darken/capod/common/bluetooth/BleScannerExtensions.kt b/app/src/main/java/eu/darken/capod/common/bluetooth/BleScannerExtensions.kt index 324e5d17..378375d0 100644 --- a/app/src/main/java/eu/darken/capod/common/bluetooth/BleScannerExtensions.kt +++ b/app/src/main/java/eu/darken/capod/common/bluetooth/BleScannerExtensions.kt @@ -1,2 +1,16 @@ package eu.darken.capod.common.bluetooth +import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.capod.common.debug.logging.log + +suspend fun Collection.onlyNewAndUnique(): List = this + .groupBy { it.address } + .values + .map { sameAdrDevs -> + // For each address we only want the newest result, upstream may batch data + val newest = sameAdrDevs.maxByOrNull { it.generatedAtNanos }!! + sameAdrDevs.minus(newest).let { + if (it.isNotEmpty()) log( VERBOSE) { "Discarding stale results: $it" } + } + newest + } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/common/serialization/NameBasedPolyJsonAdapterFactory.kt b/app/src/main/java/eu/darken/capod/common/serialization/NameBasedPolyJsonAdapterFactory.kt index 1f958f81..089db3af 100644 --- a/app/src/main/java/eu/darken/capod/common/serialization/NameBasedPolyJsonAdapterFactory.kt +++ b/app/src/main/java/eu/darken/capod/common/serialization/NameBasedPolyJsonAdapterFactory.kt @@ -1,11 +1,17 @@ @file:Suppress("MemberVisibilityCanBePrivate") -package eu.darken.sdmse.common.serialization +package eu.darken.capod.common.serialization import com.squareup.moshi.* import java.lang.reflect.Type import java.util.* import javax.annotation.CheckReturnValue +import kotlin.apply +import kotlin.collections.isNotEmpty +import kotlin.collections.plus +import kotlin.collections.toTypedArray +import kotlin.io.use +import kotlin.jvm.javaClass class NameBasedPolyJsonAdapterFactory internal constructor( val baseType: Class, diff --git a/app/src/main/java/eu/darken/capod/common/serialization/SerializationModule.kt b/app/src/main/java/eu/darken/capod/common/serialization/SerializationModule.kt index 4f2512d8..0714ed9d 100644 --- a/app/src/main/java/eu/darken/capod/common/serialization/SerializationModule.kt +++ b/app/src/main/java/eu/darken/capod/common/serialization/SerializationModule.kt @@ -5,6 +5,7 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import eu.darken.capod.devices.core.DeviceProfile import javax.inject.Singleton @InstallIn(SingletonComponent::class) @@ -13,9 +14,10 @@ class SerializationModule { @Provides @Singleton - fun moshi(): Moshi = Moshi.Builder() - .add(JavaInstantAdapter()) - .add(ByteArrayAdapter()) - .build() + fun moshi(): Moshi = Moshi.Builder().apply { + add(JavaInstantAdapter()) + add(ByteArrayAdapter()) + add(DeviceProfile.MOSHI_FACTORY) + }.build() } diff --git a/app/src/main/java/eu/darken/capod/devices/core/AppleDeviceProfile.kt b/app/src/main/java/eu/darken/capod/devices/core/AppleDeviceProfile.kt index 020ab5dd..6083e748 100644 --- a/app/src/main/java/eu/darken/capod/devices/core/AppleDeviceProfile.kt +++ b/app/src/main/java/eu/darken/capod/devices/core/AppleDeviceProfile.kt @@ -1,3 +1,22 @@ package eu.darken.capod.devices.core -data class AppleDeviceProfile() +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.apple.protocol.IdentityResolvingKey +import eu.darken.capod.pods.core.apple.protocol.ProximityEncryptionKey +import kotlinx.parcelize.Parcelize +import java.util.UUID + +@Parcelize +@JsonClass(generateAdapter = true) +data class AppleDeviceProfile( + @Json(name = "id") override val id: ProfileId = UUID.randomUUID().toString(), + @Json(name = "name") override val name: String, + @Json(name = "minimumSignalQuality") override val minimumSignalQuality: Float = 0.20f, + @Json(name = "isEnabled") override val isEnabled: Boolean = true, + @Json(name = "model") override val model: PodDevice.Model = PodDevice.Model.UNKNOWN, + @Json(name = "priority") override val priority: Int = 0, + @Json(name = "identityKey") val identityKey: IdentityResolvingKey? = null, + @Json(name = "encryptionKey") val encryptionKey: ProximityEncryptionKey? = null, +) : DeviceProfile \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/devices/core/DeviceProfile.kt b/app/src/main/java/eu/darken/capod/devices/core/DeviceProfile.kt index cb33e984..b66b578d 100644 --- a/app/src/main/java/eu/darken/capod/devices/core/DeviceProfile.kt +++ b/app/src/main/java/eu/darken/capod/devices/core/DeviceProfile.kt @@ -1,23 +1,21 @@ package eu.darken.capod.devices.core import android.os.Parcelable -import com.squareup.moshi.JsonClass -import eu.darken.capod.common.bluetooth.BluetoothAddress +import eu.darken.capod.common.serialization.NameBasedPolyJsonAdapterFactory import eu.darken.capod.pods.core.PodDevice -import eu.darken.capod.pods.core.apple.protocol.IdentityResolvingKey -import eu.darken.capod.pods.core.apple.protocol.ProximityEncryptionKey -import kotlinx.parcelize.Parcelize -import java.util.UUID +import kotlin.jvm.java -@Parcelize -@JsonClass(generateAdapter = true) -data class DeviceProfile( - val id: String = UUID.randomUUID().toString(), - val name: String, - val address: BluetoothAddress? = null, - val model: PodDevice.Model = PodDevice.Model.UNKNOWN, - val identityKey: IdentityResolvingKey? = null, - val encryptionKey: ProximityEncryptionKey? = null, - val minimumSignalQuality: Float = 0.20f, - val isEnabled: Boolean = true -) : Parcelable \ No newline at end of file +sealed interface DeviceProfile : Parcelable { + val id: ProfileId + val name: String + val minimumSignalQuality: Float + val isEnabled: Boolean + val model: PodDevice.Model + val priority: Int + + companion object { + val MOSHI_FACTORY: NameBasedPolyJsonAdapterFactory = + NameBasedPolyJsonAdapterFactory.of(DeviceProfile::class.java) + .withSubtype(AppleDeviceProfile::class.java, "identityKey") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/devices/core/DeviceProfileId.kt b/app/src/main/java/eu/darken/capod/devices/core/DeviceProfileId.kt index 99153add..966ae545 100644 --- a/app/src/main/java/eu/darken/capod/devices/core/DeviceProfileId.kt +++ b/app/src/main/java/eu/darken/capod/devices/core/DeviceProfileId.kt @@ -1,2 +1,3 @@ package eu.darken.capod.devices.core +typealias ProfileId = String \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/devices/core/DeviceProfilesRepo.kt b/app/src/main/java/eu/darken/capod/devices/core/DeviceProfilesRepo.kt index a219f506..e30cd4c6 100644 --- a/app/src/main/java/eu/darken/capod/devices/core/DeviceProfilesRepo.kt +++ b/app/src/main/java/eu/darken/capod/devices/core/DeviceProfilesRepo.kt @@ -15,7 +15,7 @@ import javax.inject.Singleton @Singleton class DeviceProfilesRepo @Inject constructor( - @param:ApplicationContext private val context: Context, + @ApplicationContext private val context: Context, private val moshi: Moshi, ) { @@ -77,10 +77,6 @@ class DeviceProfilesRepo @Inject constructor( saveProfiles() } - fun getProfile(profileId: String): DeviceProfile? { - return _profiles.value.find { it.id == profileId } - } - companion object { private const val KEY_PROFILES = "profiles" } diff --git a/app/src/main/java/eu/darken/capod/devices/ui/DeviceProfileVH.kt b/app/src/main/java/eu/darken/capod/devices/ui/DeviceProfileVH.kt index 48ce1c44..d3129852 100644 --- a/app/src/main/java/eu/darken/capod/devices/ui/DeviceProfileVH.kt +++ b/app/src/main/java/eu/darken/capod/devices/ui/DeviceProfileVH.kt @@ -21,10 +21,6 @@ class DeviceProfileVH(parent: ViewGroup) : deviceName.text = profile.name deviceDetails.text = buildString { - profile.address?.let { - append(it.toString()) - append(" • ") - } append(profile.model.name) } diff --git a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitorExtensions.kt b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitorExtensions.kt index 684dc0eb..89b8cb3d 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitorExtensions.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitorExtensions.kt @@ -6,6 +6,6 @@ import kotlinx.coroutines.flow.map import kotlin.collections.firstOrNull fun PodMonitor.devicesWithProfiles(): Flow> = devices - .map { devices -> devices.filter { it.profile != null } } + .map { devices -> devices.filter { it.meta.profile != null } } fun PodMonitor.primaryDevice(): Flow = devicesWithProfiles().map { it.firstOrNull() } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/PodDevice.kt b/app/src/main/java/eu/darken/capod/pods/core/PodDevice.kt index e9125c7f..f255a682 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/PodDevice.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/PodDevice.kt @@ -9,6 +9,7 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.bluetooth.BluetoothAddress import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE import eu.darken.capod.common.debug.logging.log +import eu.darken.capod.devices.core.DeviceProfile import java.time.Duration import java.time.Instant import java.util.UUID @@ -58,6 +59,12 @@ interface PodDevice { "${entry.key}: ${entry.value.joinToString(separator = " ") { String.format("%02X", it) }}" } + interface Meta { + val profile: DeviceProfile? + } + + val meta: Meta + fun getLabel(context: Context): String = model.label @get:DrawableRes diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt index 1dea1e48..547b2af7 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt @@ -2,13 +2,13 @@ package eu.darken.capod.pods.core.apple import eu.darken.capod.common.lowerNibble import eu.darken.capod.common.upperNibble +import eu.darken.capod.devices.core.AppleDeviceProfile import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.protocol.ProximityPayload interface ApplePods : PodDevice { val payload: ProximityPayload - val flags: Flags // We start counting at the airpods prefix byte val pubPrefix: UByte @@ -56,7 +56,10 @@ interface ApplePods : PodDevice { ) } - data class Flags( + data class AppleMeta( val isIRKMatch: Boolean, - ) + override val profile: AppleDeviceProfile?, + ) : PodDevice.Meta + + override val meta: AppleMeta } \ No newline at end of file