diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 3e4dda69..4f64e256 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -70,7 +70,7 @@ import android.widget.Toast; public class Game extends Activity implements SurfaceHolder.Callback, OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener, - OnSystemUiVisibilityChangeListener, GameGestures + OnSystemUiVisibilityChangeListener, GameGestures, StreamView.InputCallbacks { private int lastMouseX = Integer.MIN_VALUE; private int lastMouseY = Integer.MIN_VALUE; @@ -189,6 +189,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, streamView = findViewById(R.id.surfaceView); streamView.setOnGenericMotionListener(this); streamView.setOnTouchListener(this); + streamView.setInputCallbacks(this); inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this); @@ -788,9 +789,14 @@ public class Game extends Activity implements SurfaceHolder.Callback, @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + return handleKeyDown(event) || super.onKeyDown(keyCode, event); + } + + @Override + public boolean handleKeyDown(KeyEvent event) { // Pass-through virtual navigation keys if ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) { - return super.onKeyDown(keyCode, event); + return false; } // Handle a synthetic back button event that some Android OS versions @@ -802,11 +808,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, boolean handled = false; - boolean detectedGamepad = event.getDevice() != null && ((event.getDevice().getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || - (event.getDevice().getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD); - if (detectedGamepad || (event.getDevice() == null || - event.getDevice().getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC - )) { + if (ControllerHandler.isGameControllerDevice(event.getDevice())) { // Always try the controller handler first, unless it's an alphanumeric keyboard device. // Otherwise, controller handler will eat keyboard d-pad events. handled = controllerHandler.handleButtonDown(event); @@ -816,17 +818,17 @@ public class Game extends Activity implements SurfaceHolder.Callback, // Try the keyboard handler short translated = KeyboardTranslator.translate(event.getKeyCode()); if (translated == 0) { - return super.onKeyDown(keyCode, event); + return false; } // Let this method take duplicate key down events - if (handleSpecialKeys(keyCode, true)) { + if (handleSpecialKeys(event.getKeyCode(), true)) { return true; } // Pass through keyboard input if we're not grabbing if (!grabbedInput) { - return super.onKeyDown(keyCode, event); + return false; } conn.sendKeyboardInput(translated, KeyboardPacket.KEY_DOWN, getModifierState(event)); @@ -837,9 +839,14 @@ public class Game extends Activity implements SurfaceHolder.Callback, @Override public boolean onKeyUp(int keyCode, KeyEvent event) { + return handleKeyUp(event) || super.onKeyUp(keyCode, event); + } + + @Override + public boolean handleKeyUp(KeyEvent event) { // Pass-through virtual navigation keys if ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) { - return super.onKeyUp(keyCode, event); + return false; } // Handle a synthetic back button event that some Android OS versions @@ -850,11 +857,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, } boolean handled = false; - boolean detectedGamepad = event.getDevice() != null && ((event.getDevice().getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || - (event.getDevice().getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD); - if (detectedGamepad || (event.getDevice() == null || - event.getDevice().getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC - )) { + if (ControllerHandler.isGameControllerDevice(event.getDevice())) { // Always try the controller handler first, unless it's an alphanumeric keyboard device. // Otherwise, controller handler will eat keyboard d-pad events. handled = controllerHandler.handleButtonUp(event); @@ -864,16 +867,16 @@ public class Game extends Activity implements SurfaceHolder.Callback, // Try the keyboard handler short translated = KeyboardTranslator.translate(event.getKeyCode()); if (translated == 0) { - return super.onKeyUp(keyCode, event); + return false; } - if (handleSpecialKeys(keyCode, false)) { + if (handleSpecialKeys(event.getKeyCode(), false)) { return true; } // Pass through keyboard input if we're not grabbing if (!grabbedInput) { - return super.onKeyUp(keyCode, event); + return false; } conn.sendKeyboardInput(translated, KeyboardPacket.KEY_UP, getModifierState(event)); diff --git a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java index 005ad712..8644245f 100644 --- a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java +++ b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java @@ -149,6 +149,30 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD onInputDeviceAdded(deviceId); } + private static boolean hasJoystickAxes(InputDevice device) { + return (device.getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && + getMotionRangeForJoystickAxis(device, MotionEvent.AXIS_X) != null && + getMotionRangeForJoystickAxis(device, MotionEvent.AXIS_Y) != null; + } + + private static boolean hasGamepadButtons(InputDevice device) { + return (device.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; + } + + public static boolean isGameControllerDevice(InputDevice device) { + if (device == null) { + return true; + } + + if (hasJoystickAxes(device) || hasGamepadButtons(device)) { + // Has real joystick axes or gamepad buttons + return true; + } + + // Otherwise, we'll try anything that claims to be a non-alphabetic keyboard + return device.getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC; + } + public static short getAttachedControllerMask(Context context) { int count = 0; short mask = 0; @@ -161,9 +185,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD continue; } - if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 && - getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_X) != null && - getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Y) != null) { + if (hasJoystickAxes(dev)) { LimeLog.info("Counting InputDevice: "+dev.getName()); mask |= 1 << count++; } diff --git a/app/src/main/java/com/limelight/ui/StreamView.java b/app/src/main/java/com/limelight/ui/StreamView.java index c6672ead..5356f5bf 100644 --- a/app/src/main/java/com/limelight/ui/StreamView.java +++ b/app/src/main/java/com/limelight/ui/StreamView.java @@ -3,15 +3,21 @@ package com.limelight.ui; import android.annotation.TargetApi; import android.content.Context; import android.util.AttributeSet; +import android.view.KeyEvent; import android.view.SurfaceView; public class StreamView extends SurfaceView { private double desiredAspectRatio; + private InputCallbacks inputCallbacks; public void setDesiredAspectRatio(double aspectRatio) { this.desiredAspectRatio = aspectRatio; } + public void setInputCallbacks(InputCallbacks callbacks) { + this.inputCallbacks = callbacks; + } + public StreamView(Context context) { super(context); } @@ -52,4 +58,30 @@ public class StreamView extends SurfaceView { setMeasuredDimension(measuredWidth, measuredHeight); } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + // This callbacks allows us to override dumb IME behavior like when + // Samsung's default keyboard consumes Shift+Space. We'll process + // the input event directly if any modifier keys are down. + if (inputCallbacks != null && event.getModifiers() != 0) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (inputCallbacks.handleKeyDown(event)) { + return true; + } + } + else if (event.getAction() == KeyEvent.ACTION_UP) { + if (inputCallbacks.handleKeyUp(event)) { + return true; + } + } + } + + return super.onKeyPreIme(keyCode, event); + } + + public interface InputCallbacks { + boolean handleKeyUp(KeyEvent event); + boolean handleKeyDown(KeyEvent event); + } }