@oniroproject/capacitor-openharmony 0.1.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.
Files changed (48) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +4 -0
  3. package/assets/native-template/AppScope/app.json5 +10 -0
  4. package/assets/native-template/AppScope/resources/base/element/string.json +8 -0
  5. package/assets/native-template/AppScope/resources/base/media/background.png +0 -0
  6. package/assets/native-template/AppScope/resources/base/media/foreground.png +0 -0
  7. package/assets/native-template/AppScope/resources/base/media/layered_image.json +7 -0
  8. package/assets/native-template/build-profile.json5 +36 -0
  9. package/assets/native-template/entry/build-profile.json5 +33 -0
  10. package/assets/native-template/entry/hvigorfile.ts +6 -0
  11. package/assets/native-template/entry/obfuscation-rules.txt +23 -0
  12. package/assets/native-template/entry/oh-package.json5 +9 -0
  13. package/assets/native-template/entry/src/main/ets/capacitor/BridgeInterfaces.ets +49 -0
  14. package/assets/native-template/entry/src/main/ets/capacitor/CapacitorBridge.ets +355 -0
  15. package/assets/native-template/entry/src/main/ets/capacitor/CapacitorPlugin.ets +42 -0
  16. package/assets/native-template/entry/src/main/ets/capacitor/PluginCall.ets +60 -0
  17. package/assets/native-template/entry/src/main/ets/capacitor/PluginRegistry.ets +35 -0
  18. package/assets/native-template/entry/src/main/ets/entryability/EntryAbility.ets +66 -0
  19. package/assets/native-template/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +16 -0
  20. package/assets/native-template/entry/src/main/ets/pages/Index.ets +86 -0
  21. package/assets/native-template/entry/src/main/module.json5 +58 -0
  22. package/assets/native-template/entry/src/main/resources/base/element/color.json +8 -0
  23. package/assets/native-template/entry/src/main/resources/base/element/float.json +8 -0
  24. package/assets/native-template/entry/src/main/resources/base/element/string.json +16 -0
  25. package/assets/native-template/entry/src/main/resources/base/media/background.png +0 -0
  26. package/assets/native-template/entry/src/main/resources/base/media/foreground.png +0 -0
  27. package/assets/native-template/entry/src/main/resources/base/media/layered_image.json +7 -0
  28. package/assets/native-template/entry/src/main/resources/base/media/startIcon.png +0 -0
  29. package/assets/native-template/entry/src/main/resources/base/profile/backup_config.json +3 -0
  30. package/assets/native-template/entry/src/main/resources/base/profile/main_pages.json +5 -0
  31. package/assets/native-template/entry/src/main/resources/dark/element/color.json +8 -0
  32. package/assets/native-template/entry/src/main/resources/rawfile/www/assets/index-DyaJ3fSD.css +1 -0
  33. package/assets/native-template/entry/src/main/resources/rawfile/www/assets/index-f0ambvls.js +40 -0
  34. package/assets/native-template/entry/src/main/resources/rawfile/www/index.html +13 -0
  35. package/assets/native-template/entry/src/mock/mock-config.json5 +1 -0
  36. package/assets/native-template/entry/src/ohosTest/ets/test/Ability.test.ets +35 -0
  37. package/assets/native-template/entry/src/ohosTest/ets/test/List.test.ets +5 -0
  38. package/assets/native-template/entry/src/ohosTest/module.json5 +11 -0
  39. package/assets/native-template/entry/src/test/List.test.ets +5 -0
  40. package/assets/native-template/entry/src/test/LocalUnit.test.ets +33 -0
  41. package/assets/native-template/hvigor/hvigor-config.json5 +8 -0
  42. package/assets/native-template/hvigorfile.ts +6 -0
  43. package/assets/native-template/oh-package.json5 +9 -0
  44. package/dist/add.js +27 -0
  45. package/dist/common.js +58 -0
  46. package/dist/index.js +20 -0
  47. package/dist/sync.js +191 -0
  48. package/package.json +51 -0
@@ -0,0 +1,355 @@
1
+ import { webview } from "@kit.ArkWeb";
2
+
3
+ import { common } from "@kit.AbilityKit";
4
+ import { BusinessError } from "@kit.BasicServicesKit";
5
+ import { PluginCall } from "./PluginCall";
6
+ import { CapacitorPlugin } from "./CapacitorPlugin";
7
+ import { PluginRegistry } from "./PluginRegistry";
8
+ import { hilog } from "@kit.PerformanceAnalysisKit";
9
+ import {
10
+ BridgeMessage,
11
+ PluginResult,
12
+ ResponseHeader,
13
+ HarmonyProxy,
14
+ PluginHeader,
15
+ } from "./BridgeInterfaces";
16
+
17
+ const TAG = "CapacitorBridge";
18
+ const DOMAIN = 0x0000;
19
+
20
+ export let bridgeInstance: CapacitorBridge | null = null;
21
+
22
+ export class CapacitorBridge {
23
+ private controller: webview.WebviewController;
24
+ private context: common.UIAbilityContext;
25
+ private registry: PluginRegistry;
26
+ private callInfos: Map<string, BridgeMessage> = new Map();
27
+
28
+ constructor(controller: webview.WebviewController, context: common.UIAbilityContext) {
29
+ this.controller = controller;
30
+ this.context = context;
31
+ this.registry = new PluginRegistry(this, this.context);
32
+ bridgeInstance = this;
33
+ }
34
+
35
+ // The object exposed to the WebView
36
+ getProxyObject(): HarmonyProxy {
37
+ let proxy: HarmonyProxy = {
38
+ postMessage: (jsonMessage: string) => {
39
+ this.handleMessage(jsonMessage);
40
+ },
41
+ };
42
+ return proxy;
43
+ }
44
+
45
+ private handleMessage(jsonStr: string) {
46
+ try {
47
+ hilog.info(DOMAIN, TAG, "Received message: %{public}s", jsonStr);
48
+ let message: BridgeMessage = JSON.parse(jsonStr) as BridgeMessage;
49
+
50
+ let callbackId = message.callbackId;
51
+ let pluginId = message.pluginId;
52
+ let methodName = message.methodName;
53
+ let options = message.options;
54
+
55
+ if (!pluginId || !methodName) {
56
+ hilog.error(DOMAIN, TAG, "Invalid message format");
57
+ return;
58
+ }
59
+
60
+ this.callInfos.set(callbackId, message);
61
+ this.routeToPlugin(pluginId, methodName, options || {}, callbackId);
62
+ } catch (e) {
63
+ hilog.error(DOMAIN, TAG, "JSON Parse Error: %{public}s", JSON.stringify(e));
64
+ }
65
+ }
66
+
67
+ private routeToPlugin(
68
+ pluginId: string,
69
+ methodName: string,
70
+ options: Record<string, Object>,
71
+ callbackId: string,
72
+ ) {
73
+ // Phase 4: Registry-based dispatch
74
+ hilog.info(
75
+ DOMAIN,
76
+ TAG,
77
+ "Routing to plugin: %{public}s method: %{public}s",
78
+ pluginId,
79
+ methodName,
80
+ );
81
+
82
+ // Helper class for error messages to satisfy ArkTS strict mode
83
+ class BridgeError {
84
+ message: string = "";
85
+ }
86
+
87
+ let plugin = this.registry.getPlugin(pluginId);
88
+ if (!plugin) {
89
+ hilog.error(DOMAIN, TAG, "Plugin not found: %{public}s", pluginId);
90
+ let error = new BridgeError();
91
+ error.message = `Plugin ${pluginId} not found`;
92
+ this.sendResult(callbackId, error, false);
93
+ return;
94
+ }
95
+
96
+ // Dynamic method invocation
97
+ // In ArkTS, accessing methods by string index on class instances is restricted.
98
+ // We cast to any to allow indexing, and invoke directly to preserve 'this' (if possible)
99
+ // or use the method from the record.
100
+ // Since 'call' and 'apply' are forbidden, we must invoke properties.
101
+
102
+ // We can cast plugin to a type that allows indexing
103
+ let pluginAny: Record<string, (call: PluginCall) => void> = plugin as Object as Record<
104
+ string,
105
+ (call: PluginCall) => void
106
+ >;
107
+ let func = pluginAny[methodName];
108
+
109
+ if (typeof func === "function") {
110
+ try {
111
+ let call = new PluginCall(this, pluginId, methodName, callbackId, options);
112
+ // Bind context to ensure 'this' is correct inside the plugin
113
+ // In ArkTS, we cannot use .call().
114
+ // We hope the method is bound or we call it as a method of the object
115
+ pluginAny[methodName](call);
116
+ } catch (e) {
117
+ hilog.error(DOMAIN, TAG, "Error executing plugin method: %{public}s", JSON.stringify(e));
118
+ let error = new BridgeError();
119
+ error.message = `Error executing method ${methodName}`;
120
+ this.sendResult(callbackId, error, false);
121
+ }
122
+ } else {
123
+ hilog.error(DOMAIN, TAG, "Method not found: %{public}s", methodName);
124
+ let error = new BridgeError();
125
+ error.message = `Method ${methodName} not found`;
126
+ this.sendResult(callbackId, error, false);
127
+ }
128
+ }
129
+
130
+ sendResult(callbackId: string, data: Object, success: boolean, save: boolean = false) {
131
+ let callInfo = this.callInfos.get(callbackId);
132
+ let resultPayload: PluginResult = {
133
+ pluginId: callInfo?.pluginId ?? "Unknown",
134
+ methodName: callInfo?.methodName ?? "Unknown",
135
+ callbackId: callbackId,
136
+ success: success,
137
+ data: data,
138
+ error: success ? null : data,
139
+ save: save,
140
+ };
141
+
142
+ if (!save) {
143
+ this.callInfos.delete(callbackId);
144
+ }
145
+
146
+ let payloadStr = JSON.stringify(resultPayload);
147
+ let jsCode = `window.Capacitor.fromNative(${payloadStr})`;
148
+
149
+ try {
150
+ this.controller.runJavaScript(jsCode);
151
+ } catch (e) {
152
+ hilog.error(DOMAIN, TAG, "Failed to run JavaScript: %{public}s", JSON.stringify(e));
153
+ }
154
+ }
155
+
156
+ notifyListeners(pluginId: string, eventName: string, data: Object) {
157
+ let jsCode = `window.Capacitor.triggerEvent('${pluginId}', '${eventName}', ${JSON.stringify(data)})`;
158
+ this.controller.runJavaScript(jsCode);
159
+ }
160
+
161
+ handleResourceRequest(event: WebResourceRequest): WebResourceResponse | null {
162
+ if (!event) return null;
163
+
164
+ try {
165
+ let url = event.getRequestUrl();
166
+ hilog.info(DOMAIN, TAG, "Intercepted request: %{public}s", url);
167
+
168
+ if (url.startsWith("http://localhost/")) {
169
+ try {
170
+ let path = decodeURIComponent(url.replace("http://localhost/", ""));
171
+ if (path === "" || path.endsWith("/")) {
172
+ path += "index.html";
173
+ }
174
+
175
+ // Security check
176
+ if (path.includes("..")) {
177
+ let response = new WebResourceResponse();
178
+ response.setResponseCode(403);
179
+ response.setReasonMessage("Forbidden");
180
+ return response;
181
+ }
182
+
183
+ let resourceManager = this.context.resourceManager;
184
+ // 'www' is expected to be in rawfile
185
+ let rawFileContent = resourceManager.getRawFileContentSync(`www/${path}`);
186
+
187
+ let mimeType = this.getMimeType(path);
188
+ hilog.info(
189
+ DOMAIN,
190
+ TAG,
191
+ "Serving asset: %{public}s (%{public}d bytes, %{public}s)",
192
+ path,
193
+ rawFileContent.byteLength,
194
+ mimeType,
195
+ );
196
+
197
+ let response = new WebResourceResponse();
198
+
199
+ // Cast to ArrayBuffer to satisfy ArkTS strict checks
200
+ let bufferData = rawFileContent.buffer.slice(
201
+ 0,
202
+ rawFileContent.buffer.byteLength,
203
+ ) as ArrayBuffer;
204
+
205
+ response.setResponseData(bufferData);
206
+ response.setResponseEncoding("utf-8");
207
+ response.setResponseMimeType(mimeType);
208
+ response.setResponseCode(200);
209
+ response.setReasonMessage("OK");
210
+
211
+ // Allow CORS
212
+ response.setResponseHeader([
213
+ { headerKey: "Access-Control-Allow-Origin", headerValue: "*" },
214
+ ]);
215
+
216
+ return response;
217
+ } catch (error) {
218
+ hilog.error(
219
+ DOMAIN,
220
+ TAG,
221
+ "Failed to load asset or decode path: %{public}s error: %{public}s",
222
+ url,
223
+ JSON.stringify(error),
224
+ );
225
+ let response = new WebResourceResponse();
226
+ response.setResponseCode(404);
227
+ response.setReasonMessage("Not Found");
228
+ return response;
229
+ }
230
+ }
231
+ } catch (error) {
232
+ hilog.error(DOMAIN, TAG, "Error in handleResourceRequest: %{public}s", JSON.stringify(error));
233
+ }
234
+
235
+ return null;
236
+ }
237
+
238
+ private getMimeType(path: string): string {
239
+ const p = path.toLowerCase();
240
+ if (p.endsWith(".html")) return "text/html";
241
+ if (p.endsWith(".js") || p.endsWith(".mjs")) return "application/javascript";
242
+ if (p.endsWith(".css")) return "text/css";
243
+ if (p.endsWith(".png")) return "image/png";
244
+ if (p.endsWith(".jpg") || p.endsWith(".jpeg")) return "image/jpeg";
245
+ if (p.endsWith(".webp")) return "image/webp";
246
+ if (p.endsWith(".gif")) return "image/gif";
247
+ if (p.endsWith(".svg")) return "image/svg+xml";
248
+ if (p.endsWith(".json")) return "application/json";
249
+ if (p.endsWith(".wasm")) return "application/wasm";
250
+ if (p.endsWith(".pdf")) return "application/pdf";
251
+ if (p.endsWith(".woff")) return "font/woff";
252
+ if (p.endsWith(".woff2")) return "font/woff2";
253
+ if (p.endsWith(".ttf")) return "font/ttf";
254
+ if (p.endsWith(".otf")) return "font/otf";
255
+ return "application/octet-stream";
256
+ }
257
+
258
+ injectCapacitorGlobal() {
259
+ hilog.info(DOMAIN, TAG, "Capacitor Global Injection Hook");
260
+ let js = `
261
+ (function() {
262
+ console.log("Starting Capacitor OpenHarmony Bridge Injection");
263
+
264
+ window.Capacitor = window.Capacitor || {};
265
+ window.Capacitor.Plugins = window.Capacitor.Plugins || {};
266
+
267
+ // 1. Define Plugin Headers
268
+ window.Capacitor.PluginHeaders = ${JSON.stringify(this.registry.getPluginHeaders())};
269
+
270
+ // 2. Define Callback Management
271
+ window.Capacitor.callbacks = window.Capacitor.callbacks || {};
272
+
273
+ window.Capacitor.toNative = function(pluginId, methodName, options, storedCallback) {
274
+ // Not used in Android bridge style, but good to have
275
+ };
276
+
277
+ window.Capacitor.fromNative = function(result) {
278
+ console.log("fromNative", JSON.stringify(result));
279
+ if (!result) return;
280
+
281
+ var callback = window.Capacitor.callbacks[result.callbackId];
282
+ if (!callback) return;
283
+
284
+ if (result.success) {
285
+ callback.resolve(result.data);
286
+ } else {
287
+ callback.reject(result.error);
288
+ }
289
+
290
+ if (!result.save) {
291
+ delete window.Capacitor.callbacks[result.callbackId];
292
+ }
293
+ };
294
+
295
+ // 3. Define nativePromise
296
+ window.Capacitor.nativePromise = function(pluginId, methodName, options) {
297
+ return new Promise(function(resolve, reject) {
298
+ var callbackId = String(Date.now()) + '-' + Math.floor(Math.random() * 1000);
299
+ window.Capacitor.callbacks[callbackId] = { resolve: resolve, reject: reject };
300
+
301
+ var message = {
302
+ type: 'message',
303
+ callbackId: callbackId,
304
+ pluginId: pluginId,
305
+ methodName: methodName,
306
+ options: options || {}
307
+ };
308
+
309
+ if (window.androidBridge) {
310
+ window.androidBridge.postMessage(JSON.stringify(message));
311
+ } else {
312
+ console.error("androidBridge not found");
313
+ reject("Bridge not found");
314
+ }
315
+ });
316
+ };
317
+
318
+ // 4. Define nativeCallback (for listeners)
319
+ window.Capacitor.nativeCallback = function(pluginId, methodName, options, callback) {
320
+ // TODO: specific implementation if needed, usually nativePromise covers it for one-off
321
+ // For listeners, we need to handle 'save' in fromNative.
322
+ // This is a simplified version.
323
+ var callbackId = String(Date.now()) + '-' + Math.floor(Math.random() * 1000);
324
+ window.Capacitor.callbacks[callbackId] = { resolve: callback, reject: console.error };
325
+
326
+ var message = {
327
+ type: 'message',
328
+ callbackId: callbackId,
329
+ pluginId: pluginId,
330
+ methodName: methodName,
331
+ options: options || {}
332
+ };
333
+ if (window.androidBridge) {
334
+ window.androidBridge.postMessage(JSON.stringify(message));
335
+ }
336
+ return callbackId;
337
+ };
338
+
339
+ console.log("Capacitor OpenHarmony Bridge Injected");
340
+ })();
341
+ `;
342
+
343
+ try {
344
+ this.controller.runJavaScript(js);
345
+ } catch (e) {
346
+ hilog.error(DOMAIN, TAG, "Failed to inject bridge: %{public}s", JSON.stringify(e));
347
+ }
348
+ }
349
+
350
+ handleBackPressed(): boolean {
351
+ let data: Record<string, Object> = {};
352
+ this.notifyListeners("App", "backButton", data);
353
+ return true;
354
+ }
355
+ }
@@ -0,0 +1,42 @@
1
+ import { common } from "@kit.AbilityKit";
2
+ import { CapacitorBridge } from "./CapacitorBridge";
3
+ import { PluginCall } from "./PluginCall";
4
+
5
+ export abstract class CapacitorPlugin {
6
+ protected bridge: CapacitorBridge;
7
+ protected context: common.UIAbilityContext;
8
+
9
+ constructor(bridge: CapacitorBridge, context: common.UIAbilityContext) {
10
+ this.bridge = bridge;
11
+ this.context = context;
12
+ }
13
+
14
+ load(): void {}
15
+
16
+ notifyListeners(eventName: string, data: Object): void {
17
+ this.bridge.notifyListeners(this.getPluginId(), eventName, data);
18
+ }
19
+
20
+ abstract getPluginId(): string;
21
+
22
+ getMethods(): string[] {
23
+ return [];
24
+ }
25
+
26
+ addListener(call: PluginCall): void {
27
+ // Default implementation: just resolve.
28
+ // The bridge handles the JS side event registration ?
29
+ // Actually, in Capacitor, the native plugin 'addListener' is called to let the native side know
30
+ // that someone is listening, so it can start emitting events if it was lazy.
31
+ // For standard events, we might not need to do anything.
32
+ call.resolve();
33
+ }
34
+
35
+ removeListener(call: PluginCall): void {
36
+ call.resolve();
37
+ }
38
+
39
+ removeAllListeners(call: PluginCall): void {
40
+ call.resolve();
41
+ }
42
+ }
@@ -0,0 +1,60 @@
1
+ import { CapacitorBridge } from './CapacitorBridge';
2
+ import { PluginErrorData } from './BridgeInterfaces';
3
+
4
+ export class PluginCall {
5
+ pluginId: string;
6
+ methodName: string;
7
+ callbackId: string;
8
+ options: Record<string, Object>;
9
+ bridge: CapacitorBridge;
10
+
11
+ constructor(bridge: CapacitorBridge, pluginId: string, methodName: string, callbackId: string, options: Record<string, Object>) {
12
+ this.bridge = bridge;
13
+ this.pluginId = pluginId;
14
+ this.methodName = methodName;
15
+ this.callbackId = callbackId;
16
+ this.options = options;
17
+ }
18
+
19
+ getString(key: string): string {
20
+ let value = this.options[key];
21
+ if (typeof value === 'string') {
22
+ return value;
23
+ }
24
+ return "";
25
+ }
26
+
27
+ getObject(key: string): Object | null {
28
+ let value = this.options[key];
29
+ if (typeof value === 'object' && value !== null) {
30
+ return value;
31
+ }
32
+ return null;
33
+ }
34
+
35
+ getBoolean(key: string): boolean {
36
+ return this.options[key] === true;
37
+ }
38
+
39
+ getNumber(key: string): number {
40
+ let value = this.options[key];
41
+ if (typeof value === 'number') {
42
+ return value;
43
+ }
44
+ return 0;
45
+ }
46
+
47
+ resolve(data?: Object) {
48
+ this.bridge.sendResult(this.callbackId, data ?? ({} as Record<string, Object>), true);
49
+ }
50
+
51
+ reject(message: string, code?: string, error?: Object) {
52
+ let errData: PluginErrorData = {
53
+ message: message
54
+ };
55
+ if (code) errData.code = code;
56
+ if (error) errData.error = error;
57
+
58
+ this.bridge.sendResult(this.callbackId, errData, false);
59
+ }
60
+ }
@@ -0,0 +1,35 @@
1
+ import { CapacitorPlugin } from "./CapacitorPlugin";
2
+ import { CapacitorBridge } from "./CapacitorBridge";
3
+ import { PluginHeader, PluginMethod } from "./BridgeInterfaces";
4
+ import { common } from "@kit.AbilityKit";
5
+
6
+ export class PluginRegistry {
7
+ private plugins: Map<string, CapacitorPlugin> = new Map();
8
+ private bridge: CapacitorBridge;
9
+ private context: common.UIAbilityContext;
10
+
11
+ constructor(bridge: CapacitorBridge, context: common.UIAbilityContext) {
12
+ this.bridge = bridge;
13
+ this.context = context;
14
+ this.registerPlugins();
15
+ }
16
+
17
+ private registerPlugins() {}
18
+
19
+ getPlugin(pluginId: string): CapacitorPlugin | undefined {
20
+ return this.plugins.get(pluginId);
21
+ }
22
+
23
+ getPluginHeaders(): PluginHeader[] {
24
+ let headers: PluginHeader[] = [];
25
+ this.plugins.forEach((plugin) => {
26
+ headers.push({
27
+ name: plugin.getPluginId(),
28
+ methods: plugin.getMethods().map((m: string): PluginMethod => {
29
+ return { name: m, rtype: "promise" };
30
+ }),
31
+ });
32
+ });
33
+ return headers;
34
+ }
35
+ }
@@ -0,0 +1,66 @@
1
+ import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
2
+ import { hilog } from '@kit.PerformanceAnalysisKit';
3
+ import { window } from '@kit.ArkUI';
4
+ import { bridgeInstance } from '../capacitor/CapacitorBridge';
5
+ import { AppState, AppUrl } from '../capacitor/BridgeInterfaces';
6
+
7
+ const DOMAIN = 0x0000;
8
+
9
+ export default class EntryAbility extends UIAbility {
10
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
11
+ try {
12
+ this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
13
+ } catch (err) {
14
+ hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
15
+ }
16
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
17
+ }
18
+
19
+ onDestroy(): void {
20
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
21
+ }
22
+
23
+ onWindowStageCreate(windowStage: window.WindowStage): void {
24
+ // Main window is created, set main page for this ability
25
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
26
+
27
+ windowStage.loadContent('pages/Index', (err) => {
28
+ if (err.code) {
29
+ hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
30
+ return;
31
+ }
32
+ hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
33
+ });
34
+ }
35
+
36
+ onWindowStageDestroy(): void {
37
+ // Main window is destroyed, release UI related resources
38
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
39
+ }
40
+
41
+ onForeground(): void {
42
+ // Ability has brought to foreground
43
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
44
+ if (bridgeInstance) {
45
+ let appState: AppState = { isActive: true };
46
+ bridgeInstance.notifyListeners('App', 'appStateChange', appState);
47
+ }
48
+ }
49
+
50
+ onBackground(): void {
51
+ // Ability has back to background
52
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
53
+ if (bridgeInstance) {
54
+ let appState: AppState = { isActive: false };
55
+ bridgeInstance.notifyListeners('App', 'appStateChange', appState);
56
+ }
57
+ }
58
+
59
+ onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
60
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onNewWant');
61
+ if (bridgeInstance) {
62
+ let appUrl: AppUrl = { url: want.uri || '' };
63
+ bridgeInstance.notifyListeners('App', 'appUrlOpen', appUrl);
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,16 @@
1
+ import { hilog } from '@kit.PerformanceAnalysisKit';
2
+ import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';
3
+
4
+ const DOMAIN = 0x0000;
5
+
6
+ export default class EntryBackupAbility extends BackupExtensionAbility {
7
+ async onBackup() {
8
+ hilog.info(DOMAIN, 'testTag', 'onBackup ok');
9
+ await Promise.resolve();
10
+ }
11
+
12
+ async onRestore(bundleVersion: BundleVersion) {
13
+ hilog.info(DOMAIN, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion));
14
+ await Promise.resolve();
15
+ }
16
+ }
@@ -0,0 +1,86 @@
1
+ import { webview } from "@kit.ArkWeb";
2
+ import { common } from "@kit.AbilityKit";
3
+ import { hilog } from "@kit.PerformanceAnalysisKit";
4
+ import { CapacitorBridge } from "../capacitor/CapacitorBridge";
5
+
6
+ class EmptyProxy {
7
+ postMessage(data: string) {}
8
+ }
9
+
10
+ @Entry
11
+ @Component
12
+ struct Index {
13
+ controller: webview.WebviewController = new webview.WebviewController();
14
+ bridge: CapacitorBridge | null = null;
15
+ context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
16
+
17
+ aboutToAppear(): void {
18
+ // Initialize bridge
19
+ this.bridge = new CapacitorBridge(this.controller, this.context);
20
+ }
21
+
22
+ build() {
23
+ Column() {
24
+ Web({
25
+ src: "http://localhost/index.html",
26
+ controller: this.controller,
27
+ })
28
+ .javaScriptAccess(true)
29
+ .domStorageAccess(true)
30
+ .fileAccess(true)
31
+ .onInterceptRequest((event) => {
32
+ if (this.bridge && event) {
33
+ let response = this.bridge.handleResourceRequest(event.request);
34
+ if (response) {
35
+ return response;
36
+ }
37
+ }
38
+ return null;
39
+ })
40
+ .javaScriptProxy({
41
+ object: this.bridge ? this.bridge.getProxyObject() : new EmptyProxy(),
42
+ name: "androidBridge",
43
+ methodList: ["postMessage"],
44
+ controller: this.controller,
45
+ })
46
+ .onConsole((event) => {
47
+ hilog.info(0x0000, "WebViewConsole", "Console event triggered");
48
+ if (event) {
49
+ hilog.info(0x0000, "WebViewConsole", "%{public}s", event.message.getMessage());
50
+ return false;
51
+ }
52
+ return false;
53
+ })
54
+ .onPageBegin(() => {
55
+ this.bridge?.injectCapacitorGlobal();
56
+ })
57
+ .onPageEnd(() => {
58
+ hilog.info(0x0000, "WebViewStatus", "Page load finished");
59
+ })
60
+ .onErrorReceive((event) => {
61
+ if (event) {
62
+ hilog.error(
63
+ 0x0000,
64
+ "WebViewError",
65
+ "Error: %{public}s, %{public}s",
66
+ event.request.getRequestUrl(),
67
+ event.error.getErrorInfo(),
68
+ );
69
+ }
70
+ })
71
+ .onHttpErrorReceive((event) => {
72
+ if (event) {
73
+ hilog.error(
74
+ 0x0000,
75
+ "WebViewHttpError",
76
+ "HttpError: %{public}s, %{public}d",
77
+ event.request.getRequestUrl(),
78
+ event.response.getResponseCode(),
79
+ );
80
+ }
81
+ })
82
+ .width("100%")
83
+ .height("100%")
84
+ };
85
+ }
86
+ }