mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Track first time a device is seen and display it.
This commit is contained in:
+5
-1
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user