package expo.modules.appmetrics.utils import expo.modules.appmetrics.frames.FrameMetricsRecord import expo.modules.appmetrics.networkrequests.NetworkRequestSummary /** * Single source of truth for the `expo.*` keys we attach to metrics. Takes * typed inputs (`DeviceState`, `NetworkState`, `FrameMetricsRecord`) and * produces the flat `Map` map the metric envelope expects. * * Framework-emitted keys override user-supplied keys on collision so the OS * readings always win — a user passing `expo.device.lowPowerMode` as a * string doesn't get to overwrite the actual OS bool. */ object MetricParamsBuilder { fun build( userParams: Map? = null, frameMetrics: FrameMetricsRecord? = null, deviceState: DeviceState? = null, networkState: NetworkState? = null, networkRequests: NetworkRequestSummary? = null ): Map { val params = mutableMapOf() userParams?.let { params.putAll(it) } if (frameMetrics != null && frameMetrics.expectedFrames > 0) { params["expo.frameRate.slowFrames"] = frameMetrics.slowFrames params["expo.frameRate.frozenFrames"] = frameMetrics.frozenFrames params["expo.frameRate.totalDelay"] = frameMetrics.freezeTimeMs.toDouble() / 1000.0 } if (deviceState != null) { deviceState.lowPowerMode?.let { params["expo.device.lowPowerMode"] = it } deviceState.thermalState?.let { params["expo.device.thermalState"] = thermalStateString(it) } deviceState.batteryLevel?.let { params["expo.device.batteryLevel"] = it } deviceState.batteryCharging?.let { params["expo.device.batteryCharging"] = it } } if (networkState != null) { params["expo.network.connected"] = networkState.connected params["expo.network.type"] = networkTransportString(networkState.transport) } if (networkRequests != null && !networkRequests.isEmpty) { params["expo.network.requests.count"] = networkRequests.count params["expo.network.requests.failed"] = networkRequests.failed params["expo.network.requests.bytesReceived"] = networkRequests.bytesReceived params["expo.network.requests.bytesSent"] = networkRequests.bytesSent params["expo.network.requests.totalDuration"] = networkRequests.totalDuration networkRequests.slowestDuration?.let { params["expo.network.requests.slowestDuration"] = it } networkRequests.slowestHost?.let { params["expo.network.requests.slowestHost"] = it } } return params } private fun thermalStateString(state: ThermalState): String { return when (state) { ThermalState.NOMINAL -> "nominal" ThermalState.FAIR -> "fair" ThermalState.SERIOUS -> "serious" ThermalState.CRITICAL -> "critical" ThermalState.UNKNOWN -> "unknown" } } private fun networkTransportString(transport: NetworkTransport): String { return when (transport) { NetworkTransport.WIFI -> "wifi" NetworkTransport.CELLULAR -> "cellular" NetworkTransport.ETHERNET -> "ethernet" NetworkTransport.OTHER -> "other" NetworkTransport.NONE -> "none" } } }