@quicktvui/web-renderer 1.0.5 → 1.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quicktvui/web-renderer",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Web renderer for QuickTVUI - provides web browser rendering support",
5
5
  "author": "QuickTVUI Team",
6
6
  "license": "Apache-2.0",
@@ -21,4 +21,4 @@
21
21
  "peerDependencies": {
22
22
  "vue": "^3.0.0"
23
23
  }
24
- }
24
+ }
@@ -2,6 +2,7 @@
2
2
  // Handles keyboard arrow keys for TV-style focus navigation
3
3
 
4
4
  import { syncDomAutoWidthIn } from './templateBinding'
5
+ import { sendKeyDownEvent, sendKeyUpEvent, sendHardwareBackPress } from './nativeEventDispatcher'
5
6
 
6
7
  const DEBUG = false
7
8
 
@@ -566,16 +567,10 @@ export class TVFocusManager {
566
567
 
567
568
  _dispatchPageKeyDown(e) {
568
569
  const keyEvent = this._toESKeyEvent(e)
569
- // action: 0 = keydown, 1 = keyup
570
- keyEvent.action = 0
571
570
 
572
571
  // 模拟 Android 平台发送 DispatchKeyEvent
573
- try {
574
- const { EventBus } = require('@extscreen/es3-vue')
575
- if (EventBus && EventBus.$emit) {
576
- EventBus.$emit('DispatchKeyEvent', keyEvent)
577
- }
578
- } catch (err) {
572
+ const sent = sendKeyDownEvent(keyEvent)
573
+ if (!sent) {
579
574
  // fallback to direct method call
580
575
  this._callCurrentPageMethod('onKeyDown', keyEvent)
581
576
  }
@@ -583,16 +578,10 @@ export class TVFocusManager {
583
578
 
584
579
  _dispatchPageKeyUp(e) {
585
580
  const keyEvent = this._toESKeyEvent(e)
586
- // action: 0 = keydown, 1 = keyup
587
- keyEvent.action = 1
588
581
 
589
582
  // 模拟 Android 平台发送 DispatchKeyEvent
590
- try {
591
- const { EventBus } = require('@extscreen/es3-vue')
592
- if (EventBus && EventBus.$emit) {
593
- EventBus.$emit('DispatchKeyEvent', keyEvent)
594
- }
595
- } catch (err) {
583
+ const sent = sendKeyUpEvent(keyEvent)
584
+ if (!sent) {
596
585
  // fallback to direct method call
597
586
  this._callCurrentPageMethod('onKeyUp', keyEvent)
598
587
  }
@@ -1876,18 +1865,8 @@ export class TVFocusManager {
1876
1865
  console.log('[TVFocusManager] dispatchBackPressed called - emitting hardwareBackPress event')
1877
1866
 
1878
1867
  // 模拟 Android 平台的硬件返回按键事件
1879
- // 通过 EventBus 发射 hardwareBackPress 事件
1880
1868
  // ESCore 监听此事件并调用 router.back()
1881
- try {
1882
- // 从 es3-vue 动态获取 EventBus(避免模块级别导入问题)
1883
- const { EventBus } = require('@extscreen/es3-vue')
1884
- if (EventBus && EventBus.$emit) {
1885
- console.log('[TVFocusManager] Emitting hardwareBackPress event')
1886
- EventBus.$emit('hardwareBackPress')
1887
- }
1888
- } catch (err) {
1889
- console.error('[TVFocusManager] Error emitting hardwareBackPress:', err)
1890
- }
1869
+ sendHardwareBackPress()
1891
1870
  }
1892
1871
 
1893
1872
  // Focus direction constants (matching QTFocusDirection enum)
package/src/core/index.js CHANGED
@@ -6,3 +6,4 @@ export * from './patches'
6
6
  export * from './TVFocusManager'
7
7
  export * from './autoProxy'
8
8
  export * from './asyncLocalStorage'
9
+ export * from './nativeEventDispatcher'
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Native Event Dispatcher
3
+ * 封装原生事件发送功能,使用 EventDispatcher.receiveNativeEvent 发送事件到原生端
4
+ */
5
+
6
+ /**
7
+ * 获取 EventDispatcher 实例
8
+ * @returns {Object|null} EventDispatcher 实例
9
+ */
10
+ function getEventDispatcher() {
11
+ try {
12
+ const jsModuleList = global.__GLOBAL__?.jsModuleList
13
+ if (!jsModuleList || !jsModuleList.EventDispatcher) {
14
+ console.warn('[NativeEventDispatcher] EventDispatcher not available in jsModuleList')
15
+ return null
16
+ }
17
+ return jsModuleList.EventDispatcher
18
+ } catch (err) {
19
+ console.error('[NativeEventDispatcher] Error getting EventDispatcher:', err)
20
+ return null
21
+ }
22
+ }
23
+
24
+ /**
25
+ * 发送原生事件
26
+ * @param {string} eventName - 事件名称
27
+ * @param {any} eventData - 事件数据,可以是 null 或任意对象
28
+ * @returns {boolean} 是否发送成功
29
+ */
30
+ export function sendNativeEvent(eventName, eventData = null) {
31
+ const eventDispatcher = getEventDispatcher()
32
+ if (!eventDispatcher) {
33
+ return false
34
+ }
35
+
36
+ try {
37
+ // 事件格式: [eventName, eventData]
38
+ eventDispatcher.receiveNativeEvent([eventName, eventData])
39
+ return true
40
+ } catch (err) {
41
+ console.error(`[NativeEventDispatcher] Error sending event "${eventName}":`, err)
42
+ return false
43
+ }
44
+ }
45
+
46
+ /**
47
+ * 发送硬件返回按键事件
48
+ * @returns {boolean} 是否发送成功
49
+ */
50
+ export function sendHardwareBackPress() {
51
+ return sendNativeEvent('hardwareBackPress', null)
52
+ }
53
+
54
+ /**
55
+ * 发送按键事件
56
+ * @param {Object} keyEvent - 按键事件对象
57
+ * @returns {boolean} 是否发送成功
58
+ */
59
+ export function sendKeyEvent(keyEvent) {
60
+ return sendNativeEvent('DispatchKeyEvent', keyEvent)
61
+ }
62
+
63
+ /**
64
+ * 发送键盘按下事件
65
+ * @param {Object} keyEvent - 按键事件对象
66
+ * @returns {boolean} 是否发送成功
67
+ */
68
+ export function sendKeyDownEvent(keyEvent) {
69
+ return sendKeyEvent({
70
+ ...keyEvent,
71
+ action: 0, // 0 = keydown
72
+ })
73
+ }
74
+
75
+ /**
76
+ * 发送键盘释放事件
77
+ * @param {Object} keyEvent - 按键事件对象
78
+ * @returns {boolean} 是否发送成功
79
+ */
80
+ export function sendKeyUpEvent(keyEvent) {
81
+ return sendKeyEvent({
82
+ ...keyEvent,
83
+ action: 1, // 1 = keyup
84
+ })
85
+ }
86
+
87
+ // 默认导出
88
+ export default {
89
+ sendNativeEvent,
90
+ sendHardwareBackPress,
91
+ sendKeyEvent,
92
+ sendKeyDownEvent,
93
+ sendKeyUpEvent,
94
+ }
package/src/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  applyAllPatches,
14
14
  TVFocusManager,
15
15
  initAutoProxy,
16
- initAsyncLocalStorage
16
+ initAsyncLocalStorage,
17
17
  } from './core'
18
18
 
19
19
  // Create component registry for web renderer
@@ -2,6 +2,7 @@
2
2
  // Uses HTML5 Audio API to implement audio playback
3
3
 
4
4
  import { resolveAddon, createModuleInit } from '../core/moduleUtils'
5
+ import { sendNativeEvent } from '../core/nativeEventDispatcher'
5
6
 
6
7
  // Player states (matching native ESPlayerState)
7
8
  const ESPlayerState = {
@@ -38,34 +39,6 @@ const ESPlayerState = {
38
39
  ES_PLAYER_STATE_INITIALIZE_ERROR: 30,
39
40
  }
40
41
 
41
- // Get EventBus lazily
42
- let _eventBus = null
43
- function getEventBus() {
44
- if (!_eventBus) {
45
- try {
46
- const esVue = require('@extscreen/es3-vue')
47
- if (esVue && esVue.EventBus) {
48
- _eventBus = esVue.EventBus
49
- }
50
- } catch (e) {
51
- console.warn('[ESIJKAudioPlayerModule] EventBus not available:', e.message)
52
- }
53
- }
54
- return _eventBus
55
- }
56
-
57
- // Dispatch event to EventBus
58
- function dispatchEvent(eventName, data) {
59
- const eventBus = getEventBus()
60
- if (eventBus && eventBus.$emit) {
61
- eventBus.$emit(eventName, data)
62
- }
63
- // Also dispatch custom event for global listeners
64
- if (typeof window !== 'undefined') {
65
- window.dispatchEvent(new CustomEvent(eventName, { detail: data }))
66
- }
67
- }
68
-
69
42
  // Audio instance manager
70
43
  class AudioPlayerManager {
71
44
  constructor() {
@@ -95,7 +68,7 @@ class AudioPlayerManager {
95
68
  audio.addEventListener('play', () => {
96
69
  if (currentState !== ESPlayerState.ES_PLAYER_STATE_PLAYING) {
97
70
  currentState = ESPlayerState.ES_PLAYER_STATE_PLAYING
98
- dispatchEvent('onESAudioPlayerStatusChanged', {
71
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
99
72
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PLAYING,
100
73
  playerState: ESPlayerState.ES_PLAYER_STATE_PLAYING,
101
74
  })
@@ -106,7 +79,7 @@ class AudioPlayerManager {
106
79
  audio.addEventListener('playing', () => {
107
80
  if (currentState !== ESPlayerState.ES_PLAYER_STATE_PLAYING) {
108
81
  currentState = ESPlayerState.ES_PLAYER_STATE_PLAYING
109
- dispatchEvent('onESAudioPlayerStatusChanged', {
82
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
110
83
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PLAYING,
111
84
  playerState: ESPlayerState.ES_PLAYER_STATE_PLAYING,
112
85
  })
@@ -116,7 +89,7 @@ class AudioPlayerManager {
116
89
  // Pause event
117
90
  audio.addEventListener('pause', () => {
118
91
  currentState = ESPlayerState.ES_PLAYER_STATE_PAUSED
119
- dispatchEvent('onESAudioPlayerStatusChanged', {
92
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
120
93
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PAUSED,
121
94
  playerState: ESPlayerState.ES_PLAYER_STATE_PAUSED,
122
95
  })
@@ -126,7 +99,7 @@ class AudioPlayerManager {
126
99
  audio.addEventListener('ended', () => {
127
100
  currentState = ESPlayerState.ES_PLAYER_STATE_PLAYBACK_COMPLETED
128
101
  hasPrepared = true // Prevent canplay from sending PREPARED again
129
- dispatchEvent('onESAudioPlayerStatusChanged', {
102
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
130
103
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PLAYBACK_COMPLETED,
131
104
  playerState: ESPlayerState.ES_PLAYER_STATE_PLAYBACK_COMPLETED,
132
105
  })
@@ -135,7 +108,7 @@ class AudioPlayerManager {
135
108
  // Error event
136
109
  audio.addEventListener('error', (e) => {
137
110
  currentState = ESPlayerState.ES_PLAYER_STATE_ERROR
138
- dispatchEvent('onESAudioPlayerError', {
111
+ sendNativeEvent('onESAudioPlayerError', {
139
112
  errorCode: e.target.error?.code || -1,
140
113
  errorMessage: e.target.error?.message || 'Audio error',
141
114
  })
@@ -145,7 +118,7 @@ class AudioPlayerManager {
145
118
  audio.addEventListener('loadstart', () => {
146
119
  hasPrepared = false
147
120
  currentState = ESPlayerState.ES_PLAYER_STATE_PREPARING
148
- dispatchEvent('onESAudioPlayerStatusChanged', {
121
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
149
122
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PREPARING,
150
123
  playerState: ESPlayerState.ES_PLAYER_STATE_PREPARING,
151
124
  })
@@ -156,7 +129,7 @@ class AudioPlayerManager {
156
129
  if (!hasPrepared) {
157
130
  hasPrepared = true
158
131
  currentState = ESPlayerState.ES_PLAYER_STATE_PREPARED
159
- dispatchEvent('onESAudioPlayerStatusChanged', {
132
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
160
133
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PREPARED,
161
134
  playerState: ESPlayerState.ES_PLAYER_STATE_PREPARED,
162
135
  })
@@ -165,7 +138,7 @@ class AudioPlayerManager {
165
138
 
166
139
  // Waiting (buffering)
167
140
  audio.addEventListener('waiting', () => {
168
- dispatchEvent('onESAudioPlayerStatusChanged', {
141
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
169
142
  playerStatus: ESPlayerState.ES_PLAYER_STATE_BUFFER_START,
170
143
  playerState: ESPlayerState.ES_PLAYER_STATE_BUFFER_START,
171
144
  })
@@ -173,7 +146,7 @@ class AudioPlayerManager {
173
146
 
174
147
  // Can play through (buffer end)
175
148
  audio.addEventListener('canplaythrough', () => {
176
- dispatchEvent('onESAudioPlayerStatusChanged', {
149
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
177
150
  playerStatus: ESPlayerState.ES_PLAYER_STATE_BUFFER_END,
178
151
  playerState: ESPlayerState.ES_PLAYER_STATE_BUFFER_END,
179
152
  })
@@ -182,7 +155,7 @@ class AudioPlayerManager {
182
155
  // Duration change
183
156
  audio.addEventListener('durationchange', () => {
184
157
  if (audio.duration && !isNaN(audio.duration)) {
185
- dispatchEvent('onESAudioPlayerInfo', {
158
+ sendNativeEvent('onESAudioPlayerInfo', {
186
159
  what: 1, // MEDIA_INFO_DURATION
187
160
  extra: Math.floor(audio.duration * 1000),
188
161
  })
@@ -191,14 +164,14 @@ class AudioPlayerManager {
191
164
 
192
165
  // Rate change
193
166
  audio.addEventListener('ratechange', () => {
194
- dispatchEvent('onESAudioPlayRateChanged', {
167
+ sendNativeEvent('onESAudioPlayRateChanged', {
195
168
  playRate: audio.playbackRate,
196
169
  })
197
170
  })
198
171
 
199
172
  // Seeking
200
173
  audio.addEventListener('seeking', () => {
201
- dispatchEvent('onESAudioPlayerStatusChanged', {
174
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
202
175
  playerStatus: ESPlayerState.ES_PLAYER_STATE_SEEK_START,
203
176
  playerState: ESPlayerState.ES_PLAYER_STATE_SEEK_START,
204
177
  })
@@ -206,7 +179,7 @@ class AudioPlayerManager {
206
179
 
207
180
  // Seeked
208
181
  audio.addEventListener('seeked', () => {
209
- dispatchEvent('onESAudioPlayerStatusChanged', {
182
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
210
183
  playerStatus: ESPlayerState.ES_PLAYER_STATE_SEEK_COMPLETED,
211
184
  playerState: ESPlayerState.ES_PLAYER_STATE_SEEK_COMPLETED,
212
185
  })
@@ -214,7 +187,7 @@ class AudioPlayerManager {
214
187
 
215
188
  // Volume change
216
189
  audio.addEventListener('volumechange', () => {
217
- dispatchEvent('onESAudioPlayerInfo', {
190
+ sendNativeEvent('onESAudioPlayerInfo', {
218
191
  what: 2, // MEDIA_INFO_VOLUME_CHANGED
219
192
  extra: Math.floor(audio.volume * 100),
220
193
  })
@@ -252,12 +225,12 @@ export class ESIJKAudioPlayerModule {
252
225
 
253
226
  async init(...args) {
254
227
  // Send initialized event
255
- dispatchEvent('onESAudioPlayerStatusChanged', {
228
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
256
229
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PLAYER_INITIALIZED,
257
230
  playerState: ESPlayerState.ES_PLAYER_STATE_PLAYER_INITIALIZED,
258
231
  })
259
232
  // Send init success
260
- dispatchEvent('onESAudioPlayerStatusChanged', {
233
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
261
234
  playerStatus: ESPlayerState.ES_PLAYER_STATE_INITIALIZE_SUCCESS,
262
235
  playerState: ESPlayerState.ES_PLAYER_STATE_INITIALIZE_SUCCESS,
263
236
  })
@@ -266,12 +239,12 @@ export class ESIJKAudioPlayerModule {
266
239
 
267
240
  initMediaPlayer(...args) {
268
241
  // Send initialized event
269
- dispatchEvent('onESAudioPlayerStatusChanged', {
242
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
270
243
  playerStatus: ESPlayerState.ES_PLAYER_STATE_PLAYER_INITIALIZED,
271
244
  playerState: ESPlayerState.ES_PLAYER_STATE_PLAYER_INITIALIZED,
272
245
  })
273
246
  // Send init success
274
- dispatchEvent('onESAudioPlayerStatusChanged', {
247
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
275
248
  playerStatus: ESPlayerState.ES_PLAYER_STATE_INITIALIZE_SUCCESS,
276
249
  playerState: ESPlayerState.ES_PLAYER_STATE_INITIALIZE_SUCCESS,
277
250
  })
@@ -319,7 +292,7 @@ export class ESIJKAudioPlayerModule {
319
292
  if (autoplay && audio.src) {
320
293
  audio.play().catch((e) => {
321
294
  console.warn('[ESIJKAudioPlayerModule] play error:', e)
322
- dispatchEvent('onESAudioPlayerError', {
295
+ sendNativeEvent('onESAudioPlayerError', {
323
296
  errorCode: -1,
324
297
  errorMessage: e.message || 'Play error',
325
298
  })
@@ -350,7 +323,7 @@ export class ESIJKAudioPlayerModule {
350
323
  console.warn('[ESIJKAudioPlayerModule] resume error:', e)
351
324
  })
352
325
  // Send resumed event
353
- dispatchEvent('onESAudioPlayerStatusChanged', {
326
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
354
327
  playerStatus: ESPlayerState.ES_PLAYER_STATE_RESUMED,
355
328
  playerState: ESPlayerState.ES_PLAYER_STATE_RESUMED,
356
329
  })
@@ -368,14 +341,14 @@ export class ESIJKAudioPlayerModule {
368
341
  stop(id, ...args) {
369
342
  const audio = audioManager.getInstance(id)
370
343
  // Send before stop event
371
- dispatchEvent('onESAudioPlayerStatusChanged', {
344
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
372
345
  playerStatus: ESPlayerState.ES_PLAYER_STATE_BEFORE_STOP,
373
346
  playerState: ESPlayerState.ES_PLAYER_STATE_BEFORE_STOP,
374
347
  })
375
348
  audio.pause()
376
349
  audio.currentTime = 0
377
350
  // Send stopped event
378
- dispatchEvent('onESAudioPlayerStatusChanged', {
351
+ sendNativeEvent('onESAudioPlayerStatusChanged', {
379
352
  playerStatus: ESPlayerState.ES_PLAYER_STATE_STOP,
380
353
  playerState: ESPlayerState.ES_PLAYER_STATE_STOP,
381
354
  })
@@ -385,7 +358,7 @@ export class ESIJKAudioPlayerModule {
385
358
  setPlayRate(id, speed, ...args) {
386
359
  const audio = audioManager.getInstance(id)
387
360
  audio.playbackRate = Number(speed) || 1.0
388
- dispatchEvent('onESAudioPlayRateChanged', {
361
+ sendNativeEvent('onESAudioPlayRateChanged', {
389
362
  playRate: audio.playbackRate,
390
363
  })
391
364
  return resolveAddon(args, null)