From e11f8803fec1b8a19235ba9e638afcba2d1328b8 Mon Sep 17 00:00:00 2001 From: Nikolai Trukhin Date: Thu, 26 Feb 2026 16:16:12 +0000 Subject: [PATCH] [android] Guard PiP calls on unsupported/problematic devices --- .../yuzu_emu/activities/EmulationActivity.kt | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 949edc1ee3..a49188cac7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -15,6 +15,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Rect import android.graphics.drawable.Icon @@ -100,6 +101,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager private var romSwapGeneration = 0 private var hasEmulationSession = processHasEmulationSession private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() } + private val pictureInPictureFailureActions: MutableSet = mutableSetOf() private fun onRomSwapStopTimeout() { if (!isWaitingForRomSwapStop) { @@ -266,12 +268,18 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager } override fun onUserLeaveHint() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { - if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) { - val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() - .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() - enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || + !isPictureInPictureSupported() || + !BooleanSetting.PICTURE_IN_PICTURE.getBoolean() || + isInPictureInPictureMode + ) { + return + } + + val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() + .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() + runPictureInPictureAction("enter picture-in-picture mode") { + enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) } } @@ -651,7 +659,30 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager return this.apply { setActions(pictureInPictureActions) } } + private fun isPictureInPictureSupported(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) + } + + private fun runPictureInPictureAction(actionName: String, action: () -> Unit) { + try { + action() + } catch (e: IllegalStateException) { + if (pictureInPictureFailureActions.add(actionName)) { + Log.warning("[PiP] Failed to $actionName: ${e.message}") + } + } catch (e: UnsupportedOperationException) { + if (pictureInPictureFailureActions.add(actionName)) { + Log.warning("[PiP] Failed to $actionName: ${e.message}") + } + } + } + fun buildPictureInPictureParams() { + if (!isPictureInPictureSupported()) { + return + } + val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -661,7 +692,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive ) } - setPictureInPictureParams(pictureInPictureParamsBuilder.build()) + runPictureInPictureAction("set picture-in-picture params") { + setPictureInPictureParams(pictureInPictureParamsBuilder.build()) + } } fun displayMultiplayerDialog() {