Add gestures to bring up the software keyboard - Long press start or tap with 3 fingers

This commit is contained in:
Cameron Gutman 2015-01-25 22:55:12 -05:00
parent 4d24c654b9
commit bcc67269ab
3 changed files with 71 additions and 9 deletions

View File

@ -16,6 +16,7 @@ import com.limelight.nvstream.av.video.VideoDecoderRenderer;
import com.limelight.nvstream.input.KeyboardPacket; import com.limelight.nvstream.input.KeyboardPacket;
import com.limelight.nvstream.input.MouseButtonPacket; import com.limelight.nvstream.input.MouseButtonPacket;
import com.limelight.preferences.PreferenceConfiguration; import com.limelight.preferences.PreferenceConfiguration;
import com.limelight.ui.GameGestures;
import com.limelight.utils.Dialog; import com.limelight.utils.Dialog;
import com.limelight.utils.SpinnerDialog; import com.limelight.utils.SpinnerDialog;
@ -30,6 +31,7 @@ import android.net.ConnectivityManager;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.view.Display; import android.view.Display;
import android.view.InputDevice; import android.view.InputDevice;
@ -44,6 +46,7 @@ import android.view.View.OnTouchListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast; import android.widget.Toast;
import java.util.Locale; import java.util.Locale;
@ -51,7 +54,7 @@ import java.util.Locale;
public class Game extends Activity implements SurfaceHolder.Callback, public class Game extends Activity implements SurfaceHolder.Callback,
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener, OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
OnSystemUiVisibilityChangeListener OnSystemUiVisibilityChangeListener, GameGestures
{ {
private int lastMouseX = Integer.MIN_VALUE; private int lastMouseX = Integer.MIN_VALUE;
private int lastMouseY = Integer.MIN_VALUE; private int lastMouseY = Integer.MIN_VALUE;
@ -59,6 +62,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Only 2 touches are supported // Only 2 touches are supported
private TouchContext[] touchContextMap = new TouchContext[2]; private TouchContext[] touchContextMap = new TouchContext[2];
private long threeFingerDownTime = 0;
private static final int THREE_FINGER_TAP_THRESHOLD = 300;
private ControllerHandler controllerHandler; private ControllerHandler controllerHandler;
private KeyboardTranslator keybTranslator; private KeyboardTranslator keybTranslator;
@ -189,7 +195,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Initialize the connection // Initialize the connection
conn = new NvConnection(host, uniqueId, Game.this, config, PlatformBinding.getCryptoProvider(this)); conn = new NvConnection(host, uniqueId, Game.this, config, PlatformBinding.getCryptoProvider(this));
keybTranslator = new KeyboardTranslator(conn); keybTranslator = new KeyboardTranslator(conn);
controllerHandler = new ControllerHandler(conn, prefConfig.deadzonePercentage); controllerHandler = new ControllerHandler(conn, this, prefConfig.deadzonePercentage);
SurfaceHolder sh = sv.getHolder(); SurfaceHolder sh = sv.getHolder();
if (prefConfig.stretchVideo || !decoderRenderer.isHardwareAccelerated()) { if (prefConfig.stretchVideo || !decoderRenderer.isHardwareAccelerated()) {
@ -480,6 +486,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
} }
@Override
public void showKeyboard() {
LimeLog.info("Showing keyboard overlay");
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
// Returns true if the event was consumed // Returns true if the event was consumed
private boolean handleMotionEvent(MotionEvent event) { private boolean handleMotionEvent(MotionEvent event) {
// Pass through keyboard input if we're not grabbing // Pass through keyboard input if we're not grabbing
@ -501,7 +514,22 @@ public class Game extends Activity implements SurfaceHolder.Callback,
int actionIndex = event.getActionIndex(); int actionIndex = event.getActionIndex();
int eventX = (int)event.getX(actionIndex); int eventX = (int)event.getX(actionIndex);
int eventY = (int)event.getY(actionIndex); int eventY = (int)event.getY(actionIndex);
// Special handling for 3 finger gesture
if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN &&
event.getPointerCount() == 3) {
// Three fingers down
threeFingerDownTime = SystemClock.uptimeMillis();
// Cancel the first and second touches to avoid
// erroneous events
for (TouchContext aTouchContext : touchContextMap) {
aTouchContext.cancelTouch();
}
return true;
}
TouchContext context = getTouchContext(actionIndex); TouchContext context = getTouchContext(actionIndex);
if (context == null) { if (context == null) {
@ -516,8 +544,16 @@ public class Game extends Activity implements SurfaceHolder.Callback,
break; break;
case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
if (event.getPointerCount() == 1) {
// All fingers up
if (SystemClock.uptimeMillis() - threeFingerDownTime < THREE_FINGER_TAP_THRESHOLD) {
// This is a 3 finger tap to bring up the keyboard
showKeyboard();
return true;
}
}
context.touchUpEvent(eventX, eventY); context.touchUpEvent(eventX, eventY);
if (actionIndex == 0 && event.getPointerCount() > 1) { if (actionIndex == 0 && event.getPointerCount() > 1 && !context.isCancelled()) {
// The original secondary touch now becomes primary // The original secondary touch now becomes primary
context.touchDownEvent((int)event.getX(1), (int)event.getY(1)); context.touchDownEvent((int)event.getX(1), (int)event.getY(1));
} }

View File

@ -10,6 +10,7 @@ import android.view.MotionEvent;
import com.limelight.LimeLog; import com.limelight.LimeLog;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket; import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.ui.GameGestures;
import com.limelight.utils.Vector2d; import com.limelight.utils.Vector2d;
public class ControllerHandler { public class ControllerHandler {
@ -31,6 +32,9 @@ public class ControllerHandler {
private long lastRbUpTime = 0; private long lastRbUpTime = 0;
private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100; private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100;
private long startDownTime = 0;
private static final int START_DOWN_TIME_KEYB_MS = 750;
private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25; private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25;
private static final int EMULATING_SPECIAL = 0x1; private static final int EMULATING_SPECIAL = 0x1;
@ -46,10 +50,12 @@ public class ControllerHandler {
private NvConnection conn; private NvConnection conn;
private double stickDeadzone; private double stickDeadzone;
private final ControllerMapping defaultMapping = new ControllerMapping(); private final ControllerMapping defaultMapping = new ControllerMapping();
private GameGestures gestures;
private boolean hasGameController; private boolean hasGameController;
public ControllerHandler(NvConnection conn, int deadzonePercentage) { public ControllerHandler(NvConnection conn, GameGestures gestures, int deadzonePercentage) {
this.conn = conn; this.conn = conn;
this.gestures = gestures;
// HACK: For now we're hardcoding a 10% deadzone. Some deadzone // HACK: For now we're hardcoding a 10% deadzone. Some deadzone
// is required for controller batching support to work. // is required for controller batching support to work.
@ -513,6 +519,9 @@ public class ControllerHandler {
break; break;
case KeyEvent.KEYCODE_BUTTON_START: case KeyEvent.KEYCODE_BUTTON_START:
case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_MENU:
if (SystemClock.uptimeMillis() - startDownTime > ControllerHandler.START_DOWN_TIME_KEYB_MS) {
gestures.showKeyboard();
}
inputMap &= ~ControllerPacket.PLAY_FLAG; inputMap &= ~ControllerPacket.PLAY_FLAG;
break; break;
case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_BACK:
@ -621,6 +630,9 @@ public class ControllerHandler {
break; break;
case KeyEvent.KEYCODE_BUTTON_START: case KeyEvent.KEYCODE_BUTTON_START:
case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_MENU:
if (event.getRepeatCount() == 0) {
startDownTime = SystemClock.uptimeMillis();
}
inputMap |= ControllerPacket.PLAY_FLAG; inputMap |= ControllerPacket.PLAY_FLAG;
break; break;
case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_BACK:

View File

@ -9,6 +9,7 @@ public class TouchContext {
private int originalTouchX = 0; private int originalTouchX = 0;
private int originalTouchY = 0; private int originalTouchY = 0;
private long originalTouchTime = 0; private long originalTouchTime = 0;
private boolean cancelled;
private NvConnection conn; private NvConnection conn;
private int actionIndex; private int actionIndex;
@ -56,12 +57,17 @@ public class TouchContext {
originalTouchX = lastTouchX = eventX; originalTouchX = lastTouchX = eventX;
originalTouchY = lastTouchY = eventY; originalTouchY = lastTouchY = eventY;
originalTouchTime = System.currentTimeMillis(); originalTouchTime = System.currentTimeMillis();
cancelled = false;
return true; return true;
} }
public void touchUpEvent(int eventX, int eventY) public void touchUpEvent(int eventX, int eventY)
{ {
if (cancelled) {
return;
}
if (isTap()) if (isTap())
{ {
byte buttonIndex = getMouseButtonIndex(); byte buttonIndex = getMouseButtonIndex();
@ -81,8 +87,8 @@ public class TouchContext {
} }
public boolean touchMoveEvent(int eventX, int eventY) public boolean touchMoveEvent(int eventX, int eventY)
{ {
if (eventX != lastTouchX || eventY != lastTouchY) if (eventX != lastTouchX || eventY != lastTouchY)
{ {
// We only send moves for the primary touch point // We only send moves for the primary touch point
if (actionIndex == 0) { if (actionIndex == 0) {
@ -102,4 +108,12 @@ public class TouchContext {
return true; return true;
} }
public void cancelTouch() {
cancelled = true;
}
public boolean isCancelled() {
return cancelled;
}
} }