From 529a2f7bf889f95058d1473643ac8a629ad3b1c2 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 13 Jun 2022 19:23:03 -0500 Subject: [PATCH] Prevent PiP entry while the USB permission dialog is open --- app/src/main/java/com/limelight/Game.java | 27 ++++++- .../input/driver/UsbDriverService.java | 80 +++++++++++++++---- 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 6159a176..0119b641 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -87,7 +87,7 @@ import java.util.Locale; public class Game extends Activity implements SurfaceHolder.Callback, OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener, OnSystemUiVisibilityChangeListener, GameGestures, StreamView.InputCallbacks, - PerfOverlayListener + PerfOverlayListener, UsbDriverService.UsbDriverStateListener { private int lastButtonState = 0; @@ -121,6 +121,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, private boolean autoEnterPip = false; private boolean surfaceCreated = false; private boolean attemptedConnection = false; + private int suppressPipRefCount = 0; private String pcName; private String appName; @@ -153,6 +154,8 @@ public class Game extends Activity implements SurfaceHolder.Callback, public void onServiceConnected(ComponentName componentName, IBinder iBinder) { UsbDriverService.UsbDriverBinder binder = (UsbDriverService.UsbDriverBinder) iBinder; binder.setListener(controllerHandler); + binder.setStateListener(Game.this); + binder.start(); connectedToUsbDriverService = true; } @@ -573,11 +576,13 @@ public class Game extends Activity implements SurfaceHolder.Callback, return builder.build(); } - private void setPipAutoEnter(boolean autoEnter) { + private void updatePipAutoEnter() { if (!prefConfig.enablePip) { return; } + boolean autoEnter = connected && suppressPipRefCount == 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { setPictureInPictureParams(getPictureInPictureParams(autoEnter)); } @@ -1608,8 +1613,8 @@ public class Game extends Activity implements SurfaceHolder.Callback, private void stopConnection() { if (connecting || connected) { - setPipAutoEnter(false); connecting = connected = false; + updatePipAutoEnter(); controllerHandler.stop(); @@ -1776,9 +1781,9 @@ public class Game extends Activity implements SurfaceHolder.Callback, spinner = null; } - setPipAutoEnter(true); connected = true; connecting = false; + updatePipAutoEnter(); // Hide the mouse cursor now after a short delay. // Doing it before dismissing the spinner seems to be undone @@ -1976,4 +1981,18 @@ public class Game extends Activity implements SurfaceHolder.Callback, } }); } + + @Override + public void onUsbPermissionPromptStarting() { + // Disable PiP auto-enter while the USB permission prompt is on-screen. This prevents + // us from entering PiP while the user is interacting with the OS permission dialog. + suppressPipRefCount++; + updatePipAutoEnter(); + } + + @Override + public void onUsbPermissionPromptCompleted() { + suppressPipRefCount--; + updatePipAutoEnter(); + } } diff --git a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java index f96f7e8d..b6602be9 100644 --- a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java +++ b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java @@ -29,6 +29,7 @@ public class UsbDriverService extends Service implements UsbDriverListener { private UsbManager usbManager; private PreferenceConfiguration prefConfig; + private boolean started; private final UsbEventReceiver receiver = new UsbEventReceiver(); private final UsbDriverBinder binder = new UsbDriverBinder(); @@ -36,6 +37,7 @@ public class UsbDriverService extends Service implements UsbDriverListener { private final ArrayList controllers = new ArrayList<>(); private UsbDriverListener listener; + private UsbDriverStateListener stateListener; private int nextDeviceId; @Override @@ -93,6 +95,11 @@ public class UsbDriverService extends Service implements UsbDriverListener { else if (action.equals(ACTION_USB_PERMISSION)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + // Permission dialog is now closed + if (stateListener != null) { + stateListener.onUsbPermissionPromptCompleted(); + } + // If we got this far, we've already found we're able to handle this device if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { handleUsbDeviceState(device); @@ -112,6 +119,18 @@ public class UsbDriverService extends Service implements UsbDriverListener { } } } + + public void setStateListener(UsbDriverStateListener stateListener) { + UsbDriverService.this.stateListener = stateListener; + } + + public void start() { + UsbDriverService.this.start(); + } + + public void stop() { + UsbDriverService.this.stop(); + } } private void handleUsbDeviceState(UsbDevice device) { @@ -121,20 +140,29 @@ public class UsbDriverService extends Service implements UsbDriverListener { if (!usbManager.hasPermission(device)) { // Let's ask for permission try { + // Tell the state listener that we're about to display a permission dialog + if (stateListener != null) { + stateListener.onUsbPermissionPromptStarting(); + } + + int intentFlags = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // This PendingIntent must be mutable to allow the framework to populate EXTRA_DEVICE and EXTRA_PERMISSION_GRANTED. + intentFlags |= PendingIntent.FLAG_MUTABLE; + } + // This function is not documented as throwing any exceptions (denying access // is indicated by calling the PendingIntent with a false result). However, // Samsung Knox has some policies which block this request, but rather than // just returning a false result or returning 0 enumerated devices, // they throw an undocumented SecurityException from this call, crashing // the whole app. :( - int intentFlags = 0; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - // This PendingIntent must be mutable to allow the framework to populate EXTRA_DEVICE and EXTRA_PERMISSION_GRANTED. - intentFlags |= PendingIntent.FLAG_MUTABLE; - } usbManager.requestPermission(device, PendingIntent.getBroadcast(UsbDriverService.this, 0, new Intent(ACTION_USB_PERMISSION), intentFlags)); } catch (SecurityException e) { Toast.makeText(this, this.getText(R.string.error_usb_prohibited), Toast.LENGTH_LONG).show(); + if (stateListener != null) { + stateListener.onUsbPermissionPromptCompleted(); + } } return; } @@ -225,10 +253,12 @@ public class UsbDriverService extends Service implements UsbDriverListener { ((!isRecognizedInputDevice(device) || claimAllAvailable) && Xbox360Controller.canClaimDevice(device)); } - @Override - public void onCreate() { - this.usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); - this.prefConfig = PreferenceConfiguration.readPreferences(this); + private void start() { + if (started) { + return; + } + + started = true; // Register for USB attach broadcasts and permission completions IntentFilter filter = new IntentFilter(); @@ -250,14 +280,16 @@ public class UsbDriverService extends Service implements UsbDriverListener { } } - @Override - public void onDestroy() { + private void stop() { + if (!started) { + return; + } + + started = false; + // Stop the attachment receiver unregisterReceiver(receiver); - // Remove listeners - listener = null; - // Stop all controllers while (controllers.size() > 0) { // Stop and remove the controller @@ -265,8 +297,28 @@ public class UsbDriverService extends Service implements UsbDriverListener { } } + @Override + public void onCreate() { + this.usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); + this.prefConfig = PreferenceConfiguration.readPreferences(this); + } + + @Override + public void onDestroy() { + stop(); + + // Remove listeners + listener = null; + stateListener = null; + } + @Override public IBinder onBind(Intent intent) { return binder; } + + public interface UsbDriverStateListener { + void onUsbPermissionPromptStarting(); + void onUsbPermissionPromptCompleted(); + } }