@craft-native/ios 0.0.70
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/README.md +619 -0
- package/dist/cli.js +664 -0
- package/dist/index.js +584 -0
- package/package.json +33 -0
- package/templates/CraftActivityAttributes.swift +14 -0
- package/templates/CraftApp.swift +5169 -0
- package/templates/CraftAppExtensions.swift +361 -0
- package/templates/CraftLiveActivityWidget.swift.template +62 -0
- package/templates/CraftWatchApp.swift.template +155 -0
- package/templates/CraftWidget.swift +247 -0
- package/templates/Info.plist.template +66 -0
- package/templates/WatchApp.Info.plist.template +26 -0
- package/templates/WidgetExtension.Info.plist +31 -0
- package/templates/fastlane/Appfile +45 -0
- package/templates/fastlane/Fastfile +189 -0
- package/templates/fastlane/Gemfile +7 -0
- package/templates/fastlane/Matchfile +58 -0
- package/templates/github-workflow-ios.yml +192 -0
- package/templates/github-workflow-release.yml +175 -0
- package/templates/github-workflow-test.yml +272 -0
- package/templates/project.yml.template +36 -0
- package/templates/test-bridges.html +1463 -0
|
@@ -0,0 +1,1463 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
6
|
+
<title>Craft Bridge Test</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
|
+
body {
|
|
10
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
11
|
+
background: #1a1a2e;
|
|
12
|
+
color: #eee;
|
|
13
|
+
padding: 20px;
|
|
14
|
+
padding-top: max(20px, env(safe-area-inset-top));
|
|
15
|
+
padding-bottom: max(20px, env(safe-area-inset-bottom));
|
|
16
|
+
}
|
|
17
|
+
h1 { font-size: 24px; margin-bottom: 20px; text-align: center; }
|
|
18
|
+
h2 { font-size: 18px; margin: 20px 0 10px; color: #888; }
|
|
19
|
+
.section { margin-bottom: 30px; }
|
|
20
|
+
.btn {
|
|
21
|
+
display: inline-block;
|
|
22
|
+
background: #4a4a8a;
|
|
23
|
+
color: white;
|
|
24
|
+
border: none;
|
|
25
|
+
padding: 12px 20px;
|
|
26
|
+
border-radius: 8px;
|
|
27
|
+
margin: 5px;
|
|
28
|
+
font-size: 14px;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
.btn:active { background: #5a5a9a; }
|
|
32
|
+
.btn.success { background: #2a7a3a; }
|
|
33
|
+
.btn.error { background: #7a2a2a; }
|
|
34
|
+
.result {
|
|
35
|
+
background: #2a2a4e;
|
|
36
|
+
padding: 15px;
|
|
37
|
+
border-radius: 8px;
|
|
38
|
+
margin: 10px 0;
|
|
39
|
+
font-family: monospace;
|
|
40
|
+
font-size: 12px;
|
|
41
|
+
white-space: pre-wrap;
|
|
42
|
+
word-break: break-all;
|
|
43
|
+
max-height: 200px;
|
|
44
|
+
overflow-y: auto;
|
|
45
|
+
}
|
|
46
|
+
.status {
|
|
47
|
+
display: inline-block;
|
|
48
|
+
padding: 4px 8px;
|
|
49
|
+
border-radius: 4px;
|
|
50
|
+
font-size: 12px;
|
|
51
|
+
margin-left: 10px;
|
|
52
|
+
}
|
|
53
|
+
.status.ready { background: #2a7a3a; }
|
|
54
|
+
.status.waiting { background: #7a5a2a; }
|
|
55
|
+
.status.error { background: #7a2a2a; }
|
|
56
|
+
#log {
|
|
57
|
+
position: fixed;
|
|
58
|
+
bottom: 0;
|
|
59
|
+
left: 0;
|
|
60
|
+
right: 0;
|
|
61
|
+
background: #000;
|
|
62
|
+
padding: 10px;
|
|
63
|
+
max-height: 150px;
|
|
64
|
+
overflow-y: auto;
|
|
65
|
+
font-family: monospace;
|
|
66
|
+
font-size: 11px;
|
|
67
|
+
z-index: 100;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
</head>
|
|
71
|
+
<body>
|
|
72
|
+
<h1>Craft Bridge Test <span id="status" class="status waiting">Waiting...</span></h1>
|
|
73
|
+
<div id="platform"></div>
|
|
74
|
+
|
|
75
|
+
<!-- Debug Mode -->
|
|
76
|
+
<div class="section">
|
|
77
|
+
<h2>Debug Mode</h2>
|
|
78
|
+
<button class="btn" onclick="toggleDebugMode()">Toggle Debug</button>
|
|
79
|
+
<button class="btn" onclick="showLastError()">Last Error</button>
|
|
80
|
+
<button class="btn" onclick="showCallLog()">Call Log</button>
|
|
81
|
+
<button class="btn" onclick="clearCallLog()">Clear Log</button>
|
|
82
|
+
<button class="btn" onclick="showErrorHistory()">Error History</button>
|
|
83
|
+
<button class="btn" onclick="showConsoleLog()">Console Log</button>
|
|
84
|
+
<button class="btn" onclick="showNetworkLog()">Network Log</button>
|
|
85
|
+
<button class="btn" onclick="showDebugReport()">Full Report</button>
|
|
86
|
+
<div id="debug-result" class="result">Debug mode: OFF</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<!-- Core -->
|
|
90
|
+
<div class="section">
|
|
91
|
+
<h2>Core</h2>
|
|
92
|
+
<button class="btn" onclick="testHaptic()">Test Haptics</button>
|
|
93
|
+
<button class="btn" onclick="testShare()">Test Share</button>
|
|
94
|
+
<button class="btn" onclick="testLog()">Test Log</button>
|
|
95
|
+
<button class="btn" onclick="testDeviceInfo()">Device Info</button>
|
|
96
|
+
<div id="core-result" class="result">Results will appear here...</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!-- Camera & Media -->
|
|
100
|
+
<div class="section">
|
|
101
|
+
<h2>Camera & Media</h2>
|
|
102
|
+
<button class="btn" onclick="testCamera()">Open Camera</button>
|
|
103
|
+
<button class="btn" onclick="testGallery()">Pick Image</button>
|
|
104
|
+
<button class="btn" onclick="testQRScanner()">Scan QR Code</button>
|
|
105
|
+
<button class="btn" onclick="testScreenshot()">Screenshot</button>
|
|
106
|
+
<div id="camera-result" class="result">Results will appear here...</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<!-- Audio/Video Recording -->
|
|
110
|
+
<div class="section">
|
|
111
|
+
<h2>Audio/Video Recording</h2>
|
|
112
|
+
<button class="btn" onclick="testStartAudio()">Start Audio</button>
|
|
113
|
+
<button class="btn" onclick="testStopAudio()">Stop Audio</button>
|
|
114
|
+
<button class="btn" onclick="testVideoRecording()">Record Video</button>
|
|
115
|
+
<div id="recording-result" class="result">Results will appear here...</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<!-- Files -->
|
|
119
|
+
<div class="section">
|
|
120
|
+
<h2>Files</h2>
|
|
121
|
+
<button class="btn" onclick="testFilePicker()">Pick File</button>
|
|
122
|
+
<button class="btn" onclick="testDownloadFile()">Download File</button>
|
|
123
|
+
<div id="files-result" class="result">Results will appear here...</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- Location & Sensors -->
|
|
127
|
+
<div class="section">
|
|
128
|
+
<h2>Location & Sensors</h2>
|
|
129
|
+
<button class="btn" onclick="testLocation()">Get Location</button>
|
|
130
|
+
<button class="btn" onclick="testStartMotion()">Start Motion</button>
|
|
131
|
+
<button class="btn" onclick="testStopMotion()">Stop Motion</button>
|
|
132
|
+
<div id="sensors-result" class="result">Results will appear here...</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<!-- Contacts -->
|
|
136
|
+
<div class="section">
|
|
137
|
+
<h2>Contacts</h2>
|
|
138
|
+
<button class="btn" onclick="testGetContacts()">Get Contacts</button>
|
|
139
|
+
<button class="btn" onclick="testAddContact()">Add Contact</button>
|
|
140
|
+
<div id="contacts-result" class="result">Results will appear here...</div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<!-- Calendar -->
|
|
144
|
+
<div class="section">
|
|
145
|
+
<h2>Calendar</h2>
|
|
146
|
+
<button class="btn" onclick="testGetCalendarEvents()">Get Events</button>
|
|
147
|
+
<button class="btn" onclick="testCreateCalendarEvent()">Create Event</button>
|
|
148
|
+
<div id="calendar-result" class="result">Results will appear here...</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<!-- Local Notifications -->
|
|
152
|
+
<div class="section">
|
|
153
|
+
<h2>Local Notifications</h2>
|
|
154
|
+
<button class="btn" onclick="testScheduleNotification()">Schedule (5s)</button>
|
|
155
|
+
<button class="btn" onclick="testCancelNotification()">Cancel</button>
|
|
156
|
+
<button class="btn" onclick="testGetPendingNotifications()">Get Pending</button>
|
|
157
|
+
<div id="notifications-result" class="result">Results will appear here...</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<!-- In-App Purchase -->
|
|
161
|
+
<div class="section">
|
|
162
|
+
<h2>In-App Purchase</h2>
|
|
163
|
+
<button class="btn" onclick="testGetProducts()">Get Products</button>
|
|
164
|
+
<button class="btn" onclick="testRestorePurchases()">Restore Purchases</button>
|
|
165
|
+
<div id="iap-result" class="result">Results will appear here...</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<!-- Screen Control -->
|
|
169
|
+
<div class="section">
|
|
170
|
+
<h2>Screen Control</h2>
|
|
171
|
+
<button class="btn" onclick="testKeepAwakeOn()">Keep Awake ON</button>
|
|
172
|
+
<button class="btn" onclick="testKeepAwakeOff()">Keep Awake OFF</button>
|
|
173
|
+
<button class="btn" onclick="testLockPortrait()">Lock Portrait</button>
|
|
174
|
+
<button class="btn" onclick="testLockLandscape()">Lock Landscape</button>
|
|
175
|
+
<button class="btn" onclick="testUnlockOrientation()">Unlock</button>
|
|
176
|
+
<div id="screen-result" class="result">Results will appear here...</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<!-- Connectivity -->
|
|
180
|
+
<div class="section">
|
|
181
|
+
<h2>Connectivity</h2>
|
|
182
|
+
<button class="btn" onclick="testNetwork()">Network Status</button>
|
|
183
|
+
<button class="btn" onclick="testStartBluetooth()">Start BLE Scan</button>
|
|
184
|
+
<button class="btn" onclick="testStopBluetooth()">Stop BLE Scan</button>
|
|
185
|
+
<button class="btn" onclick="testNFC()">Scan NFC</button>
|
|
186
|
+
<div id="connectivity-result" class="result">Results will appear here...</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<!-- Database -->
|
|
190
|
+
<div class="section">
|
|
191
|
+
<h2>Local Database (SQLite)</h2>
|
|
192
|
+
<button class="btn" onclick="testDBCreate()">Create Table</button>
|
|
193
|
+
<button class="btn" onclick="testDBInsert()">Insert Data</button>
|
|
194
|
+
<button class="btn" onclick="testDBQuery()">Query Data</button>
|
|
195
|
+
<div id="db-result" class="result">Results will appear here...</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<!-- Auth & Security -->
|
|
199
|
+
<div class="section">
|
|
200
|
+
<h2>Auth & Security</h2>
|
|
201
|
+
<button class="btn" onclick="testBiometric()">Biometric Auth</button>
|
|
202
|
+
<button class="btn" onclick="testAppleSignIn()">Apple Sign In</button>
|
|
203
|
+
<button class="btn" onclick="testSecureStore()">Secure Storage</button>
|
|
204
|
+
<div id="auth-result" class="result">Results will appear here...</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<!-- Clipboard -->
|
|
208
|
+
<div class="section">
|
|
209
|
+
<h2>Clipboard</h2>
|
|
210
|
+
<button class="btn" onclick="testClipboardWrite()">Write Clipboard</button>
|
|
211
|
+
<button class="btn" onclick="testClipboardRead()">Read Clipboard</button>
|
|
212
|
+
<div id="clipboard-result" class="result">Results will appear here...</div>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<!-- System -->
|
|
216
|
+
<div class="section">
|
|
217
|
+
<h2>System</h2>
|
|
218
|
+
<button class="btn" onclick="testFlashlight()">Toggle Flashlight</button>
|
|
219
|
+
<button class="btn" onclick="testVibrate()">Vibrate Pattern</button>
|
|
220
|
+
<button class="btn" onclick="testOpenURL()">Open URL</button>
|
|
221
|
+
<button class="btn" onclick="testAppState()">App State</button>
|
|
222
|
+
<button class="btn" onclick="testBadge()">Set Badge</button>
|
|
223
|
+
<button class="btn" onclick="testReview()">Request Review</button>
|
|
224
|
+
<div id="system-result" class="result">Results will appear here...</div>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<!-- Speech -->
|
|
228
|
+
<div class="section">
|
|
229
|
+
<h2>Speech Recognition</h2>
|
|
230
|
+
<button class="btn" onclick="testStartListening()">Start Listening</button>
|
|
231
|
+
<button class="btn" onclick="testStopListening()">Stop Listening</button>
|
|
232
|
+
<div id="speech-result" class="result">Results will appear here...</div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<!-- Medium Value Bridges -->
|
|
236
|
+
<div class="section">
|
|
237
|
+
<h2>Background Tasks</h2>
|
|
238
|
+
<button class="btn" onclick="testRegisterBgTask()">Register Task</button>
|
|
239
|
+
<button class="btn" onclick="testScheduleBgTask()">Schedule Task</button>
|
|
240
|
+
<button class="btn" onclick="testCancelBgTask()">Cancel Task</button>
|
|
241
|
+
<button class="btn" onclick="testCancelAllBgTasks()">Cancel All</button>
|
|
242
|
+
<div id="bgtask-result" class="result">Results will appear here...</div>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<div class="section">
|
|
246
|
+
<h2>PDF Viewer</h2>
|
|
247
|
+
<button class="btn" onclick="testOpenPDFUrl()">Open PDF (URL)</button>
|
|
248
|
+
<button class="btn" onclick="testClosePDF()">Close PDF</button>
|
|
249
|
+
<div id="pdf-result" class="result">Results will appear here...</div>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div class="section">
|
|
253
|
+
<h2>Contacts Picker</h2>
|
|
254
|
+
<button class="btn" onclick="testPickContact()">Pick Contact</button>
|
|
255
|
+
<button class="btn" onclick="testPickMultipleContacts()">Pick Multiple</button>
|
|
256
|
+
<div id="pickcontact-result" class="result">Results will appear here...</div>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<div class="section">
|
|
260
|
+
<h2>App Shortcuts</h2>
|
|
261
|
+
<button class="btn" onclick="testSetShortcuts()">Set Shortcuts</button>
|
|
262
|
+
<button class="btn" onclick="testClearShortcuts()">Clear Shortcuts</button>
|
|
263
|
+
<div id="shortcuts-result" class="result">Results will appear here...</div>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div class="section">
|
|
267
|
+
<h2>Shared Keychain</h2>
|
|
268
|
+
<button class="btn" onclick="testSetSharedItem()">Set Item</button>
|
|
269
|
+
<button class="btn" onclick="testGetSharedItem()">Get Item</button>
|
|
270
|
+
<button class="btn" onclick="testRemoveSharedItem()">Remove Item</button>
|
|
271
|
+
<div id="keychain-result" class="result">Results will appear here...</div>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div class="section">
|
|
275
|
+
<h2>Auth Persistence</h2>
|
|
276
|
+
<button class="btn" onclick="testEnableAuthPersistence()">Enable (5 min)</button>
|
|
277
|
+
<button class="btn" onclick="testCheckAuthPersistence()">Check Status</button>
|
|
278
|
+
<button class="btn" onclick="testClearAuthPersistence()">Clear</button>
|
|
279
|
+
<div id="authpersist-result" class="result">Results will appear here...</div>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<!-- Nice to Have Bridges -->
|
|
283
|
+
<div class="section">
|
|
284
|
+
<h2>AR (ARKit/ARCore)</h2>
|
|
285
|
+
<button class="btn" onclick="testStartAR()">Start AR</button>
|
|
286
|
+
<button class="btn" onclick="testPlaceARObject()">Place Box</button>
|
|
287
|
+
<button class="btn" onclick="testGetARPlanes()">Get Planes</button>
|
|
288
|
+
<button class="btn" onclick="testStopAR()">Stop AR</button>
|
|
289
|
+
<div id="ar-result" class="result">Results will appear here...</div>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<div class="section">
|
|
293
|
+
<h2>ML (Vision/ML Kit)</h2>
|
|
294
|
+
<button class="btn" onclick="testCaptureAndClassify()">Capture & Classify</button>
|
|
295
|
+
<button class="btn" onclick="testCaptureAndDetect()">Capture & Detect</button>
|
|
296
|
+
<button class="btn" onclick="testCaptureAndOCR()">Capture & OCR</button>
|
|
297
|
+
<div id="ml-result" class="result">Results will appear here...</div>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<!-- Widgets -->
|
|
301
|
+
<div class="section">
|
|
302
|
+
<h2>Widgets (WidgetKit/AppWidgetProvider)</h2>
|
|
303
|
+
<button class="btn" onclick="testUpdateWidget()">Update Widget</button>
|
|
304
|
+
<button class="btn" onclick="testReloadWidgets()">Reload Widgets</button>
|
|
305
|
+
<div id="widget-result" class="result">Results will appear here...</div>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<!-- Voice Assistant -->
|
|
309
|
+
<div class="section">
|
|
310
|
+
<h2>Siri / Google Assistant</h2>
|
|
311
|
+
<button class="btn" onclick="testRegisterSiri()">Register Shortcut</button>
|
|
312
|
+
<button class="btn" onclick="testRemoveSiri()">Remove Shortcut</button>
|
|
313
|
+
<div id="siri-result" class="result">Results will appear here...</div>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<!-- Watch Connectivity -->
|
|
317
|
+
<div class="section">
|
|
318
|
+
<h2>Watch Connectivity</h2>
|
|
319
|
+
<button class="btn" onclick="testWatchReachable()">Check Reachable</button>
|
|
320
|
+
<button class="btn" onclick="testSendToWatch()">Send Message</button>
|
|
321
|
+
<button class="btn" onclick="testUpdateWatchContext()">Update Context</button>
|
|
322
|
+
<div id="watch-result" class="result">Results will appear here...</div>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<!-- Deep Links -->
|
|
326
|
+
<div class="section">
|
|
327
|
+
<h2>Deep Links</h2>
|
|
328
|
+
<button class="btn" onclick="testGetInitialURL()">Get Initial URL</button>
|
|
329
|
+
<button class="btn" onclick="setupDeepLinkListener()">Setup Listener</button>
|
|
330
|
+
<div id="deeplink-result" class="result">Results will appear here...</div>
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
<!-- Performance Profiling -->
|
|
334
|
+
<div class="section">
|
|
335
|
+
<h2>Performance Profiling</h2>
|
|
336
|
+
<button class="btn" onclick="testStartProfiling()">Start Profiling</button>
|
|
337
|
+
<button class="btn" onclick="testStopProfiling()">Stop Profiling</button>
|
|
338
|
+
<button class="btn" onclick="testGetProfilingData()">Get Data</button>
|
|
339
|
+
<button class="btn" onclick="testGetMemoryUsage()">Memory Usage</button>
|
|
340
|
+
<div id="profiling-result" class="result">Results will appear here...</div>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<!-- OTA Updates -->
|
|
344
|
+
<div class="section">
|
|
345
|
+
<h2>OTA Updates</h2>
|
|
346
|
+
<button class="btn" onclick="testOTAConfigure()">Configure</button>
|
|
347
|
+
<button class="btn" onclick="testCheckForUpdate()">Check Update</button>
|
|
348
|
+
<button class="btn" onclick="testDownloadUpdate()">Download</button>
|
|
349
|
+
<button class="btn" onclick="testGetCurrentBundle()">Current Bundle</button>
|
|
350
|
+
<div id="ota-result" class="result">Results will appear here...</div>
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<div id="log"></div>
|
|
354
|
+
|
|
355
|
+
<script>
|
|
356
|
+
// Logging utility
|
|
357
|
+
function log(msg) {
|
|
358
|
+
const logEl = document.getElementById('log');
|
|
359
|
+
const time = new Date().toLocaleTimeString();
|
|
360
|
+
logEl.innerHTML = `[${time}] ${msg}\n` + logEl.innerHTML;
|
|
361
|
+
console.log(msg);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function showResult(id, data) {
|
|
365
|
+
document.getElementById(id).textContent = typeof data === 'object'
|
|
366
|
+
? JSON.stringify(data, null, 2)
|
|
367
|
+
: String(data);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Wait for Craft
|
|
371
|
+
window.addEventListener('craftReady', (e) => {
|
|
372
|
+
document.getElementById('status').textContent = 'Ready';
|
|
373
|
+
document.getElementById('status').className = 'status ready';
|
|
374
|
+
document.getElementById('platform').textContent = `Platform: ${e.detail.platform}`;
|
|
375
|
+
log('Craft ready! Platform: ' + e.detail.platform);
|
|
376
|
+
log('Capabilities: ' + JSON.stringify(e.detail.capabilities));
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Speech events
|
|
380
|
+
window.addEventListener('craftSpeechStart', () => {
|
|
381
|
+
log('Speech started');
|
|
382
|
+
showResult('speech-result', 'Listening...');
|
|
383
|
+
});
|
|
384
|
+
window.addEventListener('craftSpeechResult', (e) => {
|
|
385
|
+
log('Speech result: ' + e.detail.transcript);
|
|
386
|
+
showResult('speech-result', `Transcript: ${e.detail.transcript}\nFinal: ${e.detail.isFinal}`);
|
|
387
|
+
});
|
|
388
|
+
window.addEventListener('craftSpeechEnd', () => {
|
|
389
|
+
log('Speech ended');
|
|
390
|
+
});
|
|
391
|
+
window.addEventListener('craftSpeechError', (e) => {
|
|
392
|
+
log('Speech error: ' + e.detail.error);
|
|
393
|
+
showResult('speech-result', 'Error: ' + e.detail.error);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Motion events
|
|
397
|
+
window.addEventListener('craftMotionUpdate', (e) => {
|
|
398
|
+
showResult('sensors-result', {
|
|
399
|
+
accelerometer: e.detail.accelerometer,
|
|
400
|
+
gyroscope: e.detail.gyroscope
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// Bluetooth events
|
|
405
|
+
window.addEventListener('craftBluetoothDevice', (e) => {
|
|
406
|
+
log('BLE device: ' + e.detail.name);
|
|
407
|
+
const current = document.getElementById('connectivity-result').textContent;
|
|
408
|
+
showResult('connectivity-result', current + '\nDevice: ' + JSON.stringify(e.detail));
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Debug mode error events
|
|
412
|
+
window.addEventListener('craftError', (e) => {
|
|
413
|
+
log('Craft error: ' + e.detail.code + ' - ' + e.detail.message);
|
|
414
|
+
showResult('debug-result', 'Error: ' + JSON.stringify(e.detail, null, 2));
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Debug mode functions
|
|
418
|
+
var debugModeOn = false;
|
|
419
|
+
function toggleDebugMode() {
|
|
420
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
421
|
+
debugModeOn = !debugModeOn;
|
|
422
|
+
window.craft.setDebugMode(debugModeOn);
|
|
423
|
+
showResult('debug-result', 'Debug mode: ' + (debugModeOn ? 'ON' : 'OFF'));
|
|
424
|
+
log('Debug mode ' + (debugModeOn ? 'enabled' : 'disabled'));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function showLastError() {
|
|
428
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
429
|
+
const error = window.craft.getLastError();
|
|
430
|
+
showResult('debug-result', error ? JSON.stringify(error, null, 2) : 'No errors recorded');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function showCallLog() {
|
|
434
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
435
|
+
const callLog = window.craft.getCallLog();
|
|
436
|
+
showResult('debug-result', callLog.length > 0
|
|
437
|
+
? 'Call log (' + callLog.length + ' entries):\n' + JSON.stringify(callLog.slice(-10), null, 2)
|
|
438
|
+
: 'No calls logged (enable debug mode first)');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function clearCallLog() {
|
|
442
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
443
|
+
window.craft.clearCallLog();
|
|
444
|
+
showResult('debug-result', 'Call log cleared');
|
|
445
|
+
log('Call log cleared');
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function showErrorHistory() {
|
|
449
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
450
|
+
const history = window.craft.getErrorHistory ? window.craft.getErrorHistory() : [];
|
|
451
|
+
showResult('debug-result', history.length > 0
|
|
452
|
+
? 'Error history (' + history.length + ' errors):\n' + JSON.stringify(history.slice(-5), null, 2)
|
|
453
|
+
: 'No errors recorded');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function showConsoleLog() {
|
|
457
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
458
|
+
const consoleLogs = window.craft.getConsoleLog ? window.craft.getConsoleLog() : [];
|
|
459
|
+
showResult('debug-result', consoleLogs.length > 0
|
|
460
|
+
? 'Console log (' + consoleLogs.length + ' entries):\n' + JSON.stringify(consoleLogs.slice(-10), null, 2)
|
|
461
|
+
: 'No console logs captured (enable debug mode first)');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function showNetworkLog() {
|
|
465
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
466
|
+
const networkLogs = window.craft.getNetworkLog ? window.craft.getNetworkLog() : [];
|
|
467
|
+
showResult('debug-result', networkLogs.length > 0
|
|
468
|
+
? 'Network log (' + networkLogs.length + ' requests):\n' + JSON.stringify(networkLogs.slice(-10), null, 2)
|
|
469
|
+
: 'No network requests captured (enable debug mode first)');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function showDebugReport() {
|
|
473
|
+
if (!window.craft) return showResult('debug-result', 'Craft not ready');
|
|
474
|
+
const report = window.craft.getDebugReport ? window.craft.getDebugReport() : null;
|
|
475
|
+
if (report) {
|
|
476
|
+
showResult('debug-result', 'Debug Report:\n' + JSON.stringify(report, null, 2));
|
|
477
|
+
} else {
|
|
478
|
+
showResult('debug-result', 'Debug report not available');
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Core tests
|
|
483
|
+
function testHaptic() {
|
|
484
|
+
if (!window.craft) return showResult('core-result', 'Craft not ready');
|
|
485
|
+
window.craft.haptic('success');
|
|
486
|
+
window.craft.haptic('light');
|
|
487
|
+
window.craft.haptic('selection');
|
|
488
|
+
showResult('core-result', 'Haptic feedback triggered (success, light, selection)');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function testShare() {
|
|
492
|
+
if (!window.craft) return showResult('core-result', 'Craft not ready');
|
|
493
|
+
window.craft.share('Testing Craft bridges!', 'Craft Test');
|
|
494
|
+
showResult('core-result', 'Share sheet opened');
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function testLog() {
|
|
498
|
+
if (!window.craft) return showResult('core-result', 'Craft not ready');
|
|
499
|
+
window.craft.log('Test log message from JavaScript');
|
|
500
|
+
showResult('core-result', 'Log message sent to native console');
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function testDeviceInfo() {
|
|
504
|
+
if (!window.craft) return showResult('core-result', 'Craft not ready');
|
|
505
|
+
const info = window.craft.getDeviceInfo();
|
|
506
|
+
showResult('core-result', info);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Camera tests
|
|
510
|
+
async function testCamera() {
|
|
511
|
+
if (!window.craft) return showResult('camera-result', 'Craft not ready');
|
|
512
|
+
try {
|
|
513
|
+
const image = await window.craft.openCamera();
|
|
514
|
+
showResult('camera-result', 'Image captured! Base64 length: ' + (image?.length || 0));
|
|
515
|
+
} catch (e) {
|
|
516
|
+
showResult('camera-result', 'Error: ' + e.message);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
async function testGallery() {
|
|
521
|
+
if (!window.craft) return showResult('camera-result', 'Craft not ready');
|
|
522
|
+
try {
|
|
523
|
+
const image = await window.craft.pickImage();
|
|
524
|
+
showResult('camera-result', 'Image selected! Base64 length: ' + (image?.length || 0));
|
|
525
|
+
} catch (e) {
|
|
526
|
+
showResult('camera-result', 'Error: ' + e.message);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async function testQRScanner() {
|
|
531
|
+
if (!window.craft) return showResult('camera-result', 'Craft not ready');
|
|
532
|
+
try {
|
|
533
|
+
const code = await window.craft.scanQRCode();
|
|
534
|
+
showResult('camera-result', 'Scanned: ' + code);
|
|
535
|
+
} catch (e) {
|
|
536
|
+
showResult('camera-result', 'Error: ' + e.message);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async function testScreenshot() {
|
|
541
|
+
if (!window.craft) return showResult('camera-result', 'Craft not ready');
|
|
542
|
+
try {
|
|
543
|
+
const screenshot = await window.craft.takeScreenshot();
|
|
544
|
+
showResult('camera-result', 'Screenshot captured! Base64 length: ' + (screenshot?.length || 0));
|
|
545
|
+
} catch (e) {
|
|
546
|
+
showResult('camera-result', 'Error: ' + e.message);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Audio/Video tests
|
|
551
|
+
async function testStartAudio() {
|
|
552
|
+
if (!window.craft) return showResult('recording-result', 'Craft not ready');
|
|
553
|
+
try {
|
|
554
|
+
await window.craft.startAudioRecording();
|
|
555
|
+
showResult('recording-result', 'Audio recording started...');
|
|
556
|
+
} catch (e) {
|
|
557
|
+
showResult('recording-result', 'Error: ' + e.message);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function testStopAudio() {
|
|
562
|
+
if (!window.craft) return showResult('recording-result', 'Craft not ready');
|
|
563
|
+
try {
|
|
564
|
+
const audio = await window.craft.stopAudioRecording();
|
|
565
|
+
showResult('recording-result', 'Audio recorded! Base64 length: ' + (audio?.length || 0));
|
|
566
|
+
} catch (e) {
|
|
567
|
+
showResult('recording-result', 'Error: ' + e.message);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
async function testVideoRecording() {
|
|
572
|
+
if (!window.craft) return showResult('recording-result', 'Craft not ready');
|
|
573
|
+
try {
|
|
574
|
+
const video = await window.craft.startVideoRecording();
|
|
575
|
+
showResult('recording-result', 'Video recorded! Base64 length: ' + (video?.length || 0));
|
|
576
|
+
} catch (e) {
|
|
577
|
+
showResult('recording-result', 'Error: ' + e.message);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// File tests
|
|
582
|
+
async function testFilePicker() {
|
|
583
|
+
if (!window.craft) return showResult('files-result', 'Craft not ready');
|
|
584
|
+
try {
|
|
585
|
+
const file = await window.craft.pickFile();
|
|
586
|
+
showResult('files-result', {
|
|
587
|
+
name: file?.name,
|
|
588
|
+
dataLength: file?.data?.length || 0
|
|
589
|
+
});
|
|
590
|
+
} catch (e) {
|
|
591
|
+
showResult('files-result', 'Error: ' + e.message);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
async function testDownloadFile() {
|
|
596
|
+
if (!window.craft) return showResult('files-result', 'Craft not ready');
|
|
597
|
+
try {
|
|
598
|
+
await window.craft.downloadFile('https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf', 'test.pdf');
|
|
599
|
+
showResult('files-result', 'File download started');
|
|
600
|
+
} catch (e) {
|
|
601
|
+
showResult('files-result', 'Error: ' + e.message);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Location & Sensors tests
|
|
606
|
+
async function testLocation() {
|
|
607
|
+
if (!window.craft) return showResult('sensors-result', 'Craft not ready');
|
|
608
|
+
try {
|
|
609
|
+
const pos = await window.craft.getCurrentPosition();
|
|
610
|
+
showResult('sensors-result', pos);
|
|
611
|
+
} catch (e) {
|
|
612
|
+
showResult('sensors-result', 'Error: ' + e.message);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function testStartMotion() {
|
|
617
|
+
if (!window.craft) return showResult('sensors-result', 'Craft not ready');
|
|
618
|
+
window.craft.startMotionUpdates();
|
|
619
|
+
showResult('sensors-result', 'Motion updates started (watch for updates)...');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function testStopMotion() {
|
|
623
|
+
if (!window.craft) return showResult('sensors-result', 'Craft not ready');
|
|
624
|
+
window.craft.stopMotionUpdates();
|
|
625
|
+
showResult('sensors-result', 'Motion updates stopped');
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Connectivity tests
|
|
629
|
+
function testNetwork() {
|
|
630
|
+
if (!window.craft) return showResult('connectivity-result', 'Craft not ready');
|
|
631
|
+
const status = window.craft.getNetworkStatus();
|
|
632
|
+
showResult('connectivity-result', status);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function testStartBluetooth() {
|
|
636
|
+
if (!window.craft) return showResult('connectivity-result', 'Craft not ready');
|
|
637
|
+
window.craft.startBluetoothScan();
|
|
638
|
+
showResult('connectivity-result', 'Bluetooth scan started...');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function testStopBluetooth() {
|
|
642
|
+
if (!window.craft) return showResult('connectivity-result', 'Craft not ready');
|
|
643
|
+
window.craft.stopBluetoothScan();
|
|
644
|
+
showResult('connectivity-result', 'Bluetooth scan stopped');
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async function testNFC() {
|
|
648
|
+
if (!window.craft) return showResult('connectivity-result', 'Craft not ready');
|
|
649
|
+
try {
|
|
650
|
+
const data = await window.craft.scanNFC();
|
|
651
|
+
showResult('connectivity-result', 'NFC data: ' + data);
|
|
652
|
+
} catch (e) {
|
|
653
|
+
showResult('connectivity-result', 'Error: ' + e.message);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Database tests
|
|
658
|
+
async function testDBCreate() {
|
|
659
|
+
if (!window.craft?.db) return showResult('db-result', 'Craft DB not ready');
|
|
660
|
+
try {
|
|
661
|
+
await window.craft.db.execute('CREATE TABLE IF NOT EXISTS test_users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, created_at TEXT)');
|
|
662
|
+
showResult('db-result', 'Table created successfully');
|
|
663
|
+
} catch (e) {
|
|
664
|
+
showResult('db-result', 'Error: ' + e.message);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
async function testDBInsert() {
|
|
669
|
+
if (!window.craft?.db) return showResult('db-result', 'Craft DB not ready');
|
|
670
|
+
try {
|
|
671
|
+
const now = new Date().toISOString();
|
|
672
|
+
await window.craft.db.execute('INSERT INTO test_users (name, created_at) VALUES (?, ?)', ['Test User ' + Date.now(), now]);
|
|
673
|
+
showResult('db-result', 'Data inserted successfully');
|
|
674
|
+
} catch (e) {
|
|
675
|
+
showResult('db-result', 'Error: ' + e.message);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
async function testDBQuery() {
|
|
680
|
+
if (!window.craft?.db) return showResult('db-result', 'Craft DB not ready');
|
|
681
|
+
try {
|
|
682
|
+
const results = await window.craft.db.query('SELECT * FROM test_users ORDER BY id DESC LIMIT 5');
|
|
683
|
+
showResult('db-result', results);
|
|
684
|
+
} catch (e) {
|
|
685
|
+
showResult('db-result', 'Error: ' + e.message);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Auth tests
|
|
690
|
+
async function testBiometric() {
|
|
691
|
+
if (!window.craft) return showResult('auth-result', 'Craft not ready');
|
|
692
|
+
try {
|
|
693
|
+
const result = await window.craft.authenticate('Confirm your identity');
|
|
694
|
+
showResult('auth-result', 'Authenticated: ' + result);
|
|
695
|
+
} catch (e) {
|
|
696
|
+
showResult('auth-result', 'Error: ' + e.message);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function testAppleSignIn() {
|
|
701
|
+
if (!window.craft) return showResult('auth-result', 'Craft not ready');
|
|
702
|
+
try {
|
|
703
|
+
const user = await window.craft.signInWithApple();
|
|
704
|
+
showResult('auth-result', user);
|
|
705
|
+
} catch (e) {
|
|
706
|
+
showResult('auth-result', 'Error: ' + e.message);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
async function testSecureStore() {
|
|
711
|
+
if (!window.craft?.secureStore) return showResult('auth-result', 'Craft secureStore not ready');
|
|
712
|
+
try {
|
|
713
|
+
await window.craft.secureStore.set('test_key', 'test_value_' + Date.now());
|
|
714
|
+
const value = await window.craft.secureStore.get('test_key');
|
|
715
|
+
showResult('auth-result', 'Stored and retrieved: ' + value);
|
|
716
|
+
} catch (e) {
|
|
717
|
+
showResult('auth-result', 'Error: ' + e.message);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Clipboard tests
|
|
722
|
+
async function testClipboardWrite() {
|
|
723
|
+
if (!window.craft?.clipboard) return showResult('clipboard-result', 'Craft clipboard not ready');
|
|
724
|
+
try {
|
|
725
|
+
await window.craft.clipboard.write('Hello from Craft! ' + Date.now());
|
|
726
|
+
showResult('clipboard-result', 'Text written to clipboard');
|
|
727
|
+
} catch (e) {
|
|
728
|
+
showResult('clipboard-result', 'Error: ' + e.message);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
async function testClipboardRead() {
|
|
733
|
+
if (!window.craft?.clipboard) return showResult('clipboard-result', 'Craft clipboard not ready');
|
|
734
|
+
try {
|
|
735
|
+
const text = await window.craft.clipboard.read();
|
|
736
|
+
showResult('clipboard-result', 'Clipboard: ' + text);
|
|
737
|
+
} catch (e) {
|
|
738
|
+
showResult('clipboard-result', 'Error: ' + e.message);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// System tests
|
|
743
|
+
function testFlashlight() {
|
|
744
|
+
if (!window.craft) return showResult('system-result', 'Craft not ready');
|
|
745
|
+
window.craft.toggleFlashlight();
|
|
746
|
+
showResult('system-result', 'Flashlight toggled');
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function testVibrate() {
|
|
750
|
+
if (!window.craft) return showResult('system-result', 'Craft not ready');
|
|
751
|
+
window.craft.vibrate([100, 50, 100, 50, 200]);
|
|
752
|
+
showResult('system-result', 'Vibration pattern triggered');
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function testOpenURL() {
|
|
756
|
+
if (!window.craft) return showResult('system-result', 'Craft not ready');
|
|
757
|
+
window.craft.openURL('https://github.com');
|
|
758
|
+
showResult('system-result', 'Opening URL in browser...');
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function testAppState() {
|
|
762
|
+
if (!window.craft) return showResult('system-result', 'Craft not ready');
|
|
763
|
+
const state = window.craft.getAppState();
|
|
764
|
+
showResult('system-result', 'App state: ' + state);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function testBadge() {
|
|
768
|
+
if (!window.craft) return showResult('system-result', 'Craft not ready');
|
|
769
|
+
window.craft.setBadge(5);
|
|
770
|
+
setTimeout(() => window.craft.clearBadge(), 3000);
|
|
771
|
+
showResult('system-result', 'Badge set to 5 (will clear in 3s)');
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
async function testReview() {
|
|
775
|
+
if (!window.craft) return showResult('system-result', 'Craft not ready');
|
|
776
|
+
try {
|
|
777
|
+
await window.craft.requestReview();
|
|
778
|
+
showResult('system-result', 'Review requested');
|
|
779
|
+
} catch (e) {
|
|
780
|
+
showResult('system-result', 'Error: ' + e.message);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Speech tests
|
|
785
|
+
function testStartListening() {
|
|
786
|
+
if (!window.craft) return showResult('speech-result', 'Craft not ready');
|
|
787
|
+
window.craft.startListening();
|
|
788
|
+
showResult('speech-result', 'Started listening...');
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function testStopListening() {
|
|
792
|
+
if (!window.craft) return showResult('speech-result', 'Craft not ready');
|
|
793
|
+
window.craft.stopListening();
|
|
794
|
+
showResult('speech-result', 'Stopped listening');
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// ==================== High Value Bridges Tests ====================
|
|
798
|
+
|
|
799
|
+
// Contacts tests
|
|
800
|
+
async function testGetContacts() {
|
|
801
|
+
if (!window.craft) return showResult('contacts-result', 'Craft not ready');
|
|
802
|
+
try {
|
|
803
|
+
const contacts = await window.craft.getContacts();
|
|
804
|
+
showResult('contacts-result', 'Found ' + (contacts?.length || 0) + ' contacts:\n' + JSON.stringify(contacts?.slice(0, 5), null, 2));
|
|
805
|
+
} catch (e) {
|
|
806
|
+
showResult('contacts-result', 'Error: ' + e.message);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
async function testAddContact() {
|
|
811
|
+
if (!window.craft) return showResult('contacts-result', 'Craft not ready');
|
|
812
|
+
try {
|
|
813
|
+
const id = await window.craft.addContact({
|
|
814
|
+
givenName: 'Test',
|
|
815
|
+
familyName: 'User',
|
|
816
|
+
displayName: 'Test User',
|
|
817
|
+
phone: '+1234567890',
|
|
818
|
+
email: 'test@example.com'
|
|
819
|
+
});
|
|
820
|
+
showResult('contacts-result', 'Contact created with ID: ' + id);
|
|
821
|
+
} catch (e) {
|
|
822
|
+
showResult('contacts-result', 'Error: ' + e.message);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Calendar tests
|
|
827
|
+
async function testGetCalendarEvents() {
|
|
828
|
+
if (!window.craft) return showResult('calendar-result', 'Craft not ready');
|
|
829
|
+
try {
|
|
830
|
+
const now = Date.now();
|
|
831
|
+
const events = await window.craft.getCalendarEvents(now, now + 30 * 24 * 60 * 60 * 1000);
|
|
832
|
+
showResult('calendar-result', 'Found ' + (events?.length || 0) + ' events:\n' + JSON.stringify(events?.slice(0, 5), null, 2));
|
|
833
|
+
} catch (e) {
|
|
834
|
+
showResult('calendar-result', 'Error: ' + e.message);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
async function testCreateCalendarEvent() {
|
|
839
|
+
if (!window.craft) return showResult('calendar-result', 'Craft not ready');
|
|
840
|
+
try {
|
|
841
|
+
const now = Date.now();
|
|
842
|
+
const id = await window.craft.createCalendarEvent({
|
|
843
|
+
title: 'Test Event from Craft',
|
|
844
|
+
location: 'Test Location',
|
|
845
|
+
notes: 'Created by Craft bridge test',
|
|
846
|
+
startDate: now + 3600000,
|
|
847
|
+
endDate: now + 7200000,
|
|
848
|
+
isAllDay: false
|
|
849
|
+
});
|
|
850
|
+
showResult('calendar-result', 'Event created with ID: ' + id);
|
|
851
|
+
} catch (e) {
|
|
852
|
+
showResult('calendar-result', 'Error: ' + e.message);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Local Notifications tests
|
|
857
|
+
let lastNotificationId = null;
|
|
858
|
+
async function testScheduleNotification() {
|
|
859
|
+
if (!window.craft) return showResult('notifications-result', 'Craft not ready');
|
|
860
|
+
try {
|
|
861
|
+
lastNotificationId = await window.craft.scheduleNotification({
|
|
862
|
+
id: 'test-' + Date.now(),
|
|
863
|
+
title: 'Test Notification',
|
|
864
|
+
body: 'This is a test notification from Craft!',
|
|
865
|
+
badge: 1,
|
|
866
|
+
delay: 5000 // 5 seconds
|
|
867
|
+
});
|
|
868
|
+
showResult('notifications-result', 'Notification scheduled with ID: ' + lastNotificationId + '\n(Will appear in 5 seconds)');
|
|
869
|
+
} catch (e) {
|
|
870
|
+
showResult('notifications-result', 'Error: ' + e.message);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
async function testCancelNotification() {
|
|
875
|
+
if (!window.craft) return showResult('notifications-result', 'Craft not ready');
|
|
876
|
+
if (!lastNotificationId) return showResult('notifications-result', 'No notification to cancel');
|
|
877
|
+
try {
|
|
878
|
+
await window.craft.cancelNotification(lastNotificationId);
|
|
879
|
+
showResult('notifications-result', 'Notification cancelled: ' + lastNotificationId);
|
|
880
|
+
} catch (e) {
|
|
881
|
+
showResult('notifications-result', 'Error: ' + e.message);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
async function testGetPendingNotifications() {
|
|
886
|
+
if (!window.craft) return showResult('notifications-result', 'Craft not ready');
|
|
887
|
+
try {
|
|
888
|
+
const pending = await window.craft.getPendingNotifications();
|
|
889
|
+
showResult('notifications-result', 'Pending notifications: ' + JSON.stringify(pending, null, 2));
|
|
890
|
+
} catch (e) {
|
|
891
|
+
showResult('notifications-result', 'Error: ' + e.message);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// IAP tests
|
|
896
|
+
async function testGetProducts() {
|
|
897
|
+
if (!window.craft) return showResult('iap-result', 'Craft not ready');
|
|
898
|
+
try {
|
|
899
|
+
const products = await window.craft.getProducts(['premium', 'coins_100']);
|
|
900
|
+
showResult('iap-result', 'Products: ' + JSON.stringify(products, null, 2));
|
|
901
|
+
} catch (e) {
|
|
902
|
+
showResult('iap-result', 'Error: ' + e.message);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
async function testRestorePurchases() {
|
|
907
|
+
if (!window.craft) return showResult('iap-result', 'Craft not ready');
|
|
908
|
+
try {
|
|
909
|
+
await window.craft.restorePurchases();
|
|
910
|
+
showResult('iap-result', 'Restore purchases initiated');
|
|
911
|
+
} catch (e) {
|
|
912
|
+
showResult('iap-result', 'Error: ' + e.message);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// Screen control tests
|
|
917
|
+
function testKeepAwakeOn() {
|
|
918
|
+
if (!window.craft) return showResult('screen-result', 'Craft not ready');
|
|
919
|
+
window.craft.setKeepAwake(true);
|
|
920
|
+
showResult('screen-result', 'Keep awake: ON (screen will not dim)');
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function testKeepAwakeOff() {
|
|
924
|
+
if (!window.craft) return showResult('screen-result', 'Craft not ready');
|
|
925
|
+
window.craft.setKeepAwake(false);
|
|
926
|
+
showResult('screen-result', 'Keep awake: OFF (screen will dim normally)');
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function testLockPortrait() {
|
|
930
|
+
if (!window.craft) return showResult('screen-result', 'Craft not ready');
|
|
931
|
+
window.craft.lockOrientation('portrait');
|
|
932
|
+
showResult('screen-result', 'Orientation locked: PORTRAIT');
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function testLockLandscape() {
|
|
936
|
+
if (!window.craft) return showResult('screen-result', 'Craft not ready');
|
|
937
|
+
window.craft.lockOrientation('landscape');
|
|
938
|
+
showResult('screen-result', 'Orientation locked: LANDSCAPE');
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function testUnlockOrientation() {
|
|
942
|
+
if (!window.craft) return showResult('screen-result', 'Craft not ready');
|
|
943
|
+
window.craft.unlockOrientation();
|
|
944
|
+
showResult('screen-result', 'Orientation unlocked: ALL');
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// ==================== Medium Value Bridges Tests ====================
|
|
948
|
+
|
|
949
|
+
// Background Tasks tests
|
|
950
|
+
async function testRegisterBgTask() {
|
|
951
|
+
if (!window.craft?.backgroundTask) return showResult('bgtask-result', 'backgroundTask not available');
|
|
952
|
+
try {
|
|
953
|
+
const result = await window.craft.backgroundTask.register('craft-sync');
|
|
954
|
+
showResult('bgtask-result', 'Registered: ' + JSON.stringify(result));
|
|
955
|
+
} catch (e) {
|
|
956
|
+
showResult('bgtask-result', 'Error: ' + e.message);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
async function testScheduleBgTask() {
|
|
961
|
+
if (!window.craft?.backgroundTask) return showResult('bgtask-result', 'backgroundTask not available');
|
|
962
|
+
try {
|
|
963
|
+
const result = await window.craft.backgroundTask.schedule('craft-sync', {
|
|
964
|
+
delay: 900,
|
|
965
|
+
requiresNetwork: true,
|
|
966
|
+
requiresCharging: false
|
|
967
|
+
});
|
|
968
|
+
showResult('bgtask-result', 'Scheduled: ' + JSON.stringify(result));
|
|
969
|
+
} catch (e) {
|
|
970
|
+
showResult('bgtask-result', 'Error: ' + e.message);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
async function testCancelBgTask() {
|
|
975
|
+
if (!window.craft?.backgroundTask) return showResult('bgtask-result', 'backgroundTask not available');
|
|
976
|
+
try {
|
|
977
|
+
const result = await window.craft.backgroundTask.cancel('craft-sync');
|
|
978
|
+
showResult('bgtask-result', 'Cancelled: ' + JSON.stringify(result));
|
|
979
|
+
} catch (e) {
|
|
980
|
+
showResult('bgtask-result', 'Error: ' + e.message);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
async function testCancelAllBgTasks() {
|
|
985
|
+
if (!window.craft?.backgroundTask) return showResult('bgtask-result', 'backgroundTask not available');
|
|
986
|
+
try {
|
|
987
|
+
const result = await window.craft.backgroundTask.cancelAll();
|
|
988
|
+
showResult('bgtask-result', 'Cancelled all: ' + JSON.stringify(result));
|
|
989
|
+
} catch (e) {
|
|
990
|
+
showResult('bgtask-result', 'Error: ' + e.message);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// PDF Viewer tests
|
|
995
|
+
async function testOpenPDFUrl() {
|
|
996
|
+
if (!window.craft) return showResult('pdf-result', 'Craft not ready');
|
|
997
|
+
try {
|
|
998
|
+
const result = await window.craft.openPDF('https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf');
|
|
999
|
+
showResult('pdf-result', 'PDF opened: ' + JSON.stringify(result));
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
showResult('pdf-result', 'Error: ' + e.message);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
async function testClosePDF() {
|
|
1006
|
+
if (!window.craft) return showResult('pdf-result', 'Craft not ready');
|
|
1007
|
+
try {
|
|
1008
|
+
const result = await window.craft.closePDF();
|
|
1009
|
+
showResult('pdf-result', 'PDF closed: ' + result);
|
|
1010
|
+
} catch (e) {
|
|
1011
|
+
showResult('pdf-result', 'Error: ' + e.message);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Contacts Picker tests
|
|
1016
|
+
async function testPickContact() {
|
|
1017
|
+
if (!window.craft) return showResult('pickcontact-result', 'Craft not ready');
|
|
1018
|
+
try {
|
|
1019
|
+
const contact = await window.craft.pickContact();
|
|
1020
|
+
showResult('pickcontact-result', 'Selected: ' + JSON.stringify(contact, null, 2));
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
showResult('pickcontact-result', 'Error: ' + e.message);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
async function testPickMultipleContacts() {
|
|
1027
|
+
if (!window.craft) return showResult('pickcontact-result', 'Craft not ready');
|
|
1028
|
+
try {
|
|
1029
|
+
const contacts = await window.craft.pickContact({multiple: true});
|
|
1030
|
+
showResult('pickcontact-result', 'Selected ' + (contacts?.length || 0) + ' contacts:\n' + JSON.stringify(contacts, null, 2));
|
|
1031
|
+
} catch (e) {
|
|
1032
|
+
showResult('pickcontact-result', 'Error: ' + e.message);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// App Shortcuts tests
|
|
1037
|
+
async function testSetShortcuts() {
|
|
1038
|
+
if (!window.craft?.shortcuts) return showResult('shortcuts-result', 'shortcuts not available');
|
|
1039
|
+
try {
|
|
1040
|
+
const result = await window.craft.shortcuts.set([
|
|
1041
|
+
{type: 'new-message', title: 'New Message', subtitle: 'Start composing', iconName: 'square.and.pencil'},
|
|
1042
|
+
{type: 'search', title: 'Search', iconName: 'magnifyingglass'},
|
|
1043
|
+
{type: 'settings', title: 'Settings', iconName: 'gear'}
|
|
1044
|
+
]);
|
|
1045
|
+
showResult('shortcuts-result', 'Shortcuts set: ' + JSON.stringify(result));
|
|
1046
|
+
} catch (e) {
|
|
1047
|
+
showResult('shortcuts-result', 'Error: ' + e.message);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
async function testClearShortcuts() {
|
|
1052
|
+
if (!window.craft?.shortcuts) return showResult('shortcuts-result', 'shortcuts not available');
|
|
1053
|
+
try {
|
|
1054
|
+
const result = await window.craft.shortcuts.clear();
|
|
1055
|
+
showResult('shortcuts-result', 'Shortcuts cleared: ' + result);
|
|
1056
|
+
} catch (e) {
|
|
1057
|
+
showResult('shortcuts-result', 'Error: ' + e.message);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// Listen for shortcut events
|
|
1062
|
+
window.addEventListener('craftShortcut', (e) => {
|
|
1063
|
+
log('Shortcut activated: ' + e.detail.type);
|
|
1064
|
+
showResult('shortcuts-result', 'Shortcut activated: ' + JSON.stringify(e.detail));
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
// Shared Keychain tests
|
|
1068
|
+
async function testSetSharedItem() {
|
|
1069
|
+
if (!window.craft?.sharedKeychain) return showResult('keychain-result', 'sharedKeychain not available');
|
|
1070
|
+
try {
|
|
1071
|
+
const result = await window.craft.sharedKeychain.set('test_key', 'test_value_' + Date.now());
|
|
1072
|
+
showResult('keychain-result', 'Set: ' + JSON.stringify(result));
|
|
1073
|
+
} catch (e) {
|
|
1074
|
+
showResult('keychain-result', 'Error: ' + e.message);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
async function testGetSharedItem() {
|
|
1079
|
+
if (!window.craft?.sharedKeychain) return showResult('keychain-result', 'sharedKeychain not available');
|
|
1080
|
+
try {
|
|
1081
|
+
const result = await window.craft.sharedKeychain.get('test_key');
|
|
1082
|
+
showResult('keychain-result', 'Get: ' + JSON.stringify(result));
|
|
1083
|
+
} catch (e) {
|
|
1084
|
+
showResult('keychain-result', 'Error: ' + e.message);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
async function testRemoveSharedItem() {
|
|
1089
|
+
if (!window.craft?.sharedKeychain) return showResult('keychain-result', 'sharedKeychain not available');
|
|
1090
|
+
try {
|
|
1091
|
+
const result = await window.craft.sharedKeychain.remove('test_key');
|
|
1092
|
+
showResult('keychain-result', 'Removed: ' + JSON.stringify(result));
|
|
1093
|
+
} catch (e) {
|
|
1094
|
+
showResult('keychain-result', 'Error: ' + e.message);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// Auth Persistence tests
|
|
1099
|
+
async function testEnableAuthPersistence() {
|
|
1100
|
+
if (!window.craft?.authPersistence) return showResult('authpersist-result', 'authPersistence not available');
|
|
1101
|
+
try {
|
|
1102
|
+
const result = await window.craft.authPersistence.enable(300); // 5 minutes
|
|
1103
|
+
showResult('authpersist-result', 'Enabled: ' + JSON.stringify(result));
|
|
1104
|
+
} catch (e) {
|
|
1105
|
+
showResult('authpersist-result', 'Error: ' + e.message);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
async function testCheckAuthPersistence() {
|
|
1110
|
+
if (!window.craft?.authPersistence) return showResult('authpersist-result', 'authPersistence not available');
|
|
1111
|
+
try {
|
|
1112
|
+
const result = await window.craft.authPersistence.check();
|
|
1113
|
+
showResult('authpersist-result', 'Status: ' + JSON.stringify(result));
|
|
1114
|
+
} catch (e) {
|
|
1115
|
+
showResult('authpersist-result', 'Error: ' + e.message);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
async function testClearAuthPersistence() {
|
|
1120
|
+
if (!window.craft?.authPersistence) return showResult('authpersist-result', 'authPersistence not available');
|
|
1121
|
+
try {
|
|
1122
|
+
const result = await window.craft.authPersistence.clear();
|
|
1123
|
+
showResult('authpersist-result', 'Cleared: ' + JSON.stringify(result));
|
|
1124
|
+
} catch (e) {
|
|
1125
|
+
showResult('authpersist-result', 'Error: ' + e.message);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// ==================== Nice to Have Bridges Tests ====================
|
|
1130
|
+
|
|
1131
|
+
// AR tests
|
|
1132
|
+
async function testStartAR() {
|
|
1133
|
+
if (!window.craft?.ar) return showResult('ar-result', 'AR not available');
|
|
1134
|
+
try {
|
|
1135
|
+
const result = await window.craft.ar.start({planeDetection: true});
|
|
1136
|
+
showResult('ar-result', 'AR Started: ' + JSON.stringify(result));
|
|
1137
|
+
} catch (e) {
|
|
1138
|
+
showResult('ar-result', 'Error: ' + e.message);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
async function testPlaceARObject() {
|
|
1143
|
+
if (!window.craft?.ar) return showResult('ar-result', 'AR not available');
|
|
1144
|
+
try {
|
|
1145
|
+
const result = await window.craft.ar.placeObject('box', {x: 0, y: 0, z: -0.5});
|
|
1146
|
+
showResult('ar-result', 'Object placed: ' + JSON.stringify(result));
|
|
1147
|
+
} catch (e) {
|
|
1148
|
+
showResult('ar-result', 'Error: ' + e.message);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
async function testGetARPlanes() {
|
|
1153
|
+
if (!window.craft?.ar) return showResult('ar-result', 'AR not available');
|
|
1154
|
+
try {
|
|
1155
|
+
const planes = await window.craft.ar.getPlanes();
|
|
1156
|
+
showResult('ar-result', 'Detected planes: ' + JSON.stringify(planes, null, 2));
|
|
1157
|
+
} catch (e) {
|
|
1158
|
+
showResult('ar-result', 'Error: ' + e.message);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
async function testStopAR() {
|
|
1163
|
+
if (!window.craft?.ar) return showResult('ar-result', 'AR not available');
|
|
1164
|
+
try {
|
|
1165
|
+
const result = await window.craft.ar.stop();
|
|
1166
|
+
showResult('ar-result', 'AR Stopped: ' + JSON.stringify(result));
|
|
1167
|
+
} catch (e) {
|
|
1168
|
+
showResult('ar-result', 'Error: ' + e.message);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// Listen for AR plane events
|
|
1173
|
+
window.addEventListener('craftARPlane', (e) => {
|
|
1174
|
+
log('AR Plane: ' + e.detail.type + ' - ' + e.detail.id);
|
|
1175
|
+
showResult('ar-result', 'Plane event: ' + JSON.stringify(e.detail));
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
// ML tests
|
|
1179
|
+
async function testCaptureAndClassify() {
|
|
1180
|
+
if (!window.craft) return showResult('ml-result', 'Craft not ready');
|
|
1181
|
+
try {
|
|
1182
|
+
showResult('ml-result', 'Capturing image...');
|
|
1183
|
+
const image = await window.craft.openCamera();
|
|
1184
|
+
if (!image) {
|
|
1185
|
+
showResult('ml-result', 'No image captured');
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
showResult('ml-result', 'Classifying...');
|
|
1189
|
+
const results = await window.craft.ml.classifyImage(image);
|
|
1190
|
+
showResult('ml-result', 'Classification results:\n' + JSON.stringify(results, null, 2));
|
|
1191
|
+
} catch (e) {
|
|
1192
|
+
showResult('ml-result', 'Error: ' + e.message);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
async function testCaptureAndDetect() {
|
|
1197
|
+
if (!window.craft) return showResult('ml-result', 'Craft not ready');
|
|
1198
|
+
try {
|
|
1199
|
+
showResult('ml-result', 'Capturing image...');
|
|
1200
|
+
const image = await window.craft.openCamera();
|
|
1201
|
+
if (!image) {
|
|
1202
|
+
showResult('ml-result', 'No image captured');
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
showResult('ml-result', 'Detecting objects...');
|
|
1206
|
+
const results = await window.craft.ml.detectObjects(image);
|
|
1207
|
+
showResult('ml-result', 'Detection results:\n' + JSON.stringify(results, null, 2));
|
|
1208
|
+
} catch (e) {
|
|
1209
|
+
showResult('ml-result', 'Error: ' + e.message);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
async function testCaptureAndOCR() {
|
|
1214
|
+
if (!window.craft) return showResult('ml-result', 'Craft not ready');
|
|
1215
|
+
try {
|
|
1216
|
+
showResult('ml-result', 'Capturing image...');
|
|
1217
|
+
const image = await window.craft.openCamera();
|
|
1218
|
+
if (!image) {
|
|
1219
|
+
showResult('ml-result', 'No image captured');
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
showResult('ml-result', 'Recognizing text...');
|
|
1223
|
+
const results = await window.craft.ml.recognizeText(image);
|
|
1224
|
+
showResult('ml-result', 'OCR results:\n' + JSON.stringify(results, null, 2));
|
|
1225
|
+
} catch (e) {
|
|
1226
|
+
showResult('ml-result', 'Error: ' + e.message);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// Widget tests
|
|
1231
|
+
async function testUpdateWidget() {
|
|
1232
|
+
if (!window.craft?.widget) return showResult('widget-result', 'Widget API not available');
|
|
1233
|
+
try {
|
|
1234
|
+
const result = await window.craft.widget.update({
|
|
1235
|
+
title: 'Craft Widget',
|
|
1236
|
+
subtitle: 'Updated at ' + new Date().toLocaleTimeString(),
|
|
1237
|
+
value: Math.floor(Math.random() * 100).toString(),
|
|
1238
|
+
icon: 'star.fill'
|
|
1239
|
+
});
|
|
1240
|
+
showResult('widget-result', 'Widget updated: ' + JSON.stringify(result));
|
|
1241
|
+
log('Widget updated');
|
|
1242
|
+
} catch (e) {
|
|
1243
|
+
showResult('widget-result', 'Error: ' + e.message);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
async function testReloadWidgets() {
|
|
1248
|
+
if (!window.craft?.widget) return showResult('widget-result', 'Widget API not available');
|
|
1249
|
+
try {
|
|
1250
|
+
const result = await window.craft.widget.reload();
|
|
1251
|
+
showResult('widget-result', 'Widgets reloaded: ' + JSON.stringify(result));
|
|
1252
|
+
log('Widgets reloaded');
|
|
1253
|
+
} catch (e) {
|
|
1254
|
+
showResult('widget-result', 'Error: ' + e.message);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// Siri / Voice Assistant tests
|
|
1259
|
+
async function testRegisterSiri() {
|
|
1260
|
+
if (!window.craft?.siri) return showResult('siri-result', 'Siri API not available');
|
|
1261
|
+
try {
|
|
1262
|
+
const result = await window.craft.siri.register('Open my app', 'open_app');
|
|
1263
|
+
showResult('siri-result', 'Siri shortcut registered: ' + JSON.stringify(result));
|
|
1264
|
+
log('Siri shortcut registered');
|
|
1265
|
+
} catch (e) {
|
|
1266
|
+
showResult('siri-result', 'Error: ' + e.message);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
async function testRemoveSiri() {
|
|
1271
|
+
if (!window.craft?.siri) return showResult('siri-result', 'Siri API not available');
|
|
1272
|
+
try {
|
|
1273
|
+
const result = await window.craft.siri.remove('open_app');
|
|
1274
|
+
showResult('siri-result', 'Siri shortcut removed: ' + JSON.stringify(result));
|
|
1275
|
+
log('Siri shortcut removed');
|
|
1276
|
+
} catch (e) {
|
|
1277
|
+
showResult('siri-result', 'Error: ' + e.message);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// Listen for Siri invocations
|
|
1282
|
+
if (window.craft?.siri) {
|
|
1283
|
+
window.craft.siri.onInvoke((detail) => {
|
|
1284
|
+
log('Siri shortcut invoked: ' + JSON.stringify(detail));
|
|
1285
|
+
showResult('siri-result', 'Siri invoked: ' + JSON.stringify(detail));
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// Watch Connectivity tests
|
|
1290
|
+
async function testWatchReachable() {
|
|
1291
|
+
if (!window.craft?.watch) return showResult('watch-result', 'Watch API not available');
|
|
1292
|
+
try {
|
|
1293
|
+
const result = await window.craft.watch.isReachable();
|
|
1294
|
+
showResult('watch-result', 'Watch reachable: ' + JSON.stringify(result));
|
|
1295
|
+
log('Watch reachable: ' + JSON.stringify(result));
|
|
1296
|
+
} catch (e) {
|
|
1297
|
+
showResult('watch-result', 'Error: ' + e.message);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
async function testSendToWatch() {
|
|
1302
|
+
if (!window.craft?.watch) return showResult('watch-result', 'Watch API not available');
|
|
1303
|
+
try {
|
|
1304
|
+
const result = await window.craft.watch.send({
|
|
1305
|
+
action: 'ping',
|
|
1306
|
+
timestamp: Date.now()
|
|
1307
|
+
});
|
|
1308
|
+
showResult('watch-result', 'Sent to watch: ' + JSON.stringify(result));
|
|
1309
|
+
log('Sent message to watch');
|
|
1310
|
+
} catch (e) {
|
|
1311
|
+
showResult('watch-result', 'Error: ' + e.message);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
async function testUpdateWatchContext() {
|
|
1316
|
+
if (!window.craft?.watch) return showResult('watch-result', 'Watch API not available');
|
|
1317
|
+
try {
|
|
1318
|
+
const result = await window.craft.watch.updateContext({
|
|
1319
|
+
lastUpdate: Date.now(),
|
|
1320
|
+
status: 'active'
|
|
1321
|
+
});
|
|
1322
|
+
showResult('watch-result', 'Watch context updated: ' + JSON.stringify(result));
|
|
1323
|
+
log('Watch context updated');
|
|
1324
|
+
} catch (e) {
|
|
1325
|
+
showResult('watch-result', 'Error: ' + e.message);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Listen for Watch events
|
|
1330
|
+
if (window.craft?.watch) {
|
|
1331
|
+
window.craft.watch.onMessage((detail) => {
|
|
1332
|
+
log('Watch message received: ' + JSON.stringify(detail));
|
|
1333
|
+
showResult('watch-result', 'Watch message: ' + JSON.stringify(detail));
|
|
1334
|
+
});
|
|
1335
|
+
window.craft.watch.onReachabilityChange((detail) => {
|
|
1336
|
+
log('Watch reachability changed: ' + JSON.stringify(detail));
|
|
1337
|
+
showResult('watch-result', 'Reachability: ' + JSON.stringify(detail));
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// ==================== Deep Links ====================
|
|
1342
|
+
async function testGetInitialURL() {
|
|
1343
|
+
if (!window.craft?.deepLinks) return showResult('deeplink-result', 'Deep Links API not available');
|
|
1344
|
+
try {
|
|
1345
|
+
const result = await window.craft.deepLinks.getInitialURL();
|
|
1346
|
+
if (result) {
|
|
1347
|
+
showResult('deeplink-result', 'Initial URL: ' + JSON.stringify(result, null, 2));
|
|
1348
|
+
log('Initial URL: ' + result.url);
|
|
1349
|
+
} else {
|
|
1350
|
+
showResult('deeplink-result', 'App was not launched via deep link');
|
|
1351
|
+
log('No initial URL');
|
|
1352
|
+
}
|
|
1353
|
+
} catch (e) {
|
|
1354
|
+
showResult('deeplink-result', 'Error: ' + e.message);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
function setupDeepLinkListener() {
|
|
1359
|
+
if (!window.craft?.deepLinks) return showResult('deeplink-result', 'Deep Links API not available');
|
|
1360
|
+
window.craft.deepLinks.onLink((detail) => {
|
|
1361
|
+
log('Deep link received: ' + detail.url);
|
|
1362
|
+
showResult('deeplink-result', 'Deep Link: ' + JSON.stringify(detail, null, 2));
|
|
1363
|
+
});
|
|
1364
|
+
showResult('deeplink-result', 'Listener setup! Open a deep link to test.');
|
|
1365
|
+
log('Deep link listener setup');
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// ==================== Performance Profiling ====================
|
|
1369
|
+
function testStartProfiling() {
|
|
1370
|
+
if (!window.craft) return showResult('profiling-result', 'Craft not ready');
|
|
1371
|
+
window.craft.startProfiling();
|
|
1372
|
+
showResult('profiling-result', 'Profiling started. Make some API calls, then stop profiling.');
|
|
1373
|
+
log('Profiling started');
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
function testStopProfiling() {
|
|
1377
|
+
if (!window.craft) return showResult('profiling-result', 'Craft not ready');
|
|
1378
|
+
const report = window.craft.stopProfiling();
|
|
1379
|
+
showResult('profiling-result', JSON.stringify(report, null, 2));
|
|
1380
|
+
log('Profiling stopped. Total calls: ' + report.totalCalls);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function testGetProfilingData() {
|
|
1384
|
+
if (!window.craft) return showResult('profiling-result', 'Craft not ready');
|
|
1385
|
+
const data = window.craft.getProfilingData();
|
|
1386
|
+
showResult('profiling-result', JSON.stringify(data, null, 2));
|
|
1387
|
+
log('Profiling data retrieved');
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
async function testGetMemoryUsage() {
|
|
1391
|
+
if (!window.craft) return showResult('profiling-result', 'Craft not ready');
|
|
1392
|
+
try {
|
|
1393
|
+
const memory = await window.craft.getMemoryUsage();
|
|
1394
|
+
showResult('profiling-result', 'Memory Usage:\n' + JSON.stringify(memory, null, 2));
|
|
1395
|
+
log('Memory: ' + memory.usedMB + ' MB used');
|
|
1396
|
+
} catch (e) {
|
|
1397
|
+
showResult('profiling-result', 'Error: ' + e.message);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// ==================== OTA Updates ====================
|
|
1402
|
+
function testOTAConfigure() {
|
|
1403
|
+
if (!window.craft?.ota) return showResult('ota-result', 'OTA API not available');
|
|
1404
|
+
try {
|
|
1405
|
+
window.craft.ota.configure({
|
|
1406
|
+
updateUrl: 'https://updates.example.com/api/check',
|
|
1407
|
+
checkOnLaunch: true,
|
|
1408
|
+
checkInterval: 3600000 // 1 hour
|
|
1409
|
+
});
|
|
1410
|
+
showResult('ota-result', 'OTA configured successfully.\nURL: https://updates.example.com/api/check');
|
|
1411
|
+
log('OTA configured');
|
|
1412
|
+
} catch (e) {
|
|
1413
|
+
showResult('ota-result', 'Error: ' + e.message);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
async function testCheckForUpdate() {
|
|
1418
|
+
if (!window.craft?.ota) return showResult('ota-result', 'OTA API not available');
|
|
1419
|
+
try {
|
|
1420
|
+
showResult('ota-result', 'Checking for updates...');
|
|
1421
|
+
const update = await window.craft.ota.checkForUpdate();
|
|
1422
|
+
showResult('ota-result', 'Update check result:\n' + JSON.stringify(update, null, 2));
|
|
1423
|
+
log('Update available: ' + update.available);
|
|
1424
|
+
} catch (e) {
|
|
1425
|
+
showResult('ota-result', 'Error: ' + e.message);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
async function testDownloadUpdate() {
|
|
1430
|
+
if (!window.craft?.ota) return showResult('ota-result', 'OTA API not available');
|
|
1431
|
+
try {
|
|
1432
|
+
showResult('ota-result', 'Downloading update...');
|
|
1433
|
+
const result = await window.craft.ota.downloadUpdate();
|
|
1434
|
+
showResult('ota-result', 'Download result:\n' + JSON.stringify(result, null, 2));
|
|
1435
|
+
log('Download complete: ' + result.success);
|
|
1436
|
+
} catch (e) {
|
|
1437
|
+
showResult('ota-result', 'Error: ' + e.message);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
function testGetCurrentBundle() {
|
|
1442
|
+
if (!window.craft?.ota) return showResult('ota-result', 'OTA API not available');
|
|
1443
|
+
const bundle = window.craft.ota.getCurrentBundle();
|
|
1444
|
+
showResult('ota-result', 'Current bundle:\n' + JSON.stringify(bundle, null, 2));
|
|
1445
|
+
log('Current bundle version: ' + bundle.version);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// OTA event listeners
|
|
1449
|
+
if (window.craft?.ota) {
|
|
1450
|
+
window.craft.ota.onProgress((progress) => {
|
|
1451
|
+
log('OTA Download: ' + progress.percent + '%');
|
|
1452
|
+
showResult('ota-result', 'Downloading: ' + progress.percent + '% (' + progress.bytesDownloaded + '/' + progress.totalBytes + ')');
|
|
1453
|
+
});
|
|
1454
|
+
window.craft.ota.onStatusChange((status) => {
|
|
1455
|
+
log('OTA Status: ' + status);
|
|
1456
|
+
showResult('ota-result', 'Status: ' + status);
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
log('Test page loaded, waiting for Craft...');
|
|
1461
|
+
</script>
|
|
1462
|
+
</body>
|
|
1463
|
+
</html>
|