diff --git a/android/app/src/main/kotlin/app/cogwheel/conduit/BackgroundStreamingHandler.kt b/android/app/src/main/kotlin/app/cogwheel/conduit/BackgroundStreamingHandler.kt index 9b8ce36..a69a02b 100644 --- a/android/app/src/main/kotlin/app/cogwheel/conduit/BackgroundStreamingHandler.kt +++ b/android/app/src/main/kotlin/app/cogwheel/conduit/BackgroundStreamingHandler.kt @@ -21,93 +21,53 @@ import org.json.JSONObject class BackgroundStreamingService : Service() { private var wakeLock: PowerManager.WakeLock? = null private val activeStreams = mutableSetOf() - + companion object { const val CHANNEL_ID = "conduit_streaming_channel" const val NOTIFICATION_ID = 1001 const val ACTION_START = "START_STREAMING" const val ACTION_STOP = "STOP_STREAMING" } - + override fun onCreate() { super.onCreate() - // Immediately start foreground to prevent timeout - will update with proper notification later - startForeground(NOTIFICATION_ID, createNotification(1)) - println("BackgroundStreamingService: Service created with foreground notification") + // Start foreground with minimal notification (required for foreground service) + startForeground(NOTIFICATION_ID, createMinimalNotification()) + println("BackgroundStreamingService: Service created") } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - // Always ensure we're foreground first to prevent timeout exceptions - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundWithNotification(1) - } + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { when (intent?.action) { ACTION_START -> { - val streamCount = intent.getIntExtra("streamCount", 1) acquireWakeLock() - updateNotification(streamCount) - println("BackgroundStreamingService: Started foreground service for $streamCount streams") + println("BackgroundStreamingService: Started foreground service") } ACTION_STOP -> { stopStreaming() } "KEEP_ALIVE" -> { - val streamCount = intent.getIntExtra("streamCount", 1) keepAlive() - updateNotification(streamCount) } } return START_STICKY // Restart if killed by system } - - private fun startForegroundWithNotification(streamCount: Int) { - val notification = createNotification(streamCount) - startForeground(NOTIFICATION_ID, notification) - } - - private fun createNotification(streamCount: Int): Notification { - val title = if (streamCount == 1) { - "Chat streaming in progress" - } else { - "$streamCount chats streaming" - } - - // Create intent to return to app - val intent = packageManager.getLaunchIntentForPackage(packageName) - val pendingIntent = PendingIntent.getActivity( - this, - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - + + private fun createMinimalNotification(): Notification { + // Create a minimal, silent notification (required for foreground service) return NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(title) - .setContentText("Processing chat responses...") + .setContentTitle("Conduit") + .setContentText("Background service active") .setSmallIcon(android.R.drawable.ic_dialog_info) - .setContentIntent(pendingIntent) - .setPriority(NotificationCompat.PRIORITY_LOW) + .setPriority(NotificationCompat.PRIORITY_MIN) .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setVisibility(NotificationCompat.VISIBILITY_SECRET) .setOngoing(true) .setShowWhen(false) - .setAutoCancel(false) + .setSilent(true) .build() } - private fun updateNotification(streamCount: Int) { - val notification = createNotification(streamCount) - val notificationManager = NotificationManagerCompat.from(this) - - try { - notificationManager.notify(NOTIFICATION_ID, notification) - } catch (e: SecurityException) { - println("BackgroundStreamingService: Notification permission not granted") - } - } - private fun acquireWakeLock() { if (wakeLock?.isHeld == true) return @@ -315,17 +275,18 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val name = "Conduit Streaming" - val descriptionText = "Keeps chat streams active in background" - val importance = NotificationManager.IMPORTANCE_LOW + val name = "Background Service" + val descriptionText = "Background service for Conduit" + val importance = NotificationManager.IMPORTANCE_MIN val channel = NotificationChannel(BackgroundStreamingService.CHANNEL_ID, name, importance).apply { description = descriptionText setShowBadge(false) enableLights(false) enableVibration(false) setSound(null, null) + lockscreenVisibility = Notification.VISIBILITY_SECRET } - + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } diff --git a/lib/features/chat/services/voice_call_notification_service.dart b/lib/features/chat/services/voice_call_notification_service.dart index 41ca45b..5cbdb5f 100644 --- a/lib/features/chat/services/voice_call_notification_service.dart +++ b/lib/features/chat/services/voice_call_notification_service.dart @@ -99,12 +99,9 @@ class VoiceCallNotificationService { required bool isSpeaking, }) async { if (!_initialized) { - print('VoiceCallNotification: Initializing...'); await initialize(); } - print('VoiceCallNotification: Showing notification for $modelName (muted: $isMuted, speaking: $isSpeaking)'); - final status = isSpeaking ? 'Speaking...' : 'Listening...'; final muteAction = isMuted ? 'Unmute' : 'Mute'; final muteActionId = isMuted ? _actionUnmute : _actionMute; diff --git a/lib/features/chat/services/voice_call_service.dart b/lib/features/chat/services/voice_call_service.dart index a106a56..5a57449 100644 --- a/lib/features/chat/services/voice_call_service.dart +++ b/lib/features/chat/services/voice_call_service.dart @@ -32,7 +32,6 @@ class VoiceCallService { VoiceCallState _state = VoiceCallState.idle; String? _sessionId; - String? _activeConversationId; StreamSubscription? _transcriptSubscription; StreamSubscription? _intensitySubscription; String _accumulatedTranscript = ''; @@ -116,9 +115,6 @@ class VoiceCallService { if (_isDisposed) return; try { - // Set conversation ID first before updating state - _activeConversationId = conversationId; - // Update state (this will trigger notification) _updateState(VoiceCallState.connecting); @@ -333,7 +329,6 @@ class VoiceCallService { await WakelockPlus.disable(); _sessionId = null; - _activeConversationId = null; _accumulatedTranscript = ''; _isMuted = false; _updateState(VoiceCallState.disconnected); @@ -374,7 +369,6 @@ class VoiceCallService { if (_state == VoiceCallState.idle || _state == VoiceCallState.error || _state == VoiceCallState.disconnected) { - print('VoiceCall: Skipping notification - state: $_state'); return; } @@ -382,15 +376,13 @@ class VoiceCallService { final selectedModel = _ref.read(selectedModelProvider); final modelName = selectedModel?.name ?? 'Assistant'; - print('VoiceCall: Updating notification - model: $modelName, muted: $_isMuted, state: $_state'); - await _notificationService.updateCallStatus( modelName: modelName, isMuted: _isMuted, isSpeaking: _state == VoiceCallState.speaking, ); } catch (e) { - print('VoiceCall: Failed to update notification: $e'); + // Silently ignore notification errors } }