davidliu
Committed by GitHub

Foreground service example to keep call running in background (#175)

@@ -12,4 +12,8 @@ @@ -12,4 +12,8 @@
12 android:name="android.permission.BLUETOOTH_ADMIN" 12 android:name="android.permission.BLUETOOTH_ADMIN"
13 android:maxSdkVersion="30" /> 13 android:maxSdkVersion="30" />
14 14
  15 + <application>
  16 + <service android:name="io.livekit.android.sample.service.ForegroundService" />
  17 + </application>
  18 +
15 </manifest> 19 </manifest>
@@ -3,6 +3,7 @@ package io.livekit.android.sample @@ -3,6 +3,7 @@ package io.livekit.android.sample
3 import android.app.Application 3 import android.app.Application
4 import android.content.Intent 4 import android.content.Intent
5 import android.media.projection.MediaProjectionManager 5 import android.media.projection.MediaProjectionManager
  6 +import android.os.Build
6 import androidx.lifecycle.AndroidViewModel 7 import androidx.lifecycle.AndroidViewModel
7 import androidx.lifecycle.LiveData 8 import androidx.lifecycle.LiveData
8 import androidx.lifecycle.MutableLiveData 9 import androidx.lifecycle.MutableLiveData
@@ -22,6 +23,7 @@ import io.livekit.android.room.track.CameraPosition @@ -22,6 +23,7 @@ import io.livekit.android.room.track.CameraPosition
22 import io.livekit.android.room.track.LocalScreencastVideoTrack 23 import io.livekit.android.room.track.LocalScreencastVideoTrack
23 import io.livekit.android.room.track.LocalVideoTrack 24 import io.livekit.android.room.track.LocalVideoTrack
24 import io.livekit.android.room.track.Track 25 import io.livekit.android.room.track.Track
  26 +import io.livekit.android.sample.service.ForegroundService
25 import io.livekit.android.util.flow 27 import io.livekit.android.util.flow
26 import kotlinx.coroutines.flow.* 28 import kotlinx.coroutines.flow.*
27 import kotlinx.coroutines.launch 29 import kotlinx.coroutines.launch
@@ -119,6 +121,16 @@ class CallViewModel( @@ -119,6 +121,16 @@ class CallViewModel(
119 } 121 }
120 connectToRoom() 122 connectToRoom()
121 } 123 }
  124 +
  125 +
  126 + // Start a foreground service to keep the call from being interrupted if the
  127 + // app goes into the background.
  128 + val foregroundServiceIntent = Intent(application, ForegroundService::class.java)
  129 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  130 + application.startForegroundService(foregroundServiceIntent)
  131 + } else {
  132 + application.startService(foregroundServiceIntent)
  133 + }
122 } 134 }
123 135
124 private suspend fun connectToRoom() { 136 private suspend fun connectToRoom() {
@@ -216,6 +228,11 @@ class CallViewModel( @@ -216,6 +228,11 @@ class CallViewModel(
216 super.onCleared() 228 super.onCleared()
217 room.disconnect() 229 room.disconnect()
218 room.release() 230 room.release()
  231 +
  232 + // Clean up foreground service
  233 + val application = getApplication<Application>()
  234 + val foregroundServiceIntent = Intent(application, ForegroundService::class.java)
  235 + application.stopService(foregroundServiceIntent)
219 } 236 }
220 237
221 fun setMicEnabled(enabled: Boolean) { 238 fun setMicEnabled(enabled: Boolean) {
  1 +package io.livekit.android.sample.service
  2 +
  3 +import android.app.NotificationChannel
  4 +import android.app.NotificationManager
  5 +import android.app.Service
  6 +import android.content.Context
  7 +import android.content.Intent
  8 +import android.os.Build
  9 +import android.os.IBinder
  10 +import androidx.annotation.RequiresApi
  11 +import androidx.core.app.NotificationCompat
  12 +
  13 +/**
  14 + * A foreground service is required for screen capture on API level Q (29) and up.
  15 + * This a simple default foreground service to display a notification while screen
  16 + * capturing.
  17 + */
  18 +
  19 +open class ForegroundService : Service() {
  20 +
  21 + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  22 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  23 + createNotificationChannel()
  24 + }
  25 +
  26 + val actualNotification =
  27 + NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID)
  28 + .setPriority(NotificationCompat.PRIORITY_DEFAULT)
  29 + .build()
  30 +
  31 + startForeground(DEFAULT_NOTIFICATION_ID, actualNotification)
  32 + return START_NOT_STICKY
  33 + }
  34 +
  35 + @RequiresApi(Build.VERSION_CODES.O)
  36 + private fun createNotificationChannel() {
  37 + val channel = NotificationChannel(
  38 + DEFAULT_CHANNEL_ID,
  39 + "Foreground",
  40 + NotificationManager.IMPORTANCE_LOW
  41 + )
  42 + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  43 + notificationManager.createNotificationChannel(channel)
  44 + }
  45 +
  46 + override fun onBind(intent: Intent?): IBinder? {
  47 + return null // not used.
  48 + }
  49 +
  50 + companion object {
  51 + const val DEFAULT_NOTIFICATION_ID = 3456
  52 + const val DEFAULT_CHANNEL_ID = "livekit_example_foreground"
  53 + }
  54 +}