-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathview.html
More file actions
241 lines (210 loc) · 9.18 KB
/
view.html
File metadata and controls
241 lines (210 loc) · 9.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebAppInterfaceInterfaceInterface Tester</title>
<style>
body { font-family: sans-serif; padding: 20px; }
h1, h2 { margin-top: 20px; }
button { margin: 5px 0; display: block; }
input, textarea { width: 100%; margin: 5px 0; }
video { border: 1px solid #ccc; margin: 5px 0; }
</style>
</head>
<body>
<h1>WebAppInterfaceInterface Tester</h1>
<!-- Toast -->
<button onclick="WebAppInterface.toast('Hello from JS!')">Show Toast</button>
<!-- Quest Completed -->
<button onclick="WebAppInterface.onQuestCompleted()">Mark Quest Completed</button>
<!-- Send Broadcast -->
<input id="broadcastAction" placeholder="Broadcast Action"/>
<textarea id="broadcastData" placeholder='Broadcast JSON data (optional)'></textarea>
<button onclick="sendBroadcast()">Send Broadcast</button>
<!-- Get User Data -->
<button onclick="WebAppInterface.toast(WebAppInterface.getUserData())">Get User Data</button>
<!-- Register / Unregister Broadcast -->
<textarea id="actionsJson" placeholder='Actions JSON array, e.g. ["ACTION_1","ACTION_2"]'></textarea>
<button onclick="WebAppInterface.registerReceiver(document.getElementById('actionsJson').value)">Register Receiver</button>
<button onclick="WebAppInterface.unregisterAll()">Unregister All Receivers</button>
<!-- Quest Completed Check -->
<button onclick="WebAppInterface.toast(WebAppInterface.isQuestCompleted())">Is Quest Completed?</button>
<!-- Fullscreen -->
<button onclick="WebAppInterface.enableFullScreen()">Enable Fullscreen</button>
<button onclick="WebAppInterface.disableFullScreen()">Disable Fullscreen</button>
<!-- App Version -->
<button onclick="WebAppInterface.toast(WebAppInterface.getVersion())">Get App Version</button>
<!-- Alarm Notification -->
<input type="number" id="alarmTime" placeholder="Trigger Time (ms timestamp)">
<input type="text" id="alarmTitle" placeholder="Title">
<input type="text" id="alarmDesc" placeholder="Description">
<button onclick="setAlarm()">Set Alarm</button>
<!-- Open App -->
<input type="text" id="packageName" placeholder="Package Name">
<button onclick="WebAppInterface.openApp(document.getElementById('packageName').value)">Open App</button>
<!-- Share Text -->
<input type="text" id="shareTitle" placeholder="Share Title">
<textarea id="shareText" placeholder="Share Text"></textarea>
<button onclick="WebAppInterface.shareText(document.getElementById('shareTitle').value, document.getElementById('shareText').value)">Share Text</button>
<!-- Share Image from File -->
<h2>Image File Test</h2>
<input type="file" id="imageInput" accept="image/*">
<button onclick="encodeAndShareImage()">Share Selected Image</button>
<!-- Live Camera -->
<h2>Live Camera Test</h2>
<p id="cameraHint" style="color:#666;">Note: getUserMedia requires a secure context (HTTPS) or localhost. If you're opening this file directly (file://) the camera won't work. Serve via a local server (e.g. <code>localhost</code>).</p>
<video id="cameraPreview" autoplay playsinline muted width="300" height="400"></video>
<div>
<button id="startCameraBtn">Start Camera</button>
<button id="stopCameraBtn" disabled>Stop Camera</button>
<button id="takePhotoBtn" disabled>Take Photo & Share</button>
</div>
<!-- Coin Reward -->
<button onclick="WebAppInterface.toast(WebAppInterface.getCoinRewardRatio())">Get Coin Reward Ratio</button>
<!-- Fetch Quest Stats -->
<button onclick="WebAppInterface.getAllStatsForQuest()">Get All Stats for Quest</button>
<!-- Fetch Data Without CORS -->
<input type="text" id="fetchUrl" placeholder="URL to fetch">
<textarea id="fetchHeaders" placeholder='Headers JSON (optional)'></textarea>
<input type="text" id="fetchCallback" placeholder="Callback function name">
<button onclick="fetchWithoutCors()">Fetch Data Without CORS</button>
<script>
// Broadcast
function sendBroadcast() {
const action = document.getElementById('broadcastAction').value;
const data = document.getElementById('broadcastData').value;
WebAppInterface.sendBroadcast(action, data);
}
// Alarm
function setAlarm() {
const trigger = parseInt(document.getElementById('alarmTime').value) || Date.now();
const title = document.getElementById('alarmTitle').value;
const desc = document.getElementById('alarmDesc').value;
WebAppInterface.setAlarmedNotification(trigger, title, desc);
}
// Fetch Data Without CORS
function fetchWithoutCors() {
const url = document.getElementById('fetchUrl').value;
const headers = document.getElementById('fetchHeaders').value;
const callback = document.getElementById('fetchCallback').value;
WebAppInterface.fetchDataWithoutCorsAsync(url, headers, callback);
}
// Image File -> Base64 -> Share
function encodeAndShareImage() {
const fileInput = document.getElementById('imageInput');
const file = fileInput.files[0];
if (!file) {
WebAppInterface.toast("No image selected!");
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const base64Data = e.target.result; // includes data:image/...;base64,
WebAppInterface.shareImage(base64Data);
};
reader.readAsDataURL(file);
}
// Live Camera
let stream;
const startBtn = document.getElementById('startCameraBtn');
const stopBtn = document.getElementById('stopCameraBtn');
const takeBtn = document.getElementById('takePhotoBtn');
const video = document.getElementById('cameraPreview');
async function startCamera() {
try {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
const msg = 'Browser does not support getUserMedia API.';
console.error(msg);
WebAppInterface.toast(msg);
return;
}
stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: false });
video.srcObject = stream;
// Enable buttons once metadata is loaded (width/height available)
video.onloadedmetadata = () => {
takeBtn.disabled = false;
stopBtn.disabled = false;
startBtn.disabled = true;
console.log('Camera started, resolution:', video.videoWidth, 'x', video.videoHeight);
WebAppInterface.toast('Camera started');
};
} catch (err) {
console.error('Error accessing camera:', err);
let msg = 'Error accessing camera: ' + (err && err.message ? err.message : String(err));
// Provide more actionable hints for common errors
if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
msg += ' — Permission denied. Check browser camera permissions.';
} else if (err.name === 'NotFoundError' || err.name === 'OverconstrainedError') {
msg += ' — No suitable camera found.';
}
WebAppInterface.toast(msg);
}
}
function stopCamera() {
if (stream) {
stream.getTracks().forEach(t => t.stop());
stream = null;
video.srcObject = null;
takeBtn.disabled = true;
stopBtn.disabled = true;
startBtn.disabled = false;
WebAppInterface.toast('Camera stopped');
}
}
function takePhoto() {
if (!stream) {
WebAppInterface.toast('Camera not started yet!');
return;
}
// Ensure the video has dimensions
const width = video.videoWidth || 300;
const height = video.videoHeight || 400;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const base64Image = canvas.toDataURL('image/png');
WebAppInterface.shareImage(base64Image);
}
// Wire buttons
startBtn.addEventListener('click', startCamera);
stopBtn.addEventListener('click', stopCamera);
takeBtn.addEventListener('click', takePhoto);
// Example callbacks
function handleFetch(result) {
WebAppInterface.toast("Fetch result: " + result);
}
function onStatsReceived(stats) {
WebAppInterface.toast("Quest stats: " + stats);
}
// This is the function that automatically receives the quest_json data injected from the setup page when questphone loads this page
function injectData(data) {
try {
let parsed;
if (typeof data === "object") {
parsed = data;
} else if (typeof data === "string") {
parsed = JSON.parse(data);
if (typeof parsed === "string") {
parsed = JSON.parse(parsed);
WebAppInterface.toast("Parsed nested JSON string");
}
} else {
throw new Error("Invalid data format");
}
}
catch (e) {
WebAppInterface.toast("Error parsing injected data: " + e.message);
return;
}
}
// This is a function that automatically applies the theme based on user preference in questphone
function applyTheme(theme) {
// the theme data is in this format https://github.com/QuestPhone/questphone/blob/0e965be267e60f870aff11a86eeaff9de9966171/app/src/main/java/neth/iecal/questphone/app/screens/quest/view/external_integration/webview/ExtIntWebview.kt#L107
// example usage: https://github.com/nethical6/qp_pomodoro/blob/main/index.html
}
</script>
</body>
</html>