diff --git a/LICENSE b/LICENSE index 0210352ae2ade0dd7b4c841cb6e8ba08b4780038..338e5b0bc22082e0ffcc7121c2ed3897a3ddccb0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved. + Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index b21041a7aa49933dc7e3a5b443ffe861e35a2d26..d2e9558e0ad6a6dc5ea6dfe3834833b5ec810a9a 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,47 @@ # 实现ArkTS与H5的交互功能 -### 简介 +## 简介 -本篇Codelab主要介绍H5如何调用ArkTS侧相关功能并且获取ArkTS侧的回调数据。以“获取通讯录”为示例分步去讲解JSBridge桥接的实现。 +本示例主要介绍ArkTS侧与H5侧如何交互,通过JSBridge的相关接口实现了话费充值的场景案例,展示了ArkTS侧如何调用H5侧的方法、H5侧如何调用ArkTS侧的方法,帮助开发者掌握ArkTS侧与H5侧的交互。 +## 效果预览 ![SelectContact](screenshots/device/SelectContact.gif) -### 相关概念 +## 使用说明 -- Web组件:提供具有网页显示能力的Web组件。 -- @ohos.web.webview:提供web控制能力。 +1. 在应用首页,点击按钮进入话费充值页面。 +2. 点击右侧联系人图标拉起系统通讯录页面,选中联系人后异步返回联系人信息。 +3. 选择金额点击底部充值按钮会出现toast提示。 -### 相关权限 +## 工程目录 -无 +``` +├──entry/src/main/ets // 代码区 +│ ├──entryability +│ │ └──EntryAbility.ets +│ ├──entrybackupability +│ │ └──EntryBackupAbility.ets +│ └──pages +│ ├──Index.ets // 主页面 +│ └──SelectContact.ets // 话费充值页面 +└──entry/src/main/resources // 应用资源目录 +``` + +## 具体实现 -### 使用说明 +1. 在ArkTS侧实现拉起联系人的方法chooseContact,调用系统提供的contact接口,拉起通讯录,并返回选择的联系人信息。 +2. 调用javaScriptProxy或registerJavaScriptProxy注册JS对象和方法chooseContact。 +3. 在H5侧调用chooseContact方法。 +4. 在H5侧实现话费充值的方法recharge。 +5. 在ArkTS侧使用runJavaScript调用H5的recharge方法。 -1. 进入应用首页,点击右侧联系人图标拉起系统通讯录页面,选中联系人后异步返回联系人信息。 -2. 选择金额点击底部充值按钮会出现toast提示。 +## 相关权限 + +无 -### 约束与限制 +## 约束与限制 1. 本示例仅支持标准系统上运行,支持设备:华为手机。 -2. HarmonyOS系统:HarmonyOS NEXT Developer Beta1及以上。 -3. DevEco Studio版本:DevEco Studio NEXT Developer Beta1及以上。 -4. HarmonyOS SDK版本:HarmonyOS NEXT Developer Beta1 SDK及以上。 +2. HarmonyOS系统:HarmonyOS 5.0.0 Release及以上。 +3. DevEco Studio版本:DevEco Studio 5.0.0 Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS 5.0.0 Release SDK及以上。 diff --git a/entry/src/main/ets/common/constant/CodeConstant.ets b/entry/src/main/ets/common/constant/CodeConstant.ets deleted file mode 100644 index 917851ceb2ba2fc79325855db27a9ac6f8384a82..0000000000000000000000000000000000000000 --- a/entry/src/main/ets/common/constant/CodeConstant.ets +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * define the runJavaScript code. - */ -export const code = ` - const JSBridgeMap = {}; - let callID = 0; - - function JSBridgeCallback (id, params) { - JSBridgeMap[id](params); - JSBridgeMap[id] = null; - delete JSBridgeMap[id]; - } - - window.ohosCallNative = { - callNative(method, params, callback) { - const id = callID++; - const paramsObj = { - callID: id, - data: params || null - } - JSBridgeMap[id] = callback || (() => {}); - JSBridgeHandle.call(method, JSON.stringify(paramsObj)); - } - } -`; \ No newline at end of file diff --git a/entry/src/main/ets/common/constant/CommonConstant.ets b/entry/src/main/ets/common/constant/CommonConstant.ets deleted file mode 100644 index ec68d513588f23d807abb3d0c92680ee2326ada9..0000000000000000000000000000000000000000 --- a/entry/src/main/ets/common/constant/CommonConstant.ets +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Constants for common text and style. - */ -export class CommonConstants { - /** - * The full size of container. - */ - static readonly FULL_SIZE: string = '100%'; - - /** - * Content width. - */ - static readonly CONTENT_WIDTH: string = '87%'; - - /** - * Content height. - */ - static readonly CONTENT_HEIGHT: string = '90%'; - - /** - * Common value. - */ - static readonly COMMON_VALUE: number = 480; -} \ No newline at end of file diff --git a/entry/src/main/ets/common/utils/JsBridge.ets b/entry/src/main/ets/common/utils/JsBridge.ets deleted file mode 100644 index 6c3887ca6fbb6a12b714a22b0909e3e8e73409c7..0000000000000000000000000000000000000000 --- a/entry/src/main/ets/common/utils/JsBridge.ets +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { webview } from '@kit.ArkWeb'; -import { contact } from '@kit.ContactsKit'; -import { code } from '../constant/CodeConstant'; -import { ParamsItem as ParamsItem } from '../../viewmodel/ParamsItem'; -import { JavaScriptItem } from '../../viewmodel/JavaScriptItem'; -import Logger from './Logger'; - -/** - * Define bridge class connect WebView and ArkTS. - */ -export default class JsBridge { - controller: webview.WebviewController; - - constructor(controller: webview.WebviewController) { - this.controller = controller; - } - - /** - * Injects the JavaScript object into window and invoke the function in window. - * - * @returns javaScriptProxy object. - */ - get javaScriptProxy(): JavaScriptItem { - let result: JavaScriptItem = { - object: { - call: this.call - }, - name: 'JSBridgeHandle', - methodList: ['call'], - controller: this.controller - } - return result; - } - - /** - * initialize the bridge. - */ - initJsBridge(): void { - this.controller.runJavaScript(code); - } - - /** - * Use system ability of getting contacts. - */ - chooseContact = (): Promise => { - let phone = ''; - let name = ''; - return new Promise((resolve) => { - let promise = contact.selectContacts(); - promise.then((info: Array) => { - info.forEach((item: contact.Contact) => { - phone = item?.phoneNumbers ? item?.phoneNumbers[0].phoneNumber : ''; - name = item?.name ? item?.name?.fullName : ''; - }) - resolve(phone + '_' + name); - }).catch((err: object | string) => { - Logger.error(`selectContact fail: err->${JSON.stringify(err)}`); - }); - }) - } - - /** - * Change tel function. - */ - changeTel = (params: ParamsItem): Promise => { - Logger.info('手机号', JSON.stringify(params)); - const tel: string = params.data.tel ?? ''; - AppStorage.set('tel', tel); - return new Promise((resolve) => { - resolve('success'); - }) - } - - /** - * Change amount function. - */ - changeAmount = (): Promise => { - AppStorage.set('isClick', true); - return new Promise((resolve) => { - resolve('success'); - }) - } - - /** - * Compatible with different dpi. - */ - getProportion = (): Promise => { - return new Promise((resolve) => { - resolve(String(AppStorage.get('proportion'))); - }) - } - - /** - * Invoke the chooseContact function. - */ - call = (func: string, params: string): void => { - const paramsObject: ParamsItem = JSON.parse(params); - let result: Promise = new Promise((resolve) => resolve('')); - switch (func) { - case 'chooseContact': - result = this.chooseContact(); - break; - case 'changeTel': - result = this.changeTel(paramsObject); - break; - case 'changeAmount': - result = this.changeAmount(); - break; - case 'getProportion': - result = this.getProportion(); - break; - default: - break; - } - result.then((data: string) => { - this.callback(paramsObject?.callID, data); - }) - } - - /** - * The ArkTS invoke the WebView by using runJavaScript. - */ - callback = (id: number, data: string): void => { - this.controller.runJavaScript(`JSBridgeCallback('${id}', ${JSON.stringify(data)})`); - } -} \ No newline at end of file diff --git a/entry/src/main/ets/common/utils/Logger.ets b/entry/src/main/ets/common/utils/Logger.ets deleted file mode 100644 index b88b3890094daf4f97d9d408d313f6acb02855a3..0000000000000000000000000000000000000000 --- a/entry/src/main/ets/common/utils/Logger.ets +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { hilog } from '@kit.PerformanceAnalysisKit'; - -/** - * Log printing tool class. - */ -class Logger { - private domain: number; - private prefix: string; - private format: string = '%{public}s, %{public}s'; - - /** - * Constructor. - * - * @param Prefix Identifies the log tag. - * @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF. - */ - constructor(prefix: string = 'MyApp', domain: number = 0xFF00) { - this.prefix = prefix; - this.domain = domain; - } - - debug(...args: string[]): void { - hilog.debug(this.domain, this.prefix, this.format, args); - } - - info(...args: string[]): void { - hilog.info(this.domain, this.prefix, this.format, args); - } - - warn(...args: string[]): void { - hilog.warn(this.domain, this.prefix, this.format, args); - } - - error(...args: string[]): void { - hilog.error(this.domain, this.prefix, this.format, args); - } -} - -export default new Logger('[JsBridge]', 0xFF00) \ No newline at end of file diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index 4dcab5f8795b6c63475c74d34615f679a7015317..5445a8161ce2d3ff8f56569191677570a3bd3fe2 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -1,65 +1,42 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - +import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; -import { UIAbility } from '@kit.AbilityKit'; -import Logger from '../common/utils/Logger'; export default class EntryAbility extends UIAbility { - onCreate() { - Logger.info('EntryAbility', 'Ability onCreate'); + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); } - onDestroy() { - Logger.info('EntryAbility', 'Ability onDestroy'); + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); } - onWindowStageCreate(windowStage: window.WindowStage) { + onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability - Logger.info('EntryAbility', 'Ability onWindowStageCreate'); - windowStage.getMainWindow((_err, windowClass) => { - windowClass.setWindowLayoutFullScreen(false, (err) => { - if (err.code) { - Logger.error('EntryAbility', 'Failed to set the window layout to no-full-screen mode. Cause:' + JSON.stringify(err)); - return; - } - Logger.info('EntryAbility', 'Succeeded in setting the window layout to no-full-screen mode.') - }); - }) + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); - windowStage.loadContent('pages/SelectContact', (err, data) => { + windowStage.loadContent('pages/Index', (err) => { if (err.code) { - Logger.error('EntryAbility', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } - Logger.info('EntryAbility', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); }); } - onWindowStageDestroy() { + onWindowStageDestroy(): void { // Main window is destroyed, release UI related resources - Logger.info('EntryAbility', 'Ability onWindowStageDestroy'); + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); } - onForeground() { + onForeground(): void { // Ability has brought to foreground - Logger.info('EntryAbility', 'Ability onForeground'); + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); } - onBackground() { + onBackground(): void { // Ability has back to background - Logger.info('EntryAbility', ' onBackground'); + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); } } \ No newline at end of file diff --git a/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..dc55c03d3eea7ce53d5346c732a39ce9bf5267e1 --- /dev/null +++ b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,12 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..b53ba902a23dbee042b65d6168c9bade5d10b167 --- /dev/null +++ b/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,72 @@ +/* +* Copyright (C) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { SelectContact } from './SelectContact'; + +@Entry +@Component +struct Index { + @Provide('NavPathStack') pageStack: NavPathStack = new NavPathStack(); + @State pageTitle: string = getContext(this).resourceManager.getStringSync($r('app.string.mobile_phone_recharge')); + + @Builder + PagesMap(name: string) { + if (name === 'SelectContact') { + NavDestination() { + SelectContact(); + } + .title(this.pageTitle) + .backgroundColor('#F1F3F5') + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + } + } + + build() { + Navigation(this.pageStack) { + Column() { + Text($r('app.string.select_contact')) + .width('100%') + .fontColor('#E6000000') + .fontSize(30) + .fontWeight(700) + .lineHeight(40) + .textAlign(TextAlign.Start) + .margin({ top: 64 }) + + Blank() + + Button($r('app.string.mobile_phone_recharge')) + .width('100%') + .margin({ top: 12 }) + .onClick(() => { + this.pageStack.pushPathByName('SelectContact', ''); + }) + } + .padding({ + left: 16, + right: 16, + bottom: 16 + }) + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Start) + } + .hideTitleBar(true) + .mode(NavigationMode.Stack) + .backgroundColor('#F1F3F5') + .navDestination(this.PagesMap) + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + } +} diff --git a/entry/src/main/ets/pages/SelectContact.ets b/entry/src/main/ets/pages/SelectContact.ets index 6e747228ca0f6f5991604a1e9bace7d683e1b113..a7068a313898cf1809ee317912963efa45d0073b 100644 --- a/entry/src/main/ets/pages/SelectContact.ets +++ b/entry/src/main/ets/pages/SelectContact.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Huawei Device Co., Ltd. + * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -14,75 +14,108 @@ */ import { webview } from '@kit.ArkWeb'; -import { display } from '@kit.ArkUI'; import { promptAction } from '@kit.ArkUI'; -import JSBridge from '../common/utils/JsBridge'; -import { CommonConstants } from '../common/constant/CommonConstant'; -import Logger from '../common/utils/Logger'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { contact } from '@kit.ContactsKit'; +import { deviceInfo } from '@kit.BasicServicesKit'; +import { i18n } from '@kit.LocalizationKit'; -@Entry @Component -struct SelectContact { - @StorageLink('isClick') isClick: boolean = false; - @StorageLink('tel') phoneNumber: string = ''; - @StorageLink('proportion') proportion: number = 0; - @State chargeTip: Resource = $r('app.string.recharge_button'); - webController: webview.WebviewController = new webview.WebviewController(); - private jsBridge: JSBridge = new JSBridge(this.webController); +export struct SelectContact { + webviewController: webview.WebviewController = new webview.WebviewController(); aboutToAppear() { - display.getAllDisplays((err, displayClass: display.Display[]) => { - if (err.code) { - Logger.error('SelectContact Page', 'Failed to obtain all the display objects. Code: ' + JSON.stringify(err)); - return; - } - this.proportion = displayClass[0].densityDPI / CommonConstants.COMMON_VALUE; - Logger.info('Succeeded in obtaining all the display objects. Data: ' + JSON.stringify(displayClass)); - }); + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] aboutToAppear'); + } + + aboutToDisappear() { + this.webviewController.deleteJavaScriptRegister('jsbridgeHandle'); + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] aboutToDisappear'); } build() { Column() { - Row() { - Text($r('app.string.navigation_title')) - .fontSize($r('app.float.font_size_large')) - .fontWeight(FontWeight.Medium) - } - .height($r('app.float.navigation_height')) - .width(CommonConstants.FULL_SIZE) - .alignItems(VerticalAlign.Center) - .padding({ left: $r('app.float.padding_left_normal') }) - Web({ - src: $rawfile('MainPage.html'), - controller: this.webController + src: $rawfile(/zh/.test(i18n.System.getSystemLanguage()) ? 'index.html' : 'index_en.html'), + controller: this.webviewController }) .javaScriptAccess(true) - .javaScriptProxy(this.jsBridge.javaScriptProxy) + .javaScriptProxy({ + object: { + call: this.chooseContact + }, + name: 'jsbridgeHandle', + methodList: ['call'], + controller: this.webviewController + }) .height($r('app.float.web_height')) + .onAppear(() => { + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] onAppear'); + }) + .onDisAppear(() => { + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] onDisAppear'); + }) + .onControllerAttached(() => { + this.webviewController.registerJavaScriptProxy({ call: this.chooseContact }, 'jsbridgeHandle', ['call']); + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] onControllerAttached'); + }) + .onLoadIntercept(() => { + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] onLoadIntercept'); + return false; + }) + .onInterceptRequest((event) => { + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] onInterceptRequest, ' + + event.request.getRequestUrl()); + return null; + }) .onPageBegin(() => { - this.jsBridge.initJsBridge(); + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] onPageBegin'); + }) + .onPageEnd(() => { + hilog.info(0xFF00, 'SelectContact', '%{public}s', '[LifeCycle] onPageEnd'); }) Blank() - Button(this.chargeTip) - .width(CommonConstants.FULL_SIZE) + Button($r('app.string.recharge_button')) + .width('100%') .height($r('app.float.button_height')) .margin({ bottom: $r('app.float.button_margin_bottom') }) - .enabled(this.isClick) .onClick(() => { - promptAction.showToast({ - message: this.phoneNumber === '' ? $r('app.string.phone_check') : $r('app.string.recharge_success') - }); + this.webviewController.runJavaScript('recharge()') + .then((result) => { + promptAction.showToast({ message: result.replaceAll('"', '') }); + }); }) } - .width(CommonConstants.FULL_SIZE) - .height(CommonConstants.FULL_SIZE) - .backgroundColor($r('app.color.page_color')) + .width('100%') + .height('100%') .padding({ left: $r('app.float.margin_left_normal'), right: $r('app.float.margin_right_normal') }) } + + /** + * Use system ability of getting contacts. + */ + chooseContact(): Promise { + let phone: string = ''; + let name: string = ''; + return new Promise((resolve) => { + if (deviceInfo.productModel === 'emulator') { + return resolve('13800000000_test'); + } + let promise = contact.selectContacts(); + promise.then((info: Array) => { + info.forEach((item: contact.Contact) => { + phone = item?.phoneNumbers ? item?.phoneNumbers[0].phoneNumber : ''; + name = item?.name ? item?.name?.fullName : ''; + }) + resolve(phone + '_' + name); + }).catch((err: object) => { + hilog.error(0xFF00, 'SelectContact', '%{public}s', `selectContact fail: err->${JSON.stringify(err)}`); + }); + }) + } } \ No newline at end of file diff --git a/entry/src/main/ets/viewmodel/JavaScriptItem.ets b/entry/src/main/ets/viewmodel/JavaScriptItem.ets deleted file mode 100644 index 5badb1d2c0d4efbd42a56d1739c7d17d06b9f583..0000000000000000000000000000000000000000 --- a/entry/src/main/ets/viewmodel/JavaScriptItem.ets +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * javaScriptProxy object type. - */ -export interface callType { - call: (func: string, params: string) => void -} - -export interface JavaScriptItem { - object: callType, - name: string, - methodList: Array, - controller: WebviewController -} - diff --git a/entry/src/main/ets/viewmodel/ParamsItem.ets b/entry/src/main/ets/viewmodel/ParamsItem.ets deleted file mode 100644 index cf5b0a0c6bbfb72c60a096349e9598a31a126893..0000000000000000000000000000000000000000 --- a/entry/src/main/ets/viewmodel/ParamsItem.ets +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Function params object. - */ -export interface ParamsItem { - callID: number, - data: ParamsDataItem -} - -/** - * ParamsDataItem. - */ -export interface ParamsDataItem { - name?: string; - tel?: string -} \ No newline at end of file diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 index 3800d8e5f16b3f5d69bfa6dc050ca574b22f5238..ad219d733f6afa5ea07f85f580208b08cc3b9041 100644 --- a/entry/src/main/module.json5 +++ b/entry/src/main/module.json5 @@ -15,9 +15,9 @@ "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "description": "$string:EntryAbility_desc", - "icon": "$media:icon", + "icon": "$media:layered_image", "label": "$string:EntryAbility_label", - "startWindowIcon": "$media:icon", + "startWindowIcon": "$media:startIcon", "startWindowBackground": "$color:start_window_background", "exported": true, "skills": [ @@ -31,6 +31,20 @@ } ] } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ], + } ] } } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json index 90355f5b24f20d5f58b1f41beef244fb4590a60c..c07c91fcfbb2207910df382c3689c882fca01d6c 100644 --- a/entry/src/main/resources/base/element/float.json +++ b/entry/src/main/resources/base/element/float.json @@ -38,7 +38,7 @@ }, { "name": "button_margin_bottom", - "value": "24vp" + "value": "16vp" }, { "name": "web_height", diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index 565058b0c35eb8724e6c176d76b7618b73a2cc42..12c1c21f65287a40f7a2498d78d9f0594ffb803a 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -12,13 +12,21 @@ "name": "EntryAbility_label", "value": "SelectContact" }, + { + "name": "select_contact", + "value": "Interaction between ArkTS and H5" + }, + { + "name": "mobile_phone_recharge", + "value": "Recharge" + }, { "name": "navigation_title", "value": "charge credit" }, { "name": "recharge_button", - "value": "recharge" + "value": "Recharge" }, { "name": "recharge_success", diff --git a/entry/src/main/resources/base/media/background.png b/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/entry/src/main/resources/base/media/background.png differ diff --git a/entry/src/main/resources/base/media/foreground.png b/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/entry/src/main/resources/base/media/foreground.png differ diff --git a/entry/src/main/resources/base/media/layered_image.json b/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/media/startIcon.png b/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/entry/src/main/resources/base/media/startIcon.png differ diff --git a/entry/src/main/resources/base/profile/backup_config.json b/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index bf453ef8f9cb39082ce1e02b2904e446abc09d1d..1898d94f58d6128ab712be2c68acc7c98e9ab9ce 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -1,5 +1,5 @@ { "src": [ - "pages/SelectContact" + "pages/Index" ] } diff --git a/entry/src/main/resources/dark/element/color.json b/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..79b11c2747aec33e710fd3a7b2b3c94dd9965499 --- /dev/null +++ b/entry/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#000000" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json index 565058b0c35eb8724e6c176d76b7618b73a2cc42..12c1c21f65287a40f7a2498d78d9f0594ffb803a 100644 --- a/entry/src/main/resources/en_US/element/string.json +++ b/entry/src/main/resources/en_US/element/string.json @@ -12,13 +12,21 @@ "name": "EntryAbility_label", "value": "SelectContact" }, + { + "name": "select_contact", + "value": "Interaction between ArkTS and H5" + }, + { + "name": "mobile_phone_recharge", + "value": "Recharge" + }, { "name": "navigation_title", "value": "charge credit" }, { "name": "recharge_button", - "value": "recharge" + "value": "Recharge" }, { "name": "recharge_success", diff --git a/entry/src/main/resources/rawfile/css/main.css b/entry/src/main/resources/rawfile/css/main.css index 6abd17323afe6543fa0f067156c49aee6b6586ef..5253957fa583749dec9f27407c09077a5bfe6f30 100644 --- a/entry/src/main/resources/rawfile/css/main.css +++ b/entry/src/main/resources/rawfile/css/main.css @@ -2,7 +2,12 @@ * { margin: 0; padding: 0; - background-color: #f2f2f2; + background-color: #f1f3f5; +} + +/* Container Style */ +html { + font-size: 1.08px; } /* Container Style */ @@ -25,7 +30,7 @@ /* Left text div style */ #phone_tip { float: left; - margin-left: 12rem; + margin-left: 16px; } /* Right text div style */ @@ -43,7 +48,7 @@ input[type='tel'] { border-left-width: 0; border-top-width: 0; border-right-width: 0; - border-bottom: 0.5rem solid rgba(24, 36, 49, 0.05); + border-bottom: 1.5px solid rgba(0, 0, 0, 0.2); } /* Icon select style */ diff --git a/entry/src/main/resources/rawfile/css/main_en.css b/entry/src/main/resources/rawfile/css/main_en.css new file mode 100644 index 0000000000000000000000000000000000000000..06cc9b1cc70c029d6cb3903e7420d76fff69f5a7 --- /dev/null +++ b/entry/src/main/resources/rawfile/css/main_en.css @@ -0,0 +1,121 @@ +/* Common Style */ +* { + margin: 0; + padding: 0; + background-color: #f1f3f5; +} + +/* Container Style */ +html { + font-size: 1.08px; +} + +/* Container Style */ +.container { + position: relative; +} + +/* Text div style */ +.select_tips { + height: 24rem; + width: 100%; + overflow: auto; +} + +.select_tips > div { + font-size: 12rem; + color: #999; +} + +/* Left text div style */ +#phone_tip { + float: left; + margin-left: 16px; +} + +/* Right text div style */ +#concat_tip { + float: right; + margin-right: 12rem; +} + +/* Contact input type */ +input[type='tel'] { + width: 80%; + height: 36rem; + font-size: 24rem; + outline: none; + border-left-width: 0; + border-top-width: 0; + border-right-width: 0; + border-bottom: 1.5px solid rgba(0, 0, 0, 0.2); +} + +/* Icon select style */ +#icon-select { + width: 24rem; + height: 24rem; + text-align: right; +} + +/* Select input div style */ +.flex-input { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + height: 36rem; + margin-left: 12rem; + margin-right: 12rem; +} + +/* Amount select container */ +.amount-content { + background-color: #fff; + padding: 12rem; + border-radius: 24rem; + margin-top: 16rem; +} + +/* Amount select ttp */ +.amount-tip { + background-color: #fff; + font-size: 16rem; + color: #182431; + font-weight: 500; +} + +/* Grid container */ +.grid-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-gap: 12rem; + margin-top: 12rem; + background-color: #fff; +} + +.grid-item { + display: flex; + justify-content: center; + align-items: center; + height: 53rem; + background-color: #fff; + color: #182431; + font-size: 18rem; + border-radius: 8rem; + border: 1rem solid rgba(24, 36, 49, 0.20); +} + +/* Grid item selected */ +.selected { + background-color: #007DFF; + color: #fff; +} + +.bottom-tip { + font-size: 12rem; + color: rgba(24, 36, 49, 0.60); + font-weight: 400; + margin-top: 8rem; + margin-left: 8rem; +} diff --git a/entry/src/main/resources/rawfile/img/ic_contacts.svg b/entry/src/main/resources/rawfile/img/ic_contacts.svg index abff568ec02dff600b70e3fa0ff92fde607709c0..f0cd8419c514fa28aadca861cfa32be1c58509ec 100644 --- a/entry/src/main/resources/rawfile/img/ic_contacts.svg +++ b/entry/src/main/resources/rawfile/img/ic_contacts.svg @@ -8,6 +8,6 @@ - + \ No newline at end of file diff --git a/entry/src/main/resources/rawfile/MainPage.html b/entry/src/main/resources/rawfile/index.html similarity index 88% rename from entry/src/main/resources/rawfile/MainPage.html rename to entry/src/main/resources/rawfile/index.html index 0819dd6656120ae111a9400e26e31830871041f1..1715bea4e9336c60797ec654a98f4694d9c3ea4a 100644 --- a/entry/src/main/resources/rawfile/MainPage.html +++ b/entry/src/main/resources/rawfile/index.html @@ -7,11 +7,7 @@ testApp - +
diff --git a/entry/src/main/resources/rawfile/index_en.html b/entry/src/main/resources/rawfile/index_en.html new file mode 100644 index 0000000000000000000000000000000000000000..80cd869fe8270b484562edc70e45c86243682f7c --- /dev/null +++ b/entry/src/main/resources/rawfile/index_en.html @@ -0,0 +1,41 @@ + + + + + + + + testApp + + + +
+
+
+
Phone Number
+
Contact
+
+
+ + +
+
+
+
Credit Top-up
+
+
+
+
+
+
+
+
+
+
+
+
+
Please select amount
+
+ + + \ No newline at end of file diff --git a/entry/src/main/resources/rawfile/js/mainPage.js b/entry/src/main/resources/rawfile/js/mainPage.js index 8c567117ad23882657b314c3c2f8563991b491f2..fdb35470de0fc533bfd0f3b92db70a6e4409ce5e 100644 --- a/entry/src/main/resources/rawfile/js/mainPage.js +++ b/entry/src/main/resources/rawfile/js/mainPage.js @@ -3,12 +3,13 @@ const chargeAmount = [10, 20, 30, 50, 100, 200, 300, 500, 1000]; // Get all amount grid. let allGridItem = document.getElementsByClassName('grid-item'); +let amount = null; // Set amount and onclick function of grid. for (let i = 0;i < allGridItem.length; i++) { allGridItem[i].innerHTML = chargeAmount[i] + '元'; allGridItem[i].onclick = function () { - let amount = chargeAmount[i]; + amount = chargeAmount[i]; selectAmount(amount); } } @@ -24,14 +25,13 @@ function splitPhone(phone) { // Invoke ArkTS call and use the data. function chooseContact() { - window.ohosCallNative.callNative('chooseContact', {}, (data) => { + jsbridgeHandle.call().then((data) => { const phone = data.split('_')[0]; const name = data.split('_')[1]; const result = splitPhone(phone); document.getElementById('phone_tip').innerHTML = name; document.getElementById('phone').value = result; - window.ohosCallNative.callNative('changeTel', { tel: result }); - }) + }); } // Click the amount grid. @@ -40,7 +40,6 @@ function selectAmount(amount) { allGridItem[i].classList.remove('selected'); } event.target.classList.add('selected'); - window.ohosCallNative.callNative('changeAmount', { amount: amount }); } // Contact input onclick. @@ -48,7 +47,6 @@ function changeVal() { let input = event.target.value; let name = document.getElementById('phone_tip').innerHTML; const tel = document.getElementById('phone').value; - window.ohosCallNative.callNative('changeTel', { tel: tel }); if (name !== '手机号' && input.length < 13) { document.getElementById('phone_tip').innerHTML = '手机号'; } @@ -56,4 +54,11 @@ function changeVal() { let phoneNumber = input.replace(/\D/g, '').slice(0, 11); result = phoneNumber.replace(/(\d{3})(\d{4})(\d{1,4})/, '$1 $2 $3'); event.target.value = result; +} + +function recharge() { + if(amount == null) { + return '请选择充值金额'; + } + return document.getElementById('phone').value.length === 13 ? '充值成功' : '请输入正确的手机号'; } \ No newline at end of file diff --git a/entry/src/main/resources/rawfile/js/mainPage_en.js b/entry/src/main/resources/rawfile/js/mainPage_en.js new file mode 100644 index 0000000000000000000000000000000000000000..94a51981e8490c0a20bc7a466533ac16d303756a --- /dev/null +++ b/entry/src/main/resources/rawfile/js/mainPage_en.js @@ -0,0 +1,64 @@ +// The different charge amount. +const chargeAmount = [10, 20, 30, 50, 100, 200, 300, 500, 1000]; + +// Get all amount grid. +let allGridItem = document.getElementsByClassName('grid-item'); +let amount = null; + +// Set amount and onclick function of grid. +for (let i = 0;i < allGridItem.length; i++) { + allGridItem[i].innerHTML = chargeAmount[i] + 'CNY'; + allGridItem[i].onclick = function () { + amount = chargeAmount[i]; + selectAmount(amount); + } +} + +// Split the phone function. +function splitPhone(phone) { + let regex = /(\d{3})(\d{4})(\d{4})/; + let result = phone.replace(regex, function () { + return RegExp.$1 + " " + RegExp.$2 + " " + RegExp.$3; + }); + return result; +} + +// Invoke ArkTS call and use the data. +function chooseContact() { + jsbridgeHandle.call().then((data) => { + const phone = data.split('_')[0]; + const name = data.split('_')[1]; + const result = splitPhone(phone); + document.getElementById('phone_tip').innerHTML = name; + document.getElementById('phone').value = result; + }); +} + +// Click the amount grid. +function selectAmount(amount) { + for (let i = 0;i < allGridItem.length; i++) { + allGridItem[i].classList.remove('selected'); + } + event.target.classList.add('selected'); +} + +// Contact input onclick. +function changeVal() { + let input = event.target.value; + let name = document.getElementById('phone_tip').innerHTML; + const tel = document.getElementById('phone').value; + if (name !== 'Phone Number' && input.length < 13) { + document.getElementById('phone_tip').innerHTML = 'Phone Number'; + } + let result = input; + let phoneNumber = input.replace(/\D/g, '').slice(0, 11); + result = phoneNumber.replace(/(\d{3})(\d{4})(\d{1,4})/, '$1 $2 $3'); + event.target.value = result; +} + +function recharge() { + if(amount == null) { + return 'Please select amount'; + } + return document.getElementById('phone').value.length === 13 ? 'Recharge successful' : 'Please enter right number'; +} \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index 7d86ed90faa0b795bf0104459e654fefc17f6f5c..a7e016c59a5a4cc1403e61e090109ba432e04af7 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -12,6 +12,14 @@ "name": "EntryAbility_label", "value": "SelectContact" }, + { + "name": "select_contact", + "value": "实现ArkTS和H5交互" + }, + { + "name": "mobile_phone_recharge", + "value": "话费充值" + }, { "name": "navigation_title", "value": "手机充值" diff --git a/screenshots/device/SelectContact.gif b/screenshots/device/SelectContact.gif index e07a6b3dd33615da73b5ac2eb523ebc34830fdb6..a55c9d6093bbdf98077fc732ff36015f90557241 100644 Binary files a/screenshots/device/SelectContact.gif and b/screenshots/device/SelectContact.gif differ diff --git a/screenshots/device/homePage.png b/screenshots/device/homePage.png index bed5b8bfdcc3ee4551ff23ef2a50c33804190490..ef5ab88b84123686754e60bd6640121dc35095ed 100644 Binary files a/screenshots/device/homePage.png and b/screenshots/device/homePage.png differ