הגדרת האפליקציה ל'תמונה בתוך תמונה'

בתג הפעילות של הקובץ AndroidManifest.xml, מבצעים את הפעולות הבאות:

  1. מוסיפים את supportsPictureInPicture ומגדירים אותו כ-true כדי להצהיר שאתם משתמשים בתמונה בתוך תמונה (PiP) באפליקציה.
  2. מוסיפים את configChanges ומגדירים אותו כ-orientation|screenLayout|screenSize|smallestScreenSize כדי לציין שהפעילות מטפלת בשינויים בהגדרות הפריסה. כך הפעילות לא תופעל מחדש כשמתרחשים שינויים בפריסה במהלך המעברים למצב PiP.
<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 12

כדי להוסיף "תמונה בתוך תמונה" בגרסאות קודמות ל-Android 12, משתמשים ב-addOnUserLeaveHintProvider. כדי להוסיף תמונה בתוך תמונה בגרסאות Android שקדמו ל-Android 12:

  1. הוסיפו שער גרסה כדי שהקוד הזה יהיה נגיש רק בגרסאות O עד R.
  2. משתמשים ב-DisposableEffect עם Context כמפתח.
  3. בתוך DisposableEffect, מגדירים את ההתנהגות של onUserLeaveHintProvider כשהוא מופעל באמצעות lambda. ב-lambda, מפעילים את 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 אחרי Android 12, ה-PictureInPictureParams.Builder מתווסף באמצעות משתנה שמועבר לנגן הווידאו של האפליקציה.

  1. יוצרים modifier ומפעילים בו את onGloballyPositioned. הקואורדינטות של הפריסה ישמשו בשלב מאוחר יותר.
  2. יוצרים משתנה ל-PictureInPictureParams.Builder().
  3. מוסיפים הצהרת if כדי לבדוק אם ה-SDK הוא S ואילך. אם כן, מוסיפים את setAutoEnterEnabled ל-builder ומגדירים אותו ל-true כדי להיכנס למצב PiP בהחלקה. כך האנימציה חלקה יותר מאשר כשעוברים דרך 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 כדי להגדיר את יחס הגובה-רוחב של חלון ה-PiP

כדי להגדיר את יחס הגובה-רוחב של חלון ה-PiP, אפשר לבחור יחס גובה-רוחב ספציפי או להשתמש ברוחב ובגובה של גודל הסרטון בנגן. אם אתם משתמשים בנגן media3, ודאו שהנגן לא ריק ושגודל הסרטון בנגן לא שווה ל-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.