md-app/lib/services/live_activities_service.dart

382 lines
12 KiB
Dart

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<bool> 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<bool> 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<bool> _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<String?> 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<String, dynamic> 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<bool> 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<String, dynamic> 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<bool> 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<List<String>?> 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<bool> 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<bool> 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;
}