feat: implement background task management for improved streaming continuity

- Integrated BackgroundTasks framework in iOS to manage background processing for audio streams.
- Added methods to register, schedule, and handle background tasks, allowing streams to continue for extended periods.
- Enhanced the BackgroundStreamingHandler to support background task notifications and keep-alive signals.
- Updated Info.plist to permit background task identifiers, ensuring compliance with iOS requirements.
- Improved the PersistentStreamingService to handle background task extensions and keep-alive signals effectively, enhancing overall streaming reliability.
This commit is contained in:
cogwheel0
2025-10-11 13:53:30 +05:30
parent 3d45154182
commit 4003941482
4 changed files with 152 additions and 6 deletions

View File

@@ -5,7 +5,7 @@ import '../utils/debug_logger.dart';
/// Handles background streaming continuation for iOS and Android
///
/// On iOS: Uses background tasks to keep streams alive for ~30 seconds
/// On iOS: Uses beginBackgroundTask (~30s) + BGTaskScheduler (~3+ minutes)
/// On Android: Uses foreground service notifications
class BackgroundStreamingHandler {
static const MethodChannel _channel = MethodChannel(
@@ -26,6 +26,8 @@ class BackgroundStreamingHandler {
// Callbacks for platform-specific events
void Function(List<String> streamIds)? onStreamsSuspending;
void Function()? onBackgroundTaskExpiring;
void Function(List<String> streamIds, int estimatedSeconds)? onBackgroundTaskExtended;
void Function()? onBackgroundKeepAlive;
bool Function()? shouldContinueInBackground;
void _setupMethodCallHandler() {
@@ -56,6 +58,26 @@ class BackgroundStreamingHandler {
DebugLogger.stream('task-expiring', scope: 'background');
onBackgroundTaskExpiring?.call();
break;
case 'backgroundTaskExtended':
final Map<String, dynamic> args =
call.arguments as Map<String, dynamic>;
final List<String> streamIds = (args['streamIds'] as List)
.cast<String>();
final int estimatedTime = args['estimatedTime'] as int;
DebugLogger.stream(
'task-extended',
scope: 'background',
data: {'count': streamIds.length, 'time': estimatedTime},
);
onBackgroundTaskExtended?.call(streamIds, estimatedTime);
break;
case 'backgroundKeepAlive':
DebugLogger.stream('keepalive-signal', scope: 'background');
onBackgroundKeepAlive?.call();
break;
}
});
}

View File

@@ -62,6 +62,28 @@ class PersistentStreamingService with WidgetsBindingObserver {
_saveStreamStatesForRecovery();
};
_backgroundHandler.onBackgroundTaskExtended = (streamIds, estimatedSeconds) {
DebugLogger.stream(
'PersistentStreaming: Background task extended for $estimatedSeconds seconds',
);
// BGTaskScheduler has given us more time - streams can continue
for (final streamId in streamIds) {
final metadata = _streamMetadata[streamId];
if (metadata != null) {
metadata['bgTaskExtended'] = true;
metadata['bgTaskExtendedAt'] = DateTime.now();
metadata['bgTaskEstimatedTime'] = estimatedSeconds;
}
}
};
_backgroundHandler.onBackgroundKeepAlive = () {
DebugLogger.stream('PersistentStreaming: Background keep-alive signal');
// BGTaskScheduler is keeping us alive - we can continue streaming
_heartbeatTimer?.cancel();
_startHeartbeat(); // Restart heartbeat timer
};
_backgroundHandler.shouldContinueInBackground = () {
return _activeStreams.isNotEmpty;
};