import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart'; import '../../../../push/push_registration_store.dart'; import '../../../../push/push_registration_type.dart'; import '../../marianumconnect_query.dart'; import 'telemetry_device_id.dart'; /// Sends a telemetry heartbeat to MarianumConnect (`POST me/telemetry`) — /// upserts the stable install id, platform, app version and device info. Sent /// on app start, again once push registration completes that session (so a /// fresh registration isn't under-reported until the next launch), and on /// resume after the app spent more than 15 minutes in the background. /// Bearer-authenticated via the shared dio interceptor. Replaces the legacy /// mhsl.eu `server/userIndex/update` call. class TelemetryHeartbeat extends MarianumConnectQuery { TelemetryHeartbeat({super.dio}); /// Fire-and-forget: schedules a heartbeat and swallows any error, so a failed /// send never disrupts app start. Used from the app shell's initState and /// lifecycle handler, and re-emitted once push registration completes (see /// `_MainState._syncPush`). static void report({required bool notificationsEnabled}) { unawaited( TelemetryHeartbeat() .send(notificationsEnabled: notificationsEnabled) .catchError((Object _) {}), ); } Future send({required bool notificationsEnabled}) => guard(() async { final info = DeviceInfoPlugin(); final package = await PackageInfo.fromPlatform(); final deviceIdentifier = await TelemetryDeviceId.resolve(); final pushDeviceIdentifier = await const PushRegistrationStore() .deviceIdentifier(PushRegistrationType.general); var platform = 'unknown'; String? deviceModel; String? osVersion; var raw = {}; if (Platform.isAndroid) { platform = 'android'; final androidInfo = await info.androidInfo; deviceModel = androidInfo.model; osVersion = androidInfo.version.release; raw = androidInfo.data; } else if (Platform.isIOS) { platform = 'ios'; final appleInfo = await info.iosInfo; deviceModel = appleInfo.utsname.machine; osVersion = appleInfo.systemVersion; raw = appleInfo.data; } await dio.post( endpoint('me/telemetry'), data: { 'deviceIdentifier': deviceIdentifier, // `pushDeviceIdentifier` reflects a *completed* registration and is // absent until it lands; `pushEnabled` carries the user's intent // (the notification toggle) so the backend can tell "user wants push" // apart from "registration not finished yet". 'pushDeviceIdentifier': ?pushDeviceIdentifier, 'pushEnabled': notificationsEnabled, 'platform': platform, 'appVersion': package.version, 'appBuild': int.tryParse(package.buildNumber), 'deviceModel': deviceModel, 'osVersion': osVersion, 'deviceInfo': jsonEncode(raw), }, ); }); }