package com.sendbird.calls.reactnative.service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
/**
* Placeholder foreground service for Android MediaProjection.
*
* Android Q+ requires a foreground service with type `mediaProjection` to be running
* before MediaProjection can be started. This service only holds the foreground notification;
* the actual MediaProjection is managed internally by the Sendbird Calls SDK.
*
* The service implementation is provided by the library, but registration in AndroidManifest.xml
* is opt-in by the app developer. Add the following to your app's AndroidManifest.xml:
*
* ```xml
*
*
*
*
* ```
*
* @since 1.2.0
*/
class ScreenSharingService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
stopSelf()
return START_NOT_STICKY
}
val notification = try {
ScreenSharingServiceConfig.notificationBuilder?.invoke(this)
} catch (e: Exception) {
Log.w(TAG, "Custom notification builder failed, using default", e)
null
} ?: buildDefaultNotification()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)
} else {
startForeground(NOTIFICATION_ID, notification)
}
return START_NOT_STICKY
}
private fun getNotificationIcon(): Int {
// Try ic_notification first, then fall back to app's default icon
val custom = resources.getIdentifier("ic_notification", "drawable", packageName)
if (custom != 0) return custom
return applicationInfo.icon.takeIf { it != 0 }
?: android.R.drawable.ic_menu_camera
}
private fun buildDefaultNotification(): Notification {
ensureChannel()
return NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID)
.setSmallIcon(getNotificationIcon())
.setContentTitle("Screen sharing")
.setContentText("You are sharing your screen.")
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.build()
}
private fun ensureChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
val manager = getSystemService(NotificationManager::class.java)
if (manager.getNotificationChannel(DEFAULT_CHANNEL_ID) != null) return
val channel = NotificationChannel(
DEFAULT_CHANNEL_ID,
"Screen Sharing",
NotificationManager.IMPORTANCE_LOW
)
manager.createNotificationChannel(channel)
}
companion object {
private const val TAG = "ScreenSharingService"
private const val DEFAULT_CHANNEL_ID = "sendbird_calls_screen_sharing"
private const val NOTIFICATION_ID = 440_983_927
fun launch(context: Context): Boolean {
val intent = Intent(context, ScreenSharingService::class.java)
return try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
true
} catch (e: RuntimeException) {
Log.w(TAG, "Service not started", e)
false
}
}
fun stop(context: Context) {
context.stopService(Intent(context, ScreenSharingService::class.java))
}
}
}