Implement Picture-in-Picture on Android

更新时间:
复制 MD 格式

Implement the Picture-in-Picture (PiP) feature on Android so users can keep watching video in a floating window while using other apps.

Function introduction

Picture-in-Picture (PiP) displays a video in a small, floating window in a corner of the screen. Users can continue watching the video while using other apps, such as replying to messages or browsing the web.

Sample code

ARTC provides open-source sample code. For more information, see Implement Picture-in-Picture on Android.

Prerequisites

Before you begin, ensure the following requirement is met:

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

Implementation

1. Configure properties

The PiP API is scoped to an Activity. 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

  • Not all devices support PiP. Call the hasSystemFeature method to check for support before entering this mode.

  • Call the enterPictureInPictureMode system API to enter PiP 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

Override the onPictureInPictureModeChanged system callback to update the UI when the mode changes.

@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;
    }
}