Core features
The Video SDK for Android lets users share content with others in a session. The SDK supports three kinds of share sources:
- Screen share: mirror the device's screen using Android's
MediaProjectionManager. - Camera share: broadcast a camera as a content stream alongside the user's normal video, useful for document cameras or secondary perspectives.
- External source: feed in an arbitrary frame source through the SDK's preprocessing pipe.
Sharing the screen involves requesting permission, running a foreground service, and starting, stopping, and pausing a share. To broadcast a camera or external frame source instead, see Share a camera or external source.
To render an incoming share from another participant, see Receive a screen share. To draw on shared content, see Annotation.
Sharing the device's screen takes three steps:
- Request screen-capture permission from the user through Android's
MediaProjectionManager. - Start a foreground service with the
mediaProjectionforeground service type. - Call
startShareScreenon theZoomVideoSDKShareHelper, passing theIntentreturned by the permission flow.
Note
Screen sharing relies on Android's foreground service, notification, and Media Projection APIs. Before starting, get comfortable with:
Request screen-capture permission
Use ActivityResultLauncher (the modern replacement for startActivityForResult) to launch the system's screen-capture permission dialog.
private val screenCaptureLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK && result.data != null) {
startScreenShare(result.data!!)
}
}
fun onShareScreenClicked() {
val manager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
screenCaptureLauncher.launch(manager.createScreenCaptureIntent())
}
private final ActivityResultLauncher<Intent> screenCaptureLauncher =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
startScreenShare(result.getData());
}
});
void onShareScreenClicked() {
MediaProjectionManager manager =
(MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
screenCaptureLauncher.launch(manager.createScreenCaptureIntent());
}
Run a foreground service
Screen capture must run inside a foreground service. Implement a service that calls startForeground from its onCreate.
class ScreenShareService : Service() {
companion object {
private const val CHANNEL_ID = "screen_share_channel"
private const val NOTIFICATION_ID = 1
}
override fun onCreate() {
super.onCreate()
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (manager.getNotificationChannel(CHANNEL_ID) == null) {
manager.createNotificationChannel(
NotificationChannel(
CHANNEL_ID,
"Screen share",
NotificationManager.IMPORTANCE_LOW
)
)
}
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentText("Screen share")
.setOngoing(true)
.setAutoCancel(false)
.build()
startForeground(NOTIFICATION_ID, notification)
}
override fun onBind(intent: Intent?): IBinder? = null
}
public class ScreenShareService extends Service {
private static final String CHANNEL_ID = "screen_share_channel";
private static final int NOTIFICATION_ID = 1;
@Override
public void onCreate() {
super.onCreate();
NotificationManager manager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (manager.getNotificationChannel(CHANNEL_ID) == null) {
manager.createNotificationChannel(new NotificationChannel(
CHANNEL_ID,
"Screen share",
NotificationManager.IMPORTANCE_LOW
));
}
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentText("Screen share")
.setOngoing(true)
.setAutoCancel(false)
.build();
startForeground(NOTIFICATION_ID, notification);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Declare the service in AndroidManifest.xml with the mediaProjection type.
<service
android:name=".ScreenShareService"
android:foregroundServiceType="mediaProjection" />
Foreground permissions
Starting with Android 14, Google requires developers to specify appropriate foreground service types in their apps. The Video SDK uses these foreground service permissions.
| Permission | Description |
|---|---|
FOREGROUND_SERVICE_MICROPHONE | Allow the capability to receive audio when the app is put in the background. |
FOREGROUND_SERVICE_MEDIA_PLAYBACK | Allow the capability to share audio while screen sharing. |
FOREGROUND_SERVICE_MEDIA_PROJECTION | Allow the capability to project the screen while screen sharing. |
FOREGROUND_SERVICE_CONNECTED_DEVICE | Allow the capability to use Bluetooth device as the audio source. |
FOREGROUND_SERVICE_PHONE_CALL | Allow the capability to use Bluetooth device for the meeting call. |
The screen sharing feature in Video SDK requires foreground service. If the screen sharing feature is not included in your project, add the following code in Androidmanifest.xml to remove the service and permissions. Note that removing FOREGROUND_SERVICE_MICROPHONE permission might cause an audio issue when the application is in the background. Therefore, to ensure the audio works as expected, maintain FOREGROUND_SERVICE_MICROPHONE and FOREGROUND_SERVICE_CONNECTED_DEVICE permissions in the project and start the foreground service when joining a meeting.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" tools:node="remove"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" tools:node="remove"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" tools:node="remove"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" tools:node="remove"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" tools:node="remove"/>
Start the share
After the foreground service is running, call startShareScreen on the ZoomVideoSDKShareHelper, passing the Intent returned from the permission flow.
private fun startScreenShare(data: Intent) {
val serviceIntent = Intent(this, ScreenShareService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
ZoomVideoSDK.getInstance().shareHelper.startShareScreen(data)
}
private void startScreenShare(Intent data) {
Intent serviceIntent = new Intent(this, ScreenShareService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent);
} else {
startService(serviceIntent);
}
ZoomVideoSDK.getInstance().getShareHelper().startShareScreen(data);
}
startShareScreen returns an int. ZoomVideoSDKErrors.Errors_Success (value 0) means the share is starting; any other value is an error from ZoomVideoSDKErrors. Common reasons a call fails: another user is sharing while multi-share is disabled, the session host has locked sharing, or you're calling from outside an active session.
Asynchronous failures arrive on the ZoomVideoSDKDelegate through onFailedToStartShare. Surface the error to the user and stop the foreground service if you started one.
override fun onFailedToStartShare() {
// The screen-capture session never began. Tear down the foreground service.
stopService(Intent(this, ScreenShareService::class.java))
showShareError()
}
@Override
public void onFailedToStartShare() {
// The screen-capture session never began. Tear down the foreground service.
stopService(new Intent(this, ScreenShareService.class));
showShareError();
}
Stop the share
To end a share programmatically, call stopShare. This works for screen, camera, and external-source shares.
ZoomVideoSDK.getInstance().shareHelper.stopShare()
stopService(Intent(this, ScreenShareService::class.java))
ZoomVideoSDK.getInstance().getShareHelper().stopShare();
stopService(new Intent(this, ScreenShareService.class));
Inside the service, the recommended Android pattern for shutting down is stopForeground(STOP_FOREGROUND_REMOVE) followed by stopSelf().
override fun onDestroy() {
stopForeground(STOP_FOREGROUND_REMOVE)
super.onDestroy()
}
@Override
public void onDestroy() {
stopForeground(STOP_FOREGROUND_REMOVE);
super.onDestroy();
}
A share also stops when the user taps Stop sharing in the system Media Projection notification. The SDK reports both programmatic and system-initiated stops the same way: onUserShareStatusChanged fires with ZoomVideoSDKShareStatus_Stop. Tear down your foreground service from that callback so both paths converge on the same cleanup code. For the callback signature, see Receive a screen share.
Pause and resume
Pause an ongoing share without disconnecting from the screen-capture session. Viewers see a frozen frame until you resume.
val shareHelper = ZoomVideoSDK.getInstance().shareHelper
shareHelper.pauseShare()
// Later:
shareHelper.resumeShare()
ZoomVideoSDKShareHelper shareHelper = ZoomVideoSDK.getInstance().getShareHelper();
shareHelper.pauseShare();
// Later:
shareHelper.resumeShare();
Receivers detect pause and resume through the ZoomVideoSDKShareStatus_Pause and ZoomVideoSDKShareStatus_Resume values on onUserShareStatusChanged.
Share device audio
To send the device's audio alongside the screen capture (so viewers hear music, video sound, or app audio), enable share device audio before or during the share.
val shareHelper = ZoomVideoSDK.getInstance().shareHelper
shareHelper.enableShareDeviceAudio(true)
// Check whether device-audio capture is on.
val isOn = shareHelper.isShareDeviceAudioEnabled
ZoomVideoSDKShareHelper shareHelper = ZoomVideoSDK.getInstance().getShareHelper();
shareHelper.enableShareDeviceAudio(true);
// Check whether device-audio capture is on.
boolean isOn = shareHelper.isShareDeviceAudioEnabled();
Device-audio capture requires:
- Android 10 (API 29) or later.
- The
RECORD_AUDIOruntime permission.- The
FOREGROUND_SERVICE_MEDIA_PLAYBACKforeground service type on your share service (see Foreground permissions above).The SDK does not capture device audio while the virtual speaker is enabled.
Check the local share state
Use the share helper's state queries to drive your UI. For example, to disable a Share screen button while the local user is already sharing, or while another participant is.
val shareHelper = ZoomVideoSDK.getInstance().shareHelper
// The local user is currently sharing (any share type).
val sharing = shareHelper.isSharingOut
// The local user is specifically sharing the screen.
val sharingScreen = shareHelper.isScreenSharingOut
// Some other participant is sharing.
val otherSharing = shareHelper.isOtherSharing
ZoomVideoSDKShareHelper shareHelper = ZoomVideoSDK.getInstance().getShareHelper();
boolean sharing = shareHelper.isSharingOut();
boolean sharingScreen = shareHelper.isScreenSharingOut();
boolean otherSharing = shareHelper.isOtherSharing();