# Android: Picture-in-picture Mode

In fact, all the needed steps are already well documented in Android developers community [article](https://developer.android.com/develop/ui/views/picture-in-picture), but here we would like to outline what we have changed in our [Android sample application](https://github.com/VidyoAPI-SDK/Kotlin-sample) in order to make it switch to PiP mode under certain conditions.

You can also refer to  [Android Kotlin PictureInPicture sample](https://github.com/android/media-samples/tree/main/PictureInPictureKotlin/#readme) without Vidyo integration if you need toi dive deeper.

### Declare PiP support <a href="#declaring" id="declaring"></a>

By default, the system does not automatically support PiP for apps. If you want support PiP in your app, register your video activity in your manifest by setting `android:supportsPictureInPicture` to `true`. Also, specify that your activity handles layout configuration changes so that your activity doesn't relaunch when layout changes occur during PiP mode transitions.

```xml
<activity
            android:name="com.vidyo.vidyoconnector.ui.MainActivity"
            ...
            android:supportsPictureInPicture="true"
            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
            ...
</activity>
```

### Switch your activity to PiP <a href="#pip_button" id="pip_button"></a>

Starting with Android 12, you can switch your activity to PiP mode by setting the [`setAutoEnterEnabled`](https://developer.android.com/reference/android/app/PictureInPictureParams.Builder#setAutoEnterEnabled\(boolean\)) flag to `true`. With this setting, an activity automatically switches to PiP mode as needed without having to explicitly call [`enterPictureInPictureMode()`](https://developer.android.com/reference/android/app/Activity#enterPictureInPictureMode\(android.app.PictureInPictureParams\)) in [`onUserLeaveHint`](https://developer.android.com/reference/android/app/Activity#onUserLeaveHint\(\)).&#x20;

#### Check if PiP is supported on device

The following code will return whether PiP is supported on the current device:

```kotlin
private val isPipSupported by lazy {
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
        packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
}
```

#### Observe PiP

When the MainActivity is created, we have to add a logic to observe the PiP mdoe and track all the actions related to it. Add the following to the `onCreate` function of `MainActivity` class:

```kotlin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    checkAndObservePip()
}
```

And here is how it actually functions:

```kotlin
@RequiresApi(Build.VERSION_CODES.O)
private fun checkAndObservePip(){
    if(isPipSupported) {
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                trackPipAnimationHintView(ConnectorManager.layout)
            }
        }
        observePipActionsAndParams()
        observeConferenceCallAction()
    }
    logD { "$logTag, checkAndApplyPip isPipSupported: $isPipSupported" }
}

```

Adding observers to handle PIP actions click event and any update related to PiP actions like mic mute/unmute etc:

```kotlin
@RequiresApi(Build.VERSION_CODES.O)
private fun observePipActionsAndParams(){
    logD { "$logTag, observePipActionsAndParams"}
    pipActionTypeLiveData.observe(this) { 
        viewModel.onPipActionReceived(it) 
    }
    viewModel.pipParamsLiveData.observe(this) { 
        (isMicroPhoneMute, isCameraMute) ->
            doOnPipParamsReceived(isMicroPhoneMute, isCameraMute)
    }
}

```

Observe Conference Call State to check if user is in PiP mode in conference end then exit from PiP mode:

```kotlin
private fun observeConferenceCallAction(){
    ConnectorManager.conference.conference.collectInScope(lifecycleScope) {
        val isPipActive = viewModel.pipModeActive.value
        logD { "$logTag, observeConferenceCallAction Conference State: ${it.state}, isPipActive: $isPipActive" }
        if (!it.state.isActive && isPipActive == true){
            exitFromPipMode()
        }
    }
}
```

### Define PiP Actions

You can and should define which actions will be available on PiP window:

```kotlin
sealed class PipAction(@DrawableRes val iconResId: Int, @StringRes val titleResId: Int, @PipControlType val controlType: Int){
    object MicrophoneMute :PipAction(R.drawable.ic_microphone_off, R.string.CONFERENCE__contentdesc_mic_muted, CONTROL_TYPE_MICROPHONE_MUTE)
    object MicrophoneUnMute :PipAction(R.drawable.ic_microphone_on, R.string.CONFERENCE__contentdesc_mic_unmuted, CONTROL_TYPE_MICROPHONE_UN_MUTE)
    object CameraMute :PipAction(R.drawable.ic_camera_off, R.string.CONFERENCE__contentdesc_camera_muted, CONTROL_TYPE_CAMERA_MUTE)
    object CameraUnMute :PipAction(R.drawable.ic_camera_on, R.string.CONFERENCE__contentdesc_camera_unmuted, CONTROL_TYPE_CAMERA_UN_MUTE)
    object EndCall :PipAction(R.drawable.ic_call_end_24, R.string.CONFERENCESERVICE__notification_end_call, CONTROL_TYPE_END_CALL)
}
```

### PiP Management

We have encapsulated all PiP mode support management into `PipManager.kt` file. The main part here is building PiP parameters that will rule how to react on previously defined actions:

```kotlin
@RequiresApi(Build.VERSION_CODES.O)
fun buildPictureInPictureParams(isMicroPhoneMute: Boolean, isCameraMute: Boolean): PictureInPictureParams.Builder {
    logD { "$logTag, buildPictureInPictureParams: isMicroPhoneMute = $isMicroPhoneMute, isCameraMute = $isCameraMute" }

    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                PictureInPictureParams.Builder()
                // Set action items for the picture-in-picture mode. These are the only custom controls
                // available during the picture-in-picture mode.
                .setActions(createPipActions(isMicroPhoneMute, isCameraMute))
                // Set the aspect ratio of the picture-in-picture mode.
                .setAspectRatio(pipRational)
                // if TRUE, Turn the screen into the picture-in-picture mode if it's hidden by the "Home" button.
                .setAutoEnterEnabled(false)
                // Disables the seamless resize. The seamless resize works great for videos where the
                // content can be arbitrarily scaled, but you can disable this for non-video content so
                // that the picture-in-picture mode is resized with a cross fade animation.
                .setSeamlessResizeEnabled(false)
        } else {
            PictureInPictureParams.Builder()
            // Set action items for the picture-in-picture mode. These are the only custom controls
            // available during the picture-in-picture mode.
            .setActions(createPipActions(isMicroPhoneMute, isCameraMute))
            // Set the aspect ratio of the picture-in-picture mode.
            .setAspectRatio(pipRational)
     }
}
```

### Handle PiP

Here are some outlines on what you should be handling in termsof PiP actions.

#### Handle PiP mode changes:

```kotlin
override fun onPictureInPictureModeChanged(
        isInPictureInPictureMode: Boolean,
        newConfig: Configuration
    ) {
        logD { "$logTag, onPictureInPictureModeChanged isInPictureInPictureMode: $isInPictureInPictureMode, isPipEnabled: ${appContext.isPipEnabled}" }
        if(appContext.isPipEnabled) {
            viewModel.onPictureInPictureModeChanged(isInPictureInPictureMode)
            doOnPictureInPictureModeChanged(isInPictureInPictureMode)
        }
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
}
```

#### Add broadcast receiver to listen to PiP actions:

```kotlin
@SuppressLint("UnspecifiedRegisterReceiverFlag")
private fun doOnPictureInPictureModeChanged(isInPictureInPictureMode: Boolean){
    logD { "$logTag, onPictureInPictureModeChanged isInPictureInPictureMode: $isInPictureInPictureMode" }
    if (isInPictureInPictureMode) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            registerReceiver(
                    pipActionReceiver, IntentFilter(ACTION_PIP_CONTROL),
                    Context.RECEIVER_EXPORTED
            )
        } else {
            registerReceiver(pipActionReceiver, IntentFilter(ACTION_PIP_CONTROL))
        }
    } else {
        unregisterPipActionReceiver()
    }
}
```

#### Unregister PiP actions receiver when exiting PiP mode (used in [#observe-pip](#observe-pip "mention")):

```kotlin
private fun exitFromPipMode(){
    if (isPipSupported && viewModel.pipModeActive.value == true) {
        unregisterPipActionReceiver()
        apply {
            val startIntent = intent.apply {
                addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
                putExtra(ACTION_PIP_CONTROL, true)
            }
            startActivity(startIntent)
        }
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://enghouse-vidyo.gitbook.io/vidyoplatform/use-cases/android-picture-in-picture-mode.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
