@applicaster/zapp-react-dom-app 12.0.0-alpha.5227180498 → 12.0.0-alpha.5230293331
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/App/InteractionManager/index.tsx +556 -578
- package/App/Loader/utils/platform.js +9 -6
- package/Polyfills/AnalyticsBridge/index.ts +5 -1
- package/index.js +5 -6
- package/main.css +2 -2
- package/package.json +6 -6
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
|
+
import { connectToStore } from "@applicaster/zapp-react-native-redux";
|
|
5
|
+
|
|
4
6
|
import {
|
|
5
7
|
QUICK_BRICK_EVENTS,
|
|
6
8
|
sendQuickBrickEvent,
|
|
@@ -31,30 +33,27 @@ import {
|
|
|
31
33
|
import { PlayerContentContext } from "@applicaster/zapp-react-dom-ui-components/Contexts";
|
|
32
34
|
|
|
33
35
|
import { withNavigator } from "@applicaster/zapp-react-native-ui-components/Decorators/Navigator";
|
|
34
|
-
import { Platform } from "react-native";
|
|
35
|
-
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
|
|
36
36
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
37
|
-
import { confirmationDialogStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
|
|
38
|
-
import { hasVizioAPIs, isVizioPlatform } from "../Loader/utils/platform";
|
|
39
37
|
|
|
40
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
confirmationDialogStore,
|
|
40
|
+
ConfirmationDialogState,
|
|
41
|
+
} from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
}
|
|
43
|
+
import {
|
|
44
|
+
isLgPlatform,
|
|
45
|
+
isSamsungPlatform,
|
|
46
|
+
isVizioPlatform,
|
|
47
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
48
|
+
|
|
49
|
+
import { OnScreenKeyboard } from "@applicaster/zapp-react-dom-ui-components/Components/OnScreenKeyboard";
|
|
50
|
+
import { KeyboardLongPressManager } from "@applicaster/zapp-react-dom-ui-components/Utils/KeyboardLongPressManager";
|
|
52
51
|
|
|
53
52
|
const { withXray } = getXray();
|
|
54
53
|
const globalAny: any = global;
|
|
55
54
|
|
|
56
|
-
const shouldUseOnScreenKeyboard =
|
|
57
|
-
isVizioPlatform() ||
|
|
55
|
+
const shouldUseOnScreenKeyboard = () =>
|
|
56
|
+
isVizioPlatform() || window.applicaster.useOnScreenKeyboard;
|
|
58
57
|
|
|
59
58
|
type Props = {
|
|
60
59
|
displayState: string;
|
|
@@ -71,714 +70,693 @@ type Props = {
|
|
|
71
70
|
onRewindPressOut: () => void;
|
|
72
71
|
onForwardPressOut: () => void;
|
|
73
72
|
};
|
|
73
|
+
confirmDialog: typeof confirmationDialogStore;
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
interface State {
|
|
77
|
+
isKeyboardVisible: boolean;
|
|
78
|
+
keyboardInput: string;
|
|
79
|
+
activeInputRef: HTMLInputElement | null;
|
|
80
|
+
}
|
|
77
81
|
|
|
78
82
|
type KeyCode = { code: string; keyCode: number | string };
|
|
79
83
|
|
|
80
84
|
export const alertConfirmationMesage =
|
|
81
85
|
"Are you sure you want to exit application?";
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
class InteractionManagerClass extends React.Component<Props, State> {
|
|
88
|
+
onKeyDownListener: (keyCode: KeyCode) => void;
|
|
89
|
+
onMouseDownListener: (event: MouseEvent) => void;
|
|
90
|
+
onMouseMoveListener: (event: MouseEvent) => void;
|
|
91
|
+
onScrollListener: (event: MouseEvent) => void;
|
|
92
|
+
|
|
93
|
+
private longPressDetector: KeyboardLongPressManager;
|
|
94
|
+
private longPressActive: boolean = false;
|
|
95
|
+
private timeout: any | null = null;
|
|
96
|
+
private sequenceTimeout: NodeJS.Timeout | null;
|
|
97
|
+
private userSequence: number[] = [];
|
|
98
|
+
private isDismissing = false;
|
|
99
|
+
private confirmDialog: ConfirmationDialogState =
|
|
100
|
+
confirmationDialogStore.getState();
|
|
101
|
+
|
|
102
|
+
state: State = {
|
|
103
|
+
isKeyboardVisible: false,
|
|
104
|
+
keyboardInput: "",
|
|
105
|
+
activeInputRef: null,
|
|
106
|
+
};
|
|
89
107
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
108
|
+
constructor(props) {
|
|
109
|
+
super(props);
|
|
110
|
+
|
|
111
|
+
this.onMouseMoveListener = debounce({
|
|
112
|
+
fn: this.onMouseMove,
|
|
113
|
+
context: this,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.onScrollListener = debounce({
|
|
117
|
+
fn: this.onScroll,
|
|
118
|
+
wait: 100,
|
|
119
|
+
context: this,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
this.onMouseDownListener = debounce({
|
|
123
|
+
fn: this.onMouseDown,
|
|
124
|
+
context: this,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
this.onKeyDownListener = this.onKeyDown.bind(this);
|
|
128
|
+
this.resetHudTimeout = this.resetHudTimeout.bind(this);
|
|
129
|
+
this.cursorVisibilityChange = this.cursorVisibilityChange.bind(this);
|
|
130
|
+
this.onKeyDown = this.onKeyDown.bind(this);
|
|
131
|
+
this.onKeyUp = this.onKeyUp.bind(this);
|
|
132
|
+
this.onMouseMove = this.onMouseMove.bind(this);
|
|
133
|
+
this.onScroll = this.onScroll.bind(this);
|
|
134
|
+
this.onMouseDown = this.onMouseDown.bind(this);
|
|
135
|
+
this.onKeyboardStateChange = this.onKeyboardStateChange.bind(this);
|
|
136
|
+
this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
|
|
137
|
+
this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
|
|
138
|
+
this.performExit = this.performExit.bind(this);
|
|
139
|
+
this.handleInputFocus = this.handleInputFocus.bind(this);
|
|
140
|
+
this.handleInputBlur = this.handleInputBlur.bind(this);
|
|
141
|
+
this.handleKeyboardInput = this.handleKeyboardInput.bind(this);
|
|
142
|
+
this.handleKeyboardDismiss = this.handleKeyboardDismiss.bind(this);
|
|
143
|
+
this.onKeyDownListener = this.onKeyDown.bind(this);
|
|
144
|
+
this.onKeyUp = this.onKeyUp.bind(this);
|
|
145
|
+
this.onMouseMoveListener = this.onMouseMove.bind(this);
|
|
146
|
+
this.onScrollListener = this.onScroll.bind(this);
|
|
147
|
+
this.onMouseDownListener = this.onMouseDown.bind(this);
|
|
148
|
+
this.onKeyboardStateChange = this.onKeyboardStateChange.bind(this);
|
|
149
|
+
this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
|
|
150
|
+
this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
|
|
151
|
+
|
|
152
|
+
this.confirmDialog.setConfirmAction(this.performExit);
|
|
153
|
+
this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
|
|
154
|
+
this.longPressDetector = new KeyboardLongPressManager();
|
|
155
|
+
}
|
|
97
156
|
|
|
98
|
-
|
|
99
|
-
|
|
157
|
+
componentDidMount() {
|
|
158
|
+
window.addEventListener("keydown", this.onKeyDownListener);
|
|
159
|
+
window.addEventListener("keyup", this.onKeyUp);
|
|
160
|
+
window.addEventListener("mousemove", this.onMouseMoveListener);
|
|
100
161
|
|
|
101
|
-
|
|
162
|
+
document.addEventListener("wheel", this.onScrollListener, {
|
|
163
|
+
passive: true,
|
|
164
|
+
});
|
|
102
165
|
|
|
103
|
-
|
|
104
|
-
isKeyboardVisible: false,
|
|
105
|
-
keyboardInput: "",
|
|
106
|
-
};
|
|
166
|
+
document.addEventListener("mousedown", this.onMouseDownListener);
|
|
107
167
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
168
|
+
document.addEventListener(
|
|
169
|
+
"keyboardStateChange",
|
|
170
|
+
this.onKeyboardStateChange,
|
|
171
|
+
false
|
|
172
|
+
);
|
|
112
173
|
|
|
113
|
-
|
|
114
|
-
fn: this.onScroll,
|
|
115
|
-
wait: 100,
|
|
116
|
-
context: this,
|
|
117
|
-
});
|
|
174
|
+
document.addEventListener("focusin", this.handleInputFocus);
|
|
118
175
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
176
|
+
document.addEventListener(
|
|
177
|
+
"cursorStateChange",
|
|
178
|
+
this.cursorVisibilityChange,
|
|
179
|
+
false
|
|
180
|
+
);
|
|
123
181
|
|
|
124
|
-
|
|
125
|
-
this.longPressActive = false;
|
|
126
|
-
this.confirmDialog = confirmationDialogStore.getState();
|
|
127
|
-
this.resetHudTimeout = this.resetHudTimeout.bind(this);
|
|
128
|
-
this.cursorVisibilityChange = this.cursorVisibilityChange.bind(this);
|
|
129
|
-
this.onKeyDown = this.onKeyDown.bind(this);
|
|
130
|
-
this.onKeyUp = this.onKeyUp.bind(this);
|
|
131
|
-
this.onMouseMove = this.onMouseMove.bind(this);
|
|
132
|
-
this.onScroll = this.onScroll.bind(this);
|
|
133
|
-
this.onMouseDown = this.onMouseDown.bind(this);
|
|
134
|
-
this.onKeyboardStateChange = this.onKeyboardStateChange.bind(this);
|
|
135
|
-
this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
|
|
136
|
-
this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
|
|
137
|
-
this.performExit = this.performExit.bind(this);
|
|
138
|
-
this.handleInputFocus = this.handleInputFocus.bind(this);
|
|
139
|
-
this.handleInputBlur = this.handleInputBlur.bind(this);
|
|
140
|
-
this.handleKeyboardInput = this.handleKeyboardInput.bind(this);
|
|
141
|
-
this.handleKeyboardDismiss = this.handleKeyboardDismiss.bind(this);
|
|
142
|
-
|
|
143
|
-
this.confirmDialog.setConfirmAction(this.performExit);
|
|
144
|
-
this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
|
|
145
|
-
}
|
|
182
|
+
window.addEventListener("onLongPress", this.handleLongPress.bind(this));
|
|
146
183
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
184
|
+
window.addEventListener(
|
|
185
|
+
"onLongPressRelease",
|
|
186
|
+
this.handleLongPressRelease.bind(this)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
151
189
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
190
|
+
componentWillUnmount() {
|
|
191
|
+
window.removeEventListener("keydown", this.onKeyDownListener);
|
|
192
|
+
window.removeEventListener("keyup", this.onKeyUp);
|
|
193
|
+
document.removeEventListener("wheel", this.onScrollListener);
|
|
155
194
|
|
|
156
|
-
|
|
195
|
+
document.removeEventListener(
|
|
196
|
+
"cursorStateChange",
|
|
197
|
+
this.cursorVisibilityChange
|
|
198
|
+
);
|
|
157
199
|
|
|
158
|
-
|
|
159
|
-
"keyboardStateChange",
|
|
160
|
-
this.onKeyboardStateChange,
|
|
161
|
-
false
|
|
162
|
-
);
|
|
200
|
+
document.removeEventListener("focusin", this.handleInputFocus);
|
|
163
201
|
|
|
164
|
-
|
|
165
|
-
// document.addEventListener("focusout", this.handleInputBlur);
|
|
202
|
+
document.removeEventListener("mousedown", this.onMouseDownListener);
|
|
166
203
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
this.cursorVisibilityChange,
|
|
170
|
-
false
|
|
171
|
-
);
|
|
204
|
+
if (this.timeout) {
|
|
205
|
+
this.clearTimeout();
|
|
172
206
|
}
|
|
173
207
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
document.removeEventListener("wheel", this.onScrollListener);
|
|
208
|
+
this.isDismissing = false;
|
|
209
|
+
this.longPressDetector.cleanup();
|
|
210
|
+
window.removeEventListener("onLongPress", this.handleLongPress);
|
|
178
211
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
document.removeEventListener("focusin", this.handleInputFocus);
|
|
212
|
+
window.removeEventListener(
|
|
213
|
+
"onLongPressRelease",
|
|
214
|
+
this.handleLongPressRelease
|
|
215
|
+
);
|
|
216
|
+
}
|
|
185
217
|
|
|
186
|
-
|
|
218
|
+
performExit() {
|
|
219
|
+
if (isSamsungPlatform()) {
|
|
220
|
+
this.onConfirmDialogClose();
|
|
221
|
+
sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND);
|
|
222
|
+
}
|
|
187
223
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
224
|
+
if (isLgPlatform()) {
|
|
225
|
+
// webOS recommend to use this method when implemention a custom exit
|
|
226
|
+
globalAny.close();
|
|
227
|
+
}
|
|
191
228
|
|
|
192
|
-
|
|
229
|
+
if (isVizioPlatform()) {
|
|
230
|
+
window?.VIZIO?.exitApplication();
|
|
193
231
|
}
|
|
232
|
+
}
|
|
194
233
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
this.onConfirmDialogClose();
|
|
198
|
-
sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND);
|
|
199
|
-
}
|
|
234
|
+
handleWebOsBack() {
|
|
235
|
+
const systemBack = globalAny?.webOS?.platformBack;
|
|
200
236
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
237
|
+
if (systemBack) {
|
|
238
|
+
systemBack();
|
|
239
|
+
} else {
|
|
240
|
+
/**
|
|
241
|
+
* In case webOS failed to show the exit pop-up, we show a regular promtp as fallback
|
|
242
|
+
*/
|
|
243
|
+
showConfirmationDialog({
|
|
244
|
+
title: "",
|
|
245
|
+
message: alertConfirmationMesage,
|
|
246
|
+
confirmCompletion: this.performExit,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
205
250
|
|
|
206
|
-
|
|
207
|
-
|
|
251
|
+
/**
|
|
252
|
+
* This methods sends the app to background in whatever way is appropriate for each platform
|
|
253
|
+
* In the case of Tizen it shows a custom dialog on confirm it exits the app
|
|
254
|
+
* WebOS behavior depends on the SDK version, WebOS 4 would pull up the launcher menu
|
|
255
|
+
* following versions would show a native exit prompt
|
|
256
|
+
*/
|
|
257
|
+
platformBack() {
|
|
258
|
+
try {
|
|
259
|
+
// TODO: temporary hack until this code is refactored
|
|
260
|
+
if (isSamsungPlatform() || isVizioPlatform()) {
|
|
261
|
+
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
262
|
+
|
|
263
|
+
if (isDialogVisible) {
|
|
264
|
+
this.onConfirmDialogClose();
|
|
265
|
+
} else {
|
|
266
|
+
showConfirmationDialog({
|
|
267
|
+
title: "",
|
|
268
|
+
message: alertConfirmationMesage,
|
|
269
|
+
confirmCompletion: this.performExit,
|
|
270
|
+
cancelCompletion: this.onConfirmDialogClose,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
} else if (isLgPlatform()) {
|
|
274
|
+
this.handleWebOsBack();
|
|
208
275
|
}
|
|
276
|
+
} catch (e) {
|
|
277
|
+
// eslint-disable-next-line no-console
|
|
278
|
+
console.warn("Failed to execute platformBack", e);
|
|
209
279
|
}
|
|
280
|
+
}
|
|
210
281
|
|
|
211
|
-
|
|
212
|
-
|
|
282
|
+
cursorVisibilityChange(event) {
|
|
283
|
+
const visible = event.detail.visibility;
|
|
284
|
+
const { setDisplayState, displayState } = this.props;
|
|
213
285
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* In case webOS failed to show the exit pop-up, we show a regular promtp as fallback
|
|
219
|
-
*/
|
|
220
|
-
showConfirmationDialog({
|
|
221
|
-
title: "",
|
|
222
|
-
message: alertConfirmationMesage,
|
|
223
|
-
confirmCompletion: this.performExit,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
286
|
+
if (visible && displayState === DISPLAY_STATES.PLAYER) {
|
|
287
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
288
|
+
this.resetHudTimeout();
|
|
226
289
|
}
|
|
290
|
+
}
|
|
227
291
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
* In the case of Tizen it shows a custom dialog on confirm it exits the app
|
|
231
|
-
* WebOS behavior depends on the SDK version, WebOS 4 would pull up the launcher menu
|
|
232
|
-
* following versions would show a native exit prompt
|
|
233
|
-
*/
|
|
234
|
-
platformBack() {
|
|
235
|
-
try {
|
|
236
|
-
// TODO: temporary hack until this code is refactored
|
|
237
|
-
if (
|
|
238
|
-
Platform.OS === ("samsung_tv" as QuickBrickPlatforms) ||
|
|
239
|
-
hasVizioAPIs()
|
|
240
|
-
) {
|
|
241
|
-
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
292
|
+
onMouseMove() {
|
|
293
|
+
const { setDisplayState, displayState } = this.props;
|
|
242
294
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
showConfirmationDialog({
|
|
247
|
-
title: "",
|
|
248
|
-
message: alertConfirmationMesage,
|
|
249
|
-
confirmCompletion: this.performExit,
|
|
250
|
-
cancelCompletion: this.onConfirmDialogClose,
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
} else if (Platform.OS === ("lg_tv" as QuickBrickPlatforms)) {
|
|
254
|
-
this.handleWebOsBack();
|
|
255
|
-
}
|
|
256
|
-
} catch (e) {
|
|
257
|
-
// eslint-disable-next-line no-console
|
|
258
|
-
console.warn("Failed to execute platformBack", e);
|
|
259
|
-
}
|
|
295
|
+
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
296
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
297
|
+
this.resetHudTimeout();
|
|
260
298
|
}
|
|
299
|
+
}
|
|
261
300
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
301
|
+
isHomeScreen() {
|
|
302
|
+
const { navigator } = this.props;
|
|
303
|
+
const { rivers } = this.props;
|
|
265
304
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
305
|
+
const homeId = R.compose(
|
|
306
|
+
R.prop("id"),
|
|
307
|
+
R.find(R.prop("home")),
|
|
308
|
+
R.values
|
|
309
|
+
)(rivers);
|
|
271
310
|
|
|
272
|
-
|
|
273
|
-
const { setDisplayState, displayState } = this.props;
|
|
311
|
+
const homePath = `/river/${homeId}`;
|
|
274
312
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
313
|
+
return homePath === navigator.currentRoute;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
goToHome() {
|
|
317
|
+
const { navigator } = this.props;
|
|
318
|
+
const { rivers } = this.props;
|
|
319
|
+
const homeRiver = R.find(R.propEq("home", true), R.values(rivers));
|
|
280
320
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const { rivers } = this.props;
|
|
321
|
+
navigator.replace(homeRiver);
|
|
322
|
+
}
|
|
284
323
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
R.find(R.prop("home")),
|
|
288
|
-
R.values
|
|
289
|
-
)(rivers);
|
|
324
|
+
resetHudTimeout() {
|
|
325
|
+
const { resetHudTimer = noop } = this.props;
|
|
290
326
|
|
|
291
|
-
|
|
327
|
+
resetHudTimer?.();
|
|
328
|
+
}
|
|
292
329
|
|
|
293
|
-
|
|
330
|
+
clearTimeout() {
|
|
331
|
+
if (this.timeout) {
|
|
332
|
+
clearTimeout(this.timeout);
|
|
333
|
+
this.timeout = null;
|
|
294
334
|
}
|
|
335
|
+
}
|
|
295
336
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const homeRiver = R.find(R.propEq("home", true), R.values(rivers));
|
|
337
|
+
cancelHudTimeout() {
|
|
338
|
+
this.resetHudTimeout();
|
|
339
|
+
}
|
|
300
340
|
|
|
301
|
-
|
|
341
|
+
onKeyUp(event: KeyboardEvent) {
|
|
342
|
+
if (this.keySupportsLongPress(event)) {
|
|
343
|
+
this.longPressDetector.endKeyPressMonitoring(event.code);
|
|
302
344
|
}
|
|
345
|
+
}
|
|
303
346
|
|
|
304
|
-
|
|
305
|
-
|
|
347
|
+
onKeyDown(event) {
|
|
348
|
+
const { displayState, setDisplayState, navigator } = this.props;
|
|
349
|
+
const { isKeyboardVisible } = this.state;
|
|
306
350
|
|
|
307
|
-
|
|
308
|
-
}
|
|
351
|
+
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
309
352
|
|
|
310
|
-
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
this.timeout = null;
|
|
353
|
+
if (isKeyboardVisible && shouldUseOnScreenKeyboard()) {
|
|
354
|
+
if (keyCode(event).matchesAny("Escape", "Back")) {
|
|
355
|
+
this.handleKeyboardDismiss();
|
|
314
356
|
}
|
|
357
|
+
|
|
358
|
+
event.preventDefault();
|
|
359
|
+
event.stopPropagation();
|
|
360
|
+
|
|
361
|
+
return;
|
|
315
362
|
}
|
|
316
363
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
clearTimeout(this.longKeyPressTimeout);
|
|
320
|
-
this.longKeyPressTimeout = null;
|
|
321
|
-
}
|
|
364
|
+
if (this.keySupportsLongPress(event)) {
|
|
365
|
+
this.longPressDetector.monitorKeyPress(event.code, event);
|
|
322
366
|
}
|
|
323
367
|
|
|
368
|
+
// None of the logic below should be executed for repeated keys
|
|
369
|
+
if (event.repeat) return;
|
|
370
|
+
|
|
324
371
|
/**
|
|
325
|
-
|
|
326
|
-
|
|
372
|
+
When the player is displayed, we listen to Native Play / Pause / Stop
|
|
373
|
+
buttons. Any other key press will display the transport controls
|
|
327
374
|
*/
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
switch (event.code) {
|
|
338
|
-
case KEYS.Back.code:
|
|
339
|
-
// If you longpress the back button we probably want to exit
|
|
340
|
-
this.platformBack();
|
|
375
|
+
if (
|
|
376
|
+
displayState === DISPLAY_STATES.PLAYER ||
|
|
377
|
+
displayState === DISPLAY_STATES.HUD
|
|
378
|
+
) {
|
|
379
|
+
/**
|
|
380
|
+
registering native keys for playback
|
|
381
|
+
*/
|
|
382
|
+
if (keyCode(event).matches(KEYS.TogglePlayPause)) {
|
|
383
|
+
const { playing } = playerManager.getState() || {};
|
|
341
384
|
|
|
342
|
-
|
|
385
|
+
if (!playing) {
|
|
386
|
+
return playerManager.togglePlayPause();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
343
389
|
|
|
344
|
-
|
|
345
|
-
|
|
390
|
+
if (keyCode(event).matches(KEYS.Play)) {
|
|
391
|
+
return playerManager.play();
|
|
392
|
+
}
|
|
346
393
|
|
|
347
|
-
|
|
394
|
+
if (keyCode(event).matches(KEYS.Pause)) {
|
|
395
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
348
396
|
|
|
349
|
-
|
|
350
|
-
|
|
397
|
+
return playerManager.pause();
|
|
398
|
+
}
|
|
351
399
|
|
|
352
|
-
|
|
400
|
+
if (keyCode(event).matches(KEYS.Stop)) {
|
|
401
|
+
setDisplayState(DISPLAY_STATES.DEFAULT);
|
|
402
|
+
navigator.goBack();
|
|
403
|
+
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
353
406
|
|
|
354
|
-
|
|
355
|
-
|
|
407
|
+
if (keyCode(event).matches(KEYS.Forward)) {
|
|
408
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
409
|
+
this.props.seekMethods.onForwardPress();
|
|
356
410
|
|
|
357
|
-
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
358
413
|
|
|
359
|
-
|
|
360
|
-
|
|
414
|
+
if (keyCode(event).matches(KEYS.Rewind)) {
|
|
415
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
416
|
+
this.props.seekMethods.onRewindPress();
|
|
417
|
+
|
|
418
|
+
return;
|
|
361
419
|
}
|
|
362
420
|
}
|
|
363
421
|
|
|
364
422
|
/**
|
|
365
|
-
|
|
366
|
-
|
|
423
|
+
When pressing backspace or the Native "Back" button:
|
|
424
|
+
if on the transport controls: we show the player
|
|
425
|
+
if on the player : we show the UI
|
|
426
|
+
if on the UI : we exit the app
|
|
367
427
|
*/
|
|
368
|
-
onLongPressRelease(event) {
|
|
369
|
-
this.longPressActive = false;
|
|
370
|
-
this.resetHudTimeout();
|
|
371
428
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
429
|
+
if (
|
|
430
|
+
keyCode(event).matchesAny(
|
|
431
|
+
KEYS.Back,
|
|
432
|
+
KEYS.Escape,
|
|
433
|
+
KEYS.EscapeDevice,
|
|
434
|
+
KEYS.Backspace
|
|
435
|
+
)
|
|
436
|
+
) {
|
|
437
|
+
switch (displayState) {
|
|
438
|
+
case DISPLAY_STATES.DEFAULT:
|
|
439
|
+
case DISPLAY_STATES.PLAYER:
|
|
440
|
+
if (isDialogVisible) {
|
|
441
|
+
this.onConfirmDialogClose();
|
|
442
|
+
} else if (navigator.canGoBack()) {
|
|
443
|
+
navigator.goBack();
|
|
444
|
+
} else if (this.isHomeScreen()) {
|
|
445
|
+
this.platformBack();
|
|
446
|
+
} else if (navigator.currentRoute.includes("hook")) {
|
|
447
|
+
// If code reaches this block navigator cant go back, it is not home,
|
|
448
|
+
// and this is a login plugin that was pushed on top of the home
|
|
449
|
+
// Treat it like a home screen and run platformBack()
|
|
450
|
+
const entryData = navigator?.data?.entry;
|
|
451
|
+
const isHookInHomescreen = entryData?.payload?.home;
|
|
452
|
+
|
|
453
|
+
const isHookFlowBlocker =
|
|
454
|
+
entryData?.hookPlugin?.module?.isFlowBlocker();
|
|
455
|
+
|
|
456
|
+
if (isHookInHomescreen && isHookFlowBlocker) {
|
|
457
|
+
this.platformBack();
|
|
458
|
+
} else {
|
|
459
|
+
navigator.goBack(false, true);
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
this.goToHome();
|
|
463
|
+
}
|
|
377
464
|
|
|
378
465
|
break;
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
466
|
+
case DISPLAY_STATES.HUD:
|
|
467
|
+
// IF transport controls are open, close them instead of closing player
|
|
468
|
+
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
383
469
|
break;
|
|
384
|
-
|
|
385
470
|
default:
|
|
471
|
+
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
386
472
|
break;
|
|
387
473
|
}
|
|
388
474
|
}
|
|
389
475
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
resetSequenceTimeout() {
|
|
396
|
-
const SEQUENCE_ENTRY_TIME = 1900;
|
|
397
|
-
|
|
398
|
-
if (this.sequenceTimeout) {
|
|
399
|
-
// Start timer over
|
|
400
|
-
clearTimeout(this.sequenceTimeout);
|
|
476
|
+
if (keyCode(event).matches(KEYS.Enter)) {
|
|
477
|
+
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
478
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
479
|
+
} else {
|
|
480
|
+
focusManager.press();
|
|
401
481
|
}
|
|
402
482
|
|
|
403
|
-
|
|
404
|
-
// User needs to complete sequence before SEQUENCE_ENTRY_TIME
|
|
405
|
-
// or we set userSequence, and prev sequenceTimeout to empty values
|
|
406
|
-
this.sequenceTimeout = null;
|
|
407
|
-
this.userSequence = [];
|
|
408
|
-
}, SEQUENCE_ENTRY_TIME);
|
|
483
|
+
return true;
|
|
409
484
|
}
|
|
410
485
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
*/
|
|
415
|
-
checkSequence(key) {
|
|
416
|
-
const SINK_SEQUENCE = [
|
|
417
|
-
KEYS.ArrowUp.keyCode,
|
|
418
|
-
KEYS.ArrowUp.keyCode,
|
|
419
|
-
KEYS.ArrowDown.keyCode,
|
|
420
|
-
KEYS.ArrowDown.keyCode,
|
|
421
|
-
KEYS.ArrowLeft.keyCode,
|
|
422
|
-
KEYS.ArrowRight.keyCode,
|
|
423
|
-
KEYS.ArrowLeft.keyCode,
|
|
424
|
-
KEYS.ArrowRight.keyCode,
|
|
425
|
-
];
|
|
426
|
-
|
|
427
|
-
this.resetSequenceTimeout();
|
|
428
|
-
this.userSequence.push(key);
|
|
429
|
-
|
|
430
|
-
// Remember [1,2,3] !== [1,2,3] in js, so string comparison is better
|
|
431
|
-
const shouldAddSink =
|
|
432
|
-
this.userSequence.toString() === SINK_SEQUENCE.toString();
|
|
433
|
-
|
|
434
|
-
if (shouldAddSink) {
|
|
435
|
-
this.props.xray.addRemoteSink({});
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Determines whether the key supports or handles longpress
|
|
441
|
-
* At the moment the only keys that support are the ones defined in
|
|
442
|
-
* supportedEvents array. This logic just prevents us from registering timeouts for every key
|
|
443
|
-
*/
|
|
444
|
-
keySupportsLongPress(event): boolean {
|
|
445
|
-
const supportedEvents = [
|
|
446
|
-
KEYS.Back,
|
|
447
|
-
KEYS.Enter,
|
|
448
|
-
KEYS.Rewind,
|
|
449
|
-
KEYS.Forward,
|
|
450
|
-
];
|
|
451
|
-
|
|
452
|
-
const eventMatcher = R.compose(R.prop("matches"), keyCode)(event);
|
|
486
|
+
if (keyCode(event).matches(KEYS.Exit)) {
|
|
487
|
+
if (isSamsungPlatform() || shouldUseOnScreenKeyboard()) {
|
|
488
|
+
this.performExit();
|
|
453
489
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
R.map(eventMatcher)
|
|
457
|
-
)(supportedEvents);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
458
492
|
|
|
459
|
-
|
|
493
|
+
this.platformBack();
|
|
460
494
|
}
|
|
461
495
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
} else {
|
|
469
|
-
this.clearLongPressTimeout();
|
|
470
|
-
}
|
|
496
|
+
if (
|
|
497
|
+
keyCode(event).matchesAny(...ARROW_KEYS) &&
|
|
498
|
+
!this.state.isKeyboardVisible
|
|
499
|
+
) {
|
|
500
|
+
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
501
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
471
502
|
}
|
|
472
|
-
}
|
|
473
503
|
|
|
474
|
-
|
|
475
|
-
const { displayState, setDisplayState, navigator } = this.props;
|
|
476
|
-
const { isKeyboardVisible } = this.state;
|
|
504
|
+
this.checkSequence(event.keyCode);
|
|
477
505
|
|
|
478
|
-
const
|
|
506
|
+
const direction = keyCode(event).direction();
|
|
479
507
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.longKeyPressTimeout = setTimeout(() => {
|
|
483
|
-
this.onLongPress(event);
|
|
484
|
-
}, LONG_KEY_PRESS_TIMEOUT);
|
|
508
|
+
if (!isDialogVisible || !["up", "down"].includes(direction.value)) {
|
|
509
|
+
focusManager.moveFocus(direction);
|
|
485
510
|
}
|
|
486
511
|
|
|
487
|
-
if (
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
this.handleKeyboardDismiss();
|
|
492
|
-
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
512
|
+
if (!isDialogVisible) {
|
|
513
|
+
// run focus recovery on user interaction attempt
|
|
514
|
+
// probably we failed to set initial focus for some reason
|
|
515
|
+
focusManager.recoverFocus();
|
|
495
516
|
}
|
|
496
517
|
|
|
497
|
-
/**
|
|
498
|
-
When the player is displayed, we listen to Native Play / Pause / Stop
|
|
499
|
-
buttons. Any other key press will display the transport controls
|
|
500
|
-
*/
|
|
501
518
|
if (
|
|
502
519
|
displayState === DISPLAY_STATES.PLAYER ||
|
|
503
520
|
displayState === DISPLAY_STATES.HUD
|
|
504
521
|
) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
*/
|
|
508
|
-
if (keyCode(event).matches(KEYS.TogglePlayPause)) {
|
|
509
|
-
const { playing } = playerManager.getState() || {};
|
|
510
|
-
|
|
511
|
-
if (!playing) {
|
|
512
|
-
return playerManager.togglePlayPause();
|
|
513
|
-
}
|
|
514
|
-
}
|
|
522
|
+
this.resetHudTimeout();
|
|
523
|
+
}
|
|
515
524
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
519
528
|
|
|
520
|
-
|
|
521
|
-
|
|
529
|
+
onScroll(event) {
|
|
530
|
+
const { displayState } = this.props;
|
|
522
531
|
|
|
523
|
-
|
|
524
|
-
}
|
|
532
|
+
const deltaY = event?.deltaY;
|
|
525
533
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
534
|
+
const DIRECTION = {
|
|
535
|
+
axis: "y",
|
|
536
|
+
isHorizontal: false,
|
|
537
|
+
isVertical: true,
|
|
538
|
+
value: "",
|
|
539
|
+
};
|
|
529
540
|
|
|
530
|
-
|
|
531
|
-
|
|
541
|
+
if (deltaY > 0) {
|
|
542
|
+
DIRECTION.value = isLgPlatform() ? "down" : "up";
|
|
543
|
+
} else if (deltaY < 0) {
|
|
544
|
+
DIRECTION.value = isLgPlatform() ? "up" : "down";
|
|
545
|
+
}
|
|
532
546
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
547
|
+
if (
|
|
548
|
+
displayState === DISPLAY_STATES.PLAYER ||
|
|
549
|
+
displayState === DISPLAY_STATES.HUD
|
|
550
|
+
) {
|
|
551
|
+
this.resetHudTimeout();
|
|
552
|
+
} else {
|
|
553
|
+
focusManager.moveFocus(DIRECTION);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
536
556
|
|
|
537
|
-
|
|
538
|
-
|
|
557
|
+
onMouseDown(event) {
|
|
558
|
+
const { button } = event;
|
|
559
|
+
const { displayState } = this.props;
|
|
539
560
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
561
|
+
if (
|
|
562
|
+
button === 1 && // number 1 represents press on the scroll wheel
|
|
563
|
+
(displayState === DISPLAY_STATES.PLAYER ||
|
|
564
|
+
displayState === DISPLAY_STATES.HUD)
|
|
565
|
+
) {
|
|
566
|
+
this.resetHudTimeout();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
543
569
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
570
|
+
onKeyboardStateChange(event) {
|
|
571
|
+
this.setState({ isKeyboardVisible: event.visibility });
|
|
572
|
+
}
|
|
547
573
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
if on the player : we show the UI
|
|
552
|
-
if on the UI : we exit the app
|
|
553
|
-
*/
|
|
574
|
+
onConfirmDialogOpen() {
|
|
575
|
+
this.confirmDialog.showDialog();
|
|
576
|
+
}
|
|
554
577
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
KEYS.Back,
|
|
558
|
-
KEYS.Escape,
|
|
559
|
-
KEYS.EscapeDevice,
|
|
560
|
-
KEYS.Backspace
|
|
561
|
-
)
|
|
562
|
-
) {
|
|
563
|
-
switch (displayState) {
|
|
564
|
-
case DISPLAY_STATES.DEFAULT:
|
|
565
|
-
case DISPLAY_STATES.PLAYER:
|
|
566
|
-
if (isDialogVisible) {
|
|
567
|
-
this.onConfirmDialogClose();
|
|
568
|
-
} else if (navigator.canGoBack()) {
|
|
569
|
-
navigator.goBack();
|
|
570
|
-
} else if (this.isHomeScreen()) {
|
|
571
|
-
this.platformBack();
|
|
572
|
-
} else if (navigator.currentRoute.includes("hook")) {
|
|
573
|
-
// If code reaches this block navigator cant go back, it is not home,
|
|
574
|
-
// and this is a login plugin that was pushed on top of the home
|
|
575
|
-
// Treat it like a home screen and run platformBack()
|
|
576
|
-
const entryData = navigator?.data?.entry;
|
|
577
|
-
const isHookInHomescreen = entryData?.payload?.home;
|
|
578
|
-
|
|
579
|
-
const isHookFlowBlocker =
|
|
580
|
-
entryData?.hookPlugin?.module?.isFlowBlocker();
|
|
581
|
-
|
|
582
|
-
if (isHookInHomescreen && isHookFlowBlocker) {
|
|
583
|
-
this.platformBack();
|
|
584
|
-
} else {
|
|
585
|
-
navigator.goBack(false, true);
|
|
586
|
-
}
|
|
587
|
-
} else {
|
|
588
|
-
this.goToHome();
|
|
589
|
-
}
|
|
578
|
+
onConfirmDialogClose() {
|
|
579
|
+
this.confirmDialog.hideDialog();
|
|
590
580
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
595
|
-
break;
|
|
596
|
-
default:
|
|
597
|
-
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
598
|
-
break;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
581
|
+
// restore initial focus after closing dialog
|
|
582
|
+
focusManager.setInitialFocus();
|
|
583
|
+
}
|
|
601
584
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
} else {
|
|
606
|
-
focusManager.press();
|
|
607
|
-
}
|
|
585
|
+
handleInputFocus = (event: FocusEvent) => {
|
|
586
|
+
if (!shouldUseOnScreenKeyboard()) return;
|
|
587
|
+
const target = event.target as HTMLInputElement;
|
|
608
588
|
|
|
609
|
-
|
|
610
|
-
}
|
|
589
|
+
if (this.isDismissing) return;
|
|
611
590
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
591
|
+
if (
|
|
592
|
+
(target.tagName === "INPUT" && target.type === "text") ||
|
|
593
|
+
target.tagName === "TEXTAREA"
|
|
594
|
+
) {
|
|
595
|
+
this.setState({
|
|
596
|
+
isKeyboardVisible: true,
|
|
597
|
+
keyboardInput: target.value || "",
|
|
598
|
+
activeInputRef: target,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
};
|
|
618
602
|
|
|
619
|
-
|
|
620
|
-
|
|
603
|
+
handleInputBlur(event: FocusEvent) {
|
|
604
|
+
if (!shouldUseOnScreenKeyboard()) return;
|
|
621
605
|
|
|
622
|
-
|
|
623
|
-
|
|
606
|
+
const target = event.target as HTMLElement;
|
|
607
|
+
|
|
608
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
609
|
+
const relatedTarget = event.relatedTarget as HTMLElement;
|
|
624
610
|
|
|
625
611
|
if (
|
|
626
|
-
|
|
627
|
-
|
|
612
|
+
!relatedTarget ||
|
|
613
|
+
(relatedTarget.tagName !== "INPUT" &&
|
|
614
|
+
relatedTarget.tagName !== "TEXTAREA")
|
|
628
615
|
) {
|
|
629
|
-
|
|
630
|
-
setDisplayState(DISPLAY_STATES.HUD);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
this.checkSequence(event.keyCode);
|
|
634
|
-
|
|
635
|
-
const direction = keyCode(event).direction();
|
|
636
|
-
|
|
637
|
-
if (!isDialogVisible || !["up", "down"].includes(direction.value)) {
|
|
638
|
-
focusManager.moveFocus(direction);
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (
|
|
642
|
-
displayState === DISPLAY_STATES.PLAYER ||
|
|
643
|
-
displayState === DISPLAY_STATES.HUD
|
|
644
|
-
) {
|
|
645
|
-
this.resetHudTimeout();
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
return true;
|
|
616
|
+
this.setState({ isKeyboardVisible: false });
|
|
649
617
|
}
|
|
650
618
|
}
|
|
619
|
+
}
|
|
651
620
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const deltaY = event?.deltaY;
|
|
656
|
-
|
|
657
|
-
const DIRECTION = {
|
|
658
|
-
axis: "y",
|
|
659
|
-
isHorizontal: false,
|
|
660
|
-
isVertical: true,
|
|
661
|
-
value: "",
|
|
662
|
-
};
|
|
621
|
+
handleKeyboardDismiss = () => {
|
|
622
|
+
this.isDismissing = true;
|
|
663
623
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
624
|
+
this.setState(
|
|
625
|
+
{
|
|
626
|
+
isKeyboardVisible: false,
|
|
627
|
+
keyboardInput: "",
|
|
628
|
+
activeInputRef: null,
|
|
629
|
+
},
|
|
630
|
+
() => {
|
|
631
|
+
this.isDismissing = false;
|
|
670
632
|
}
|
|
633
|
+
);
|
|
634
|
+
};
|
|
671
635
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
displayState === DISPLAY_STATES.HUD
|
|
675
|
-
) {
|
|
676
|
-
this.resetHudTimeout();
|
|
677
|
-
} else {
|
|
678
|
-
focusManager.moveFocus(DIRECTION);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
636
|
+
handleKeyboardInput = (value: string) => {
|
|
637
|
+
const { activeInputRef } = this.state;
|
|
681
638
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
639
|
+
if (activeInputRef) {
|
|
640
|
+
// Update the input's value
|
|
641
|
+
activeInputRef.value = value;
|
|
685
642
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
) {
|
|
691
|
-
this.resetHudTimeout();
|
|
692
|
-
}
|
|
693
|
-
}
|
|
643
|
+
// Dispatch keyboard update event
|
|
644
|
+
const keyboardEvent = new CustomEvent("keyboardUpdate", {
|
|
645
|
+
detail: { value },
|
|
646
|
+
});
|
|
694
647
|
|
|
695
|
-
|
|
696
|
-
this.setState({ isKeyboardVisible: event.visibility });
|
|
648
|
+
document.dispatchEvent(keyboardEvent);
|
|
697
649
|
}
|
|
698
650
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
}
|
|
651
|
+
this.setState({ keyboardInput: value });
|
|
652
|
+
};
|
|
702
653
|
|
|
703
|
-
|
|
704
|
-
|
|
654
|
+
// TODO: use RemoteSequenceHandler class
|
|
655
|
+
private checkSequence(key: number) {
|
|
656
|
+
const SINK_SEQUENCE = [
|
|
657
|
+
KEYS.ArrowUp.keyCode,
|
|
658
|
+
KEYS.ArrowUp.keyCode,
|
|
659
|
+
KEYS.ArrowDown.keyCode,
|
|
660
|
+
KEYS.ArrowDown.keyCode,
|
|
661
|
+
KEYS.ArrowLeft.keyCode,
|
|
662
|
+
KEYS.ArrowRight.keyCode,
|
|
663
|
+
KEYS.ArrowLeft.keyCode,
|
|
664
|
+
KEYS.ArrowRight.keyCode,
|
|
665
|
+
];
|
|
666
|
+
|
|
667
|
+
this.resetSequenceTimeout();
|
|
668
|
+
this.userSequence.push(key);
|
|
669
|
+
|
|
670
|
+
const shouldAddSink =
|
|
671
|
+
this.userSequence.toString() === SINK_SEQUENCE.toString();
|
|
672
|
+
|
|
673
|
+
if (shouldAddSink) {
|
|
674
|
+
this.props.xray.addRemoteSink({});
|
|
705
675
|
}
|
|
676
|
+
}
|
|
706
677
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const target = event.target as HTMLElement;
|
|
678
|
+
private resetSequenceTimeout() {
|
|
679
|
+
const SEQUENCE_ENTRY_TIME = 1900;
|
|
710
680
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
716
|
-
this.setState({
|
|
717
|
-
isKeyboardVisible: true,
|
|
718
|
-
keyboardInput: (target as HTMLInputElement).value || "",
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
};
|
|
681
|
+
if (this.sequenceTimeout) {
|
|
682
|
+
clearTimeout(this.sequenceTimeout);
|
|
683
|
+
}
|
|
722
684
|
|
|
723
|
-
|
|
724
|
-
|
|
685
|
+
this.sequenceTimeout = setTimeout(() => {
|
|
686
|
+
this.sequenceTimeout = null;
|
|
687
|
+
this.userSequence = [];
|
|
688
|
+
}, SEQUENCE_ENTRY_TIME);
|
|
689
|
+
}
|
|
725
690
|
|
|
726
|
-
|
|
691
|
+
private handleLongPress = (event: CustomEvent) => {
|
|
692
|
+
const { keyCode } = event.detail;
|
|
693
|
+
const { displayState } = this.props;
|
|
694
|
+
const playerState = displayState === DISPLAY_STATES.PLAYER;
|
|
695
|
+
const hudState = displayState === DISPLAY_STATES.HUD;
|
|
696
|
+
const playbackState = playerState || hudState;
|
|
697
|
+
this.longPressActive = true;
|
|
698
|
+
this.cancelHudTimeout();
|
|
699
|
+
|
|
700
|
+
switch (keyCode) {
|
|
701
|
+
case KEYS.Forward.code:
|
|
702
|
+
playbackState && this.props.seekMethods.onForwardLongPress();
|
|
703
|
+
break;
|
|
704
|
+
|
|
705
|
+
case KEYS.Rewind.code:
|
|
706
|
+
playbackState && this.props.seekMethods.onRewindLongPress();
|
|
707
|
+
break;
|
|
708
|
+
|
|
709
|
+
case KEYS.Enter.code:
|
|
710
|
+
focusManager.longPress();
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
};
|
|
727
714
|
|
|
728
|
-
|
|
729
|
-
|
|
715
|
+
private handleLongPressRelease = (event: CustomEvent) => {
|
|
716
|
+
const { keyCode } = event.detail;
|
|
717
|
+
this.longPressActive = false;
|
|
718
|
+
this.resetHudTimeout();
|
|
730
719
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
) {
|
|
736
|
-
this.setState({ isKeyboardVisible: false });
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
720
|
+
switch (keyCode) {
|
|
721
|
+
case KEYS.Forward.code:
|
|
722
|
+
this.props.seekMethods.onForwardPressOut();
|
|
723
|
+
break;
|
|
740
724
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
this.setState(
|
|
745
|
-
{
|
|
746
|
-
isKeyboardVisible: false,
|
|
747
|
-
keyboardInput: "",
|
|
748
|
-
},
|
|
749
|
-
() => {
|
|
750
|
-
setTimeout(() => {
|
|
751
|
-
this.isDismissing = false;
|
|
752
|
-
}, 100);
|
|
753
|
-
}
|
|
754
|
-
);
|
|
755
|
-
};
|
|
725
|
+
case KEYS.Rewind.code:
|
|
726
|
+
this.props.seekMethods.onRewindPressOut();
|
|
727
|
+
break;
|
|
756
728
|
|
|
757
|
-
|
|
758
|
-
|
|
729
|
+
case KEYS.Enter.code:
|
|
730
|
+
focusManager.pressOut();
|
|
731
|
+
break;
|
|
759
732
|
}
|
|
733
|
+
};
|
|
760
734
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
{shouldUseOnScreenKeyboard && this.state.isKeyboardVisible && (
|
|
766
|
-
<OnScreenKeyboard
|
|
767
|
-
initialValue={this.state.keyboardInput}
|
|
768
|
-
onInput={this.handleKeyboardInput}
|
|
769
|
-
onDismiss={this.handleKeyboardDismiss}
|
|
770
|
-
/>
|
|
771
|
-
)}
|
|
772
|
-
</>
|
|
773
|
-
);
|
|
774
|
-
}
|
|
735
|
+
private keySupportsLongPress(event: KeyboardEvent): boolean {
|
|
736
|
+
return [KEYS.Forward.code, KEYS.Rewind.code, KEYS.Enter.code].includes(
|
|
737
|
+
event.code
|
|
738
|
+
);
|
|
775
739
|
}
|
|
776
740
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
741
|
+
render() {
|
|
742
|
+
return (
|
|
743
|
+
(shouldUseOnScreenKeyboard() && this.state.isKeyboardVisible && (
|
|
744
|
+
<OnScreenKeyboard
|
|
745
|
+
value={this.state.keyboardInput}
|
|
746
|
+
onInput={this.handleKeyboardInput}
|
|
747
|
+
onDismiss={this.handleKeyboardDismiss}
|
|
748
|
+
/>
|
|
749
|
+
)) ||
|
|
750
|
+
null
|
|
751
|
+
);
|
|
752
|
+
}
|
|
784
753
|
}
|
|
754
|
+
|
|
755
|
+
export const InteractionManager = R.compose(
|
|
756
|
+
withNavigator,
|
|
757
|
+
withUseSeek,
|
|
758
|
+
withXray,
|
|
759
|
+
connectToStore(R.pick(["rivers"])),
|
|
760
|
+
PlayerContentContext.withConsumer,
|
|
761
|
+
DisplayStateContext.withConsumer
|
|
762
|
+
)(InteractionManagerClass);
|