mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-15 02:36:12 -04:00
Improve sorting and heuristics.
This commit is contained in:
@@ -18,41 +18,74 @@ class FakeBleData @Inject constructor(
|
||||
|
||||
fun getFakeData(): Collection<BleScanResult> {
|
||||
val fakeDevices = mutableListOf<BleScanResult>()
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Unit> {
|
||||
while (isActive) {
|
||||
trySend(Unit)
|
||||
delay(1000)
|
||||
delay(3000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -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))
|
||||
|
||||
+2
-4
@@ -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 {
|
||||
|
||||
@@ -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<D : PodDeviceVH.Item, B : ViewBinding>(
|
||||
@@ -15,6 +17,10 @@ abstract class PodDeviceVH<D : PodDeviceVH.Item, B : ViewBinding>(
|
||||
parent: ViewGroup
|
||||
) : ModularAdapter.VH(layoutId, parent), BindableVH<D, B> {
|
||||
|
||||
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
|
||||
|
||||
+1
-5
@@ -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))
|
||||
|
||||
+1
-5
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<PodDevice> { 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<PodDevice> { 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<PodDevice>.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<PodDevice>.sortPodsToInterest(): List<PodDevice> = this.let { devices ->
|
||||
val now = Instant.now()
|
||||
|
||||
return@let devices.sortedWith(
|
||||
compareByDescending<PodDevice> { 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<PodDevice>.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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<PodType : ApplePods>(private val tag: String) {
|
||||
|
||||
data class KnownDevice(
|
||||
val id: PodDevice.Id,
|
||||
val firstSeenAt: Instant,
|
||||
val seenFirstAt: Instant,
|
||||
val seenCounter: Int,
|
||||
val history: List<ApplePods>
|
||||
) {
|
||||
val lastMessage: ProximityPairing.Message
|
||||
@@ -47,14 +47,18 @@ abstract class ApplePodsFactory<PodType : ApplePods>(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<Int>.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<PodType : ApplePods>(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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user