import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; /// Service to monitor and track app lifecycle state changes /// This helps detect when app goes to background/foreground class AppLifecycleService with WidgetsBindingObserver { final _stateController = StreamController.broadcast(); /// Stream of app lifecycle state changes Stream get appStateStream => _stateController.stream; AppLifecycleState _currentState = AppLifecycleState.resumed; /// Current app lifecycle state AppLifecycleState get currentState => _currentState; /// Check if app is currently active/visible bool get isAppActive => _currentState == AppLifecycleState.resumed; /// Check if app is in background or inactive bool get isAppInBackground => _currentState == AppLifecycleState.paused || _currentState == AppLifecycleState.inactive || _currentState == AppLifecycleState.detached; /// Last time app was resumed DateTime? _lastResumedTime; DateTime? get lastResumedTime => _lastResumedTime; /// Last time app went to background DateTime? _lastPausedTime; DateTime? get lastPausedTime => _lastPausedTime; /// Duration app has been in current state Duration get timeInCurrentState { final referenceTime = _currentState == AppLifecycleState.resumed ? _lastResumedTime : _lastPausedTime; if (referenceTime == null) return Duration.zero; return DateTime.now().difference(referenceTime); } @override void didChangeAppLifecycleState(AppLifecycleState state) { _currentState = state; // Track timing if (state == AppLifecycleState.resumed) { _lastResumedTime = DateTime.now(); debugPrint('📱 App RESUMED'); } else if (state == AppLifecycleState.paused) { _lastPausedTime = DateTime.now(); debugPrint('📱 App PAUSED/BACKGROUNDED'); } else if (state == AppLifecycleState.inactive) { debugPrint('📱 App INACTIVE'); } else if (state == AppLifecycleState.detached) { debugPrint('📱 App DETACHED'); } _stateController.add(state); } String? _extractJoinSessionId(String? routeName) { if (routeName == null || routeName.isEmpty) return null; final match = RegExp(r'/join/([^?#]+)').firstMatch(routeName); if (match != null && match.group(1)!.isNotEmpty) { return Uri.decodeComponent(match.group(1)!); } return null; } Future _handleIncomingDeepLink(String? route) async { debugPrint('🔗 DeepLink received in AppLifecycleService: $route'); final sessionId = _extractJoinSessionId(route); if (sessionId != null) { final appState = getIt.get(); await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, sessionId); debugPrint('🔗 DeepLink Join Session ID parsed: $sessionId (splashDone=${appState.isSplashInitializationDone})'); if (!appState.isSplashInitializationDone) { appState.deepLinkJoinSessionId = sessionId; return true; } final navState = getIt.get().navigatorKey.currentState; if (navState != null) { String callTypeID = await Utils.getStringFromPrefs(CacheConst.callTypeID); navState.pushNamed( AppRoutes.zoomCallPage, arguments: CallArguments(sessionId, "123", "Patient", "40", "0", true, int.tryParse(callTypeID) ?? 1), ); return true; } } return false; } @override Future didPushRouteInformation(RouteInformation routeInformation) { return _handleIncomingDeepLink(routeInformation.uri.toString()); } @override Future didPushRoute(String route) { return _handleIncomingDeepLink(route); } /// Initialize the lifecycle observer void initialize() { WidgetsBinding.instance.addObserver(this); debugPrint('📱 AppLifecycleService initialized'); } /// Clean up resources void dispose() { WidgetsBinding.instance.removeObserver(this); _stateController.close(); debugPrint('📱 AppLifecycleService disposed'); } }