Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package me.kavishdevar.librepods.database.apple
import android.util.Log
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Transaction
import androidx.room3.Upsert
import me.kavishdevar.librepods.bluetooth.MacAddress
import me.kavishdevar.librepods.data.apple.AppleCache
Expand All @@ -23,18 +24,23 @@ interface AppleDao {
@Upsert
suspend fun upsert(device: AppleEntity)

// read-modify-write: without a transaction, concurrent saves (cache is saved on every state change)
// overwrite each other with stale columns, which is how metadata ended up empty in the db
@Transaction
suspend fun updateSettings(macAddress: MacAddress, settings: AppleSettings) {
Log.d(TAG, "Updating settings for $macAddress: $settings")
val device = get(macAddress)?: AppleEntity(macAddress = macAddress, settings = settings, metadata = AppleMetadata(), cache = AppleCache())
upsert(device.copy(settings = settings))
}

@Transaction
suspend fun updateMetadata(macAddress: MacAddress, metadata: AppleMetadata) {
Log.d(TAG, "Updating metadata for $macAddress: $metadata")
val device = get(macAddress)?: AppleEntity(macAddress = macAddress, settings = AppleSettings(), metadata = metadata, cache = AppleCache())
upsert(device.copy(metadata = metadata))
}

@Transaction
suspend fun saveCache(macAddress: MacAddress, cache: AppleCache) {
Log.d(TAG, "Saving cache for $macAddress: $cache")
val device = get(macAddress)?: AppleEntity(macAddress = macAddress, settings = AppleSettings(), metadata = AppleMetadata(), cache = cache)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,16 @@ class AppleDevice(
}
}

// null until the AirPods send their information packet (or it is restored from the db)
private fun firmwareMajorVersion(): Int? = metadata.value.version3.firstOrNull()?.digitToIntOrNull()

fun startHeadTracking() {
val firmwareMajor = firmwareMajorVersion() ?: run {
Log.w(TAG, "firmware version unknown, not starting head tracking")
return
}
aacp.setSensorServiceReportInterval(
sensorServiceType = if (metadata.value.version3.first().digitToInt() >= 8) SensorServiceType.DEVMOTION6 else SensorServiceType.ACTIVITY,
sensorServiceType = if (firmwareMajor >= 8) SensorServiceType.DEVMOTION6 else SensorServiceType.ACTIVITY,
interval = _settings.value.headTrackingInterval
)
_state.update {
Expand All @@ -274,10 +281,13 @@ class AppleDevice(
}

fun stopHeadTracking() {
aacp.setSensorServiceReportInterval(
sensorServiceType = if (metadata.value.version3.first().digitToInt() >= 8) SensorServiceType.DEVMOTION6 else SensorServiceType.ACTIVITY,
interval = Duration.ZERO
)
val firmwareMajor = firmwareMajorVersion()
if (firmwareMajor != null) {
aacp.setSensorServiceReportInterval(
sensorServiceType = if (firmwareMajor >= 8) SensorServiceType.DEVMOTION6 else SensorServiceType.ACTIVITY,
interval = Duration.ZERO
)
}
_state.update {
it.copy(
headTrackingState = BuddyState.INACTIVE,
Expand Down Expand Up @@ -338,8 +348,12 @@ class AppleDevice(

// AACPManager sets hrmActive true when a valid reading is received
fun startHr(): Boolean {
val firmwareMajor = firmwareMajorVersion() ?: run {
Log.w(TAG, "firmware version unknown, not starting heart rate monitoring")
return false
}
val success = aacp.setSensorServiceReportInterval(
sensorServiceType = if (metadata.value.version3.first().digitToInt() >= 9) SensorServiceType.HEARTRATE_COMMAND else SensorServiceType.HEARTRATE,
sensorServiceType = if (firmwareMajor >= 9) SensorServiceType.HEARTRATE_COMMAND else SensorServiceType.HEARTRATE,
interval = 1.seconds
)
if (success) {
Expand All @@ -352,8 +366,12 @@ class AppleDevice(
return success
}
fun stopHr(): Boolean {
val firmwareMajor = firmwareMajorVersion() ?: run {
Log.w(TAG, "firmware version unknown, can't stop heart rate monitoring")
return false
}
val success = aacp.setSensorServiceReportInterval(
sensorServiceType = if (metadata.value.version3.first().digitToInt() >= 9) SensorServiceType.HEARTRATE_COMMAND else SensorServiceType.HEARTRATE,
sensorServiceType = if (firmwareMajor >= 9) SensorServiceType.HEARTRATE_COMMAND else SensorServiceType.HEARTRATE,
interval = Duration.ZERO
)
if (state.value.hrmState != BuddyState.INACTIVE && success) _state.update {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import me.kavishdevar.librepods.bluetooth.MacAddress
import me.kavishdevar.librepods.bluetooth.aacp.types.ControlCommandIdentifier
import me.kavishdevar.librepods.bluetooth.aacp.types.MagicKeyType
import me.kavishdevar.librepods.bluetooth.verifyRPA
import me.kavishdevar.librepods.data.apple.BuddyState
import me.kavishdevar.librepods.data.heartrate.HeartRateSample
import me.kavishdevar.librepods.data.recording.Recording
import me.kavishdevar.librepods.database.app.AppSettingsEntity
Expand Down Expand Up @@ -876,6 +877,13 @@ class LibrePodsService: Service() {

appleRepository.saveMetadata(device.macAddress, metadata)

// startHr() needs the firmware version; on connect it may not have been known yet
if (previousMetadata.version3.isEmpty() && metadata.version3.isNotEmpty() &&
device.settings.value.hrmAlertEnabled && device.state.value.hrmState == BuddyState.INACTIVE
) {
device.startHr()
}

setAppleBluetoothMetadata(device)

val notificationManager = getSystemService(NotificationManager::class.java)
Expand Down