Как настроить приложение для режима "Картинка в картинке"

В теге activity файла AndroidManifest.xml выполните следующие действия:

  1. Добавьте supportsPictureInPicture и задайте значение true, чтобы указать, что в приложении будет использоваться режим "картинка в картинке".
  2. Добавьте configChanges и задайте для него значение orientation|screenLayout|screenSize|smallestScreenSize, чтобы указать, что ваше действие обрабатывает изменения конфигурации макета. Это позволит избежать перезапуска действия при изменении макета во время перехода в режим "Картинка в картинке".
<activity
    android:name=".SnippetsActivity"
    android:exported="true"
    android:supportsPictureInPicture="true"
    android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
    android:theme="@style/Theme.Snippets">

В коде Compose выполните следующие действия:

  1. Добавьте это расширение на сайте Context. Вы будете использовать это расширение несколько раз на протяжении всего руководства для доступа к действию.
    internal fun Context.findActivity(): ComponentActivity {
        var context = this
        while (context is ContextWrapper) {
            if (context is ComponentActivity) return context
            context = context.baseContext
        }
        throw IllegalStateException("Picture in picture should be called in the context of an Activity")
    }

Как добавить режим "Картинка в картинке" при выходе из приложения на устройствах с Android 11 и более ранними версиями

Чтобы добавить режим "Картинка в картинке" для версий до Android 12, используйте addOnUserLeaveHintProvider. Чтобы добавить режим "Картинка в картинке" в Android 11 и более ранних версиях, выполните следующие действия:

  1. Добавьте проверку версии, чтобы этот код выполнялся только в версиях от O до R.
  2. Используйте DisposableEffect с ключом Context.
  3. Внутри DisposableEffect определите поведение при активации onUserLeaveHintProvider с помощью лямбда-выражения. В лямбда-выражении вызовите метод enterPictureInPictureMode() для findActivity() и передайте PictureInPictureParams.Builder().build().
  4. Добавьте addOnUserLeaveHintListener с помощью findActivity() и передайте лямбда-выражение.
  5. В onDispose добавьте removeOnUserLeaveHintListener с помощью findActivity() и передайте лямбда-выражение.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
    Build.VERSION.SDK_INT < Build.VERSION_CODES.S
) {
    val context = LocalContext.current
    DisposableEffect(context) {
        val onUserLeaveBehavior = Runnable {
            context.findActivity()
                .enterPictureInPictureMode(PictureInPictureParams.Builder().build())
        }
        context.findActivity().addOnUserLeaveHintListener(
            onUserLeaveBehavior
        )
        onDispose {
            context.findActivity().removeOnUserLeaveHintListener(
                onUserLeaveBehavior
            )
        }
    }
} else {
    Log.i("PiP info", "API does not support PiP")
}

Как добавить режим "Картинка в картинке" при выходе из приложения в Android 12 и более поздних версиях

В Android 12 и более поздних версиях PictureInPictureParams.Builder добавляется с помощью модификатора, который передается в видеопроигрыватель приложения.

  1. Создайте объект modifier и вызовите для него метод onGloballyPositioned. Координаты макета понадобятся вам на следующем шаге.
  2. Создайте переменную для PictureInPictureParams.Builder().
  3. Добавьте оператор if, чтобы проверить, используется ли SDK версии S или более поздней. Если да, добавьте в конструктор setAutoEnterEnabled и задайте значение true, чтобы при проведении по экрану включался режим "Картинка в картинке". Это обеспечивает более плавную анимацию, чем при использовании enterPictureInPictureMode.
  4. Используйте findActivity(), чтобы позвонить на номер setPictureInPictureParams(). Вызовите build() для builder и передайте его.

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(true)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}
VideoPlayer(pipModifier)

Используйте setAspectRatio, чтобы задать соотношение сторон окна в режиме "картинка в картинке".

Чтобы задать соотношение сторон окна картинки в картинке, вы можете выбрать определенное соотношение сторон или использовать ширину и высоту видео в проигрывателе. Если вы используете проигрыватель media3, перед настройкой соотношения сторон убедитесь, что проигрыватель не равен null и размер видео в нем не равен VideoSize.UNKNOWN.

val context = LocalContext.current

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()
    if (shouldEnterPipMode && player != null && player.videoSize != VideoSize.UNKNOWN) {
        val sourceRect = layoutCoordinates.boundsInWindow().toAndroidRectF().toRect()
        builder.setSourceRectHint(sourceRect)
        builder.setAspectRatio(
            Rational(player.videoSize.width, player.videoSize.height)
        )
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(shouldEnterPipMode)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}

VideoPlayer(pipModifier)

Если вы используете собственный проигрыватель, задайте соотношение сторон, указав высоту и ширину проигрывателя с помощью синтаксиса, который поддерживается вашим проигрывателем. Если размер проигрывателя изменится во время инициализации и соотношение сторон выйдет за допустимые пределы, приложение завершит работу со сбоем. Возможно, вам потребуется добавить проверки, чтобы определить, когда можно рассчитать соотношение сторон, как это делается для проигрывателя media3.