diff options
author | Lauri Härsilä | 2012-10-07 04:53:52 +0300 |
---|---|---|
committer | Alyssa Milburn | 2012-10-19 22:50:09 +0200 |
commit | 21093175646b8d359e8108b93e28898ed9695457 (patch) | |
tree | e049dbbc84a1eb9599679dc3a254305ad7c34192 /backends/platform/android/org/scummvm | |
parent | ca256a23b31ba10294cf1f426ebefbc070389357 (diff) | |
download | scummvm-rg350-21093175646b8d359e8108b93e28898ed9695457.tar.gz scummvm-rg350-21093175646b8d359e8108b93e28898ed9695457.tar.bz2 scummvm-rg350-21093175646b8d359e8108b93e28898ed9695457.zip |
ANDROID: Mouse and stylus support
From pull request #285.
Diffstat (limited to 'backends/platform/android/org/scummvm')
3 files changed, 282 insertions, 4 deletions
diff --git a/backends/platform/android/org/scummvm/scummvm/MouseHelper.java b/backends/platform/android/org/scummvm/scummvm/MouseHelper.java new file mode 100644 index 0000000000..ae30bcd1da --- /dev/null +++ b/backends/platform/android/org/scummvm/scummvm/MouseHelper.java @@ -0,0 +1,214 @@ +package org.scummvm.scummvm; + +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.View; + +/** + * Contains helper methods for mouse/hover events that were introduced in Android 4.0. + * + * Mouse (hover) events seem to be a bit arbitrary, so here's couple of scenarios: + * + * + * 1. Galaxy Note 2 (4.1) + stylus: + * Tool type: TOOL_TYPE_STYLUS + * + * Key: 238 ACTION_DOWN (once) + * Key: 238 ACTION_UP (once) + * Hover: ACTION_HOVER_ENTER (once) + * Hover: ACTION_HOVER_MOVE (multiple, while hovering) + * + * touch screen with the pen: + * Hover: ACTION_HOVER_EXIT, ButtonState: 0 (once) + * Touch: ACTION_DOWN, ButtonState: 0 (once) + * Touch: ACTION_MOVE, ButtonState: 0 (multiple, while pressing) + * Touch: ACTION_UP, ButtonState: 0 (once) + * Hover: ACTION_HOVER_ENTER (once) + * + * press the stylus button while hovering: + * Hover: ACTION_HOVER_MOVE, ButtonState: 2 (multiple, while pressing button) + * Hover: ACTION_HOVER_MOVE, ButtonState: 0 (multiple, after release) + * + * + * 2. Galaxy Note 2 (4.1) + mouse (usb): + * Tool type: TOOL_TYPE_MOUSE + * + * Hover: ACTION_HOVER_ENTER (once) + * Hover: ACTION_HOVER_MOVE (multiple, while hovering) + * + * press left button: + * Hover: ACTION_HOVER_EXIT, ButtonState: 1 (once) + * Touch: ACTION_DOWN, ButtonState: 1 (once) + * Touch: ACTION_MOVE, ButtonState: 1 (multiple, while pressing) + * Touch: ACTION_UP, ButtonState: 0 (once) + * Hover: ACTION_HOVER_ENTER, ButtonState: 0 (once) + * + * press right button: + * Key: KEYCODE_BACK, ACTION_DOWN + * Hover: ACTION_HOVER_MOVE, ButtonState: 2 (multiple, while pressing button) + * Hover: ACTION_HOVER_MOVE, ButtonState: 0 (once) + * Key: KEYCODE_BACK, ACTION_UP + * + * + * 3. Asus eeePad Transformer Prime running CyanogenMod 10 (Android 4.1) + mouse (usb): + * Tool type: TOOL_TYPE_MOUSE + * + * Hover: ACTION_HOVER_ENTER (once) + * Hover: ACTION_HOVER_MOVE (multiple, while hovering) + * + * press left button: + * Hover: ACTION_HOVER_EXIT, ButtonState: 1 (once) + * Touch: ACTION_DOWN, ButtonState: 1 (once) + * Touch: ACTION_MOVE, ButtonState: 1 (multiple, while pressing) + * Touch: ACTION_UP, ButtonState: 0 (once) + * Hover: ACTION_HOVER_ENTER, ButtonState: 0 (once) + * + * press right button: + * Hover: ACTION_HOVER_EXIT, ButtonState: 2 (once) + * Touch: ACTION_DOWN, ButtonState: 2 (once) + * Touch: ACTION_MOVE, ButtonState: 2 (multiple, while pressing) + * Touch: ACTION_UP, ButtonState: 0 (once) + * Hover: ACTION_HOVER_ENTER, ButtonState: 0 (once) + * + * + * 4. Asus eeePad Transformer Prime running CyanogenMod 10 (Android 4.1) + touchpad: + * Tool type: TOOL_TYPE_FINGER + * + * Hover: ACTION_HOVER_ENTER (once) + * Hover: ACTION_HOVER_MOVE (multiple, while hovering) + * + * press left button: + * Hover: ACTION_HOVER_EXIT, ButtonState: 1 (once) + * Touch: ACTION_DOWN, ButtonState: 1 (once) + * Touch: ACTION_MOVE, ButtonState: 1 (multiple, while pressing) + * Touch: ACTION_UP, ButtonState: 0 (once) + * Hover: ACTION_HOVER_ENTER, ButtonState: 0 (once) + * + * press right button: + * Hover: ACTION_HOVER_EXIT, ButtonState: 2 (once) + * Touch: ACTION_DOWN, ButtonState: 2 (once) + * Touch: ACTION_MOVE, ButtonState: 2 (multiple, while pressing) + * Touch: ACTION_UP, ButtonState: 0 (once) + * Hover: ACTION_HOVER_ENTER, ButtonState: 0 (once) + * + */ +public class MouseHelper { + private View.OnHoverListener _listener; + private ScummVM _scummvm; + private long _rmbGuardTime; + private boolean _rmbPressed; + private boolean _lmbPressed; + + /** + * Class initialization fails when this throws an exception. + * Checking hover availability is done on static class initialization for Android 1.6 compatibility. + */ + static { + try { + Class.forName("android.view.View$OnHoverListener"); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + /** + * Calling this forces class initialization + */ + public static void checkHoverAvailable() {} + + public MouseHelper(ScummVM scummvm) { + _scummvm = scummvm; + _listener = createListener(); + } + + private View.OnHoverListener createListener() { + return new View.OnHoverListener() { + @Override + public boolean onHover(View view, MotionEvent e) { + return onTouch(e, true); + } + }; + } + + public void attach(SurfaceView main_surface) { + main_surface.setOnHoverListener(_listener); + } + + public static boolean isMouse(MotionEvent e) { + if (e == null) { + return false; + } + + InputDevice device = e.getDevice(); + + if (device == null) { + return false; + } + + int sources = device.getSources(); + + return ((sources & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || + ((sources & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) || + ((sources & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD); + } + + public boolean onTouch(MotionEvent e, boolean hover) { + _scummvm.pushEvent(ScummVMEvents.JE_MOUSE_MOVE, (int)e.getX(), (int)e.getY(), 0, 0, 0); + + int buttonState = e.getButtonState(); + + boolean lmbDown = (buttonState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY; + + if (e.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) { + // when using stylus, ButtonState is 0 + lmbDown = !hover; + } + + if (lmbDown) { + if (!_lmbPressed) { + // left mouse button was pressed just now + _scummvm.pushEvent(ScummVMEvents.JE_LMB_DOWN, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + } + + _lmbPressed = true; + } else { + if (_lmbPressed) { + // left mouse button was released just now + _scummvm.pushEvent(ScummVMEvents.JE_LMB_UP, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + } + + _lmbPressed = false; + } + + boolean rmbDown = (buttonState & MotionEvent.BUTTON_SECONDARY) == MotionEvent.BUTTON_SECONDARY; + if (rmbDown) { + if (!_rmbPressed) { + // right mouse button was pressed just now + _scummvm.pushEvent(ScummVMEvents.JE_RMB_DOWN, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + } + + _rmbPressed = true; + } else { + if (_rmbPressed) { + // right mouse button was released just now + _scummvm.pushEvent(ScummVMEvents.JE_RMB_UP, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + _rmbGuardTime = System.currentTimeMillis(); + } + + _rmbPressed = false; + } + + return true; + } + + /** + * Checks whether right mouse button is pressed or was pressed just previously. This is used to prevent sending + * extra back key on right mouse click which is the default behaviour in some platforms. + * + * @return true if right mouse button is (or was in the last 200ms) pressed + */ + public boolean getRmbGuard() { + return _rmbPressed || _rmbGuardTime + 200 > System.currentTimeMillis(); + } +} diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java index fbd6513761..34c6df3a3a 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java @@ -18,6 +18,18 @@ import java.io.File; public class ScummVMActivity extends Activity { + /* Establish whether the hover events are available */ + private static boolean _hoverAvailable; + + static { + try { + MouseHelper.checkHoverAvailable(); // this throws exception if we're on too old version + _hoverAvailable = true; + } catch (Throwable t) { + _hoverAvailable = false; + } + } + private class MyScummVM extends ScummVM { private boolean usingSmallScreen() { // Multiple screen sizes came in with Android 1.6. Have @@ -94,6 +106,7 @@ public class ScummVMActivity extends Activity { private MyScummVM _scummvm; private ScummVMEvents _events; + private MouseHelper _mouseHelper; private Thread _scummvm_thread; @Override @@ -151,7 +164,13 @@ public class ScummVMActivity extends Activity { "--savepath=" + savePath }); - _events = new ScummVMEvents(this, _scummvm); + Log.d(ScummVM.LOG_TAG, "Hover available: " + _hoverAvailable); + if (_hoverAvailable) { + _mouseHelper = new MouseHelper(_scummvm); + _mouseHelper.attach(main_surface); + } + + _events = new ScummVMEvents(this, _scummvm, _mouseHelper); main_surface.setOnKeyListener(_events); main_surface.setOnTouchListener(_events); diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java index 86227b9352..45c1f8bcb5 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java @@ -2,7 +2,6 @@ package org.scummvm.scummvm; import android.os.Handler; import android.os.Message; -import android.util.Log; import android.content.Context; import android.view.KeyEvent; import android.view.KeyCharacterMap; @@ -27,16 +26,23 @@ public class ScummVMEvents implements public static final int JE_DOUBLE_TAP = 6; public static final int JE_MULTI = 7; public static final int JE_BALL = 8; + public static final int JE_LMB_DOWN = 9; + public static final int JE_LMB_UP = 10; + public static final int JE_RMB_DOWN = 11; + public static final int JE_RMB_UP = 12; + public static final int JE_MOUSE_MOVE = 13; public static final int JE_QUIT = 0x1000; final protected Context _context; final protected ScummVM _scummvm; final protected GestureDetector _gd; final protected int _longPress; + final protected MouseHelper _mouseHelper; - public ScummVMEvents(Context context, ScummVM scummvm) { + public ScummVMEvents(Context context, ScummVM scummvm, MouseHelper mouseHelper) { _context = context; _scummvm = scummvm; + _mouseHelper = mouseHelper; _gd = new GestureDetector(context, this); _gd.setOnDoubleTapListener(this); @@ -64,7 +70,7 @@ public class ScummVMEvents implements public void handleMessage(Message msg) { if (msg.what == MSG_MENU_LONG_PRESS) { InputMethodManager imm = (InputMethodManager) - _context.getSystemService(_context.INPUT_METHOD_SERVICE); + _context.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); @@ -73,9 +79,30 @@ public class ScummVMEvents implements }; // OnKeyListener + @Override final public boolean onKey(View v, int keyCode, KeyEvent e) { final int action = e.getAction(); + if (keyCode == 238) { + // this (undocumented) event is sent when ACTION_HOVER_ENTER or ACTION_HOVER_EXIT occurs + return false; + } + + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (action != KeyEvent.ACTION_UP) { + // only send event from back button on up event, since down event is sent on right mouse click and + // cannot be caught (thus rmb click would send escape key first) + return true; + } + + if (_mouseHelper != null) { + if (_mouseHelper.getRmbGuard()) { + // right mouse button was just clicked which sends an extra back button press + return true; + } + } + } + if (e.isSystem()) { // filter what we handle switch (keyCode) { @@ -160,7 +187,16 @@ public class ScummVMEvents implements } // OnTouchListener + @Override final public boolean onTouch(View v, MotionEvent e) { + if (_mouseHelper != null) { + boolean isMouse = MouseHelper.isMouse(e); + if (isMouse) { + // mouse button is pressed + return _mouseHelper.onTouch(e, false); + } + } + final int action = e.getAction(); // constants from APIv5: @@ -177,11 +213,13 @@ public class ScummVMEvents implements } // OnGestureListener + @Override final public boolean onDown(MotionEvent e) { _scummvm.pushEvent(JE_DOWN, (int)e.getX(), (int)e.getY(), 0, 0, 0); return true; } + @Override final public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //Log.d(ScummVM.LOG_TAG, String.format("onFling: %s -> %s (%.3f %.3f)", @@ -191,10 +229,12 @@ public class ScummVMEvents implements return true; } + @Override final public void onLongPress(MotionEvent e) { // disabled, interferes with drag&drop } + @Override final public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { _scummvm.pushEvent(JE_SCROLL, (int)e1.getX(), (int)e1.getY(), @@ -203,9 +243,11 @@ public class ScummVMEvents implements return true; } + @Override final public void onShowPress(MotionEvent e) { } + @Override final public boolean onSingleTapUp(MotionEvent e) { _scummvm.pushEvent(JE_TAP, (int)e.getX(), (int)e.getY(), (int)(e.getEventTime() - e.getDownTime()), 0, 0); @@ -214,10 +256,12 @@ public class ScummVMEvents implements } // OnDoubleTapListener + @Override final public boolean onDoubleTap(MotionEvent e) { return true; } + @Override final public boolean onDoubleTapEvent(MotionEvent e) { _scummvm.pushEvent(JE_DOUBLE_TAP, (int)e.getX(), (int)e.getY(), e.getAction(), 0, 0); @@ -225,6 +269,7 @@ public class ScummVMEvents implements return true; } + @Override final public boolean onSingleTapConfirmed(MotionEvent e) { return true; } |