feat: enhance background streaming handler with microphone support
- Updated BackgroundStreamingHandler to include microphone permission handling for background execution. - Modified startBackgroundExecution method to accept a requiresMicrophone parameter, allowing dynamic management of streams requiring microphone access. - Adjusted service intent to pass microphone requirement status, improving service behavior based on app state. - Enhanced VoiceCallService to utilize the new microphone support during voice call streaming, ensuring proper resource management.
This commit is contained in:
@@ -35,7 +35,7 @@ class BackgroundStreamingService : Service() {
|
|||||||
const val NOTIFICATION_ID = 1001
|
const val NOTIFICATION_ID = 1001
|
||||||
const val ACTION_START = "START_STREAMING"
|
const val ACTION_START = "START_STREAMING"
|
||||||
const val ACTION_STOP = "STOP_STREAMING"
|
const val ACTION_STOP = "STOP_STREAMING"
|
||||||
private const val EXTRA_REQUIRES_MICROPHONE = "requiresMicrophone"
|
const val EXTRA_REQUIRES_MICROPHONE = "requiresMicrophone"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
@@ -197,6 +197,7 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
private lateinit var sharedPrefs: SharedPreferences
|
private lateinit var sharedPrefs: SharedPreferences
|
||||||
|
|
||||||
private val activeStreams = mutableSetOf<String>()
|
private val activeStreams = mutableSetOf<String>()
|
||||||
|
private val streamsRequiringMic = mutableSetOf<String>()
|
||||||
private var backgroundJob: Job? = null
|
private var backgroundJob: Job? = null
|
||||||
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||||
|
|
||||||
@@ -219,8 +220,9 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
when (call.method) {
|
when (call.method) {
|
||||||
"startBackgroundExecution" -> {
|
"startBackgroundExecution" -> {
|
||||||
val streamIds = call.argument<List<String>>("streamIds")
|
val streamIds = call.argument<List<String>>("streamIds")
|
||||||
|
val requiresMic = call.argument<Boolean>("requiresMicrophone") ?: false
|
||||||
if (streamIds != null) {
|
if (streamIds != null) {
|
||||||
startBackgroundExecution(streamIds)
|
startBackgroundExecution(streamIds, requiresMic)
|
||||||
result.success(null)
|
result.success(null)
|
||||||
} else {
|
} else {
|
||||||
result.error("INVALID_ARGS", "Stream IDs required", null)
|
result.error("INVALID_ARGS", "Stream IDs required", null)
|
||||||
@@ -263,8 +265,11 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startBackgroundExecution(streamIds: List<String>) {
|
private fun startBackgroundExecution(streamIds: List<String>, requiresMic: Boolean) {
|
||||||
activeStreams.addAll(streamIds)
|
activeStreams.addAll(streamIds)
|
||||||
|
if (requiresMic) {
|
||||||
|
streamsRequiringMic.addAll(streamIds)
|
||||||
|
}
|
||||||
|
|
||||||
if (activeStreams.isNotEmpty()) {
|
if (activeStreams.isNotEmpty()) {
|
||||||
startForegroundService()
|
startForegroundService()
|
||||||
@@ -274,6 +279,7 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
|
|
||||||
private fun stopBackgroundExecution(streamIds: List<String>) {
|
private fun stopBackgroundExecution(streamIds: List<String>) {
|
||||||
activeStreams.removeAll(streamIds.toSet())
|
activeStreams.removeAll(streamIds.toSet())
|
||||||
|
streamsRequiringMic.removeAll(streamIds.toSet())
|
||||||
|
|
||||||
if (activeStreams.isEmpty()) {
|
if (activeStreams.isEmpty()) {
|
||||||
stopForegroundService()
|
stopForegroundService()
|
||||||
@@ -285,6 +291,10 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
try {
|
try {
|
||||||
val serviceIntent = Intent(context, BackgroundStreamingService::class.java)
|
val serviceIntent = Intent(context, BackgroundStreamingService::class.java)
|
||||||
serviceIntent.putExtra("streamCount", activeStreams.size)
|
serviceIntent.putExtra("streamCount", activeStreams.size)
|
||||||
|
serviceIntent.putExtra(
|
||||||
|
BackgroundStreamingService.EXTRA_REQUIRES_MICROPHONE,
|
||||||
|
streamsRequiringMic.isNotEmpty(),
|
||||||
|
)
|
||||||
serviceIntent.action = BackgroundStreamingService.ACTION_START
|
serviceIntent.action = BackgroundStreamingService.ACTION_START
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
@@ -296,6 +306,7 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
println("BackgroundStreamingHandler: Failed to start foreground service: ${e.message}")
|
println("BackgroundStreamingHandler: Failed to start foreground service: ${e.message}")
|
||||||
// Clear active streams as we couldn't start the service
|
// Clear active streams as we couldn't start the service
|
||||||
activeStreams.clear()
|
activeStreams.clear()
|
||||||
|
streamsRequiringMic.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,6 +357,10 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
val serviceIntent = Intent(context, BackgroundStreamingService::class.java)
|
val serviceIntent = Intent(context, BackgroundStreamingService::class.java)
|
||||||
serviceIntent.action = "KEEP_ALIVE"
|
serviceIntent.action = "KEEP_ALIVE"
|
||||||
serviceIntent.putExtra("streamCount", activeStreams.size)
|
serviceIntent.putExtra("streamCount", activeStreams.size)
|
||||||
|
serviceIntent.putExtra(
|
||||||
|
BackgroundStreamingService.EXTRA_REQUIRES_MICROPHONE,
|
||||||
|
streamsRequiringMic.isNotEmpty(),
|
||||||
|
)
|
||||||
context.startService(serviceIntent)
|
context.startService(serviceIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,10 @@ class BackgroundStreamingHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Start background execution for given stream IDs
|
/// Start background execution for given stream IDs
|
||||||
Future<void> startBackgroundExecution(List<String> streamIds) async {
|
Future<void> startBackgroundExecution(
|
||||||
|
List<String> streamIds, {
|
||||||
|
bool requiresMicrophone = false,
|
||||||
|
}) async {
|
||||||
if (!Platform.isIOS && !Platform.isAndroid) return;
|
if (!Platform.isIOS && !Platform.isAndroid) return;
|
||||||
|
|
||||||
_activeStreamIds.addAll(streamIds);
|
_activeStreamIds.addAll(streamIds);
|
||||||
@@ -69,6 +72,7 @@ class BackgroundStreamingHandler {
|
|||||||
try {
|
try {
|
||||||
await _channel.invokeMethod('startBackgroundExecution', {
|
await _channel.invokeMethod('startBackgroundExecution', {
|
||||||
'streamIds': streamIds,
|
'streamIds': streamIds,
|
||||||
|
'requiresMicrophone': requiresMicrophone,
|
||||||
});
|
});
|
||||||
|
|
||||||
DebugLogger.stream(
|
DebugLogger.stream(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
|
|
||||||
import '../../../core/providers/app_providers.dart';
|
import '../../../core/providers/app_providers.dart';
|
||||||
|
import '../../../core/services/background_streaming_handler.dart';
|
||||||
import '../../../core/services/socket_service.dart';
|
import '../../../core/services/socket_service.dart';
|
||||||
import '../../../core/utils/markdown_to_text.dart';
|
import '../../../core/utils/markdown_to_text.dart';
|
||||||
import '../providers/chat_providers.dart';
|
import '../providers/chat_providers.dart';
|
||||||
@@ -24,6 +25,8 @@ enum VoiceCallState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VoiceCallService {
|
class VoiceCallService {
|
||||||
|
static const String _voiceCallStreamId = 'voice-call';
|
||||||
|
|
||||||
final VoiceInputService _voiceInput;
|
final VoiceInputService _voiceInput;
|
||||||
final TextToSpeechService _tts;
|
final TextToSpeechService _tts;
|
||||||
final SocketService _socketService;
|
final SocketService _socketService;
|
||||||
@@ -130,6 +133,10 @@ class VoiceCallService {
|
|||||||
throw Exception('Failed to establish socket connection');
|
throw Exception('Failed to establish socket connection');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await BackgroundStreamingHandler.instance.startBackgroundExecution(const [
|
||||||
|
_voiceCallStreamId,
|
||||||
|
], requiresMicrophone: true);
|
||||||
|
|
||||||
// Set up socket event listener for assistant responses
|
// Set up socket event listener for assistant responses
|
||||||
_socketSubscription = _socketService.addChatEventHandler(
|
_socketSubscription = _socketService.addChatEventHandler(
|
||||||
conversationId: conversationId,
|
conversationId: conversationId,
|
||||||
@@ -144,6 +151,9 @@ class VoiceCallService {
|
|||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
await WakelockPlus.disable();
|
await WakelockPlus.disable();
|
||||||
await _notificationService.cancelNotification();
|
await _notificationService.cancelNotification();
|
||||||
|
await BackgroundStreamingHandler.instance.stopBackgroundExecution(const [
|
||||||
|
_voiceCallStreamId,
|
||||||
|
]);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,6 +341,10 @@ class VoiceCallService {
|
|||||||
await _voiceInput.stopListening();
|
await _voiceInput.stopListening();
|
||||||
await _tts.stop();
|
await _tts.stop();
|
||||||
|
|
||||||
|
await BackgroundStreamingHandler.instance.stopBackgroundExecution(const [
|
||||||
|
_voiceCallStreamId,
|
||||||
|
]);
|
||||||
|
|
||||||
// Cancel notification
|
// Cancel notification
|
||||||
await _notificationService.cancelNotification();
|
await _notificationService.cancelNotification();
|
||||||
|
|
||||||
@@ -435,6 +449,10 @@ class VoiceCallService {
|
|||||||
// Ensure wake lock is disabled on dispose
|
// Ensure wake lock is disabled on dispose
|
||||||
await WakelockPlus.disable();
|
await WakelockPlus.disable();
|
||||||
|
|
||||||
|
await BackgroundStreamingHandler.instance.stopBackgroundExecution(const [
|
||||||
|
_voiceCallStreamId,
|
||||||
|
]);
|
||||||
|
|
||||||
await _stateController.close();
|
await _stateController.close();
|
||||||
await _transcriptController.close();
|
await _transcriptController.close();
|
||||||
await _responseController.close();
|
await _responseController.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user