diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 8984c1c7..3d08ee14 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -170,6 +170,14 @@ public class Game extends Activity implements SurfaceHolder.Callback, streamView = (StreamView) findViewById(R.id.surfaceView); streamView.setOnGenericMotionListener(this); streamView.setOnTouchListener(this); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + streamView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() { + @Override + public boolean onCapturedPointer(View view, MotionEvent motionEvent) { + return handleMotionEvent(motionEvent, true); + } + }); + } // Warn the user if they're on a metered connection checkDataConnection(); @@ -277,6 +285,19 @@ public class Game extends Activity implements SurfaceHolder.Callback, streamView.getHolder().addCallback(this); } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // If the window gains focus while we're grabbing input, + // we'll need to request for pointer capture again + if (grabbedInput) { + inputCaptureProvider.enableCapture(); + } + } + } + private void prepareDisplayForRendering() { Display display = getWindowManager().getDefaultDisplay(); WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes(); @@ -659,8 +680,12 @@ public class Game extends Activity implements SurfaceHolder.Callback, inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); } - // Returns true if the event was consumed private boolean handleMotionEvent(MotionEvent event) { + return handleMotionEvent(event, false); + } + + // Returns true if the event was consumed + private boolean handleMotionEvent(MotionEvent event, boolean capturedMouseCallback) { // Pass through keyboard input if we're not grabbing if (!grabbedInput) { return false; @@ -714,7 +739,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, } // Get relative axis values if we can - if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) { + if (inputCaptureProvider.eventHasRelativeMouseAxes(event) || capturedMouseCallback) { // Send the deltas straight from the motion event conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event), (short) inputCaptureProvider.getRelativeAxisY(event)); diff --git a/app/src/main/java/com/limelight/binding/input/capture/AndroidNativePointerCaptureProvider.java b/app/src/main/java/com/limelight/binding/input/capture/AndroidNativePointerCaptureProvider.java new file mode 100644 index 00000000..d33cbc66 --- /dev/null +++ b/app/src/main/java/com/limelight/binding/input/capture/AndroidNativePointerCaptureProvider.java @@ -0,0 +1,46 @@ +package com.limelight.binding.input.capture; + +import android.annotation.TargetApi; +import android.os.Build; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.View; + +@TargetApi(Build.VERSION_CODES.O) +public class AndroidNativePointerCaptureProvider extends InputCaptureProvider { + + private View targetView; + + public AndroidNativePointerCaptureProvider(View targetView) { + this.targetView = targetView; + } + + public static boolean isCaptureProviderSupported() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + } + + @Override + public void enableCapture() { + targetView.requestPointerCapture(); + } + + @Override + public void disableCapture() { + targetView.releasePointerCapture(); + } + + @Override + public boolean eventHasRelativeMouseAxes(MotionEvent event) { + return (event.getSource() & InputDevice.SOURCE_MOUSE_RELATIVE) != 0; + } + + @Override + public float getRelativeAxisX(MotionEvent event) { + return event.getX(); + } + + @Override + public float getRelativeAxisY(MotionEvent event) { + return event.getY(); + } +} diff --git a/app/src/main/java/com/limelight/binding/input/capture/AndroidCaptureProvider.java b/app/src/main/java/com/limelight/binding/input/capture/AndroidPointerIconCaptureProvider.java similarity index 92% rename from app/src/main/java/com/limelight/binding/input/capture/AndroidCaptureProvider.java rename to app/src/main/java/com/limelight/binding/input/capture/AndroidPointerIconCaptureProvider.java index f1133512..f5069d38 100644 --- a/app/src/main/java/com/limelight/binding/input/capture/AndroidCaptureProvider.java +++ b/app/src/main/java/com/limelight/binding/input/capture/AndroidPointerIconCaptureProvider.java @@ -10,11 +10,11 @@ import android.view.View; import android.view.ViewGroup; @TargetApi(Build.VERSION_CODES.N) -public class AndroidCaptureProvider extends InputCaptureProvider { +public class AndroidPointerIconCaptureProvider extends InputCaptureProvider { private ViewGroup rootViewGroup; private Context context; - public AndroidCaptureProvider(Activity activity) { + public AndroidPointerIconCaptureProvider(Activity activity) { this.context = activity; this.rootViewGroup = (ViewGroup) activity.getWindow().getDecorView(); } diff --git a/app/src/main/java/com/limelight/binding/input/capture/InputCaptureManager.java b/app/src/main/java/com/limelight/binding/input/capture/InputCaptureManager.java index 4a5f634b..320968ff 100644 --- a/app/src/main/java/com/limelight/binding/input/capture/InputCaptureManager.java +++ b/app/src/main/java/com/limelight/binding/input/capture/InputCaptureManager.java @@ -3,6 +3,7 @@ package com.limelight.binding.input.capture; import android.app.Activity; import com.limelight.LimeLog; +import com.limelight.R; import com.limelight.binding.input.evdev.EvdevCaptureProvider; import com.limelight.binding.input.evdev.EvdevListener; @@ -15,13 +16,17 @@ public class InputCaptureManager { LimeLog.info("Using NVIDIA mouse capture extension"); return new ShieldCaptureProvider(activity); } + else if (AndroidNativePointerCaptureProvider.isCaptureProviderSupported()) { + LimeLog.info("Using Android O+ native mouse capture"); + return new AndroidNativePointerCaptureProvider(activity.findViewById(R.id.surfaceView)); + } else if (EvdevCaptureProvider.isCaptureProviderSupported()) { LimeLog.info("Using Evdev mouse capture"); return new EvdevCaptureProvider(activity, rootListener); } - else if (AndroidCaptureProvider.isCaptureProviderSupported()) { - LimeLog.info("Using Android N+ native mouse capture"); - return new AndroidCaptureProvider(activity); + else if (AndroidPointerIconCaptureProvider.isCaptureProviderSupported()) { + LimeLog.info("Using Android N+ pointer hiding"); + return new AndroidPointerIconCaptureProvider(activity); } else { LimeLog.info("Mouse capture not available");