From 3fc18bc40b42a50428c2e279f6635bb7b8059c43 Mon Sep 17 00:00:00 2001 From: darken Date: Sat, 29 Jan 2022 20:00:04 +0100 Subject: [PATCH] Improve sorting and heuristics. --- .../capod/common/bluetooth/FakeBleData.kt | 65 ++++++++++++++----- .../main/ui/overview/OverviewFragment.kt | 4 +- .../main/ui/overview/OverviewFragmentVM.kt | 4 +- .../cards/pods/BasicSingleApplePodsCardVH.kt | 6 +- .../cards/pods/DualApplePodsCardVH.kt | 6 +- .../ui/overview/cards/pods/PodDeviceVH.kt | 6 ++ .../cards/pods/SingleApplePodsCardVH.kt | 6 +- .../cards/pods/UnknownPodDeviceCardVH.kt | 6 +- .../darken/capod/monitor/core/PodMonitor.kt | 65 ++++++++++--------- .../eu/darken/capod/pods/core/PodDevice.kt | 6 +- .../capod/pods/core/PodDeviceExtensions.kt | 4 +- .../capod/pods/core/apple/ApplePodsFactory.kt | 28 +++++--- .../pods/core/apple/UnknownAppleDevice.kt | 10 ++- .../pods/core/apple/airpods/AirPodsGen1.kt | 11 ++-- .../pods/core/apple/airpods/AirPodsGen2.kt | 11 ++-- .../pods/core/apple/airpods/AirPodsGen3.kt | 11 ++-- .../pods/core/apple/airpods/AirPodsMax.kt | 11 ++-- .../pods/core/apple/airpods/AirPodsPro.kt | 8 ++- .../capod/pods/core/apple/beats/BeatsFlex.kt | 11 ++-- .../capod/pods/core/apple/beats/BeatsSolo3.kt | 11 ++-- .../pods/core/apple/beats/BeatsStudio3.kt | 11 ++-- .../capod/pods/core/apple/beats/BeatsX.kt | 11 ++-- .../pods/core/apple/beats/PowerBeats3.kt | 11 ++-- .../pods/core/apple/beats/PowerBeatsPro.kt | 8 ++- 24 files changed, 203 insertions(+), 128 deletions(-) diff --git a/app/src/main/java/eu/darken/capod/common/bluetooth/FakeBleData.kt b/app/src/main/java/eu/darken/capod/common/bluetooth/FakeBleData.kt index a07f4c7b..54489e8c 100644 --- a/app/src/main/java/eu/darken/capod/common/bluetooth/FakeBleData.kt +++ b/app/src/main/java/eu/darken/capod/common/bluetooth/FakeBleData.kt @@ -18,41 +18,74 @@ class FakeBleData @Inject constructor( fun getFakeData(): Collection { val fakeDevices = mutableListOf() - // AirPods Pro - BleScanResult( - address = "78:73:AF:B4:85:5E", - rssi = Random.nextInt(100) * -1, - generatedAtNanos = SystemClockWrap.elapsedRealtimeNanos + 100, - manufacturerSpecificData = mapOf(76 to "07 19 01 0E 20 75 AA B6 31 00 05 9C 5A A4 5D C0 2C A0 B4 6F B9 ED 8E CE 03 97 CA".hexToByteArray()) - ).run { fakeDevices.add(this) } // AirPods Gen1 BleScanResult( - address = "4E:9E:D1:49:D2:6D", + address = "78:73:AF:B4:85:22", rssi = Random.nextInt(100) * -1, + generatedAtNanos = SystemClockWrap.elapsedRealtimeNanos + 100, + manufacturerSpecificData = mapOf(76 to "07 19 01 02 20 75 AA B6 31 00 05 9C 5A A4 5D C0 2C A0 B4 6F B9 ED 8E CE 03 97 CA".hexToByteArray()) + ).run { + if (Random.nextBoolean()) { + fakeDevices.add(this) + } + } + + // AirPods Gen2 + BleScanResult( + address = "78:73:FF:B4:85:5E", + rssi = Random.nextInt(100) * -1, + generatedAtNanos = SystemClockWrap.elapsedRealtimeNanos + 100, + manufacturerSpecificData = mapOf(76 to "07 19 01 0F 20 75 AA B6 31 00 05 9C 5A A4 5D C0 2C A0 B4 6F B9 ED 8E CE 03 97 CA".hexToByteArray()) + ).run { + if (Random.nextBoolean()) { + fakeDevices.add(this) + } + } + + // AirPods Gen3 + BleScanResult( + address = "4E:9E:D1:49:D2:6D", + rssi = Random.nextInt(15, 75) * -1, generatedAtNanos = SystemClockWrap.elapsedRealtimeNanos + 200, - manufacturerSpecificData = mapOf(76 to "07 19 01 02 20 55 AF 56 31 00 06 6F E4 DF 10 AF 10 60 81 03 3B 76 D9 C7 11 22 88".hexToByteArray()) - ).run { fakeDevices.add(this) } + manufacturerSpecificData = mapOf(76 to "07 19 01 13 20 55 AF 56 31 00 06 6F E4 DF 10 AF 10 60 81 03 3B 76 D9 C7 11 22 88".hexToByteArray()) + ).run { + if (Random.nextBoolean()) { + fakeDevices.add(this) + } + } // AirPods Max BleScanResult( address = "7E:E5:C7:65:D2:B5", - rssi = Random.nextInt(100) * -1, + rssi = Random.nextInt(15, 75) * -1, generatedAtNanos = SystemClockWrap.elapsedRealtimeNanos + 300, manufacturerSpecificData = mapOf(76 to "07 19 01 0A 20 02 05 80 04 0F 44 A7 60 9B F8 3C FD B1 D8 1C 61 EA 82 60 A3 2C 4E".hexToByteArray()) - ).run { fakeDevices.add(this) } + ).run { + if (Random.nextBoolean()) { + fakeDevices.add(this) + } + } // BeatsFlex BleScanResult( address = "5E:9E:D1:49:D2:6D", - rssi = Random.nextInt(100) * -1, + rssi = Random.nextInt(15, 75) * -1, generatedAtNanos = SystemClockWrap.elapsedRealtimeNanos + 400, manufacturerSpecificData = mapOf(76 to "07 19 01 10 20 0A F4 8F 00 01 00 C4 71 9F 9C EF A2 E3 BA 66 FE 1D 45 9F C9 2F A0".hexToByteArray()) - ).run { fakeDevices.add(this) } + ).run { + if (Random.nextBoolean()) { + fakeDevices.add(this) + } + } // Unknown Device BleScanResult( address = "6E:9E:D1:49:D2:6D", - rssi = Random.nextInt(100) * -1, + rssi = Random.nextInt(15, 75) * -1, generatedAtNanos = SystemClockWrap.elapsedRealtimeNanos + 500, manufacturerSpecificData = mapOf(76 to "07 19 01 FF 20 0A F4 8F 00 01 00 C4 71 9F 9C EF A2 E3 BA 66 FE 1D 45 9F C9 2F A0".hexToByteArray()) - ).run { fakeDevices.add(this) } + ).run { + if (Random.nextBoolean()) { + fakeDevices.add(this) + } + } return fakeDevices } diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt index 43e7595f..fa128bcb 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt @@ -70,7 +70,9 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) { } } - vm.listItems.observe2(ui) { adapter.update(it) } + vm.listItems.observe2(ui) { + adapter.update(it) + } vm.workerAutolaunch.observe2 { // While UI is active, subscribe to the autolaunch routine diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt index 63ebe407..9c87a4bb 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt @@ -4,7 +4,6 @@ import android.app.Activity import androidx.lifecycle.LiveData import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel -import eu.darken.capod.common.WebpageTool import eu.darken.capod.common.bluetooth.BluetoothManager2 import eu.darken.capod.common.coroutine.DispatcherProvider import eu.darken.capod.common.debug.autoreport.DebugSettings @@ -46,7 +45,6 @@ class OverviewFragmentVM @Inject constructor( debugSettings: DebugSettings, private val upgradeRepo: UpgradeRepo, private val bluetoothManager: BluetoothManager2, - private val webpageTool: WebpageTool, ) : ViewModel3(dispatcherProvider = dispatcherProvider) { val upgradeState = upgradeRepo.upgradeInfo.asLiveData2() @@ -55,7 +53,7 @@ class OverviewFragmentVM @Inject constructor( private val updateTicker = channelFlow { while (isActive) { trySend(Unit) - delay(1000) + delay(3000) } } diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/BasicSingleApplePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/BasicSingleApplePodsCardVH.kt index 80dd66ca..7d1a19c2 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/BasicSingleApplePodsCardVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/BasicSingleApplePodsCardVH.kt @@ -9,7 +9,6 @@ import eu.darken.capod.databinding.OverviewPodsAppleSingleBasicItemBinding import eu.darken.capod.pods.core.apple.BasicSingleApplePods import eu.darken.capod.pods.core.getBatteryDrawable import eu.darken.capod.pods.core.getBatteryLevelHeadset -import eu.darken.capod.pods.core.getSignalQuality import eu.darken.capod.pods.core.lastSeenFormatted import java.time.Instant @@ -34,10 +33,7 @@ class BasicSingleApplePodsCardVH(parent: ViewGroup) : lastSeen.text = device.lastSeenFormatted(item.now) - reception.text = device.getSignalQuality(context) - if (item.isMainPod) { - reception.append("\n(${getString(R.string.pods_yours)})") - } + reception.text = item.getReceptionText() batteryLabel.text = device.getBatteryLevelHeadset(context) batteryIcon.setImageResource(getBatteryDrawable(device.batteryHeadsetPercent)) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt index 96409253..b3922519 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt @@ -40,11 +40,9 @@ class DualApplePodsCardVH(parent: ViewGroup) : lastSeen.text = context.getString(R.string.last_seen_x, device.lastSeenFormatted(item.now)) firstSeen.text = context.getString(R.string.first_seen_x, device.firstSeenFormatted(item.now)) - firstSeen.isGone = Duration.between(device.firstSeenAt, device.lastSeenAt).toMinutes() < 1 + firstSeen.isGone = Duration.between(device.seenFirstAt, device.seenLastAt).toMinutes() < 1 - reception.text = device.getSignalQuality(context).let { - if (item.isMainPod) "$it\n(${getString(R.string.pods_yours)})" else it - } + reception.text = item.getReceptionText() // Left Pod device.apply { diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt index 1a26d88c..7c741017 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt @@ -3,11 +3,13 @@ package eu.darken.capod.main.ui.overview.cards.pods import android.view.ViewGroup import androidx.annotation.LayoutRes import androidx.viewbinding.ViewBinding +import eu.darken.capod.R import eu.darken.capod.common.lists.BindableVH import eu.darken.capod.common.lists.differ.DifferItem import eu.darken.capod.common.lists.modular.ModularAdapter import eu.darken.capod.main.ui.overview.OverviewAdapter import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.getSignalQuality import java.time.Instant abstract class PodDeviceVH( @@ -15,6 +17,10 @@ abstract class PodDeviceVH( parent: ViewGroup ) : ModularAdapter.VH(layoutId, parent), BindableVH { + fun Item.getReceptionText(): String = device.getSignalQuality(context) + .let { if (showDebug) "$it ${device.seenCounter}" else it } + .let { if (isMainPod) "$it\n(${getString(R.string.pods_yours)})" else it } + interface Item : OverviewAdapter.Item { val now: Instant diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SingleApplePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SingleApplePodsCardVH.kt index 365d3b90..d9a99905 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SingleApplePodsCardVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SingleApplePodsCardVH.kt @@ -10,7 +10,6 @@ import eu.darken.capod.databinding.OverviewPodsAppleSingleItemBinding import eu.darken.capod.pods.core.apple.SingleApplePods import eu.darken.capod.pods.core.getBatteryDrawable import eu.darken.capod.pods.core.getBatteryLevelHeadset -import eu.darken.capod.pods.core.getSignalQuality import eu.darken.capod.pods.core.lastSeenFormatted import java.time.Instant @@ -35,10 +34,7 @@ class SingleApplePodsCardVH(parent: ViewGroup) : lastSeen.text = device.lastSeenFormatted(item.now) - reception.text = device.getSignalQuality(context) - if (item.isMainPod) { - reception.append("\n(${getString(R.string.pods_yours)})") - } + reception.text = item.getReceptionText() batteryLabel.text = device.getBatteryLevelHeadset(context) batteryIcon.setImageResource(getBatteryDrawable(device.batteryHeadsetPercent)) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt index c197f018..2874ff38 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt @@ -6,7 +6,6 @@ import eu.darken.capod.R import eu.darken.capod.common.lists.binding import eu.darken.capod.databinding.OverviewPodsUnknownItemBinding import eu.darken.capod.pods.core.PodDevice -import eu.darken.capod.pods.core.getSignalQuality import eu.darken.capod.pods.core.lastSeenFormatted import java.time.Instant @@ -30,10 +29,7 @@ class UnknownPodDeviceCardVH(parent: ViewGroup) : lastSeen.text = device.lastSeenFormatted(item.now) - reception.text = device.getSignalQuality(context) - if (item.isMainPod) { - reception.append("\n(${getString(R.string.pods_yours)})") - } + reception.text = item.getReceptionText() rawdata.text = device.rawDataHex } diff --git a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt index dcfcf3dd..f04dc526 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt @@ -75,7 +75,7 @@ class PodMonitor @Inject constructor( val now = Instant.now() deviceCache.toList().forEach { (key, value) -> - if (Duration.between(value.lastSeenAt, now) > Duration.ofSeconds(20)) { + if (Duration.between(value.seenLastAt, now) > Duration.ofSeconds(20)) { log(TAG, VERBOSE) { "Removing stale device from cache: $value" } deviceCache.remove(key) } @@ -89,28 +89,9 @@ class PodMonitor @Inject constructor( } } - val now = Instant.now() - val minimumSignalQuality = generalSettings.minimumSignalQuality.value - val mainDeviceModel = generalSettings.mainDeviceModel.value - - val aboveTresholdSorted = pods.values - .filter { it.signalQuality > minimumSignalQuality } - .sortedWith( - compareByDescending { it.model == mainDeviceModel && it.model != PodDevice.Model.UNKNOWN } - .thenBy { - val age = Duration.between(it.lastSeenAt, now).seconds - if (age < 3) 0L else age - } - .thenByDescending { it.signalQuality } - ) - val belowThresholdSorted = pods.values - .filter { it.signalQuality <= minimumSignalQuality } - .sortedWith( - compareBy { Duration.between(it.lastSeenAt, now) }.thenByDescending { it.signalQuality } - ) - - val presorted = (aboveTresholdSorted + belowThresholdSorted) + val presorted = pods.values.sortPodsToInterest() val main = presorted.determineMainDevice() + presorted.sortedByDescending { it == main } } .onStart { emit(emptyList()) } @@ -126,16 +107,40 @@ class PodMonitor @Inject constructor( .map { it.determineMainDevice() } .replayingShare(appScope) - private fun List.determineMainDevice(): PodDevice? = this.firstOrNull()?.let { - val mainDeviceModel = generalSettings.mainDeviceModel.value - when { - it.model == PodDevice.Model.UNKNOWN -> null - mainDeviceModel != PodDevice.Model.UNKNOWN && it.model != mainDeviceModel -> null - it.signalQuality <= generalSettings.minimumSignalQuality.value -> null - else -> it - } + private fun Collection.sortPodsToInterest(): List = this.let { devices -> + val now = Instant.now() + + return@let devices.sortedWith( + compareByDescending { true } + .thenBy { + val age = Duration.between(it.seenLastAt, now).seconds + if (age < 5) 0L else (age / 3L).toLong() + } + .thenByDescending { it.signalQuality } + .thenByDescending { (it.seenCounter / 10) } + + ) } + private fun List.determineMainDevice(): PodDevice? = this + .sortPodsToInterest() + .let { devices -> + val mainDeviceModel = generalSettings.mainDeviceModel.value + + val presorted = devices.sortedByDescending { + it.model == mainDeviceModel && it.model != PodDevice.Model.UNKNOWN + } + + return@let presorted.firstOrNull()?.let { candidate -> + when { + candidate.model == PodDevice.Model.UNKNOWN -> null + mainDeviceModel != PodDevice.Model.UNKNOWN && candidate.model != mainDeviceModel -> null + candidate.signalQuality <= generalSettings.minimumSignalQuality.value -> null + else -> candidate + } + } + } + companion object { private val TAG = logTag("Monitor", "PodMonitor") } 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 15a033af..ced5316a 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 @@ -20,9 +20,11 @@ interface PodDevice { val address: String get() = scanResult.address - val lastSeenAt: Instant + val seenLastAt: Instant - val firstSeenAt: Instant + val seenFirstAt: Instant + + val seenCounter: Int val scanResult: BleScanResult diff --git a/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt b/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt index bc69a792..933fa444 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt @@ -45,7 +45,7 @@ fun getBatteryDrawable(percent: Float?): Int = when { private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance() fun PodDevice.lastSeenFormatted(now: Instant): String { - val duration = Duration.between(lastSeenAt, now) + val duration = Duration.between(seenLastAt, now) return lastSeenFormatter.format( duration.seconds.toDouble(), RelativeDateTimeFormatter.Direction.LAST, @@ -54,7 +54,7 @@ fun PodDevice.lastSeenFormatted(now: Instant): String { } fun PodDevice.firstSeenFormatted(now: Instant): String { - val duration = Duration.between(firstSeenAt, now) + val duration = Duration.between(seenFirstAt, now) return lastSeenFormatter.format( duration.toMinutes().toDouble(), RelativeDateTimeFormatter.Direction.LAST, diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt index 43c23d94..3b663123 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt @@ -1,6 +1,5 @@ package eu.darken.capod.pods.core.apple -import eu.darken.capod.common.SystemClockWrap import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE import eu.darken.capod.common.debug.logging.log @@ -34,7 +33,8 @@ abstract class ApplePodsFactory(private val tag: String) { data class KnownDevice( val id: PodDevice.Id, - val firstSeenAt: Instant, + val seenFirstAt: Instant, + val seenCounter: Int, val history: List ) { val lastMessage: ProximityPairing.Message @@ -47,14 +47,18 @@ abstract class ApplePodsFactory(private val tag: String) { get() = 0.75f + (history.size / (MAX_HISTORY * 4f)) fun averageRssi(latest: Int): Int = - history.map { it.rssi }.plus(latest).takeLast(10).average().toInt() - - val timestampNanos: Duration - get() = Duration.ofNanos(history.last().scanResult.generatedAtNanos) + history.map { it.rssi }.plus(latest).takeLast(10).median() fun isOlderThan(age: Duration): Boolean { - val now = Duration.ofNanos(SystemClockWrap.elapsedRealtimeNanos) - return now - timestampNanos > age + val now = Instant.now() + return Duration.between(history.last().seenLastAt, now) > age + } + + private fun List.median(): Int = this.sorted().let { + if (it.size % 2 == 0) + (it[it.size / 2] + it[(it.size - 1) / 2]) / 2 + else + it[it.size / 2] } override fun toString(): String = "KnownDevice(history=${history.size}, last=${history.last()})" @@ -107,13 +111,17 @@ abstract class ApplePodsFactory(private val tag: String) { knownDevices[device.identifier] = when { existing != null -> { - existing.copy(history = existing.history.plus(device)) + existing.copy( + seenCounter = existing.seenCounter + 1, + history = existing.history.plus(device) + ) } else -> { log(tag) { "searchHistory1: Creating new history for $device" } KnownDevice( id = device.identifier, - firstSeenAt = device.firstSeenAt, + seenFirstAt = device.seenFirstAt, + seenCounter = 1, history = listOf(device) ) } diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/UnknownAppleDevice.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/UnknownAppleDevice.kt index 584fcc3c..c6630f55 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/UnknownAppleDevice.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/UnknownAppleDevice.kt @@ -11,8 +11,9 @@ import javax.inject.Inject data class UnknownAppleDevice( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = 0f, @@ -33,15 +34,18 @@ data class UnknownAppleDevice( scanResult: BleScanResult, proximityMessage: ProximityPairing.Message, ): ApplePods { - val basic = UnknownAppleDevice(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = UnknownAppleDevice(scanResult = scanResult, proximityMessage = proximityMessage) 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, + seenCounter = result.seenCounter, confidence = result.confidence, rssiAverage = result.averageRssi(basic.rssi), ) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt index d5e04357..b4935b85 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class AirPodsGen1 constructor( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -39,16 +40,18 @@ data class AirPodsGen1 constructor( proximityMessage.getModelInfo().full == DEVICE_CODE override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = AirPodsGen1(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = AirPodsGen1(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, cachedBatteryPercentage = result.getLatestCaseBattery(), rssiAverage = result.averageRssi(basic.rssi), diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt index ebeb7d10..a6516255 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class AirPodsGen2 constructor( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -39,16 +40,18 @@ data class AirPodsGen2 constructor( proximityMessage.getModelInfo().full == DEVICE_CODE override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = AirPodsGen2(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = AirPodsGen2(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, cachedBatteryPercentage = result.getLatestCaseBattery(), rssiAverage = result.averageRssi(basic.rssi), diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt index 4c4adb4c..9874fdc3 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class AirPodsGen3 constructor( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -38,16 +39,18 @@ data class AirPodsGen3 constructor( proximityMessage.getModelInfo().full == DEVICE_CODE override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = AirPodsGen3(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = AirPodsGen3(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, cachedBatteryPercentage = result.getLatestCaseBattery(), rssiAverage = result.averageRssi(basic.rssi), diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt index ebb3e0be..b0f79ed8 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class AirPodsMax( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -31,16 +32,18 @@ data class AirPodsMax( proximityMessage.getModelInfo().dirty == DEVICE_CODE_DIRTY override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = AirPodsMax(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = AirPodsMax(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, rssiAverage = result.averageRssi(basic.rssi), ) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt index b5e745d5..08d2166d 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt @@ -13,8 +13,9 @@ import javax.inject.Inject data class AirPodsPro( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -50,7 +51,8 @@ data class AirPodsPro( return basic.copy( identifier = result.id, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, cachedBatteryPercentage = result.getLatestCaseBattery(), rssiAverage = result.averageRssi(basic.rssi), diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt index 7da40595..87b5ac81 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class BeatsFlex( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -31,16 +32,18 @@ data class BeatsFlex( proximityMessage.getModelInfo().full == DEVICE_CODE override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = BeatsFlex(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = BeatsFlex(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, rssiAverage = result.averageRssi(basic.rssi), ) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt index c33eb396..85ce39d3 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class BeatsSolo3( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -30,16 +31,18 @@ data class BeatsSolo3( proximityMessage.getModelInfo().full == DEVICE_CODE override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = BeatsSolo3(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = BeatsSolo3(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, rssiAverage = result.averageRssi(basic.rssi), ) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt index a37b5bfc..2cba33ba 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class BeatsStudio3( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -28,16 +29,18 @@ data class BeatsStudio3( proximityMessage.getModelInfo().dirty == DEVICE_CODE_DIRTY override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = BeatsStudio3(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = BeatsStudio3(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, rssiAverage = result.averageRssi(basic.rssi), ) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt index bfbb065d..251214ed 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class BeatsX( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -31,16 +32,18 @@ data class BeatsX( proximityMessage.getModelInfo().full == DEVICE_CODE override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = BeatsX(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = BeatsX(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, rssiAverage = result.averageRssi(basic.rssi), ) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt index 09aa307d..2f6b065a 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class PowerBeats3( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -30,16 +31,18 @@ data class PowerBeats3( proximityMessage.getModelInfo().full == DEVICE_CODE override fun create(scanResult: BleScanResult, proximityMessage: ProximityPairing.Message): ApplePods { - val basic = PowerBeats3(scanResult = scanResult, proximityMessage = proximityMessage) + var basic = PowerBeats3(scanResult = scanResult, proximityMessage = proximityMessage) 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, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, rssiAverage = result.averageRssi(basic.rssi), ) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt index 16a83b3f..32c67e04 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt @@ -12,8 +12,9 @@ import javax.inject.Inject data class PowerBeatsPro( override val identifier: PodDevice.Id = PodDevice.Id(), - override val lastSeenAt: Instant = Instant.now(), - override val firstSeenAt: Instant = Instant.now(), + 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 confidence: Float = PodDevice.BASE_CONFIDENCE, @@ -49,7 +50,8 @@ data class PowerBeatsPro( return basic.copy( identifier = result.id, - firstSeenAt = result.firstSeenAt, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, confidence = result.confidence, cachedBatteryPercentage = result.getLatestCaseBattery(), rssiAverage = result.averageRssi(basic.rssi),