refactor: streamline background streaming service and notification handling

- Updated BackgroundStreamingService to use a minimal notification for foreground service, enhancing clarity and compliance with Android requirements.
- Removed redundant notification updates and logging statements in VoiceCallService to improve code readability and maintainability.
- Adjusted notification channel settings for better background service management.
This commit is contained in:
cogwheel0
2025-10-09 00:10:08 +05:30
parent e98f5cbf0f
commit 96202c7453
3 changed files with 21 additions and 71 deletions

View File

@@ -31,83 +31,43 @@ class BackgroundStreamingService : Service() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
// Immediately start foreground to prevent timeout - will update with proper notification later // Start foreground with minimal notification (required for foreground service)
startForeground(NOTIFICATION_ID, createNotification(1)) startForeground(NOTIFICATION_ID, createMinimalNotification())
println("BackgroundStreamingService: Service created with foreground notification") println("BackgroundStreamingService: Service created")
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 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)
}
when (intent?.action) { when (intent?.action) {
ACTION_START -> { ACTION_START -> {
val streamCount = intent.getIntExtra("streamCount", 1)
acquireWakeLock() acquireWakeLock()
updateNotification(streamCount) println("BackgroundStreamingService: Started foreground service")
println("BackgroundStreamingService: Started foreground service for $streamCount streams")
} }
ACTION_STOP -> { ACTION_STOP -> {
stopStreaming() stopStreaming()
} }
"KEEP_ALIVE" -> { "KEEP_ALIVE" -> {
val streamCount = intent.getIntExtra("streamCount", 1)
keepAlive() keepAlive()
updateNotification(streamCount)
} }
} }
return START_STICKY // Restart if killed by system return START_STICKY // Restart if killed by system
} }
private fun startForegroundWithNotification(streamCount: Int) { private fun createMinimalNotification(): Notification {
val notification = createNotification(streamCount) // Create a minimal, silent notification (required for foreground service)
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
)
return NotificationCompat.Builder(this, CHANNEL_ID) return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(title) .setContentTitle("Conduit")
.setContentText("Processing chat responses...") .setContentText("Background service active")
.setSmallIcon(android.R.drawable.ic_dialog_info) .setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_MIN)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE) .setCategory(NotificationCompat.CATEGORY_SERVICE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_SECRET)
.setOngoing(true) .setOngoing(true)
.setShowWhen(false) .setShowWhen(false)
.setAutoCancel(false) .setSilent(true)
.build() .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() { private fun acquireWakeLock() {
if (wakeLock?.isHeld == true) return if (wakeLock?.isHeld == true) return
@@ -315,15 +275,16 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
private fun createNotificationChannel() { private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "Conduit Streaming" val name = "Background Service"
val descriptionText = "Keeps chat streams active in background" val descriptionText = "Background service for Conduit"
val importance = NotificationManager.IMPORTANCE_LOW val importance = NotificationManager.IMPORTANCE_MIN
val channel = NotificationChannel(BackgroundStreamingService.CHANNEL_ID, name, importance).apply { val channel = NotificationChannel(BackgroundStreamingService.CHANNEL_ID, name, importance).apply {
description = descriptionText description = descriptionText
setShowBadge(false) setShowBadge(false)
enableLights(false) enableLights(false)
enableVibration(false) enableVibration(false)
setSound(null, null) setSound(null, null)
lockscreenVisibility = Notification.VISIBILITY_SECRET
} }
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

View File

@@ -99,12 +99,9 @@ class VoiceCallNotificationService {
required bool isSpeaking, required bool isSpeaking,
}) async { }) async {
if (!_initialized) { if (!_initialized) {
print('VoiceCallNotification: Initializing...');
await initialize(); await initialize();
} }
print('VoiceCallNotification: Showing notification for $modelName (muted: $isMuted, speaking: $isSpeaking)');
final status = isSpeaking ? 'Speaking...' : 'Listening...'; final status = isSpeaking ? 'Speaking...' : 'Listening...';
final muteAction = isMuted ? 'Unmute' : 'Mute'; final muteAction = isMuted ? 'Unmute' : 'Mute';
final muteActionId = isMuted ? _actionUnmute : _actionMute; final muteActionId = isMuted ? _actionUnmute : _actionMute;

View File

@@ -32,7 +32,6 @@ class VoiceCallService {
VoiceCallState _state = VoiceCallState.idle; VoiceCallState _state = VoiceCallState.idle;
String? _sessionId; String? _sessionId;
String? _activeConversationId;
StreamSubscription<String>? _transcriptSubscription; StreamSubscription<String>? _transcriptSubscription;
StreamSubscription<int>? _intensitySubscription; StreamSubscription<int>? _intensitySubscription;
String _accumulatedTranscript = ''; String _accumulatedTranscript = '';
@@ -116,9 +115,6 @@ class VoiceCallService {
if (_isDisposed) return; if (_isDisposed) return;
try { try {
// Set conversation ID first before updating state
_activeConversationId = conversationId;
// Update state (this will trigger notification) // Update state (this will trigger notification)
_updateState(VoiceCallState.connecting); _updateState(VoiceCallState.connecting);
@@ -333,7 +329,6 @@ class VoiceCallService {
await WakelockPlus.disable(); await WakelockPlus.disable();
_sessionId = null; _sessionId = null;
_activeConversationId = null;
_accumulatedTranscript = ''; _accumulatedTranscript = '';
_isMuted = false; _isMuted = false;
_updateState(VoiceCallState.disconnected); _updateState(VoiceCallState.disconnected);
@@ -374,7 +369,6 @@ class VoiceCallService {
if (_state == VoiceCallState.idle || if (_state == VoiceCallState.idle ||
_state == VoiceCallState.error || _state == VoiceCallState.error ||
_state == VoiceCallState.disconnected) { _state == VoiceCallState.disconnected) {
print('VoiceCall: Skipping notification - state: $_state');
return; return;
} }
@@ -382,15 +376,13 @@ class VoiceCallService {
final selectedModel = _ref.read(selectedModelProvider); final selectedModel = _ref.read(selectedModelProvider);
final modelName = selectedModel?.name ?? 'Assistant'; final modelName = selectedModel?.name ?? 'Assistant';
print('VoiceCall: Updating notification - model: $modelName, muted: $_isMuted, state: $_state');
await _notificationService.updateCallStatus( await _notificationService.updateCallStatus(
modelName: modelName, modelName: modelName,
isMuted: _isMuted, isMuted: _isMuted,
isSpeaking: _state == VoiceCallState.speaking, isSpeaking: _state == VoiceCallState.speaking,
); );
} catch (e) { } catch (e) {
print('VoiceCall: Failed to update notification: $e'); // Silently ignore notification errors
} }
} }