Track first time a device is seen and display it.

This commit is contained in:
darken
2022-01-29 18:00:13 +01:00
parent 6bd350ce42
commit 0f924ecb82
18 changed files with 58 additions and 3 deletions
@@ -2,6 +2,7 @@ package eu.darken.capod.main.ui.overview.cards.pods
import android.graphics.Typeface
import android.view.ViewGroup
import androidx.core.view.isGone
import androidx.core.view.isInvisible
import eu.darken.capod.R
import eu.darken.capod.common.lists.binding
@@ -10,6 +11,7 @@ import eu.darken.capod.pods.core.*
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.DualApplePods.DeviceColor
import eu.darken.capod.pods.core.apple.DualApplePods.LidState
import java.time.Duration
import java.time.Instant
class DualApplePodsCardVH(parent: ViewGroup) :
@@ -36,7 +38,9 @@ class DualApplePodsCardVH(parent: ViewGroup) :
}
deviceIcon.setImageResource(device.iconRes)
lastSeen.text = device.lastSeenFormatted(item.now)
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
reception.text = device.getSignalQuality(context).let {
if (item.isMainPod) "$it\n(${getString(R.string.pods_yours)})" else it
@@ -22,6 +22,8 @@ interface PodDevice {
val lastSeenAt: Instant
val firstSeenAt: Instant
val scanResult: BleScanResult
val rssi: Int
@@ -51,4 +51,13 @@ fun PodDevice.lastSeenFormatted(now: Instant): String {
RelativeDateTimeFormatter.Direction.LAST,
RelativeDateTimeFormatter.RelativeUnit.SECONDS
)
}
fun PodDevice.firstSeenFormatted(now: Instant): String {
val duration = Duration.between(firstSeenAt, now)
return lastSeenFormatter.format(
duration.toMinutes().toDouble(),
RelativeDateTimeFormatter.Direction.LAST,
RelativeDateTimeFormatter.RelativeUnit.MINUTES
)
}
@@ -9,6 +9,7 @@ import eu.darken.capod.common.upperNibble
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
import java.time.Duration
import java.time.Instant
abstract class ApplePodsFactory<PodType : ApplePods>(private val tag: String) {
@@ -33,6 +34,7 @@ abstract class ApplePodsFactory<PodType : ApplePods>(private val tag: String) {
data class KnownDevice(
val id: PodDevice.Id,
val firstSeenAt: Instant,
val history: List<ApplePods>
) {
val lastMessage: ProximityPairing.Message
@@ -111,6 +113,7 @@ abstract class ApplePodsFactory<PodType : ApplePods>(private val tag: String) {
log(tag) { "searchHistory1: Creating new history for $device" }
KnownDevice(
id = device.identifier,
firstSeenAt = device.firstSeenAt,
history = listOf(device)
)
}
@@ -12,6 +12,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = 0f,
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -47,6 +48,7 @@ data class AirPodsGen1 constructor(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
cachedBatteryPercentage = result.getLatestCaseBattery(),
rssiAverage = result.averageRssi(basic.rssi),
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -47,6 +48,7 @@ data class AirPodsGen2 constructor(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
cachedBatteryPercentage = result.getLatestCaseBattery(),
rssiAverage = result.averageRssi(basic.rssi),
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -46,6 +47,7 @@ data class AirPodsGen3 constructor(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
cachedBatteryPercentage = result.getLatestCaseBattery(),
rssiAverage = result.averageRssi(basic.rssi),
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -39,6 +40,7 @@ data class AirPodsMax(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
rssiAverage = result.averageRssi(basic.rssi),
)
@@ -14,6 +14,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -49,6 +50,7 @@ data class AirPodsPro(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
cachedBatteryPercentage = result.getLatestCaseBattery(),
rssiAverage = result.averageRssi(basic.rssi),
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -39,6 +40,7 @@ data class BeatsFlex(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
rssiAverage = result.averageRssi(basic.rssi),
)
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -38,6 +39,7 @@ data class BeatsSolo3(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
rssiAverage = result.averageRssi(basic.rssi),
)
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -36,6 +37,7 @@ data class BeatsStudio3(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
rssiAverage = result.averageRssi(basic.rssi),
)
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -39,6 +40,7 @@ data class BeatsX(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
rssiAverage = result.averageRssi(basic.rssi),
)
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -38,6 +39,7 @@ data class PowerBeats3(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
rssiAverage = result.averageRssi(basic.rssi),
)
@@ -13,6 +13,7 @@ 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 scanResult: BleScanResult,
override val proximityMessage: ProximityPairing.Message,
override val confidence: Float = PodDevice.BASE_CONFIDENCE,
@@ -48,6 +49,7 @@ data class PowerBeatsPro(
return basic.copy(
identifier = result.id,
firstSeenAt = result.firstSeenAt,
confidence = result.confidence,
cachedBatteryPercentage = result.getLatestCaseBattery(),
rssiAverage = result.averageRssi(basic.rssi),
@@ -45,12 +45,24 @@
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/first_seen"
app:layout_constraintEnd_toEndOf="@id/name"
app:layout_constraintStart_toStartOf="@id/name"
app:layout_constraintTop_toBottomOf="@id/name"
app:layout_goneMarginBottom="8dp"
tools:text="Last seen: 3s ago" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/first_seen"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/barrier_top"
app:layout_constraintEnd_toEndOf="@id/name"
app:layout_constraintStart_toStartOf="@id/name"
app:layout_constraintTop_toBottomOf="@id/name"
tools:text="3s ago" />
app:layout_constraintTop_toBottomOf="@id/last_seen"
tools:text="First seen: 3s ago" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/reception"
+2
View File
@@ -137,4 +137,6 @@
<string name="settings_onepod_mode_description">Wearing both pods is not required, wearing a single pod is sufficient to trigger reactions.</string>
<string name="permission_system_alert_window_label">System Alert Window</string>
<string name="permission_system_alert_window_description">Allow CAPod to draw over other apps to make the feature \"Show PopUp\" possible.</string>
<string name="last_seen_x">Last seen: %s</string>
<string name="first_seen_x">First seen: %s</string>
</resources>