All Products
Search
Document Center

ApsaraVideo Live:Implement Picture-in-Picture on Android

Last Updated:Sep 24, 2025

This topic describes how to implement the Picture-in-Picture (PiP) feature on Android.

Function introduction

Picture-in-Picture (PiP) is a feature that displays a video in a small, floating window. When you enable PiP, the video appears in a corner of the screen. This lets you continue watching the video while using other applications on your device, such as replying to messages or browsing the web, without interruption.

Sample code

Alibaba Real-Time Communication (ARTC) provides open-source sample code. For more information, see Implement Picture-in-Picture on Android.

Prerequisites

The following prerequisite is required to implement the PiP feature:

  • Your device must run Android 8.0 (API level 26) or later.

Implementation

1. Configure properties

The Picture-in-Picture API is scoped to an Activity. You must declare the following properties for the Activity in your AndroidManifest.xml file.

  • android:supportsPictureInPicture="true"

  • android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"

<activity android:name=".PictureInPicture.PictureInPictureAcitivity"
  android:label="@string/picture_in_picture"
  android:exported="false"
  android:supportsPictureInPicture="true"
  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
  android:resizeableActivity="true"
  android:launchMode="singleTask"
  tools:targetApi="24" />

2. Enter Picture-in-Picture mode

  • Some devices may not support Picture-in-Picture mode. Before entering this mode, you can call the hasSystemFeature method to check for support.

  • You can call the enterPictureInPictureMode system API to enter Picture-in-Picture mode.

private void enterPIPMode(){
    if(!hasJoined) {
        ToastHelper.showToast(this, "Join a channel first", Toast.LENGTH_SHORT);
        return;
    }
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
            Rational aspectRatio = new Rational(9, 16); // Recommended video aspect ratio
            PictureInPictureParams params = new PictureInPictureParams.Builder()
            .setAspectRatio(aspectRatio)
            .build();
            enterPictureInPictureMode(params);
        } else {
            ToastHelper.showToast(this, "This device does not support PiP", Toast.LENGTH_SHORT);
        }
    } else {
        ToastHelper.showToast(this, "PiP is supported only on Android 8.0 and later", Toast.LENGTH_SHORT);
    }
}

3. Respond to PiP mode changes

You can override the onPictureInPictureModeChanged system callback to control UI changes as needed.

@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
    if (isInPictureInPictureMode) {
        // Enter PiP mode: Hide the full-screen UI and show only the video.
        findViewById(R.id.ll_channel_layout).setVisibility(View.GONE);
        findViewById(R.id.scroll_remote_container).setVisibility(View.GONE);

        // Optional: Hide the status bar.
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);

        // Optional: Create a dedicated video container for PiP.
        showPipVideoContainer();
    } else {
        // Exit PiP mode: Restore the full-screen UI.
        findViewById(R.id.ll_channel_layout).setVisibility(View.VISIBLE);
        findViewById(R.id.scroll_remote_container).setVisibility(View.VISIBLE);
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);

        // Re-attach the local preview view.
        if (mAliRtcEngine != null && mLocalVideoCanvas != null) {
            mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
        }

        // Remove the PiP container.
        removePipVideoContainer();
    }
}

/**
 * Shows the main video container in PiP mode, such as the local preview.
 */
private void showPipVideoContainer() {
    if (mPipVideoContainer == null) {
        mPipVideoContainer = new FrameLayout(this);
        mPipVideoContainer.setBackgroundColor(Color.BLACK);
        addContentView(mPipVideoContainer, new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

        // Recreate the local preview view. You cannot move a SurfaceView, so you must recreate it.
        SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(this);
        surfaceView.setZOrderOnTop(true);
        surfaceView.setZOrderMediaOverlay(true);

        mPipVideoContainer.addView(surfaceView, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

        if(mAliRtcEngine != null) {
            AliRtcEngine.AliRtcVideoCanvas canvas = new AliRtcEngine.AliRtcVideoCanvas();
            canvas.view = surfaceView;
            mAliRtcEngine.setLocalViewConfig(canvas, AliRtcVideoTrackCamera);
        }
    }
}

/**
 * Removes the PiP video container.
 */
private void removePipVideoContainer() {
    if (mPipVideoContainer != null && mPipVideoContainer.getParent() != null) {
        ((ViewGroup) mPipVideoContainer.getParent()).removeView(mPipVideoContainer);
        mPipVideoContainer = null;
    }
}