chore(wip): consolidation intermédiaire multi-tickets (sprints Statistiques, UI, Bug resolution, Serveur-client)

Regroupe l'état de travail en cours réalisé dans un même worktree sur
plusieurs tickets/sprints (#85, #136, #145, #155-160, #162-164),
mélangeant des tickets QA et inProgress. Ne constitue pas une feature
terminée : commit de sauvegarde avant triage/split par ticket en
branches feature/* dédiées. Exclut les dossiers d'environnement de
build locaux et le heap dump parasite (.gitignore mis à jour).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 16:48:54 +02:00
parent 58272e354a
commit 917777e18b
279 changed files with 13546 additions and 674 deletions

View File

@ -9,6 +9,7 @@ import java.nio.charset.StandardCharsets
class WatchBridgeListenerService : WearableListenerService() {
override fun onDataChanged(dataEvents: DataEventBuffer) {
WatchBridgePlugin.attachApplicationContext(applicationContext)
try {
for (event in dataEvents) {
WatchBridgePlugin.handleDataEvent(event)
@ -19,6 +20,7 @@ class WatchBridgeListenerService : WearableListenerService() {
}
override fun onMessageReceived(messageEvent: MessageEvent) {
WatchBridgePlugin.attachApplicationContext(applicationContext)
if (messageEvent.path != WatchBridgePlugin.ACK_PATH) {
return
}
@ -27,6 +29,7 @@ class WatchBridgeListenerService : WearableListenerService() {
}
override fun onCapabilityChanged(capabilityInfo: CapabilityInfo) {
WatchBridgePlugin.attachApplicationContext(applicationContext)
if (capabilityInfo.name != WatchBridgePlugin.PHONE_CAPABILITY) {
return
}
@ -41,6 +44,7 @@ class WatchBridgeListenerService : WearableListenerService() {
override fun onCreate() {
super.onCreate()
WatchBridgePlugin.attachApplicationContext(applicationContext)
WatchBridgePlugin.requestCapabilityRefresh(applicationContext)
WatchBridgePlugin.requestLatestProjection(applicationContext)
}

View File

@ -56,8 +56,12 @@ object WatchBridgePlugin {
private var lastSensorPermissionRequestEpochMs = 0L
private var pendingSensorPermissionRequest = false
fun register(flutterEngine: FlutterEngine, context: Context) {
fun attachApplicationContext(context: Context) {
appContext = context.applicationContext
}
fun register(flutterEngine: FlutterEngine, context: Context) {
attachApplicationContext(context)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, METHOD_CHANNEL)
.setMethodCallHandler(::handleMethodCall)
EventChannel(flutterEngine.dartExecutor.binaryMessenger, PROJECTION_CHANNEL)

View File

@ -1,6 +1,8 @@
package com.gametime.watch.bridge
import android.annotation.SuppressLint
import android.content.Context
import android.os.PowerManager
import android.util.Log
import androidx.health.services.client.HealthServices
import androidx.health.services.client.MeasureClient
@ -37,6 +39,7 @@ internal class WatchHeartRateCollector(
private val registeredDataTypes = mutableSetOf<DeltaDataType<*, *>>()
private var shouldAggregate = false
private var appContext: Context? = null
private var wakeLock: PowerManager.WakeLock? = null
private val callback = object : MeasureCallback {
override fun onAvailabilityChanged(
@ -101,6 +104,7 @@ internal class WatchHeartRateCollector(
return
}
appContext = context.applicationContext
acquireWakeLock(context)
val measureClient = HealthServices.getClient(context).measureClient
registerMeasureCallbackIfNeeded(measureClient, DataType.HEART_RATE_BPM)
registerMeasureCallbackIfNeeded(measureClient, DataType.DISTANCE)
@ -217,6 +221,7 @@ internal class WatchHeartRateCollector(
}
registeredDataTypes.clear()
appContext = null
releaseWakeLock()
}
private fun registerMeasureCallbackIfNeeded(
@ -235,6 +240,34 @@ internal class WatchHeartRateCollector(
}
}
@SuppressLint("WakelockTimeout")
private fun acquireWakeLock(context: Context) {
val existing = wakeLock
if (existing?.isHeld == true) {
return
}
val powerManager = context.applicationContext
.getSystemService(Context.POWER_SERVICE) as? PowerManager
?: return
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"GameTime:HeartRateCollection",
).apply {
setReferenceCounted(false)
acquire()
}
Log.d(TAG, "heart rate collection wake lock acquired sessionId=$sessionId")
}
private fun releaseWakeLock() {
val lock = wakeLock
wakeLock = null
if (lock?.isHeld == true) {
lock.release()
Log.d(TAG, "heart rate collection wake lock released")
}
}
private fun reset(nextSessionId: String?) {
sessionId = nextSessionId
sampleCount = 0