@quicktvui/web-renderer 1.0.0
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 +24 -0
- package/src/adapters/es3-video-player.js +828 -0
- package/src/components/Modal.js +119 -0
- package/src/components/QtAnimationView.js +678 -0
- package/src/components/QtBaseComponent.js +165 -0
- package/src/components/QtFastListView.js +1920 -0
- package/src/components/QtFlexView.js +799 -0
- package/src/components/QtImage.js +203 -0
- package/src/components/QtItemFrame.js +239 -0
- package/src/components/QtItemStoreView.js +93 -0
- package/src/components/QtItemView.js +125 -0
- package/src/components/QtListView.js +331 -0
- package/src/components/QtLoadingView.js +55 -0
- package/src/components/QtPageRootView.js +19 -0
- package/src/components/QtPlayMark.js +168 -0
- package/src/components/QtProgressBar.js +199 -0
- package/src/components/QtQRCode.js +78 -0
- package/src/components/QtReplaceChild.js +149 -0
- package/src/components/QtRippleView.js +166 -0
- package/src/components/QtSeekBar.js +409 -0
- package/src/components/QtText.js +679 -0
- package/src/components/QtTransitionImage.js +170 -0
- package/src/components/QtView.js +706 -0
- package/src/components/QtWebView.js +613 -0
- package/src/components/TabsView.js +420 -0
- package/src/components/ViewPager.js +206 -0
- package/src/components/index.js +24 -0
- package/src/components/plugins/TextV2Component.js +70 -0
- package/src/components/plugins/index.js +7 -0
- package/src/core/SceneBuilder.js +58 -0
- package/src/core/TVFocusManager.js +2014 -0
- package/src/core/asyncLocalStorage.js +175 -0
- package/src/core/autoProxy.js +165 -0
- package/src/core/componentRegistry.js +84 -0
- package/src/core/constants.js +6 -0
- package/src/core/index.js +8 -0
- package/src/core/moduleUtils.js +36 -0
- package/src/core/patches.js +958 -0
- package/src/core/templateBinding.js +666 -0
- package/src/index.js +246 -0
- package/src/modules/AndroidDevelopModule.js +101 -0
- package/src/modules/AndroidDeviceModule.js +341 -0
- package/src/modules/AndroidNetworkModule.js +178 -0
- package/src/modules/AndroidSharedPreferencesModule.js +100 -0
- package/src/modules/ESDeviceInfoModule.js +450 -0
- package/src/modules/ESGroupDataModule.js +195 -0
- package/src/modules/ESIJKAudioPlayerModule.js +477 -0
- package/src/modules/ESLocalStorageModule.js +100 -0
- package/src/modules/ESLogModule.js +65 -0
- package/src/modules/ESModule.js +106 -0
- package/src/modules/ESNetworkSpeedModule.js +117 -0
- package/src/modules/ESToastModule.js +172 -0
- package/src/modules/EsNativeModule.js +117 -0
- package/src/modules/FastListModule.js +101 -0
- package/src/modules/FocusModule.js +145 -0
- package/src/modules/RuntimeDeviceModule.js +176 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Web Renderer Module
|
|
2
|
+
// Main entry point for web renderer components and utilities
|
|
3
|
+
|
|
4
|
+
// Core modules
|
|
5
|
+
export * from './core'
|
|
6
|
+
|
|
7
|
+
// Components
|
|
8
|
+
export * from './components'
|
|
9
|
+
|
|
10
|
+
// Create component registry for web renderer
|
|
11
|
+
import { HippyWebEngine } from '@hippy/web-renderer'
|
|
12
|
+
import { QtView, createNamedComponent } from './components/QtView'
|
|
13
|
+
import { QtText } from './components/QtText'
|
|
14
|
+
import { QtImage } from './components/QtImage'
|
|
15
|
+
import { QtTransitionImage } from './components/QtTransitionImage'
|
|
16
|
+
import { QtPageRootView } from './components/QtPageRootView'
|
|
17
|
+
import { QtFastListView } from './components/QtFastListView'
|
|
18
|
+
import { QtLoadingView } from './components/QtLoadingView'
|
|
19
|
+
import { QtAnimationView } from './components/QtAnimationView'
|
|
20
|
+
import { QtWebView } from './components/QtWebView'
|
|
21
|
+
import { QtQRCode } from './components/QtQRCode'
|
|
22
|
+
import { QtProgressBar } from './components/QtProgressBar'
|
|
23
|
+
import { QtSeekBar } from './components/QtSeekBar'
|
|
24
|
+
import { QtItemFrame } from './components/QtItemFrame'
|
|
25
|
+
import { QtItemStoreView } from './components/QtItemStoreView'
|
|
26
|
+
import { QtRippleView } from './components/QtRippleView'
|
|
27
|
+
import { QtFlexView } from './components/QtFlexView'
|
|
28
|
+
import { QtItemView } from './components/QtItemView'
|
|
29
|
+
import { TabsView } from './components/TabsView'
|
|
30
|
+
import { ViewPager } from './components/ViewPager'
|
|
31
|
+
import { Modal } from './components/Modal'
|
|
32
|
+
import { QtListView, QtListViewItem } from './components/QtListView'
|
|
33
|
+
import { QtPlayMark } from './components/QtPlayMark'
|
|
34
|
+
import { APP_NAME } from './core/constants'
|
|
35
|
+
import { ESModule } from './modules/ESModule'
|
|
36
|
+
import { ESLocalStorageModule } from './modules/ESLocalStorageModule'
|
|
37
|
+
import { EsNativeModule } from './modules/EsNativeModule'
|
|
38
|
+
import { AndroidSharedPreferencesModule } from './modules/AndroidSharedPreferencesModule'
|
|
39
|
+
import { AndroidDevelopModule } from './modules/AndroidDevelopModule'
|
|
40
|
+
import { AndroidNetworkModule } from './modules/AndroidNetworkModule'
|
|
41
|
+
import { ESToastModule } from './modules/ESToastModule'
|
|
42
|
+
import { RuntimeDeviceModule } from './modules/RuntimeDeviceModule'
|
|
43
|
+
import { ESIJKAudioPlayerModule } from './modules/ESIJKAudioPlayerModule'
|
|
44
|
+
import { ESLogModule } from './modules/ESLogModule'
|
|
45
|
+
import { FastListModule } from './modules/FastListModule'
|
|
46
|
+
import { FocusModule } from './modules/FocusModule'
|
|
47
|
+
import { ESDeviceInfoModule } from './modules/ESDeviceInfoModule'
|
|
48
|
+
import { ESGroupDataModule } from './modules/ESGroupDataModule'
|
|
49
|
+
import { TextV2Component } from './components/plugins'
|
|
50
|
+
import { IJKPlayerComponent } from './adapters/es3-video-player'
|
|
51
|
+
// Create named versions of base components
|
|
52
|
+
const QtViewNamed = createNamedComponent(QtView, 'View')
|
|
53
|
+
const QtTextNamed = createNamedComponent(QtText, 'Text')
|
|
54
|
+
const QtImageNamed = createNamedComponent(QtImage, 'Image')
|
|
55
|
+
|
|
56
|
+
// Component registry for HippyWebEngine
|
|
57
|
+
// ListView and ListViewItem are overridden with QtListView/QtListViewItem
|
|
58
|
+
// to support TV-specific features like fadingEdgeLength, horizontalFadingEdgeEnabled, etc.
|
|
59
|
+
export const quicktvuiComponents = {
|
|
60
|
+
// Core Hippy native components (overriding some defaults)
|
|
61
|
+
View: QtViewNamed,
|
|
62
|
+
Text: QtTextNamed,
|
|
63
|
+
Image: QtImageNamed,
|
|
64
|
+
// Override ListView and ListViewItem with TV-specific adapters
|
|
65
|
+
ListView: QtListView,
|
|
66
|
+
ListViewItem: QtListViewItem,
|
|
67
|
+
ScrollView: createNamedComponent(QtView, 'ScrollView'),
|
|
68
|
+
TextInput: createNamedComponent(QtView, 'TextInput'),
|
|
69
|
+
WebView: QtWebView,
|
|
70
|
+
ViewPager: createNamedComponent(QtView, 'ViewPager'),
|
|
71
|
+
FastListView: QtFastListView,
|
|
72
|
+
TextView: QtTextNamed,
|
|
73
|
+
|
|
74
|
+
// ESRouter native components
|
|
75
|
+
ESPageRouterView: createNamedComponent(QtView, 'ESPageRouterView'),
|
|
76
|
+
ESPageRootView: QtPageRootView,
|
|
77
|
+
ESSlotRootView: createNamedComponent(QtView, 'ESSlotRootView'),
|
|
78
|
+
ESSlotView: createNamedComponent(QtView, 'ESSlotView'),
|
|
79
|
+
|
|
80
|
+
// Loading component
|
|
81
|
+
LoadingViewComponent: QtLoadingView,
|
|
82
|
+
|
|
83
|
+
// Animation component
|
|
84
|
+
AnimationViewComponent: QtAnimationView,
|
|
85
|
+
|
|
86
|
+
// WebView component
|
|
87
|
+
ESWebViewComponent: QtWebView,
|
|
88
|
+
|
|
89
|
+
// X5WebView component (uses same implementation as WebView)
|
|
90
|
+
ESX5WebViewComponent: QtWebView,
|
|
91
|
+
|
|
92
|
+
// QR Code component
|
|
93
|
+
QrCodeComponent: QtQRCode,
|
|
94
|
+
|
|
95
|
+
// ProgressBar component
|
|
96
|
+
TVProgressBarViewComponent: QtProgressBar,
|
|
97
|
+
|
|
98
|
+
// SeekBar component
|
|
99
|
+
TVSeekBarViewComponent: QtSeekBar,
|
|
100
|
+
|
|
101
|
+
// GridView/List component - both use QtFastListView with spanCount
|
|
102
|
+
TVListViewComponent: QtFastListView,
|
|
103
|
+
|
|
104
|
+
// ItemFrame component (simple container for qt-poster)
|
|
105
|
+
ItemFrameComponent: QtItemFrame,
|
|
106
|
+
|
|
107
|
+
// ItemStoreView component (shared item templates for waterfall)
|
|
108
|
+
ItemStoreView: QtItemStoreView,
|
|
109
|
+
|
|
110
|
+
// RippleView component
|
|
111
|
+
RippleViewComponent: QtRippleView,
|
|
112
|
+
|
|
113
|
+
// FlexView component (for tv-flex / waterfall)
|
|
114
|
+
FastFlexView: QtFlexView,
|
|
115
|
+
|
|
116
|
+
// ItemView component (for tv-item / waterfall sections)
|
|
117
|
+
FastItemView: QtItemView,
|
|
118
|
+
|
|
119
|
+
// Tabs component (for qt-tabs)
|
|
120
|
+
TabsView: TabsView,
|
|
121
|
+
|
|
122
|
+
// RecyclerViewPager component (for qt-tabs content)
|
|
123
|
+
RecyclerViewPager: ViewPager,
|
|
124
|
+
|
|
125
|
+
// Modal component (for qt-dialog)
|
|
126
|
+
Modal: Modal,
|
|
127
|
+
|
|
128
|
+
// Plugin components
|
|
129
|
+
TextV2Component: TextV2Component,
|
|
130
|
+
|
|
131
|
+
// TransitionImage component (for img-transition)
|
|
132
|
+
TransitionImageComponent: QtTransitionImage,
|
|
133
|
+
|
|
134
|
+
// Video player component (IJKPlayer for web)
|
|
135
|
+
IJKPlayerComponent: IJKPlayerComponent,
|
|
136
|
+
|
|
137
|
+
// PlayMark component
|
|
138
|
+
ESPlayMarkViewComponent: QtPlayMark,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Create and configure the web engine
|
|
142
|
+
export function createWebEngine() {
|
|
143
|
+
console.log('[Web Renderer] Creating HippyWebEngine...')
|
|
144
|
+
|
|
145
|
+
const engine = HippyWebEngine.create({
|
|
146
|
+
modules: {
|
|
147
|
+
ESModule,
|
|
148
|
+
ESLocalStorageModule,
|
|
149
|
+
EsNativeModule,
|
|
150
|
+
AndroidSharedPreferencesModule,
|
|
151
|
+
AndroidDevelopModule,
|
|
152
|
+
AndroidNetworkModule,
|
|
153
|
+
ESToastModule,
|
|
154
|
+
RuntimeDeviceModule,
|
|
155
|
+
ESIJKAudioPlayerModule,
|
|
156
|
+
ESLogModule,
|
|
157
|
+
FastListModule,
|
|
158
|
+
FocusModule,
|
|
159
|
+
ESDeviceInfoModule,
|
|
160
|
+
ESGroupDataModule,
|
|
161
|
+
},
|
|
162
|
+
components: quicktvuiComponents,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
console.log('[Web Renderer] Engine created')
|
|
166
|
+
|
|
167
|
+
// Register ListView and ListViewItem (override Hippy's built-in)
|
|
168
|
+
// These support TV-specific features like fadingEdgeLength, horizontalFadingEdgeEnabled
|
|
169
|
+
engine.registerComponent('ListView', QtListView)
|
|
170
|
+
engine.registerComponent('ListViewItem', QtListViewItem)
|
|
171
|
+
|
|
172
|
+
// Override built-in Image component
|
|
173
|
+
engine.registerComponent('Image', QtImage)
|
|
174
|
+
|
|
175
|
+
// Register ESWebViewComponent
|
|
176
|
+
engine.registerComponent('ESWebViewComponent', QtWebView)
|
|
177
|
+
|
|
178
|
+
// Register ESX5WebViewComponent
|
|
179
|
+
engine.registerComponent('ESX5WebViewComponent', QtWebView)
|
|
180
|
+
|
|
181
|
+
// Register QrCodeComponent
|
|
182
|
+
engine.registerComponent('QrCodeComponent', QtQRCode)
|
|
183
|
+
|
|
184
|
+
// Register TVProgressBarViewComponent
|
|
185
|
+
engine.registerComponent('TVProgressBarViewComponent', QtProgressBar)
|
|
186
|
+
|
|
187
|
+
// Register TVSeekBarViewComponent
|
|
188
|
+
engine.registerComponent('TVSeekBarViewComponent', QtSeekBar)
|
|
189
|
+
|
|
190
|
+
// Register TVListViewComponent (for qt-grid-view) - uses QtFastListView
|
|
191
|
+
engine.registerComponent('TVListViewComponent', QtFastListView)
|
|
192
|
+
|
|
193
|
+
// Register ItemFrameComponent (simple container for qt-poster)
|
|
194
|
+
engine.registerComponent('ItemFrameComponent', QtItemFrame)
|
|
195
|
+
|
|
196
|
+
// Register ItemStoreView (shared item templates for waterfall)
|
|
197
|
+
engine.registerComponent('ItemStoreView', QtItemStoreView)
|
|
198
|
+
|
|
199
|
+
// Register RippleViewComponent
|
|
200
|
+
engine.registerComponent('RippleViewComponent', QtRippleView)
|
|
201
|
+
|
|
202
|
+
// Register FastFlexView (for tv-flex / waterfall)
|
|
203
|
+
engine.registerComponent('FastFlexView', QtFlexView)
|
|
204
|
+
|
|
205
|
+
// Register FastItemView (for tv-item / waterfall sections)
|
|
206
|
+
engine.registerComponent('FastItemView', QtItemView)
|
|
207
|
+
|
|
208
|
+
// Register TabsView (for qt-tabs)
|
|
209
|
+
engine.registerComponent('TabsView', TabsView)
|
|
210
|
+
|
|
211
|
+
// Register RecyclerViewPager (for qt-tabs content)
|
|
212
|
+
engine.registerComponent('RecyclerViewPager', ViewPager)
|
|
213
|
+
|
|
214
|
+
// Register Modal (for qt-dialog)
|
|
215
|
+
engine.registerComponent('Modal', Modal)
|
|
216
|
+
|
|
217
|
+
// Register Plugin components
|
|
218
|
+
engine.registerComponent('TextV2Component', TextV2Component)
|
|
219
|
+
|
|
220
|
+
// Register TransitionImageComponent
|
|
221
|
+
engine.registerComponent('TransitionImageComponent', QtTransitionImage)
|
|
222
|
+
|
|
223
|
+
// Register IJKPlayerComponent (video player)
|
|
224
|
+
engine.registerComponent('IJKPlayerComponent', IJKPlayerComponent)
|
|
225
|
+
|
|
226
|
+
// Register ESPlayMarkViewComponent (play mark animation)
|
|
227
|
+
engine.registerComponent('ESPlayMarkViewComponent', QtPlayMark)
|
|
228
|
+
|
|
229
|
+
return engine
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Start the web engine
|
|
233
|
+
export function startWebEngine(engine) {
|
|
234
|
+
engine.start({
|
|
235
|
+
id: 'app',
|
|
236
|
+
name: APP_NAME,
|
|
237
|
+
params: {
|
|
238
|
+
business: APP_NAME,
|
|
239
|
+
data: {},
|
|
240
|
+
},
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
console.log('[Web Renderer] Engine started')
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export { APP_NAME }
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESDevelopModule - Web平台适配器
|
|
3
|
+
* 提供应用开发信息(版本号、包名、渠道等)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 存储开发信息
|
|
7
|
+
let developInfo = null
|
|
8
|
+
|
|
9
|
+
function getPackageName() {
|
|
10
|
+
const g = globalThis
|
|
11
|
+
if (g && g.__CONFIG__ && g.__CONFIG__.packageName) {
|
|
12
|
+
return g.__CONFIG__.packageName
|
|
13
|
+
}
|
|
14
|
+
return 'web'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ensureDevelopInfo() {
|
|
18
|
+
if (!developInfo) {
|
|
19
|
+
developInfo = {
|
|
20
|
+
versionCode: 1,
|
|
21
|
+
versionName: '1.0.0',
|
|
22
|
+
packageName: getPackageName(),
|
|
23
|
+
channel: 'web',
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return developInfo
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class AndroidDevelopModule {
|
|
30
|
+
constructor(context) {
|
|
31
|
+
this.context = context
|
|
32
|
+
this.name = 'AndroidDevelopModule'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
init(...args) {
|
|
36
|
+
const addon = args && args.length ? args[args.length - 1] : null
|
|
37
|
+
const info = ensureDevelopInfo()
|
|
38
|
+
if (addon && typeof addon.resolve === 'function') {
|
|
39
|
+
addon.resolve(info)
|
|
40
|
+
}
|
|
41
|
+
return info
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
destroy() {
|
|
45
|
+
developInfo = null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getDevelop(...args) {
|
|
49
|
+
const addon = args && args.length ? args[args.length - 1] : null
|
|
50
|
+
const info = ensureDevelopInfo()
|
|
51
|
+
if (addon && typeof addon.resolve === 'function') {
|
|
52
|
+
addon.resolve(info)
|
|
53
|
+
}
|
|
54
|
+
// 返回 Promise,与原生平台行为一致
|
|
55
|
+
return Promise.resolve(info)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getPackageName(...args) {
|
|
59
|
+
const addon = args && args.length ? args[args.length - 1] : null
|
|
60
|
+
const name = getPackageName()
|
|
61
|
+
if (addon && typeof addon.resolve === 'function') {
|
|
62
|
+
addon.resolve(name)
|
|
63
|
+
}
|
|
64
|
+
return name
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getVersionName(...args) {
|
|
68
|
+
const addon = args && args.length ? args[args.length - 1] : null
|
|
69
|
+
const info = ensureDevelopInfo()
|
|
70
|
+
const value = info.versionName
|
|
71
|
+
if (addon && typeof addon.resolve === 'function') {
|
|
72
|
+
addon.resolve(value)
|
|
73
|
+
}
|
|
74
|
+
return value
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getVersionCode(...args) {
|
|
78
|
+
const addon = args && args.length ? args[args.length - 1] : null
|
|
79
|
+
const info = ensureDevelopInfo()
|
|
80
|
+
const value = info.versionCode
|
|
81
|
+
if (addon && typeof addon.resolve === 'function') {
|
|
82
|
+
addon.resolve(value)
|
|
83
|
+
}
|
|
84
|
+
return value
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getChannel(...args) {
|
|
88
|
+
const addon = args && args.length ? args[args.length - 1] : null
|
|
89
|
+
const info = ensureDevelopInfo()
|
|
90
|
+
const value = info.channel
|
|
91
|
+
if (addon && typeof addon.resolve === 'function') {
|
|
92
|
+
addon.resolve(value)
|
|
93
|
+
}
|
|
94
|
+
return value
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// getVendor 是 getChannel 的别名
|
|
98
|
+
getVendor(...args) {
|
|
99
|
+
return this.getChannel(...args)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// Web platform adapter for AndroidDeviceModule
|
|
2
|
+
// Provides simulated device information for web browser environment
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} ESDeviceInfo
|
|
6
|
+
* @property {string} cid - Device ID
|
|
7
|
+
* @property {string} device_name - Device name
|
|
8
|
+
* @property {string} device_ip - Device IP address
|
|
9
|
+
* @property {string} eth_mac - Ethernet MAC address
|
|
10
|
+
* @property {string} wifi_mac - WiFi MAC address
|
|
11
|
+
* @property {number} total_memory - Total memory in bytes
|
|
12
|
+
* @property {number} avail_memory - Available memory in bytes
|
|
13
|
+
* @property {number} screen_width - Screen width in pixels
|
|
14
|
+
* @property {number} screen_height - Screen height in pixels
|
|
15
|
+
* @property {string} resolution - Screen resolution string
|
|
16
|
+
* @property {number} density - Screen density
|
|
17
|
+
* @property {number} density_dpi - Screen density DPI
|
|
18
|
+
* @property {number} scaled_density - Scaled density
|
|
19
|
+
* @property {string} build_version_release - Android version
|
|
20
|
+
* @property {string} build_model - Device model
|
|
21
|
+
* @property {string} build_brand - Device brand
|
|
22
|
+
* @property {string} build_device - Device name
|
|
23
|
+
* @property {string} build_board - Board name
|
|
24
|
+
* @property {string} build_product - Product name
|
|
25
|
+
* @property {string} build_hardware - Hardware name
|
|
26
|
+
* @property {string} build_manufacturer - Manufacturer name
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const TV_WIDTH = 1920
|
|
30
|
+
const TV_HEIGHT = 1080
|
|
31
|
+
|
|
32
|
+
class AndroidDeviceModuleClass {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.name = 'AndroidDeviceModule'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get complete device information
|
|
39
|
+
* @returns {Promise<ESDeviceInfo>}
|
|
40
|
+
*/
|
|
41
|
+
async getAndroidDevice() {
|
|
42
|
+
const screen = this._getScreenInfo()
|
|
43
|
+
return {
|
|
44
|
+
cid: this._generateDeviceId(),
|
|
45
|
+
device_name: 'Web Browser',
|
|
46
|
+
device_ip: await this._getLocalIP(),
|
|
47
|
+
eth_mac: '',
|
|
48
|
+
wifi_mac: this._generateMAC(),
|
|
49
|
+
total_memory: this._estimateTotalMemory(),
|
|
50
|
+
avail_memory: this._estimateAvailMemory(),
|
|
51
|
+
screen_width: screen.width,
|
|
52
|
+
screen_height: screen.height,
|
|
53
|
+
resolution: `${screen.width}x${screen.height}`,
|
|
54
|
+
density: screen.density,
|
|
55
|
+
density_dpi: screen.densityDpi,
|
|
56
|
+
scaled_density: screen.scaledDensity,
|
|
57
|
+
build_version_release: 'Web',
|
|
58
|
+
build_model: this._getBrowserInfo(),
|
|
59
|
+
build_brand: 'Web',
|
|
60
|
+
build_device: 'Browser',
|
|
61
|
+
build_board: 'Web',
|
|
62
|
+
build_product: 'quicktvui-web',
|
|
63
|
+
build_hardware: 'Web',
|
|
64
|
+
build_manufacturer: 'quicktvui',
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get Ethernet MAC address
|
|
70
|
+
* @returns {Promise<string>}
|
|
71
|
+
*/
|
|
72
|
+
async getEthMac() {
|
|
73
|
+
return ''
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get WiFi MAC address
|
|
78
|
+
* @returns {Promise<string>}
|
|
79
|
+
*/
|
|
80
|
+
async getWifiMac() {
|
|
81
|
+
return this._generateMAC()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get total memory in bytes
|
|
86
|
+
* @returns {Promise<number>}
|
|
87
|
+
*/
|
|
88
|
+
async getTotalMemory() {
|
|
89
|
+
return this._estimateTotalMemory()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get available memory in bytes
|
|
94
|
+
* @returns {Promise<number>}
|
|
95
|
+
*/
|
|
96
|
+
async getAvailableMemory() {
|
|
97
|
+
return this._estimateAvailMemory()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get screen width
|
|
102
|
+
* @returns {Promise<number>}
|
|
103
|
+
*/
|
|
104
|
+
async getScreenWidth() {
|
|
105
|
+
return this._getScreenInfo().width
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get screen height
|
|
110
|
+
* @returns {Promise<number>}
|
|
111
|
+
*/
|
|
112
|
+
async getScreenHeight() {
|
|
113
|
+
return this._getScreenInfo().height
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get screen resolution string
|
|
118
|
+
* @returns {Promise<string>}
|
|
119
|
+
*/
|
|
120
|
+
async getResolution() {
|
|
121
|
+
const screen = this._getScreenInfo()
|
|
122
|
+
return `${screen.width}x${screen.height}`
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get screen density
|
|
127
|
+
* @returns {Promise<number>}
|
|
128
|
+
*/
|
|
129
|
+
async getDensity() {
|
|
130
|
+
return this._getScreenInfo().density
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get screen density DPI
|
|
135
|
+
* @returns {Promise<number>}
|
|
136
|
+
*/
|
|
137
|
+
async getDensityDpi() {
|
|
138
|
+
return this._getScreenInfo().densityDpi
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get scaled density
|
|
143
|
+
* @returns {Promise<number>}
|
|
144
|
+
*/
|
|
145
|
+
async getScaledDensity() {
|
|
146
|
+
return this._getScreenInfo().scaledDensity
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get Android version (simulated for web)
|
|
151
|
+
* @returns {Promise<string>}
|
|
152
|
+
*/
|
|
153
|
+
async getBuildVersionRelease() {
|
|
154
|
+
return 'Web'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get device model
|
|
159
|
+
* @returns {Promise<string>}
|
|
160
|
+
*/
|
|
161
|
+
async getBuildModel() {
|
|
162
|
+
return this._getBrowserInfo()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get device brand
|
|
167
|
+
* @returns {Promise<string>}
|
|
168
|
+
*/
|
|
169
|
+
async getBuildBrand() {
|
|
170
|
+
return 'Web'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get device name
|
|
175
|
+
* @returns {Promise<string>}
|
|
176
|
+
*/
|
|
177
|
+
async getBuildDevice() {
|
|
178
|
+
return 'Browser'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get board name
|
|
183
|
+
* @returns {Promise<string>}
|
|
184
|
+
*/
|
|
185
|
+
async getBuildBoard() {
|
|
186
|
+
return 'Web'
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get product name
|
|
191
|
+
* @returns {Promise<string>}
|
|
192
|
+
*/
|
|
193
|
+
async getBuildProduct() {
|
|
194
|
+
return 'quicktvui-web'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get hardware name
|
|
199
|
+
* @returns {Promise<string>}
|
|
200
|
+
*/
|
|
201
|
+
async getBuildHardware() {
|
|
202
|
+
return 'Web'
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get manufacturer name
|
|
207
|
+
* @returns {Promise<string>}
|
|
208
|
+
*/
|
|
209
|
+
async getBuildManufacturer() {
|
|
210
|
+
return 'quicktvui'
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ============ Private helper methods ============
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get screen information
|
|
217
|
+
* @private
|
|
218
|
+
*/
|
|
219
|
+
_getScreenInfo() {
|
|
220
|
+
if (typeof window === 'undefined') {
|
|
221
|
+
return {
|
|
222
|
+
width: TV_WIDTH,
|
|
223
|
+
height: TV_HEIGHT,
|
|
224
|
+
density: 1,
|
|
225
|
+
densityDpi: 160,
|
|
226
|
+
scaledDensity: 1,
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const dpr = window.devicePixelRatio || 1
|
|
231
|
+
return {
|
|
232
|
+
width: TV_WIDTH,
|
|
233
|
+
height: TV_HEIGHT,
|
|
234
|
+
density: dpr,
|
|
235
|
+
densityDpi: Math.round(dpr * 160),
|
|
236
|
+
scaledDensity: dpr,
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Generate a random device ID
|
|
242
|
+
* @private
|
|
243
|
+
*/
|
|
244
|
+
_generateDeviceId() {
|
|
245
|
+
// Try to get from localStorage for persistence
|
|
246
|
+
if (typeof localStorage !== 'undefined') {
|
|
247
|
+
let cid = localStorage.getItem('__ES_DEVICE_ID__')
|
|
248
|
+
if (!cid) {
|
|
249
|
+
cid = 'web-' + Math.random().toString(36).substring(2, 15)
|
|
250
|
+
localStorage.setItem('__ES_DEVICE_ID__', cid)
|
|
251
|
+
}
|
|
252
|
+
return cid
|
|
253
|
+
}
|
|
254
|
+
return 'web-' + Math.random().toString(36).substring(2, 15)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Generate a fake MAC address
|
|
259
|
+
* @private
|
|
260
|
+
*/
|
|
261
|
+
_generateMAC() {
|
|
262
|
+
const hexDigits = '0123456789ABCDEF'
|
|
263
|
+
let mac = ''
|
|
264
|
+
for (let i = 0; i < 6; i++) {
|
|
265
|
+
if (i > 0) mac += ':'
|
|
266
|
+
mac += hexDigits.charAt(Math.floor(Math.random() * 16))
|
|
267
|
+
mac += hexDigits.charAt(Math.floor(Math.random() * 16))
|
|
268
|
+
}
|
|
269
|
+
return mac
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get local IP (returns empty string as we can't detect it in browser)
|
|
274
|
+
* @private
|
|
275
|
+
*/
|
|
276
|
+
async _getLocalIP() {
|
|
277
|
+
// WebRTC can sometimes detect local IP, but it's not reliable
|
|
278
|
+
// Return empty string for simplicity
|
|
279
|
+
return ''
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Estimate total memory using Performance API
|
|
284
|
+
* @private
|
|
285
|
+
*/
|
|
286
|
+
_estimateTotalMemory() {
|
|
287
|
+
if (typeof performance !== 'undefined' && performance.memory) {
|
|
288
|
+
return performance.memory.jsHeapSizeLimit || 1073741824 // 1GB default
|
|
289
|
+
}
|
|
290
|
+
return 1073741824 // 1GB default for web
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Estimate available memory using Performance API
|
|
295
|
+
* @private
|
|
296
|
+
*/
|
|
297
|
+
_estimateAvailMemory() {
|
|
298
|
+
if (typeof performance !== 'undefined' && performance.memory) {
|
|
299
|
+
const limit = performance.memory.jsHeapSizeLimit || 1073741824
|
|
300
|
+
const used = performance.memory.usedJSHeapSize || 0
|
|
301
|
+
return Math.max(0, limit - used)
|
|
302
|
+
}
|
|
303
|
+
return 536870912 // 512MB default
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get browser information
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
_getBrowserInfo() {
|
|
311
|
+
if (typeof navigator === 'undefined') {
|
|
312
|
+
return 'Unknown Browser'
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const ua = navigator.userAgent
|
|
316
|
+
let browserName = 'Unknown Browser'
|
|
317
|
+
|
|
318
|
+
if (ua.indexOf('Firefox') > -1) {
|
|
319
|
+
browserName = 'Firefox'
|
|
320
|
+
} else if (ua.indexOf('Edg') > -1) {
|
|
321
|
+
browserName = 'Edge'
|
|
322
|
+
} else if (ua.indexOf('Chrome') > -1) {
|
|
323
|
+
browserName = 'Chrome'
|
|
324
|
+
} else if (ua.indexOf('Safari') > -1) {
|
|
325
|
+
browserName = 'Safari'
|
|
326
|
+
} else if (ua.indexOf('Opera') > -1 || ua.indexOf('OPR') > -1) {
|
|
327
|
+
browserName = 'Opera'
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Extract version
|
|
331
|
+
const match = ua.match(/(Firefox|Chrome|Safari|Edg|OPR)\/(\d+)/)
|
|
332
|
+
if (match) {
|
|
333
|
+
return `${match[1]} ${match[2]}`
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return browserName
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Create singleton instance and export
|
|
341
|
+
export const AndroidDeviceModule = new AndroidDeviceModuleClass()
|