import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:live_activities/live_activities.dart'; class LiveActivitiesService { // Use a singleton pattern static final LiveActivitiesService _instance = LiveActivitiesService._internal(); // LiveActivities plugin instance final LiveActivities _liveActivities = LiveActivities(); bool _isInitialized = false; bool _isSupported = false; String? _appGroupId; String? _urlScheme; factory LiveActivitiesService() => _instance; LiveActivitiesService._internal(); /// Initialize the Live Activities service with specific app group ID and URL scheme Future init( {required String appGroupId, required String urlScheme}) async { debugPrint( 'LiveActivitiesService: Initializing with appGroupId: $appGroupId, urlScheme: $urlScheme'); // Store directly in the instance _appGroupId = appGroupId; _urlScheme = urlScheme; // Initialize the plugin with the app group ID try { // Using the init method of the plugin with appGroupId await _liveActivities.init(appGroupId: appGroupId); debugPrint( 'LiveActivitiesService: Initialized plugin with appGroupId: $appGroupId'); } catch (e) { debugPrint('LiveActivitiesService: Error initializing plugin: $e'); } final result = await initialize(); // Debug verification debugPrint( 'LiveActivitiesService: After init - appGroupId: $_appGroupId, initialized: $_isInitialized, supported: $_isSupported'); return result; } /// Initialize the Live Activities service Future initialize() async { if (_isInitialized) { debugPrint( 'LiveActivitiesService: Already initialized, supported: $_isSupported, appGroupId: $_appGroupId'); return _isSupported; } // Live Activities are only supported on iOS 16.1+ if (!Platform.isIOS) { debugPrint( 'LiveActivitiesService: Platform is not iOS, Live Activities not supported'); _isSupported = false; _isInitialized = true; return false; } try { // Request permissions explicitly final permissionGranted = await _requestLiveActivitiesPermission(); if (!permissionGranted) { debugPrint('LiveActivitiesService: Permission not granted'); _isSupported = false; _isInitialized = true; return false; } // Check if Live Activities are supported on this device _isSupported = await _liveActivities.areActivitiesEnabled(); _isInitialized = true; debugPrint( 'LiveActivitiesService: Initialized, supported: $_isSupported, appGroupId: $_appGroupId'); return _isSupported; } catch (e) { debugPrint('LiveActivitiesService: Error initializing: $e'); _isSupported = false; _isInitialized = true; return false; } } /// Request permission for Live Activities Future _requestLiveActivitiesPermission() async { try { // Check if permission is already granted bool alreadyGranted = await _liveActivities.areActivitiesEnabled(); if (alreadyGranted) { debugPrint('LiveActivitiesService: Permission already granted'); return true; } // iOS 16.1+ requires the user to explicitly enable Live Activities // There's no direct permission API, the user must enable it in Settings // We can only check if it's enabled, not request it directly debugPrint( 'LiveActivitiesService: Checking Live Activities availability'); final isEnabled = await _liveActivities.areActivitiesEnabled(); if (!isEnabled) { debugPrint( 'LiveActivitiesService: Live Activities not enabled. User needs to enable in Settings'); } return isEnabled; } catch (e) { debugPrint('LiveActivitiesService: Error checking permission: $e'); return false; } } /// Get appGroupId safely, ensuring it's available String? _getAppGroupId() { if (_appGroupId == null) { debugPrint( 'LiveActivitiesService: Warning - appGroupId is null. Please call init() first.'); debugPrint( 'LiveActivitiesService: Current state: initialized=$_isInitialized, supported=$_isSupported'); } return _appGroupId; } /// Helper method to create a search activity Future createSearchActivity({ required String title, required String query, required String count, }) async { if (!_isInitialized) await initialize(); if (!_isSupported) return null; try { // Use instance appGroupId final String? appGroupId = _getAppGroupId(); // Verify appGroupId is set if (appGroupId == null) { throw Exception('appGroupId is null. Please call init() first.'); } // Always reinitialize with the current app group ID to ensure it's set correctly await _liveActivities.init(appGroupId: appGroupId); debugPrint( 'LiveActivitiesService: Ensuring plugin initialized with appGroupId: $appGroupId'); debugPrint( 'LiveActivitiesService: Creating activity with appGroupId: $appGroupId'); // Create data for the activity final Map activityData = { 'title': title, 'query': query, 'resultsCount': count, 'timestamp': DateTime.now().toIso8601String(), 'appGroup': appGroupId, }; // Add urlScheme if available if (_urlScheme != null) { activityData['urlScheme'] = _urlScheme; } debugPrint('LiveActivitiesService: Activity data: $activityData'); // Create the activity final activityId = await _liveActivities.createActivity(activityData); debugPrint( 'LiveActivitiesService: Created activity with ID: $activityId'); // Get current active activities count final activities = await _liveActivities.getAllActivitiesIds(); debugPrint( 'LiveActivitiesService: Active activities count: ${activities?.length ?? 0}'); return activityId; } catch (e) { debugPrint('LiveActivitiesService: Error creating activity: $e'); return null; } } /// Update an existing search activity Future updateSearchActivity({ required String activityId, required String title, required String query, required String count, }) async { if (!_isInitialized) await initialize(); if (!_isSupported) return false; try { // Use instance appGroupId final String? appGroupId = _getAppGroupId(); // Verify appGroupId is set if (appGroupId == null) { throw Exception('appGroupId is null. Please call init() first.'); } // Always reinitialize with the current app group ID to ensure it's set correctly await _liveActivities.init(appGroupId: appGroupId); debugPrint( 'LiveActivitiesService: Ensuring plugin initialized with appGroupId: $appGroupId'); // Create updated data for the activity final Map activityData = { 'title': title, 'query': query, 'resultsCount': count, 'timestamp': DateTime.now().toIso8601String(), 'appGroup': appGroupId, }; // Add urlScheme if available if (_urlScheme != null) { activityData['urlScheme'] = _urlScheme; } // Update the activity final success = await _liveActivities.updateActivity(activityId, activityData); debugPrint( 'LiveActivitiesService: Updated activity $activityId: $success'); return success ?? false; } catch (e) { debugPrint('LiveActivitiesService: Error updating activity: $e'); return false; } } /// End an existing search activity Future endSearchActivity(String activityId) async { if (!_isInitialized) await initialize(); if (!_isSupported) return false; try { // Get the appGroupId final String? appGroupId = _getAppGroupId(); // Verify appGroupId is set if (appGroupId == null) { throw Exception('appGroupId is null. Please call init() first.'); } // Always reinitialize with the current app group ID to ensure it's set correctly await _liveActivities.init(appGroupId: appGroupId); debugPrint( 'LiveActivitiesService: Ensuring plugin initialized with appGroupId: $appGroupId'); debugPrint('LiveActivitiesService: Ending activity $activityId'); // End the activity final success = await _liveActivities.endActivity(activityId); debugPrint('LiveActivitiesService: Ended activity $activityId: $success'); return success ?? false; } catch (e) { debugPrint('LiveActivitiesService: Error ending activity: $e'); return false; } } /// Get all active activities Future?> getActiveActivities() async { if (!_isInitialized) await initialize(); if (!_isSupported) return null; try { // Get the appGroupId final String? appGroupId = _getAppGroupId(); // Verify appGroupId is set if (appGroupId == null) { throw Exception('appGroupId is null. Please call init() first.'); } // Always reinitialize with the current app group ID to ensure it's set correctly await _liveActivities.init(appGroupId: appGroupId); debugPrint( 'LiveActivitiesService: Ensuring plugin initialized with appGroupId: $appGroupId'); final activities = await _liveActivities.getAllActivitiesIds(); debugPrint( 'LiveActivitiesService: Found ${activities?.length ?? 0} active activities'); return activities; } catch (e) { debugPrint('LiveActivitiesService: Error getting active activities: $e'); return null; } } /// End all active activities Future endAllActivities() async { if (!_isInitialized) await initialize(); if (!_isSupported) return false; try { // Get the appGroupId final String? appGroupId = _getAppGroupId(); // Verify appGroupId is set if (appGroupId == null) { throw Exception('appGroupId is null. Please call init() first.'); } // Always reinitialize with the current app group ID to ensure it's set correctly await _liveActivities.init(appGroupId: appGroupId); debugPrint( 'LiveActivitiesService: Ensuring plugin initialized with appGroupId: $appGroupId'); // First try using the native plugin method if available final success = await _liveActivities.endAllActivities(); if (success == true) { debugPrint( 'LiveActivitiesService: Ended all activities using plugin method'); return true; } // Fallback to manual method if plugin doesn't support endAllActivities final activities = await getActiveActivities(); if (activities == null || activities.isEmpty) { debugPrint('LiveActivitiesService: No active activities to end'); return true; } bool allEnded = true; for (final activityId in activities) { final ended = await endSearchActivity(activityId); if (!ended) { allEnded = false; debugPrint( 'LiveActivitiesService: Failed to end activity $activityId'); } } debugPrint( 'LiveActivitiesService: Ended ${activities.length} activities manually'); return allEnded; } catch (e) { debugPrint('LiveActivitiesService: Error ending all activities: $e'); return false; } } /// Request notification permission if needed Future requestNotificationPermission() async { if (!Platform.isIOS) return false; try { debugPrint('LiveActivitiesService: Requesting notification permission'); // The LiveActivities plugin doesn't have a direct method to request notification permission // This is usually handled by the main app's notification system return true; } catch (e) { debugPrint( 'LiveActivitiesService: Error requesting notification permission: $e'); return false; } } /// Check if Live Activities are supported on this device bool get isSupported => _isSupported; /// Check if Live Activities are initialized bool get isInitialized => _isInitialized; /// Get the current appGroupId String? get appGroupId => _appGroupId; }