Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Dual controller typing (#2936)
Browse files Browse the repository at this point in the history
* Dual hand typing

* Fix for the keyboard dismiss when pressing two keys simultaneously

* Fix for the key flicker when another key is pressed
  • Loading branch information
keianhzo authored Mar 17, 2020
1 parent 9cfba1f commit 685f2cf
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ void dispatchCreateWidgetLayer(final int aHandle, final Surface aSurface, final

@Keep
@SuppressWarnings("unused")
void handleMotionEvent(final int aHandle, final int aDevice, final boolean aPressed, final float aX, final float aY) {
void handleMotionEvent(final int aHandle, final int aDevice, final boolean aFocused, final boolean aPressed, final float aX, final float aY) {
runOnUiThread(() -> {
Widget widget = mWidgets.get(aHandle);
if (!isWidgetInputEnabled(widget)) {
Expand All @@ -819,12 +819,14 @@ void handleMotionEvent(final int aHandle, final int aDevice, final boolean aPres
final float y = aY / scale;

if (widget == null) {
MotionEventGenerator.dispatch(mRootWidget, aDevice, aPressed, x, y);
MotionEventGenerator.dispatch(mRootWidget, aDevice, aFocused, aPressed, x, y);

} else if (widget.getBorderWidth() > 0) {
final int border = widget.getBorderWidth();
MotionEventGenerator.dispatch(widget, aDevice, aPressed, x - border, y - border);
MotionEventGenerator.dispatch(widget, aDevice, aFocused, aPressed, x - border, y - border);

} else {
MotionEventGenerator.dispatch(widget, aDevice, aPressed, x, y);
MotionEventGenerator.dispatch(widget, aDevice, aFocused, aPressed, x, y);
}
});
}
Expand All @@ -839,7 +841,7 @@ void handleScrollEvent(final int aHandle, final int aDevice, final float aX, fin
}
if (widget != null) {
float scrollDirection = mSettings.getScrollDirection() == 0 ? 1.0f : -1.0f;
MotionEventGenerator.dispatchScroll(widget, aDevice, aX * scrollDirection, aY * scrollDirection);
MotionEventGenerator.dispatchScroll(widget, aDevice, true,aX * scrollDirection, aY * scrollDirection);
} else {
Log.e(LOGTAG, "Failed to find widget for scroll event: " + aHandle);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@

import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.InputDevice;
import android.util.SparseArray;
import android.view.InputDevice;
import android.view.MotionEvent;

import org.mozilla.vrbrowser.ui.widgets.Widget;
import org.mozilla.vrbrowser.utils.SystemUtils;

import java.util.Arrays;
import java.util.List;

public class MotionEventGenerator {
static final String LOGTAG = SystemUtils.createLogtag(MotionEventGenerator.class);
Expand Down Expand Up @@ -53,11 +52,11 @@ static class Device {
private static SparseArray<Device> devices = new SparseArray<>();


private static void generateEvent(Widget aWidget, Device aDevice, int aAction, boolean aGeneric) {
generateEvent(aWidget, aDevice, aAction, aGeneric, aDevice.mCoords);
private static void generateEvent(Widget aWidget, Device aDevice, boolean aFocused, int aAction, boolean aGeneric) {
generateEvent(aWidget, aDevice, aFocused, aAction, aGeneric, aDevice.mCoords);
}

private static void generateEvent(Widget aWidget, Device aDevice, int aAction, boolean aGeneric, MotionEvent.PointerCoords[] aCoords) {
private static void generateEvent(Widget aWidget, Device aDevice, boolean aFocused, int aAction, boolean aGeneric, MotionEvent.PointerCoords[] aCoords) {
MotionEvent event = MotionEvent.obtain(
/*mDownTime*/ aDevice.mDownTime,
/*eventTime*/ SystemClock.uptimeMillis(),
Expand All @@ -69,19 +68,24 @@ private static void generateEvent(Widget aWidget, Device aDevice, int aAction, b
/*buttonState*/ 0,
/*xPrecision*/ 0,
/*yPrecision*/ 0,
/*deviceId*/ 0, // aDevice.mDevice,
/*deviceId*/ aDevice.mDevice,
/*edgeFlags*/ 0,
/*source*/ InputDevice.SOURCE_TOUCHSCREEN,
/*flags*/ 0);
if (aGeneric) {
aWidget.handleHoverEvent(event);
if (aWidget.supportsMultipleInputDevices()) {
aWidget.handleHoverEvent(event);

} else if (aFocused) {
aWidget.handleHoverEvent(event);
}
} else {
aWidget.handleTouchEvent(event);
}
event.recycle();
}

public static void dispatch(Widget aWidget, int aDevice, boolean aPressed, float aX, float aY) {
public static void dispatch(Widget aWidget, int aDevice, boolean aFocused, boolean aPressed, float aX, float aY) {
Device device = devices.get(aDevice);
if (device == null) {
device = new Device(aDevice);
Expand All @@ -99,41 +103,66 @@ public static void dispatch(Widget aWidget, int aDevice, boolean aPressed, float
}
if (!aPressed && (device.mPreviousWidget != null) && (device.mPreviousWidget != aWidget)) {
if (device.mWasPressed) {
generateEvent(device.mPreviousWidget, device, MotionEvent.ACTION_CANCEL, false);
generateEvent(device.mPreviousWidget, device, aFocused, MotionEvent.ACTION_CANCEL, false);
device.mWasPressed = false;
}
generateEvent(device.mPreviousWidget, device, MotionEvent.ACTION_HOVER_EXIT, true, device.mMouseOutCoords);
generateEvent(device.mPreviousWidget, device, aFocused, MotionEvent.ACTION_HOVER_EXIT, true, device.mMouseOutCoords);
device.mPreviousWidget = null;
}
if (aWidget == null) {
device.mPreviousWidget = null;
return;
}
if (aWidget != device.mPreviousWidget && !aPressed) {
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_ENTER, true);
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_ENTER, true);
}
if (aPressed && !device.mWasPressed) {
device.mDownTime = SystemClock.uptimeMillis();
device.mWasPressed = true;
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_EXIT, true);
generateEvent(aWidget, device, MotionEvent.ACTION_DOWN, false);
if (!isOtherDeviceDown(device.mDevice)) {
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_EXIT, true);
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_DOWN, false);
}
device.mTouchStartWidget = aWidget;
} else if (!aPressed && device.mWasPressed) {
device.mWasPressed = false;
generateEvent(device.mTouchStartWidget, device, MotionEvent.ACTION_UP, false);
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_ENTER, true);
if (!isOtherDeviceDown(device.mDevice)) {
generateEvent(device.mTouchStartWidget, device, aFocused, MotionEvent.ACTION_UP, false);
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_ENTER, true);
}
device.mTouchStartWidget = null;
} else if (moving && aPressed) {
generateEvent(aWidget, device, MotionEvent.ACTION_MOVE, false);
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_MOVE, false);
} else if (moving) {
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_MOVE, true);
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_MOVE, true);
} else {
Log.e("VRB", "Unknown touch event action");
return;
}
device.mPreviousWidget = aWidget;
}

public static void dispatchScroll(Widget aWidget, int aDevice, float aX, float aY) {
/**
* Checks if any other device has an ongoing touch down event.
* Android throw away all previous state when starting a new touch gesture
* and this seem to make the previous touch to be sent up the view hierarchy.
* To avoid this we check if any other device has a button down before sending
* touch down/up event.
* @param deviceId Device Id to filter
* @return true if any other device has a button down, false otherwise
*/
private static boolean isOtherDeviceDown(int deviceId) {
boolean result = false;
for (int i=0; i<devices.size(); i++) {
if (i != deviceId) {
result |= devices.get(i).mTouchStartWidget != null;
}
}

return result;
}

public static void dispatchScroll(Widget aWidget, int aDevice, boolean aFocused, float aX, float aY) {
Device device = devices.get(aDevice);
if (device == null) {
device = new Device(aDevice);
Expand All @@ -142,7 +171,7 @@ public static void dispatchScroll(Widget aWidget, int aDevice, float aX, float a
device.mPreviousWidget = aWidget;
device.mCoords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, aY);
device.mCoords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, aX);
generateEvent(aWidget, device, MotionEvent.ACTION_SCROLL, true);
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_SCROLL, true);
device.mCoords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, 0.0f);
device.mCoords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, 0.0f);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public interface OnKeyboardActionListener {
private int mLastCodeY;
private int mCurrentKey = NOT_A_KEY;
// Fork
private int mHoveredKey = NOT_A_KEY;
private int[] mHoveredKey = new int[2];
private int mDownKey = NOT_A_KEY;
private long mLastKeyTime;
private long mCurrentKeyTime;
Expand Down Expand Up @@ -344,6 +344,8 @@ public CustomKeyboardView(Context context, AttributeSet attrs, int defStyleAttr,
mShadowColor = 0;
mShadowRadius = 0;
mBackgroundDimAmount = 0.5f;
mHoveredKey[0] = NOT_A_KEY;
mHoveredKey[1] = NOT_A_KEY;

mPreviewPopup = new PopupWindow(context);
if (previewLayout != 0) {
Expand Down Expand Up @@ -757,7 +759,7 @@ private void onBufferDraw() {
int[] drawableState = key.getCurrentDrawableState();

if (((CustomKeyboard)mKeyboard).isKeyEnabled(i)) {
if (mHoveredKey == i && !key.pressed) {
if ((mHoveredKey[0] == i || mHoveredKey[1] == i) && !key.pressed) {
// Fork: implement hovered key
drawableState = KEY_STATE_HOVERED;
}
Expand Down Expand Up @@ -1144,6 +1146,8 @@ private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code)
* @see #invalidateKey(int)
*/
public void invalidateAllKeys() {
mHoveredKey[0] = NOT_A_KEY;
mHoveredKey[1] = NOT_A_KEY;
mDirtyRect.union(0, 0, getWidth(), getHeight());
mDrawPending = true;
invalidate();
Expand Down Expand Up @@ -1259,17 +1263,32 @@ public boolean onHoverEvent(MotionEvent event) {
keyIndex = getKeyIndices(touchX, touchY, null);
}

int prevHovered = mHoveredKey;
mHoveredKey = keyIndex;
if (mHoveredKey != NOT_A_KEY && prevHovered != mHoveredKey) {
invalidateKey(mHoveredKey);
int prevHovered = mHoveredKey[event.getDeviceId()];
mHoveredKey[event.getDeviceId()] = keyIndex;
if (mHoveredKey[event.getDeviceId()] != NOT_A_KEY && prevHovered != mHoveredKey[event.getDeviceId()]) {
invalidateKey(mHoveredKey[event.getDeviceId()]);
}
if (prevHovered != NOT_A_KEY && prevHovered != mHoveredKey) {
if (prevHovered != NOT_A_KEY && prevHovered != mHoveredKey[event.getDeviceId()]) {
invalidateKey(prevHovered);
}
return result;
}

@Override
public void setHovered(boolean hovered) {
if (!hovered) {
mHoveredKey[0] = NOT_A_KEY;
mHoveredKey[1] = NOT_A_KEY;
invalidateAllKeys();
}
super.setHovered(hovered);
}

@Override
public boolean isHovered() {
return mHoveredKey[0] != NOT_A_KEY || mHoveredKey[1] != NOT_A_KEY;
}

public void setFeaturedKeyBackground(int resId, int[] keyCodes) {
mFeaturedKeyBackground = getResources().getDrawable(resId, getContext().getTheme());
mFeaturedKeyCodes.clear();
Expand Down
Loading

0 comments on commit 685f2cf

Please sign in to comment.