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

Commit 685f2cf

Browse files
authored
Dual controller typing (#2936)
* Dual hand typing * Fix for the keyboard dismiss when pressing two keys simultaneously * Fix for the key flicker when another key is pressed
1 parent 9cfba1f commit 685f2cf

File tree

11 files changed

+260
-77
lines changed

11 files changed

+260
-77
lines changed

app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ void dispatchCreateWidgetLayer(final int aHandle, final Surface aSurface, final
807807

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

821821
if (widget == null) {
822-
MotionEventGenerator.dispatch(mRootWidget, aDevice, aPressed, x, y);
822+
MotionEventGenerator.dispatch(mRootWidget, aDevice, aFocused, aPressed, x, y);
823+
823824
} else if (widget.getBorderWidth() > 0) {
824825
final int border = widget.getBorderWidth();
825-
MotionEventGenerator.dispatch(widget, aDevice, aPressed, x - border, y - border);
826+
MotionEventGenerator.dispatch(widget, aDevice, aFocused, aPressed, x - border, y - border);
827+
826828
} else {
827-
MotionEventGenerator.dispatch(widget, aDevice, aPressed, x, y);
829+
MotionEventGenerator.dispatch(widget, aDevice, aFocused, aPressed, x, y);
828830
}
829831
});
830832
}
@@ -839,7 +841,7 @@ void handleScrollEvent(final int aHandle, final int aDevice, final float aX, fin
839841
}
840842
if (widget != null) {
841843
float scrollDirection = mSettings.getScrollDirection() == 0 ? 1.0f : -1.0f;
842-
MotionEventGenerator.dispatchScroll(widget, aDevice, aX * scrollDirection, aY * scrollDirection);
844+
MotionEventGenerator.dispatchScroll(widget, aDevice, true,aX * scrollDirection, aY * scrollDirection);
843845
} else {
844846
Log.e(LOGTAG, "Failed to find widget for scroll event: " + aHandle);
845847
}

app/src/common/shared/org/mozilla/vrbrowser/input/MotionEventGenerator.java

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@
77

88
import android.os.SystemClock;
99
import android.util.Log;
10-
import android.view.MotionEvent;
11-
import android.view.InputDevice;
1210
import android.util.SparseArray;
11+
import android.view.InputDevice;
12+
import android.view.MotionEvent;
1313

1414
import org.mozilla.vrbrowser.ui.widgets.Widget;
1515
import org.mozilla.vrbrowser.utils.SystemUtils;
1616

1717
import java.util.Arrays;
18-
import java.util.List;
1918

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

5554

56-
private static void generateEvent(Widget aWidget, Device aDevice, int aAction, boolean aGeneric) {
57-
generateEvent(aWidget, aDevice, aAction, aGeneric, aDevice.mCoords);
55+
private static void generateEvent(Widget aWidget, Device aDevice, boolean aFocused, int aAction, boolean aGeneric) {
56+
generateEvent(aWidget, aDevice, aFocused, aAction, aGeneric, aDevice.mCoords);
5857
}
5958

60-
private static void generateEvent(Widget aWidget, Device aDevice, int aAction, boolean aGeneric, MotionEvent.PointerCoords[] aCoords) {
59+
private static void generateEvent(Widget aWidget, Device aDevice, boolean aFocused, int aAction, boolean aGeneric, MotionEvent.PointerCoords[] aCoords) {
6160
MotionEvent event = MotionEvent.obtain(
6261
/*mDownTime*/ aDevice.mDownTime,
6362
/*eventTime*/ SystemClock.uptimeMillis(),
@@ -69,19 +68,24 @@ private static void generateEvent(Widget aWidget, Device aDevice, int aAction, b
6968
/*buttonState*/ 0,
7069
/*xPrecision*/ 0,
7170
/*yPrecision*/ 0,
72-
/*deviceId*/ 0, // aDevice.mDevice,
71+
/*deviceId*/ aDevice.mDevice,
7372
/*edgeFlags*/ 0,
7473
/*source*/ InputDevice.SOURCE_TOUCHSCREEN,
7574
/*flags*/ 0);
7675
if (aGeneric) {
77-
aWidget.handleHoverEvent(event);
76+
if (aWidget.supportsMultipleInputDevices()) {
77+
aWidget.handleHoverEvent(event);
78+
79+
} else if (aFocused) {
80+
aWidget.handleHoverEvent(event);
81+
}
7882
} else {
7983
aWidget.handleTouchEvent(event);
8084
}
8185
event.recycle();
8286
}
8387

84-
public static void dispatch(Widget aWidget, int aDevice, boolean aPressed, float aX, float aY) {
88+
public static void dispatch(Widget aWidget, int aDevice, boolean aFocused, boolean aPressed, float aX, float aY) {
8589
Device device = devices.get(aDevice);
8690
if (device == null) {
8791
device = new Device(aDevice);
@@ -99,41 +103,66 @@ public static void dispatch(Widget aWidget, int aDevice, boolean aPressed, float
99103
}
100104
if (!aPressed && (device.mPreviousWidget != null) && (device.mPreviousWidget != aWidget)) {
101105
if (device.mWasPressed) {
102-
generateEvent(device.mPreviousWidget, device, MotionEvent.ACTION_CANCEL, false);
106+
generateEvent(device.mPreviousWidget, device, aFocused, MotionEvent.ACTION_CANCEL, false);
103107
device.mWasPressed = false;
104108
}
105-
generateEvent(device.mPreviousWidget, device, MotionEvent.ACTION_HOVER_EXIT, true, device.mMouseOutCoords);
109+
generateEvent(device.mPreviousWidget, device, aFocused, MotionEvent.ACTION_HOVER_EXIT, true, device.mMouseOutCoords);
106110
device.mPreviousWidget = null;
107111
}
108112
if (aWidget == null) {
109113
device.mPreviousWidget = null;
110114
return;
111115
}
112116
if (aWidget != device.mPreviousWidget && !aPressed) {
113-
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_ENTER, true);
117+
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_ENTER, true);
114118
}
115119
if (aPressed && !device.mWasPressed) {
116120
device.mDownTime = SystemClock.uptimeMillis();
117121
device.mWasPressed = true;
118-
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_EXIT, true);
119-
generateEvent(aWidget, device, MotionEvent.ACTION_DOWN, false);
122+
if (!isOtherDeviceDown(device.mDevice)) {
123+
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_EXIT, true);
124+
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_DOWN, false);
125+
}
120126
device.mTouchStartWidget = aWidget;
121127
} else if (!aPressed && device.mWasPressed) {
122128
device.mWasPressed = false;
123-
generateEvent(device.mTouchStartWidget, device, MotionEvent.ACTION_UP, false);
124-
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_ENTER, true);
129+
if (!isOtherDeviceDown(device.mDevice)) {
130+
generateEvent(device.mTouchStartWidget, device, aFocused, MotionEvent.ACTION_UP, false);
131+
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_ENTER, true);
132+
}
133+
device.mTouchStartWidget = null;
125134
} else if (moving && aPressed) {
126-
generateEvent(aWidget, device, MotionEvent.ACTION_MOVE, false);
135+
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_MOVE, false);
127136
} else if (moving) {
128-
generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_MOVE, true);
137+
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_HOVER_MOVE, true);
129138
} else {
130139
Log.e("VRB", "Unknown touch event action");
131140
return;
132141
}
133142
device.mPreviousWidget = aWidget;
134143
}
135144

136-
public static void dispatchScroll(Widget aWidget, int aDevice, float aX, float aY) {
145+
/**
146+
* Checks if any other device has an ongoing touch down event.
147+
* Android throw away all previous state when starting a new touch gesture
148+
* and this seem to make the previous touch to be sent up the view hierarchy.
149+
* To avoid this we check if any other device has a button down before sending
150+
* touch down/up event.
151+
* @param deviceId Device Id to filter
152+
* @return true if any other device has a button down, false otherwise
153+
*/
154+
private static boolean isOtherDeviceDown(int deviceId) {
155+
boolean result = false;
156+
for (int i=0; i<devices.size(); i++) {
157+
if (i != deviceId) {
158+
result |= devices.get(i).mTouchStartWidget != null;
159+
}
160+
}
161+
162+
return result;
163+
}
164+
165+
public static void dispatchScroll(Widget aWidget, int aDevice, boolean aFocused, float aX, float aY) {
137166
Device device = devices.get(aDevice);
138167
if (device == null) {
139168
device = new Device(aDevice);
@@ -142,7 +171,7 @@ public static void dispatchScroll(Widget aWidget, int aDevice, float aX, float a
142171
device.mPreviousWidget = aWidget;
143172
device.mCoords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, aY);
144173
device.mCoords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, aX);
145-
generateEvent(aWidget, device, MotionEvent.ACTION_SCROLL, true);
174+
generateEvent(aWidget, device, aFocused, MotionEvent.ACTION_SCROLL, true);
146175
device.mCoords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, 0.0f);
147176
device.mCoords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, 0.0f);
148177
}

app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomKeyboardView.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ public interface OnKeyboardActionListener {
214214
private int mLastCodeY;
215215
private int mCurrentKey = NOT_A_KEY;
216216
// Fork
217-
private int mHoveredKey = NOT_A_KEY;
217+
private int[] mHoveredKey = new int[2];
218218
private int mDownKey = NOT_A_KEY;
219219
private long mLastKeyTime;
220220
private long mCurrentKeyTime;
@@ -344,6 +344,8 @@ public CustomKeyboardView(Context context, AttributeSet attrs, int defStyleAttr,
344344
mShadowColor = 0;
345345
mShadowRadius = 0;
346346
mBackgroundDimAmount = 0.5f;
347+
mHoveredKey[0] = NOT_A_KEY;
348+
mHoveredKey[1] = NOT_A_KEY;
347349

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

759761
if (((CustomKeyboard)mKeyboard).isKeyEnabled(i)) {
760-
if (mHoveredKey == i && !key.pressed) {
762+
if ((mHoveredKey[0] == i || mHoveredKey[1] == i) && !key.pressed) {
761763
// Fork: implement hovered key
762764
drawableState = KEY_STATE_HOVERED;
763765
}
@@ -1144,6 +1146,8 @@ private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code)
11441146
* @see #invalidateKey(int)
11451147
*/
11461148
public void invalidateAllKeys() {
1149+
mHoveredKey[0] = NOT_A_KEY;
1150+
mHoveredKey[1] = NOT_A_KEY;
11471151
mDirtyRect.union(0, 0, getWidth(), getHeight());
11481152
mDrawPending = true;
11491153
invalidate();
@@ -1259,17 +1263,32 @@ public boolean onHoverEvent(MotionEvent event) {
12591263
keyIndex = getKeyIndices(touchX, touchY, null);
12601264
}
12611265

1262-
int prevHovered = mHoveredKey;
1263-
mHoveredKey = keyIndex;
1264-
if (mHoveredKey != NOT_A_KEY && prevHovered != mHoveredKey) {
1265-
invalidateKey(mHoveredKey);
1266+
int prevHovered = mHoveredKey[event.getDeviceId()];
1267+
mHoveredKey[event.getDeviceId()] = keyIndex;
1268+
if (mHoveredKey[event.getDeviceId()] != NOT_A_KEY && prevHovered != mHoveredKey[event.getDeviceId()]) {
1269+
invalidateKey(mHoveredKey[event.getDeviceId()]);
12661270
}
1267-
if (prevHovered != NOT_A_KEY && prevHovered != mHoveredKey) {
1271+
if (prevHovered != NOT_A_KEY && prevHovered != mHoveredKey[event.getDeviceId()]) {
12681272
invalidateKey(prevHovered);
12691273
}
12701274
return result;
12711275
}
12721276

1277+
@Override
1278+
public void setHovered(boolean hovered) {
1279+
if (!hovered) {
1280+
mHoveredKey[0] = NOT_A_KEY;
1281+
mHoveredKey[1] = NOT_A_KEY;
1282+
invalidateAllKeys();
1283+
}
1284+
super.setHovered(hovered);
1285+
}
1286+
1287+
@Override
1288+
public boolean isHovered() {
1289+
return mHoveredKey[0] != NOT_A_KEY || mHoveredKey[1] != NOT_A_KEY;
1290+
}
1291+
12731292
public void setFeaturedKeyBackground(int resId, int[] keyCodes) {
12741293
mFeaturedKeyBackground = getResources().getDrawable(resId, getContext().getTheme());
12751294
mFeaturedKeyCodes.clear();

0 commit comments

Comments
 (0)