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:
@@ -21,93 +21,53 @@ import org.json.JSONObject
|
|||||||
class BackgroundStreamingService : Service() {
|
class BackgroundStreamingService : Service() {
|
||||||
private var wakeLock: PowerManager.WakeLock? = null
|
private var wakeLock: PowerManager.WakeLock? = null
|
||||||
private val activeStreams = mutableSetOf<String>()
|
private val activeStreams = mutableSetOf<String>()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_ID = "conduit_streaming_channel"
|
const val CHANNEL_ID = "conduit_streaming_channel"
|
||||||
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|
||||||
// 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) {
|
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,17 +275,18 @@ 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
|
||||||
notificationManager.createNotificationChannel(channel)
|
notificationManager.createNotificationChannel(channel)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user