How to Make Picture-in-Picture Mode on Android? With Code Examples — cover illustration

Key takeaways

• Auto-enter is the 2026 default. On Android 12+ you set setAutoEnterEnabled(true) once and the system runs the home-gesture transition; onUserLeaveHint is now the fallback for API 26–30.

• Android 16 can break back-to-PiP silently. For apps targeting API 36, onBackPressed() is no longer called, so an app that entered PiP from a back override now just closes the screen. Move that logic into an OnBackPressedCallback; android:enableOnBackInvokedCallback="false" on the activity is only a temporary opt-out.

• Source rect hint kills the black flash. Without it the system falls back to a generic overlay animation that often reads as a black block; with it the player zooms from its real bounds — the single biggest perceived-quality fix.

• Manifest configChanges is non-negotiable. Miss orientation|screenSize|smallestScreenSize|screenLayout and the OS recreates your activity on every toggle, resetting the video to 0:00.

• The work is small, the payoff is large. A clean Picture-in-Picture integration on a healthy Kotlin codebase is usually 3–5 engineering days, cheap insurance against a feature your competitors already ship.

Updated October 2026: Android 17 (API 37) and the Jetpack Picture-in-Picture library added, Android 15 isTransitioningToPip, corrected Android 16 back-gesture guidance, Play target-API deadline and Media3 1.11.

What is picture-in-picture mode on Android? The short answer

Picture-in-picture (PiP) mode on Android shrinks a video or call into a small floating window that stays on top of other apps, so the user can keep watching or talking while they check a map, a calendar or a chat. Android has supported it since Android 8.0 (API 26, 2017); the user drags the window around, taps it for controls, or swipes it to the screen edge to stash it.

To turn picture-in-picture on as a user, open Settings → Apps → Special app access → Picture-in-picture and allow it for the app, then start a video or call and swipe home. The exact menu path varies slightly by OEM skin. To add it as a developer, declare android:supportsPictureInPicture="true" on the activity and, on Android 12+, set setAutoEnterEnabled(true) in PictureInPictureParams; the rest of this guide is the production version of that one line. For how PiP works across the web, iOS and Android in one place, see our picture-in-picture mode overview for web, iOS and Android.

Why Fora Soft wrote this playbook

We have built calling, streaming and video-collaboration products since 2005 — 250+ projects with a 50+ person in-house team. Picture-in-Picture is the feature clients ask for the moment one user complains that “the call dies when I open my notes.” It looks trivial in the official sample (one line, enterPictureInPictureMode()), but the production version that survives Samsung gestures, foldable hinges, the home-button race condition and Android 16’s predictive back is a few hundred lines of careful Kotlin, and our video chat app development team writes it for every Android calling client we ship.

This guide consolidates what we learned shipping PiP across BrainCert (a WebRTC-first LMS), ProVideoMeeting (an enterprise video-conferencing platform) and Sprii (a live-video shopping platform). Every snippet below is compiled against Android 16 (API 36) and verified from Android 8.0 (API 26) up, cross-checked against Google’s official Picture-in-Picture guide. Skim the key takeaways and jump to auto-enter for the modern pattern, or to source rect hint for the smooth-zoom trick. Building the same feature on iPhone or iPad? Our iOS Picture-in-Picture guide walks through the AVKit and WebRTC paths.

Building an Android video app and want PiP done right?

Book a 30-minute scoping call with our Android lead. We will sanity-check your PiP stack, source-rect handling and lifecycle hooks against your specific player.

Book a 30-min call → WhatsApp → Email us →

Why Picture-in-Picture is now table stakes

Every competitor your users compare you to has PiP. Zoom, Google Meet, WhatsApp, Telegram, YouTube, Netflix, Google Maps navigation and Twitch all treat the floating window as a default. Shipping a calling product without it in 2026 reads as outdated the moment someone tries to check their calendar mid-meeting and watches the call die.

The business case sits on three pillars. Session length: users stay in your app longer when they can multitask. Conversion to paid: on streaming apps PiP is often a paid-tier feature, so it is a lever, not just a fix. Churn defense: a one-star review saying “app closes when I switch” hurts store ranking for weeks. The work to ship it is small, usually 3–5 engineering days on a healthy codebase, which makes it one of the highest-return Android features you can ship per dev-day.

The PiP API matrix — what each Android version added

Picture-in-Picture on Android shipped on Android 8.0 (API 26) in 2017 and has been refined on every release since. The timeline below collapses that history into the four inflection points that change your code path; the table underneath is the same story in detail.

Android Picture-in-Picture API timeline: PiP in Android 8.0, auto-enter in 12, rich UI in 13, predictive-back caveat in 16.

Figure 1. Four API levels change how you enter PiP — and Android 16 adds a trap, not a method.

API / Version Capability Why it matters
API 26 — Android 8.0 (2017) PiP launched. enterPictureInPictureMode(), setSourceRectHint, setActions. Manual transition via onUserLeaveHint. The baseline for every calling app.
API 31 — Android 12 (2021) setAutoEnterEnabled for auto-enter, plus a resize-mode flag for non-video content. No more onUserLeaveHint dance; smoother resize for non-video content.
API 33 — Android 13 (2022) setTitle, setSubtitle, setExpandedAspectRatio, setCloseAction. Richer PiP UI for foldables and tablets; confirmation dialogs prevent accidental close.
API 34–35 — Android 14–15 (2023–24) Android 14: PiP on compatible Android TV OS devices. Android 15: PictureInPictureUiState.isTransitioningToPip(). Hide overlays the moment the enter animation starts, not after it ends — no flicker.
API 36 — Android 16 (2025) No new PiP methods. Predictive back on by default: onBackPressed() is not called. On screens ≥ 600 dp, orientation and resizability locks are ignored. Google Play requires API 36 for new apps and updates from 31 Aug 2026 (extension to 1 Nov 2026 on request).
API 37 — Android 17 (June 2026) Interactive PiP-style pinned windows on desktop; any app can become a bubble; the large-screen orientation opt-out is removed for API 37 targets; RAM-based app memory limits. More configuration changes and more process kills — configChanges and saved call state matter more, not less.

Picture-in-Picture has shipped on every Android release since 8.0, and the core API has not changed since Android 13; Android 15, 16 and 17 changed the behavior around it (animation state, back handling, large screens) rather than the methods, as the Android 17 release notes confirm. The branch that matters most for your code is still whether you target API 31+ (auto-enter) or support API 26–30 (the leave-hint fallback).

Manifest setup that prevents activity restarts

The single most common production bug we catch in PiP code review is a missing configChanges attribute on the call activity. When PiP toggles on or off, Android delivers a configuration change. If your activity does not declare that it handles those changes, the OS recreates it: the video resets to 0:00, the WebRTC peer connection tears down, and the UX is ruined. Add the lines below before you touch anything else.

<uses-feature
    android:name="android.software.picture_in_picture"
    android:required="false" />

<activity
    android:name=".CallActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden"
    android:launchMode="singleTask"
    android:resizeableActivity="true" />

Reach for android:launchMode="singleTask" when: the call activity must return to the foreground from PiP without being recreated. The default standard launch mode produces duplicate activity instances and breaks lifecycle assumptions.

Detecting device support before you call PiP

Android-Go devices, in-vehicle infotainment systems and a handful of low-end OEMs ship without PiP. Calling enterPictureInPictureMode() on those devices throws or silently no-ops. Always feature-check first.

val isPipSupported: Boolean
    get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
        && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)

When isPipSupported is false, fall back gracefully: hide the PiP affordance in the UI, and on the home gesture end the call cleanly with a “return to call” notification rather than letting the activity die.

Auto-enter PiP on Android 12+ — the modern default

Before Android 12 you had to listen for onUserLeaveHint — a callback the OS fires roughly when the user leaves your activity — and call enterPictureInPictureMode() from there. The race conditions were ugly: the gesture fires too late on some devices, the system kills the activity on others, and Compose apps had to wire it through an awkward side effect.

API 31 introduced setAutoEnterEnabled(true). The OS now handles the home-gesture transition for you. The only rule: the parameters must be fresh at the moment the user leaves. Set them once in onResume and never update, and an aspect-ratio change halfway through the call will not be reflected. The diagram below maps both entry paths and where the Android 16 guard sits.

Android PiP entry flow: auto-enter on API 31+ vs onUserLeaveHint on API 26-30, both guarded for Android 16 predictive back.

Figure 2. The two code paths into PiP — and the one line that keeps Android 16 from finishing your activity first.

private fun updatePipParams(call: ActiveCall) {
    if (!isPipSupported) return

    val builder = PictureInPictureParams.Builder()
        .setAspectRatio(Rational(call.videoWidth, call.videoHeight))
        .setSourceRectHint(playerView.getGlobalRect())
        .setActions(listOf(muteAction(call), endCallAction(call)))

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(call.isInProgress)
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        builder
            .setTitle(call.peerName)
            .setSubtitle(call.elapsedFormatted())
            .setCloseAction(closeWithConfirmAction(call))
    }

    setPictureInPictureParams(builder.build())
}

Call updatePipParams from onResume and from anywhere that mutates the call — aspect-ratio change after a remote camera flip, mute toggle, hold and resume. The OS reads the latest snapshot when the gesture fires. Guard the ratio: before the first frame arrives the video size is 0 × 0, and setPictureInPictureParams throws on an invalid Rational, so fall back to Rational(16, 9) and clamp to the 1:2.39–2.39:1 range.

Reach for setAutoEnterEnabled when: you target API 31+ and want the smoothest transition with the least code. Keep the params fresh on every layout and call-state change, and let the OS decide the exact moment to enter.

The Android 16 back-gesture trap

Here is the catch that surprises teams migrating in 2026. Many calling and video apps enter PiP when the user presses back, by overriding onBackPressed(). For apps targeting Android 16 (API 36), predictive back is on by default, onBackPressed() is no longer called and KEYCODE_BACK is no longer dispatched, according to Google’s Android 16 behavior changes. The override silently stops running, the system finishes the screen, and the call drops instead of shrinking. Home-gesture auto-enter is not affected; back-to-PiP is.

The durable fix is to handle back through the AndroidX callback, which predictive back does call:

onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        if (isPipSupported && callManager.isInCall) {
            enterPictureInPictureMode(buildPipParams(callManager.activeCall))
        } else {
            isEnabled = false
            onBackPressedDispatcher.onBackPressed()
        }
    }
})

If you need time to migrate, Google documents a temporary opt-out: set android:enableOnBackInvokedCallback="false" on the PiP activity (or the whole application) and onBackPressed() runs again. Treat it as a stopgap, not the fix, and keep predictive back on everywhere else in the app.

Legacy fallback for API 26–30

override fun onUserLeaveHint() {
    super.onUserLeaveHint()
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S
        && isPipSupported
        && callManager.isInCall
    ) {
        enterPictureInPictureMode(buildPipParams(callManager.activeCall))
    }
}

Lifecycle hooks: hiding the right UI in PiP

A 320 × 180 PiP window cannot fit your full call UI. Hide everything except the remote video. Action buttons live in the system PiP overlay; chat threads, mute toggles, participant lists and any sensitive surfaces should disappear entirely. onPictureInPictureModeChanged is your hook.

Collapsing a full Android call UI to a 320x180 PiP window showing only remote video plus mute, switch and end actions.

Figure 3. In PiP, everything but the remote stream goes isGone = true; the three actions render in the system overlay.

override fun onPictureInPictureModeChanged(
    isInPictureInPictureMode: Boolean,
    newConfig: Configuration
) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)

    binding.controlsScrim.isGone = isInPictureInPictureMode
    binding.chatPanel.isGone     = isInPictureInPictureMode
    binding.localPreview.isGone  = isInPictureInPictureMode
    binding.bottomSheet.isGone   = isInPictureInPictureMode
    binding.toolbar.isGone       = isInPictureInPictureMode

    // Make the player fill the window
    binding.remoteVideo.layoutParams = binding.remoteVideo.layoutParams.apply {
        width  = ViewGroup.LayoutParams.MATCH_PARENT
        height = ViewGroup.LayoutParams.MATCH_PARENT
    }
}

In Compose, expose the same state through a snapshot — PiP works the same way because Compose hosts inside a regular Activity:

@Composable
fun rememberIsInPip(): Boolean {
    val activity = LocalContext.current as ComponentActivity
    var isInPip by remember { mutableStateOf(activity.isInPictureInPictureMode) }
    DisposableEffect(activity) {
        val listener = Consumer<PictureInPictureModeChangedInfo> { info ->
            isInPip = info.isInPictureInPictureMode
        }
        activity.addOnPictureInPictureModeChangedListener(listener)
        onDispose { activity.removeOnPictureInPictureModeChangedListener(listener) }
    }
    return isInPip
}

On Android 15 and later, hide overlays even earlier. The system calls onPictureInPictureUiStateChanged() with isTransitioningToPip() as soon as the enter animation starts, so the controls are gone before the window shrinks instead of flickering through it:

override fun onPictureInPictureUiStateChanged(state: PictureInPictureUiState) {
    super.onPictureInPictureUiStateChanged(state)
    if (Build.VERSION.SDK_INT >= 35 && state.isTransitioningToPip) {
        binding.controlsScrim.isGone = true
        binding.chatPanel.isGone = true
    }
}

Source rect hint — the smooth-zoom secret

Without a source rect hint, the system has no idea where your video sits and animates a generic content overlay into the PiP corner — on many devices that looks like a black rectangle. With it, the player smoothly zooms from its real position. The fix is mechanical: hand the OS the bounds of the video view at the moment of the transition.

sourceRectHint before and after: black-rectangle flash vs the player smoothly zooming from its real bounds into PiP.

Figure 4. Same transition, two experiences: a black-block teleport without the hint, a real zoom with it.

private fun View.getGlobalRect(): Rect {
    val rect = Rect()
    getGlobalVisibleRect(rect)
    return rect
}

playerView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
    updatePipParams(callManager.activeCall)
}

Refresh the rect on every layout change — orientation flips, soft-keyboard appearance and split-screen handoff all move the player’s real bounds. When the source rect is missing you also lose the reverse animation on exit; the user gets a teleport instead of a zoom. On a coroutine-based codebase, trackPipAnimationHintView(view) from androidx.activity keeps the hint in sync with the view for you.

PiP entering with a black flash on your app?

We have shipped this exact integration on WebRTC, ExoPlayer and LiveKit-based stacks. A 30-minute call usually narrows the diagnosis to one of four root causes.

Book a 30-min call → WhatsApp → Email us →

Action buttons (RemoteAction): mute, hang up, switch camera

PiP buttons are RemoteAction objects. The system renders icons in its own colour palette — never assume your tint survives. Query getMaxNumPictureInPictureActions() for the device maximum — Google does not publish a fixed number, and extra actions are dropped silently. In our device lab three actions render on every phone we test, so we cap there.

private fun endCallAction(call: ActiveCall): RemoteAction {
    val intent = Intent(this, CallControlsReceiver::class.java)
        .setAction(CallControlsReceiver.ACTION_END_CALL)
        .putExtra(CallControlsReceiver.EXTRA_CALL_ID, call.id)

    val pendingIntent = PendingIntent.getBroadcast(
        this, REQ_END_CALL, intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    return RemoteAction(
        Icon.createWithResource(this, R.drawable.ic_call_end),
        getString(R.string.end_call),
        getString(R.string.end_call_a11y),
        pendingIntent
    )
}

Route the action through a BroadcastReceiver rather than re-launching the activity — the activity is already up, and starting it again confuses the lifecycle. Two more details: every PendingIntent needs an explicit FLAG_IMMUTABLE (or FLAG_MUTABLE) once you target API 31+, and unique request codes per action stop the OS from collapsing your intents into one.

Aspect ratio, expanded ratio, and foldables

The system clamps PiP windows between roughly 2.39:1 and 1:2.39 — anything more extreme is clipped. For a video call use Rational(16, 9) when the remote video is horizontal, Rational(9, 16) when it is portrait, and update on every camera flip.

Android 13 added setExpandedAspectRatio for an expanded PiP window. It only takes effect on devices that report FEATURE_EXPANDED_PICTURE_IN_PICTURE — today mostly Android TV and some large-screen builds — so check the feature and treat it as progressive enhancement. Wire both:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    builder
        .setAspectRatio(Rational(9, 16))           // default portrait
        .setExpandedAspectRatio(Rational(16, 9))   // expanded when user pulls
}

Reach for setExpandedAspectRatio when: Android TV or large-screen devices that report the expanded-PiP feature are a real slice of your install base. On a phone-only audience it is a day-two nicety, not a launch blocker.

Title, subtitle and the Android 13 close action

Three Android 13 additions take a stock PiP from functional to polished. setTitle and setSubtitle populate the system PiP UI when the user expands the window, and setCloseAction intercepts the system close affordance to surface a confirmation step.

Reach for setCloseAction when: a stray swipe to dismiss the PiP window would drop a live call, end a paid live stream, or hang up on a doctor mid-consultation. A confirmation step preserves session integrity without adding friction.

Title and subtitle cost two lines of code and give the expanded PiP menu context, such as who is on the call and for how long.

PiP for media playback (ExoPlayer / Media3)

If your product is a streaming app rather than a calling app, the integration looks slightly different. ExoPlayer — now Media3, stable at 1.11.1 since September 2026 — gives you the player and, through a MediaSession, the default play / pause / seek controls in the PiP window. You still trigger PiP entry yourself with setAutoEnterEnabled / setPictureInPictureParams; without a MediaSession, no system controls render. (New to the video stack? Our digital-video foundations primer covers the terms.)

val player = ExoPlayer.Builder(this).build()
val mediaSession = MediaSession.Builder(this, player).build()

playerView.player = player
playerView.useController = true

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    setPictureInPictureParams(
        PictureInPictureParams.Builder()
            .setAutoEnterEnabled(true)
            .setSeamlessResizeEnabled(true) // direct scaling for video (the default)
            .setSourceRectHint(playerView.getGlobalRect())
            .setAspectRatio(Rational(player.videoSize.width, player.videoSize.height))
            .build()
    )
}

The flag above defaults to direct scaling, which is the right choice for any moving video, call or playback: the surface just scales. Pass false to get the cross-fade instead, and reserve that for non-video PiP content such as a map or a dashboard, where a stretched UI looks worse than a fade.

PiP is one small surface of a playback product; the player, ABR ladder, DRM and CDN behind it decide whether the shrunk video still plays smoothly. If you are building that whole stack, our video and audio streaming software development team ships Media3 players with PiP, background playback and offline downloads as one piece of work.

The Jetpack Picture-in-Picture library: the 2026 shortcut

Short answer: since 2026 Google ships a Jetpack Picture-in-Picture library that wraps the version branches in this guide: androidx.core:core-pip, used together with androidx.core 1.18. It has two ready classes, BasicPictureInPicture for calls and navigation and VideoPlaybackPictureInPicture for players. In October 2026 the artifact is still alpha (1.0.0-alpha02), yet Google’s PiP guide now recommends the library first.

What it does for you, per the Jetpack PiP documentation: it calls enterPictureInPictureMode() before Android 12 and auto-enter after; VideoPlaybackPictureInPicture tracks the player bounds for the source rect hint and switches on direct scaling of the video surface; one listener replaces onPictureInPictureModeChanged and onPictureInPictureUiStateChanged with events such as ENTER_ANIMATION_START, ENTERED, STASHED and EXITED; and it ships predefined RemoteAction sets for playback and video calls.

class PlayerActivity : ComponentActivity(),
    PictureInPictureDelegate.OnPictureInPictureEventListener {

    private lateinit var pip: VideoPlaybackPictureInPicture

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pip = VideoPlaybackPictureInPicture(this)
        pip.addOnPictureInPictureEventListener(ContextCompat.getMainExecutor(this), this)
        pip.setEnabled(true)   // replaces setAutoEnterEnabled / onUserLeaveHint
    }

    override fun onDestroy() {
        pip.close()
        super.onDestroy()
    }

    override fun onPictureInPictureEvent(event: PictureInPictureDelegate.Event, config: Configuration?) {
        if (event == PictureInPictureDelegate.Event.ENTER_ANIMATION_START) hideOverlays()
    }
}

Reach for the Jetpack library when: you are starting PiP from scratch, can live with an alpha dependency, or your hand-written version keeps regressing across Android releases. You still own the manifest, the Android 16 back handling, OEM quirks and the call-specific UI; the library removes the version branching, not the product decisions.

Six pitfalls we catch in code review

1. Forgotten configChanges. The activity restarts on every PiP toggle. Symptom: video resets to 0:00, WebRTC peer connection rebuilt. Fix in the manifest, single line.

2. Stale params with auto-enter. Setting setAutoEnterEnabled(true) once in onCreate and never updating means the OS uses the initial aspect ratio for the whole call. Refresh on every layout change and call-state change.

3. Back-to-PiP living in onBackPressed(). New since apps moved to API 36: onBackPressed() is not called any more, so back closes the call instead of shrinking it. Symptom: back-to-PiP that worked on Android 15 silently stops after you bump targetSdk. Fix: an OnBackPressedCallback.

4. PiP from a background activity. enterPictureInPictureMode() from a background activity returns false silently. Always call from a resumed foreground activity, ideally through the auto-enter path.

5. RemoteAction PendingIntent without FLAG_IMMUTABLE. With targetSdk 31+ the app throws IllegalArgumentException the moment the PendingIntent is created. Audit every PendingIntent.get* call.

6. Sensitive UI leaking into PiP. Chat threads, password fields and payment forms must be hidden in onPictureInPictureModeChanged. We have seen apps expose user data in PiP recordings — treat the PiP window as a public surface.

OEM quirks: Samsung, Xiaomi, foldables

Samsung One UI uses an edge-swipe gesture instead of the stock home gesture on certain models. The auto-enter path still fires because the OS sends the same lifecycle events, but Samsung’s “reduce app screen size” option intercepts before PiP. Test on a Galaxy S device with stock gestures disabled.

Xiaomi HyperOS (and older MIUI) sometimes blocks PiP entirely under aggressive battery profiles. Mitigate by surfacing a one-time onboarding tip that deep-links to autostart settings, similar to how we ship our custom call notification stack.

Foldables (Pixel Fold, Galaxy Z Fold, Honor Magic V) trigger an onConfigurationChanged on hinge-state change. With the manifest configChanges declared correctly the activity stays alive and you can update PiP params on the fly.

Mini case: PiP rollout that lifted call duration 18%

A European video-conferencing client on our roster, similar in profile to ProVideoMeeting, came to us with a flat call-duration metric. Average call length was 9 minutes; competitor benchmarks were closer to 13. The feedback was consistent: people answered but ended quickly because they could not multitask without dropping.

Our two-week plan: full PiP support with auto-enter on Android 12+, source rect hint for smooth zoom, three action buttons (mute, switch camera, end call), Android 13 title and subtitle, and Compose lifecycle wiring. We tested across 12 devices including a Galaxy S24, a Pixel 8 Pro, a Xiaomi 14 and a Pixel Fold.

Outcome over the next 30 days: average call duration moved from 9 to 10.6 minutes (+18%), home-gesture-during-call abandonment dropped from 14% to 3%, and the Play Store rating climbed from 4.2 to 4.5 with “multitasking” appearing positively in a fifth of new reviews. Engineering effort was four Android-engineer days across two calendar weeks; our agent-assisted engineering let us reuse most of the test scaffolding from earlier projects. Want a similar assessment? Grab a 30-minute call.

Want PiP shipped without the OEM surprises?

Our team has a device lab and the OEM scars to match. We will scope your integration — auto-enter, Android 16 guard, Samsung and Xiaomi survival — and hand back a punch list.

Book a 30-min call → WhatsApp → Email us →

KPIs: what to measure once PiP ships

Quality KPIs. PiP-enter success rate over 99% on supported devices; black-flash rate (frames where the PiP window renders solid black for >100 ms) under 5%; activity-restart-on-PiP rate at 0%.

Business KPIs. Average call-duration delta (target +10% within 30 days), home-gesture abandonment rate during an active call (target under 5%), and Play Store rating with “multitasking” sentiment.

Reliability KPIs. PiP-related crash-free sessions over 99.5%; IllegalStateException on enter under 0.1%; correct aspect ratio in 100% of camera-flip scenarios.

A decision framework in five questions

The single biggest fork is calling versus playback: it flips the control source, aspect-ratio source, action set and close behavior. The matrix below is the whole answer to question one; the four questions after it scope the rest.

Android PiP config for live calling vs Media3 playback: video surface, aspect ratio, actions and close action.

Figure 5. One API surface, two tuning profiles — match the column to what your app actually plays.

Q1. Is the primary use case live video (calling) or playback (streaming)? Calling → custom RemoteAction buttons (mute, switch, end) and a dynamic aspect ratio. Playback → a MediaSession for the default play / pause / seek controls, aspect ratio from the player.

Q2. Do you target API 31+? Yes → auto-enter is the default. No → onUserLeaveHint with an explicit feature check.

Q3. How many actions do users actually need? Two or three. Anything more is invisible on most devices and clutters the system overlay.

Q4. Are Android TV or large-screen devices with expanded PiP a real portion of your install base? Yes → ship setExpandedAspectRatio on day one. No → defer to a later release.

Q5. Is an accidental call drop a paid-product problem? Yes → ship setCloseAction with confirmation. No → the default close behavior is fine.

When NOT to ship PiP at all

Two cases where PiP is the wrong investment. First, if the product has no time-sensitive visual content — an audio-only podcast app, a chat app without media — PiP adds nothing. Use the system media notification instead.

Second, if your minSdk is below API 26, or your DAU on sub-API-26 devices is significant, the cost of supporting two paths plus a graceful fallback usually outweighs the benefit. Ship a notification-driven “return to call” instead.

Reach for FLAG_SECURE when: you run end-to-end-encrypted video under a strict HIPAA posture. PiP then renders as a black rectangle in screenshots and recordings — correct behavior, but flag it to your PM so QA does not file it as a bug.

Picture-in-Picture is one piece of a calling product. Three adjacent surfaces deserve the same care, and if you are scoping the whole thing, our video-conferencing development team owns all four.

Custom call notifications wake the device when the user is not in your app — see our custom Android call notifications guide for the CallStyle and full-screen-intent patterns.

Foreground services keep the call alive when the activity goes to PiP and then to background — see our foreground services and deep links on Android guide.

Audio output switching between speaker, earpiece and Bluetooth headset is the most reported post-launch bug; the audio routing deep dive covers the edge cases.

Screen sharing for products that double as collaboration tools layers cleanly on top of a PiP-aware app — see implementing Android screen sharing on Kotlin + WebRTC.

FAQ

What is picture-in-picture mode on Android?

Picture-in-picture mode shrinks a video or call into a small floating window that stays on top of other apps, so the user can keep watching or talking while using something else. Android has supported it since Android 8.0 (API 26).

How do I turn on picture-in-picture on Android?

Open Settings → Apps → Special app access → Picture-in-picture, allow it for the app, then start a video or call and swipe home. The path varies slightly by manufacturer, and the app itself must support PiP.

How do I add Picture-in-Picture on Android?

Declare supportsPictureInPicture="true" and a wide configChanges on the activity, feature-check with FEATURE_PICTURE_IN_PICTURE, then on API 31+ call setAutoEnterEnabled(true) with a fresh PictureInPictureParams. On API 26–30 call enterPictureInPictureMode() from onUserLeaveHint.

Why did Picture-in-Picture stop working after I targeted Android 16?

For apps targeting API 36, onBackPressed() is no longer called, so PiP entered from a back override stops firing and the screen closes instead. Move the logic into an OnBackPressedCallback; android:enableOnBackInvokedCallback="false" on the activity is a temporary opt-out while you migrate. Home-gesture auto-enter keeps working.

What minSdk should a calling app target if it needs PiP?

API 26 (Android 8.0) is the floor for PiP itself. Auto-enter requires API 31; below that you fall back to onUserLeaveHint. For Google Play, new apps and updates must target API 36 (Android 16) from 31 August 2026, with an extension to 1 November 2026 on request; existing apps must target at least API 35 to stay visible to new users on newer devices.

Why does my activity recreate every time PiP toggles?

The manifest configChanges attribute is missing or incomplete. Add screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden to the activity declaration so the OS delivers a config change instead of recreating the activity.

Why does the PiP transition flash black?

You did not set setSourceRectHint, or the rect is stale. Refresh it on every layout change of the player view and pass the latest rect into setPictureInPictureParams so the player zooms instead of teleporting.

How many action buttons can a PiP window display?

Google does not publish a fixed number; query getMaxNumPictureInPictureActions() for the device maximum, because actions above it are dropped silently. Three render on every phone in our device lab, so we cap at three.

Does Picture-in-Picture work in Jetpack Compose?

Yes. Compose hosts inside a regular Activity, so the auto-enter pattern, source rect hint and lifecycle hooks all apply. Use addOnPictureInPictureModeChangedListener on the activity to drive Compose state through a remembered flag.

Is there a Jetpack library for Picture-in-Picture?

Yes. Since 2026 the alpha androidx.core:core-pip library provides BasicPictureInPicture for calls and VideoPlaybackPictureInPicture for players. They handle the pre- and post-Android 12 entry paths, the source rect hint and one unified event listener.

What changed for picture-in-picture in Android 17?

Android 17 (API 37, June 2026) adds no new PiP methods. It adds interactive pinned windows on desktop, lets users turn any app into a bubble, removes the large-screen orientation opt-out for apps targeting API 37, and enforces RAM-based memory limits, so save call state and handle configuration changes.

Can I show PiP from a background activity?

Not reliably. enterPictureInPictureMode() from a background activity returns false silently. Always trigger PiP from a resumed activity, ideally via the auto-enter path.

How much does adding PiP to a calling app usually cost?

On a healthy Kotlin codebase, three to five engineering days for the core integration and another two for OEM device-lab QA. Our agent-assisted engineering compresses that timeline. For an estimate against your codebase, a 30-minute scoping call is usually enough.

Notifications

Custom Android call notifications in 2026

CallStyle, full-screen intents, typed foreground services — the wake half of the calling stack.

Android services

Foreground services and deep links on Android

The background-service scaffolding that keeps PiP calls alive.

Audio engineering

Audio output switching on Android during a call

Speaker, earpiece, Bluetooth — the routing problem behind most post-launch bug reports.

Collaboration

Implementing Android screen sharing on Kotlin + WebRTC

MediaProjection, foreground service types, and the OEM quirks for collaboration apps.

Architecture

Custom video conferencing architecture in 2026

P2P, SFU, MCU — the scaling trade-offs behind every calling product.

Ready to ship PiP that feels native?

A correct Picture-in-Picture integration in 2026 is auto-enter guarded by feature detection, a fresh source rect hint on every layout change, manifest configChanges wide enough to absorb the transition, three immutable-PendingIntent action buttons, Android 13 metadata for foldable polish, Android 15 early hiding of overlays, and back-to-PiP moved into an OnBackPressedCallback for Android 16 and 17. The work is small and the payoff is large: longer sessions, lower abandonment, higher reviews.

If you want a second pair of eyes on your PiP stack, or a team that has shipped this pattern across 250+ projects since 2005, we are a 30-minute call away.

Book cover: Video Streaming, Volume 3 of the Fora Soft Video Engineering Handbook

The book · Volume 3 of 7

Video Streaming: A Complete Guide to Video Delivery: From ABR and CDNs to Players, WebRTC, DRM, and the Economics of Streaming

Picture-in-Picture keeps the window alive; the player and the real-time session inside it decide whether the shrunk video still plays smoothly. Our delivery volume covers that layer: players and stall recovery, ABR rung selection, WebRTC and real-time media, and QoE monitoring. It helps Android teams whose calls and streams have to survive every app switch. Written by Nikolay Sapunov, CEO at Fora Soft.

Read it on Amazon →Kindle & paperback · Fora Soft Video Engineering Handbook

Need PiP that survives every Android device?

Tell us about your stack. We will return a punch list of PiP, lifecycle and OEM survival fixes within a week.

Book a 30-min call → WhatsApp → Email us →

Continue learning
  • Development