diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2a49801dacea074448893f4f691e2bd731d6c9bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/node_modules +/local.properties +**/local.properties +/.idea +**/build +/.hvigor +.preview +*/.preview +**/package-lock.json diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..6f46fbf6ea9c9235806158be6b74b625c26c9931 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,11 @@ +{ + "app": { + "bundleName": "com.hihope.recorder", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name", + "distributedNotificationEnabled": true + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..ec39b5f097e9a18e1989672fa4be0b82d769211f --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "录音机" + } + ] +} diff --git a/AppScope/resources/base/media/app_icon.png b/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b186c704fd1345ae527fabc8a479fc92470a8c6b Binary files /dev/null and b/AppScope/resources/base/media/app_icon.png differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..67891321dae1e5bf29c3fc1b5549736ec4110030 --- /dev/null +++ b/OAT.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 8ff59b37b86f4648985688d454507cb1daababf7..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# applications_recorder - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md deleted file mode 100644 index 15a45c8d98ce999cea8c9a77397bd2ff4267238d..0000000000000000000000000000000000000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# applications_recorder - -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..0070749e8efb95552829f3c42b2befc4dfe6d6e5 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,39 @@ +# 录音机应用 + +## 简介 +录音机应用可以通过麦克风进行音频录制并完成播放。 +录音机应用采用扩展的TS语言(ArkTS)开发,主要的结构如下: +![](./figures/record.png) +- **product** + 业务形态层:区分不同产品、不同屏幕的各形态应用,含有个性化业务,组件的配置,以及个性化资源包。 + +- **feature** + 公共特性层:抽象的公共特性组件集合,可以被各应用形态引用。 + +- **common** + 公共能力层:基础能力集,每个应用形态都必须依赖的模块,包含工具类和通用的资源包 + +## 目录 +### 目录结构 +``` +/recorder/ +├── common # 公共能力层目录 +├── feature # 公共特性层目录 +│ └── model # 数据格式目录 +│ └── controller # 控制逻辑目录 +├── product # 业务形态层目录 +``` + +## 安装 +对应用完成签名,打包后,使用`hdc_std install "hap包地址"`命令进行安装 +![](./figures/signature.png) +![](./figures/buildHap.png) +![](./figures/install.png) + +## 约束 +- 开发环境 + - **DevEco Studio for OpenHarmony**: 版本号大于3.0.0.992,下载安装OpenHarmony SDK API Version 9。(初始的IDE配置可以参考IDE的使用文档) +- 语言版本 + - ArkTS +- 限制 + - 本示例仅支持标准系统上运行 \ No newline at end of file diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ec555ee5bb89d82226cf4cbd05c14269796b6aeb --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,47 @@ +{ + "app": { + "signingConfigs": [], + "compileSdkVersion": 9, + "compatibleSdkVersion": 9, + "products": [ + { + "name": "default", + "signingConfig": "default", + } + ] + }, + "modules": [ + { + "name": "phone", + "srcPath": "./product/phone", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "tablet", + "srcPath": "./product/tablet", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "common", + "srcPath": "./common" + }, + { + "name": "recorder", + "srcPath": "./feature/recorder" + } + ] +} \ No newline at end of file diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4f9a973815d0b5e49bc8547681a6b4bc7a178d12 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1,3 @@ +/node_modules +/.preview +/build \ No newline at end of file diff --git a/common/build-profile.json5 b/common/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..35dff6d53cb3a241f8de4fb68bd01d38ade0f108 --- /dev/null +++ b/common/build-profile.json5 @@ -0,0 +1,5 @@ +{ + "apiType": "stageMode", + "buildOption": { + } +} diff --git a/common/hvigorfile.js b/common/hvigorfile.js new file mode 100644 index 0000000000000000000000000000000000000000..42ed4b4a54a873e2b53441556aae93fab24b794f --- /dev/null +++ b/common/hvigorfile.js @@ -0,0 +1,3 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').harTasks + diff --git a/common/index.ets b/common/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..af9c605b34eec7395760f18018aea72ecbd1e703 --- /dev/null +++ b/common/index.ets @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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. + */ + +export { default as ConfigData } from './src/main/ets/utils/ConfigData' + +export { default as Util } from './src/main/ets/utils/Util' + +export { default as LogUtil } from './src/main/ets/utils/LogUtil' diff --git a/common/package.json b/common/package.json new file mode 100644 index 0000000000000000000000000000000000000000..6845131bfa2ddc9998b03c56d12c85edc1a861e2 --- /dev/null +++ b/common/package.json @@ -0,0 +1,14 @@ +{ + "license": "ISC", + "types": "", + "devDependencies": {}, + "name": "@ohos/common", + "description": "a npm package which contains arkUI2.0 page", + "ohos": { + "org": "" + }, + "main": "src/main/ets/components/MainPage/MainPage.ets", + "repository": {}, + "version": "1.0.0", + "dependencies": {} +} diff --git a/common/src/main/ets/utils/ConfigData.ets b/common/src/main/ets/utils/ConfigData.ets new file mode 100644 index 0000000000000000000000000000000000000000..728136a483d65992eec74b7fd766c0e7792b66cb --- /dev/null +++ b/common/src/main/ets/utils/ConfigData.ets @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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. + */ + +export class ConfigData { + WH_100_100 = '100%'; + WH_50_100 = '50%'; + WH_40_100 = '40%'; + WH_20_100 = '20%'; +} + +let configData = new ConfigData(); + +export default configData as ConfigData; \ No newline at end of file diff --git a/common/src/main/ets/utils/LogUtil.ts b/common/src/main/ets/utils/LogUtil.ts new file mode 100644 index 0000000000000000000000000000000000000000..bbead9dd9befd61c6caee01842404061533ef8aa --- /dev/null +++ b/common/src/main/ets/utils/LogUtil.ts @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 "@ohos.hilog"; + +const DOMAIN = 0x0500; +const TAG = "[Recorder]"; + +/** + * log package tool class + */ +export default class LogUtil { + static debug(msg): void { + HiLog.debug(DOMAIN, TAG, msg); + } + + static log(msg): void { + HiLog.info(DOMAIN, TAG, msg); + } + + static info(msg): void { + HiLog.info(DOMAIN, TAG, msg); + } + + static warn(msg): void { + HiLog.warn(DOMAIN, TAG, msg); + } + + static error(msg): void { + HiLog.error(DOMAIN, TAG, msg); + } +} \ No newline at end of file diff --git a/common/src/main/ets/utils/Util.ets b/common/src/main/ets/utils/Util.ets new file mode 100644 index 0000000000000000000000000000000000000000..2887d8f9f4238430c8934332067eab7c79c826c6 --- /dev/null +++ b/common/src/main/ets/utils/Util.ets @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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. + */ + +export default class Util { + + /** + * Time format is converted to seconds + * + * @param timestamp + * @param hasHour + */ + static timeFormat(timestamp: number, hasHour: boolean = true): string { + if (timestamp < 0) { + return ""; + } + let timeStr = ""; + let hour = Math.floor(timestamp % (24 * 60 * 60 * 1000) / (60 * 60 * 1000)); + if (hasHour || hour > 0) { + timeStr += this.addZero(hour) + ":"; + } + let min = Math.floor(timestamp % (60 * 60 * 1000) / (60 * 1000)); + timeStr += this.addZero(min) + ":"; + let sec = Math.floor(timestamp % (60 * 1000) / 1000); + timeStr += this.addZero(sec); + return timeStr; + } + + /** + * Time format is converted to milliseconds + * + * @param timestamp + */ + static timeFormatMs(timestamp: number): string { + if (timestamp <= 0) { + return "00:00.00"; + } + let timeStr = ""; + let hour = Math.floor(timestamp % (24 * 60 * 60 * 1000) / (60 * 60 * 1000)); + if (hour > 0) { + timeStr += this.addZero(hour) + ":"; + } + let min = Math.floor(timestamp % (60 * 60 * 1000) / (60 * 1000)); + timeStr += this.addZero(min) + ":"; + let sec = Math.floor(timestamp % (60 * 1000) / 1000); + timeStr += this.addZero(sec); + if (hour < 1) { + let mSec = Math.floor(timestamp % 1000 / 10); + timeStr += "." + this.addZero(mSec); + } + return timeStr; + } + + /** + * Gets the date at the specified time + * + * @param date + */ + static getDateFormat(date: number): string { + let nowDate = new Date(date); + return nowDate.getFullYear() + "/" + this.addZero(nowDate.getMonth() + 1) + "/" + this.addZero(nowDate.getDate()); + } + + /** + * Gets the hour, minute and second of the specified time + * + * @param date + */ + static getTimeFormat(date: number): string { + let nowDate = new Date(date); + return this.addZero(nowDate.getHours()) + ":" + this.addZero(nowDate.getMinutes()) + ":" + this.addZero(nowDate.getSeconds()); + } + + /** + * Gets the current time date + */ + static getDate(): string { + let nowDate = new Date(); + return nowDate.getFullYear() + this.addZero(nowDate.getMonth() + 1) + this.addZero(nowDate.getDate()); + } + + /** + * Gets the hour, minute and second of the current time + */ + static getTime(): string { + let nowDate = new Date(); + return this.addZero(nowDate.getHours()) + this.addZero(nowDate.getMinutes()) + this.addZero(nowDate.getSeconds()); + } + + /** + * If the number is less than 10, add "0". + * + * @param num + */ + static addZero(num: number): string{ + return (num < 10 ? "0" + num : num).toString(); + } +} \ No newline at end of file diff --git a/common/src/main/module.json5 b/common/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..56a91a87ba3ea303b57e4b8e5f405e9d6d2a800a --- /dev/null +++ b/common/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "common", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ], + "uiSyntax": "ets" + } +} diff --git a/common/src/main/resources/base/element/color.json b/common/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..ffe7272ac3029e44fd8fe56844ac7a59808be8a2 --- /dev/null +++ b/common/src/main/resources/base/element/color.json @@ -0,0 +1,32 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + }, + { + "name": "background_color", + "value": "#F1F3F5" + }, + { + "name": "shadow_color", + "value": "#11000000" + }, + { + "name": "pointer_color", + "value": "#007DFF" + }, + { + "name": "warn_color", + "value": "#FF595A" + }, + { + "name": "hint_color", + "value": "#007DFF" + }, + { + "name": "text_color", + "value": "#182431" + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/base/element/string.json b/common/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..2375b2de43748384262b8e07fa36405169d878c8 --- /dev/null +++ b/common/src/main/resources/base/element/string.json @@ -0,0 +1,56 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + }, + { + "name": "title", + "value": "录音机" + }, + { + "name": "search_recording_files", + "value": "搜索录音文件" + }, + { + "name": "mark", + "value": "标记" + }, + { + "name": "pause", + "value": "暂停" + }, + { + "name": "continue_record", + "value": "继续" + }, + { + "name": "predict_hint", + "value": "预计还能录制10小时以上" + }, + { + "name": "delete_hint", + "value": "是否删除此条录音?" + }, + { + "name": "cancel", + "value": "取消" + }, + { + "name": "delete", + "value": "删除" + }, + { + "name": "has_call_hint", + "value": "通话状态无法录音" + }, + { + "name": "recording_hint", + "value": "录音机正在录音" + }, + { + "name": "suspend_record_hint", + "value": "录音机暂停录音" + } + ] +} diff --git a/common/src/main/resources/base/media/bg_record.png b/common/src/main/resources/base/media/bg_record.png new file mode 100644 index 0000000000000000000000000000000000000000..41c12ccb5bb90cd9c05508690ca0b88f33e96008 Binary files /dev/null and b/common/src/main/resources/base/media/bg_record.png differ diff --git a/common/src/main/resources/base/media/ic_back.svg b/common/src/main/resources/base/media/ic_back.svg new file mode 100644 index 0000000000000000000000000000000000000000..6f7f039226e90d3ef91b0e83c4ad29236858e868 --- /dev/null +++ b/common/src/main/resources/base/media/ic_back.svg @@ -0,0 +1,10 @@ + + + systemicon/back + + + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_clock_add.svg b/common/src/main/resources/base/media/ic_clock_add.svg new file mode 100644 index 0000000000000000000000000000000000000000..3ec94e4b1e5f198844f5136dc14c9ff00ab95887 --- /dev/null +++ b/common/src/main/resources/base/media/ic_clock_add.svg @@ -0,0 +1,8 @@ + + + ic_clock_add + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_clock_enhance.svg b/common/src/main/resources/base/media/ic_clock_enhance.svg new file mode 100644 index 0000000000000000000000000000000000000000..14a0ba5bbea05082e4659a124dc89b79f523509b --- /dev/null +++ b/common/src/main/resources/base/media/ic_clock_enhance.svg @@ -0,0 +1,10 @@ + + + ic_clock_enhance + + + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_clock_mark.svg b/common/src/main/resources/base/media/ic_clock_mark.svg new file mode 100644 index 0000000000000000000000000000000000000000..330eda737a418114cc3389b4c14cbcada67ec5e9 --- /dev/null +++ b/common/src/main/resources/base/media/ic_clock_mark.svg @@ -0,0 +1,8 @@ + + + ic_clock_mark + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_clock_play.svg b/common/src/main/resources/base/media/ic_clock_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..4a6c56994bd7736e81307833789775d51b71cf6c --- /dev/null +++ b/common/src/main/resources/base/media/ic_clock_play.svg @@ -0,0 +1,7 @@ + + + ic_clock_play + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_clock_record.svg b/common/src/main/resources/base/media/ic_clock_record.svg new file mode 100644 index 0000000000000000000000000000000000000000..c8f7906490d4e4ae103cf1d35fd3e5c4c55f27f0 --- /dev/null +++ b/common/src/main/resources/base/media/ic_clock_record.svg @@ -0,0 +1,7 @@ + + + ic_clock_record + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_clock_stop.svg b/common/src/main/resources/base/media/ic_clock_stop.svg new file mode 100644 index 0000000000000000000000000000000000000000..02964494e7ad6466ce03c368befe5a7e3c1ed01b --- /dev/null +++ b/common/src/main/resources/base/media/ic_clock_stop.svg @@ -0,0 +1,7 @@ + + + ic_clock_stop + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_clock_suspend.svg b/common/src/main/resources/base/media/ic_clock_suspend.svg new file mode 100644 index 0000000000000000000000000000000000000000..bf6f0ea2eabe25b77ef0bed04d52cb1e6ac6bb0b --- /dev/null +++ b/common/src/main/resources/base/media/ic_clock_suspend.svg @@ -0,0 +1,8 @@ + + + ic_clock_suspend + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_record_play.svg b/common/src/main/resources/base/media/ic_record_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..34c0e947768885c982a4031b2df92c1fe7b014ab --- /dev/null +++ b/common/src/main/resources/base/media/ic_record_play.svg @@ -0,0 +1,8 @@ + + + ic_record_play + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_record_search.svg b/common/src/main/resources/base/media/ic_record_search.svg new file mode 100644 index 0000000000000000000000000000000000000000..1b2a8aa35dc2c2ea6b47fad1fdfe319947701b43 --- /dev/null +++ b/common/src/main/resources/base/media/ic_record_search.svg @@ -0,0 +1,8 @@ + + + ic_record_search + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/ic_record_stop.svg b/common/src/main/resources/base/media/ic_record_stop.svg new file mode 100644 index 0000000000000000000000000000000000000000..2b7ceaa1d1b8a10b1b18f0a1515477874df4b357 --- /dev/null +++ b/common/src/main/resources/base/media/ic_record_stop.svg @@ -0,0 +1,9 @@ + + + ic_record_stop + + + + + + \ No newline at end of file diff --git a/common/src/main/resources/base/media/public_more.svg b/common/src/main/resources/base/media/public_more.svg new file mode 100644 index 0000000000000000000000000000000000000000..400c3ef1abe1ebe1b0637b20c1c4b9711812b41e --- /dev/null +++ b/common/src/main/resources/base/media/public_more.svg @@ -0,0 +1,13 @@ + + + public_more + + + + + + + + + + \ No newline at end of file diff --git a/feature/recorder/.gitignore b/feature/recorder/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4f9a973815d0b5e49bc8547681a6b4bc7a178d12 --- /dev/null +++ b/feature/recorder/.gitignore @@ -0,0 +1,3 @@ +/node_modules +/.preview +/build \ No newline at end of file diff --git a/feature/recorder/build-profile.json5 b/feature/recorder/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..35dff6d53cb3a241f8de4fb68bd01d38ade0f108 --- /dev/null +++ b/feature/recorder/build-profile.json5 @@ -0,0 +1,5 @@ +{ + "apiType": "stageMode", + "buildOption": { + } +} diff --git a/feature/recorder/hvigorfile.js b/feature/recorder/hvigorfile.js new file mode 100644 index 0000000000000000000000000000000000000000..42ed4b4a54a873e2b53441556aae93fab24b794f --- /dev/null +++ b/feature/recorder/hvigorfile.js @@ -0,0 +1,3 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').harTasks + diff --git a/feature/recorder/index.ets b/feature/recorder/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..98c643021853a081786f585ce73e679d2f0a3df7 --- /dev/null +++ b/feature/recorder/index.ets @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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. + */ + +export { RecordController, RecordState } from './src/main/ets/controller/RecordController' + +export { default as MediaController } from './src/main/ets/controller/MediaController' + +export { AudioController, AudioState } from './src/main/ets/controller/AudioController' + +export { StateController, ControlState } from './src/main/ets/controller/StateController' + +export { RecordData } from './src/main/ets/model/RecordData' diff --git a/feature/recorder/package.json b/feature/recorder/package.json new file mode 100644 index 0000000000000000000000000000000000000000..83c74cf381e47f923fd3171be67a35594e7224e6 --- /dev/null +++ b/feature/recorder/package.json @@ -0,0 +1,16 @@ +{ + "license": "ISC", + "types": "", + "devDependencies": {}, + "name": "@ohos/recorder", + "description": "a npm package which contains arkUI2.0 page", + "ohos": { + "org": "" + }, + "main": "src/main/ets/components/MainPage/MainPage.ets", + "repository": {}, + "version": "1.0.0", + "dependencies": { + "@ohos/common": "file:../../common" + } +} diff --git a/feature/recorder/src/main/ets/controller/AudioController.ets b/feature/recorder/src/main/ets/controller/AudioController.ets new file mode 100644 index 0000000000000000000000000000000000000000..81247180eeff1ff8ad64186231295024a31c279e --- /dev/null +++ b/feature/recorder/src/main/ets/controller/AudioController.ets @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 mediaLibrary from '@ohos.multimedia.mediaLibrary'; +import media from '@ohos.multimedia.media'; +import { LogUtil } from '@ohos/common'; + +export enum AudioState { + /** + * The initial state + */ + IDLE, + /** + * Continuously update time starting from IDLE state or PAUSED state + */ + RUNNING, + /** + * Click Pause from the RUNNING state + */ + PAUSED, +} + +const NOTIFY_INTERVAL_MS = 50; +const TAG = "AudioController : "; + +export class AudioController { + private audioPlayer: media.AudioPlayer = undefined; + private state: AudioState = AudioState.IDLE; + private playFile: mediaLibrary.FileAsset = undefined; + private dataLoad: boolean = false; + private finishListener: () => void = undefined; + private currentTimeMs: number = 0; + private intervalId: number = -1; + private timeUpdateListener: (currentTimeMs: number) => void = undefined; + private stateUpdateListener: (AudioState) => void = undefined; + + /** + * Set the time update listener + * + * @param listener + */ + public setTimeUpdateListener(listener: (currentTimeMs: number) => void) { + LogUtil.info(`${TAG} setTimeUpdateListener`); + this.timeUpdateListener = listener; + } + + /** + * Set the status update listener + * + * @param listener + */ + public setStateUpdateListener(listener: (AudioState) => void) { + LogUtil.info(`${TAG} setStateUpdateListener`); + this.stateUpdateListener = listener; + } + + /** + * Notification time update + */ + private notifyTimeChanges() { + if (this.timeUpdateListener) { + this.timeUpdateListener(this.currentTimeMs); + } + } + + /** + * Notification of status updates + */ + private async notifyStateChanges() { + if (this.stateUpdateListener) { + LogUtil.info(`${TAG} state update ${this.state}`); + this.stateUpdateListener(this.state); + } + } + + /** + * Initialize the audio player + * + * @param playSrc + * @param callback + */ + initAudioPlayer(playSrc: mediaLibrary.FileAsset, callback: () => void) { + LogUtil.info(`${TAG} initAudioPlayer`); + this.playFile = playSrc; + this.dataLoad = false; + this.release(); + LogUtil.info(`${TAG} createAudioPlayer`); + this.audioPlayer = media.createAudioPlayer(); + this.audioPlayer.on('dataLoad', () => { + this.dataLoad = true; + LogUtil.info(`${TAG} audioPlayer dataLoad success`); + if (callback) { + callback(); + } + }) + this.audioPlayer.on('stop', () => { + LogUtil.info(`${TAG} audioPlayer stop success`); + this.release(); + }) + this.audioPlayer.on('error', (error) => { + LogUtil.error(`${TAG} audioPlayer error. info:${error}`); + }) + this.audioPlayer.on('timeUpdate', (num) => { + LogUtil.info(`${TAG} audioPlayer timeUpdate. time:${num}`); + }) + this.audioPlayer.on('audioInterrupt', (num) => { + LogUtil.warn(`${TAG} audioPlayer audioInterrupt. time:${num}`); + }) + this.audioPlayer.on('finish', () => { + LogUtil.info(`${TAG} audioPlayer finish.`); + clearInterval(this.intervalId); + this.intervalId = -1; + this.state = AudioState.IDLE; + this.notifyStateChanges(); + }) + LogUtil.info(`${TAG} audioPlayer reset.`) + this.audioPlayer.reset(); + LogUtil.info(`${TAG} read FileAsset`); + let fdPath = playSrc.open('r'); + fdPath.then(fdNumber => { + LogUtil.info(`${TAG} audioPlayer set src`); + this.audioPlayer.src = `fd://${fdNumber}`; + }); + } + + /** + * audioPlayer play + */ + play() { + if (typeof (this.audioPlayer) != `undefined`) { + this.audioPlayer.on('play', () => { + LogUtil.info(`${TAG} audioPlayer play success`); + this.startTimer(); + }); + if (this.dataLoad) { + LogUtil.info(`${TAG} audioPlayer play`); + this.audioPlayer.play(); + } else { + this.audioPlayer.on('dataLoad', () => { + LogUtil.info(`${TAG} audioPlayer dataLoad success`); + this.dataLoad = true; + LogUtil.info(`${TAG} audioPlayer play`); + this.audioPlayer.play(); + }); + } + } + } + + /** + * audioPlayer seek time + * + * @param timeMs + */ + seek(timeMs: number) { + if (typeof (this.audioPlayer) != `undefined`) { + LogUtil.info(`${TAG} audioPlayer time to jump. time:` + timeMs); + this.audioPlayer.seek(timeMs); + } + } + + /** + * audioPlayer pause + */ + pause() { + if (typeof (this.audioPlayer) != `undefined`) { + this.audioPlayer.on('pause', () => { + LogUtil.info(`${TAG} audioPlayer pause success`); + this.stopTimer(); + }); + LogUtil.info(`${TAG} audioPlayer pause`); + this.audioPlayer.pause(); + } + } + + /** + * audioPlayer stop + */ + stop() { + if (typeof (this.audioPlayer) != `undefined` && this.state != AudioState.IDLE) { + LogUtil.info(`${TAG} audioPlayer stop`); + this.state = AudioState.IDLE; + this.notifyStateChanges(); + clearInterval(this.intervalId); + this.intervalId = -1; + } + } + + /** + * audioPlayer continue play + */ + continuePlay() { + if (typeof (this.audioPlayer) != `undefined`) { + LogUtil.info(`${TAG} audioPlayer continue play`); + this.audioPlayer.play(); + } + } + + /** + * startTimer + */ + startTimer() { + if (this.intervalId != -1) { + return; + } + this.state = AudioState.RUNNING; + this.notifyStateChanges(); + LogUtil.info(`${TAG} audioPlayer show time update start`); + this.intervalId = setInterval(() => { + this.currentTimeMs = this.audioPlayer.currentTime; + this.notifyTimeChanges(); + }, NOTIFY_INTERVAL_MS); + } + + /** + * stopTimer + */ + stopTimer() { + if (this.intervalId == -1) { + return; + } + this.state = AudioState.PAUSED; + this.notifyStateChanges(); + LogUtil.info(`${TAG} audioPlayer show time update stop`); + clearInterval(this.intervalId); + this.intervalId = -1; + } + + /** + * audioPlayer release + */ + release() { + if (typeof (this.audioPlayer) != `undefined`) { + this.currentTimeMs = 0; + this.notifyTimeChanges(); + LogUtil.info(`${TAG} audioPlayer release`); + this.audioPlayer.release(); + this.audioPlayer = undefined; + } + } + +} \ No newline at end of file diff --git a/feature/recorder/src/main/ets/controller/MediaController.ets b/feature/recorder/src/main/ets/controller/MediaController.ets new file mode 100644 index 0000000000000000000000000000000000000000..103d07284684cdb6ff8370a1d666290e04515bcd --- /dev/null +++ b/feature/recorder/src/main/ets/controller/MediaController.ets @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 mediaLibrary from '@ohos.multimedia.mediaLibrary'; +import { Util } from '@ohos/common'; +import { RecordData } from '../model/RecordData'; +import data_preferences from '@ohos.data.preferences'; +import { LogUtil } from '@ohos/common'; + +const TAG = 'MediaController : '; + +class MediaController { + private mMediaLibrary: mediaLibrary.MediaLibrary = mediaLibrary.getMediaLibrary(globalThis.abilityContext); + private preferences: data_preferences.Preferences = undefined; + + constructor() { + this.initPreferences(); + } + + /** + * delete Record + * + * @param deleteRecordUri + * @param callback + */ + deleteRecord(deleteRecordUri: string, callback: () => void) { + LogUtil.info(`${TAG} delete Asset. uri: ${deleteRecordUri}`) + this.mMediaLibrary.deleteAsset(deleteRecordUri).then(() => { + LogUtil.info(`${TAG} delete Asset successs.`); + callback(); + }); + } + + /** + * init Preferences + */ + async initPreferences() { + LogUtil.info(`${TAG} initPreferences.`); + this.preferences = await data_preferences.getPreferences(globalThis.abilityContext, 'myRecorderStore'); + } + + /** + * save File Duration to preferences + * + * @param name + * @param value + */ + async saveFileDuration(name: string, value: number) { + LogUtil.info(`${TAG} saveFileDuration. fileName : ${name}. duration:${value}`); + if (this.preferences) { + await this.preferences.put(name, value); + this.preferences.flush(); + LogUtil.info(`${TAG} save File Duration end.`); + } + } + + /** + * get File Duration from preferences + * + * @param name + */ + async getFileDuration(name: string) { + LogUtil.info(`${TAG} getFileDuration. fileName : ${name}.`); + return this.preferences.get(name, 0); + } + + /** + * query All Audios + */ + async queryAllAudios() { + LogUtil.info(`${TAG} queryAllAudios`); + let fileKeyObj = mediaLibrary.FileKey; + let fetchOp = { + selections: `${fileKeyObj.MEDIA_TYPE}=?`, + selectionArgs: [`${mediaLibrary.MediaType.AUDIO}`], + } + LogUtil.info(`${TAG} getFileAssets`); + const fetchFileResult = await this.mMediaLibrary.getFileAssets(fetchOp); + LogUtil.info(`${TAG} Assets count : ${fetchFileResult.getCount()}`); + let result: Array = []; + if (fetchFileResult.getCount() > 0) { + let fileAssets = await fetchFileResult.getAllObject(); + for (let i = 0; i < fileAssets.length; i++) { + let record = new RecordData(fileAssets[i]); + if (record.duration == 0) { + await record.getDuration(); + } + result.push(record); + } + } + return result; + } + + /** + * create Audio File + */ + async createAudioFile() { + LogUtil.info(`${TAG} createAudioFile`); + this.mMediaLibrary = mediaLibrary.getMediaLibrary(globalThis.abilityContext); + let displayName = Util.getDate() + "_" + Util.getTime() + ".m4a"; + LogUtil.info(`${TAG} displayName : ${displayName}`); + let publicPath = await this.mMediaLibrary.getPublicDirectory(mediaLibrary.DirectoryType.DIR_AUDIO); + LogUtil.info(`${TAG} publicPath : ${publicPath}`); + return await this.mMediaLibrary.createAsset(mediaLibrary.MediaType.AUDIO, displayName, publicPath); + } +} + +export default new MediaController(); \ No newline at end of file diff --git a/feature/recorder/src/main/ets/controller/RecordController.ets b/feature/recorder/src/main/ets/controller/RecordController.ets new file mode 100644 index 0000000000000000000000000000000000000000..6dc9992f8c07eb8abf1f0e597fbacdb9426f6a8f --- /dev/null +++ b/feature/recorder/src/main/ets/controller/RecordController.ets @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 media from '@ohos.multimedia.media'; +import { LogUtil } from '@ohos/common'; +import Notification from '@ohos.notification'; + +const NOTIFY_INTERVAL_MS = 60; +const NOTIFY_ID = 10023; +const TAG = 'RecordController : '; + +let audioConfig = { + audioEncodeBitRate: 22050, + audioSampleRate: 22050, + numberOfChannels: 2, + uri: '', + location: { latitude: 31, longitude: 118 }, + audioEncoderMime: media.CodecMimeType.AUDIO_AAC, + fileFormat: media.ContainerFormatType.CFT_MPEG_4A, +} + +export enum RecordState { + IDLE, + RUNNING, + PAUSED, +} + +export class RecordController { + private beforePauseTime: number = 0; + private startTimestamp: number = 0; + private notifierId: number = -1; + private state: RecordState = RecordState.PAUSED; + private currentTimeMs: number = 0; + private audioRecorder: media.AudioRecorder = undefined; + private timeUpdateListener: (currentTimeMs: number) => void = undefined; + private stateUpdateListener: (RecordState) => void = undefined; + + /** + * Notification time update + */ + private notifyTimeChanges() { + if (this.timeUpdateListener) { + this.timeUpdateListener(this.currentTimeMs); + } + } + + /** + * Notification of status updates + */ + private async notifyStateChanges() { + if (this.stateUpdateListener) { + LogUtil.info(`${TAG} state update. ${this.state}`); + this.stateUpdateListener(this.state); + } + } + + /** + * Set the time update listener + * + * @param listener + */ + public setTimeUpdateListener(listener: (currentTimeMs: number) => void) { + LogUtil.info(`${TAG} setTimeUpdateListener.`); + this.timeUpdateListener = listener; + } + + /** + * Set the status update listener + * + * @param listener + */ + public setStateUpdateListener(listener: (RecordState) => void) { + LogUtil.info(`${TAG} setStateUpdateListener.`); + this.stateUpdateListener = listener; + } + + /** + * init AudioRecorder + */ + initAudioRecorder() { + LogUtil.info(`${TAG} initAudioRecorder.`); + let recording_hint + let suspend_record_hint + globalThis.abilityContext.resourceManager.getString($r('app.string.recording_hint').id).then((value) => { + recording_hint = value + }) + globalThis.abilityContext.resourceManager.getString($r('app.string.suspend_record_hint').id).then((value) => { + suspend_record_hint = value + }) + this.audioRecorder = media.createAudioRecorder(); + this.audioRecorder.on('prepare', () => { + LogUtil.info(`${TAG} audioRecorder prepare success.`); + this.audioRecorder.start(); + }); + this.audioRecorder.on('start', () => { + LogUtil.info(`${TAG} audioRecorder start success.`); + this.publishNotification(recording_hint) + this.startTime(); + }); + this.audioRecorder.on('pause', () => { + LogUtil.info(`${TAG} audioRecorder pause success`); + this.publishNotification(suspend_record_hint) + clearInterval(this.notifierId); + this.notifierId = -1; + this.beforePauseTime += new Date().getTime() - this.startTimestamp; + this.state = RecordState.PAUSED; + this.notifyStateChanges(); + }); + this.audioRecorder.on('resume', () => { + LogUtil.info(`${TAG} audioRecorder resume success`); + this.publishNotification(recording_hint) + this.startTime(); + }); + this.audioRecorder.on('stop', () => { + LogUtil.info(`${TAG} audioRecorder stop success`); + this.logoutNotification() + this.audioRecorder.release(); + clearInterval(this.notifierId); + this.notifierId = -1; + this.state = RecordState.IDLE; + this.currentTimeMs = 0; + this.notifyStateChanges(); + this.notifyTimeChanges(); + }); + } + + /** + * Gets the sound size of the recording + */ + getSoundVolume() { //todo + return Math.random() * 100 + 5; + } + + /** + * Recorder start + * + * @param pathName + */ + startRecorder(pathName) { + if (typeof (this.audioRecorder) != 'undefined') { + audioConfig.uri = pathName; + LogUtil.info(`${TAG} audioRecorder prepare.`); + this.audioRecorder.prepare(audioConfig); + } else { + LogUtil.error(`${TAG} case failed, audioRecorder is null.`); + } + } + + /** + * start Time + */ + startTime() { + this.state = RecordState.RUNNING; + this.startTimestamp = new Date().getTime(); + LogUtil.info(`${TAG} audioRecorder show time update start`); + this.notifierId = setInterval(() => { + let timestamp = new Date().getTime(); + this.currentTimeMs = this.beforePauseTime + timestamp - this.startTimestamp; + this.notifyTimeChanges(); + }, NOTIFY_INTERVAL_MS); + this.notifyStateChanges(); + } + + /** + * Recorder pause + */ + pause() { + if (typeof (this.audioRecorder) != `undefined`) { + LogUtil.info(`${TAG} audioRecorder pause`); + this.audioRecorder.pause(); + } + } + + /** + * Recorder resume + */ + resume() { + if (typeof (this.audioRecorder) != `undefined`) { + LogUtil.info(`${TAG} audioRecorder resume`); + this.audioRecorder.resume(); + } + } + + /** + * Recorder stop + */ + stop(callback?) { + if (typeof (this.audioRecorder) != `undefined`) { + LogUtil.info(`${TAG} audioRecorder stop`); + this.audioRecorder.stop(); + if (callback) { + callback(); + } + } + } + + /** + * publish Notification + */ + publishNotification(text) { + var notificationRequest = { + id: NOTIFY_ID, + content: { + contentType: Notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, + normal: { + title: text, + text: ``, + additionalText: `` + } + } + } + Notification.publish(notificationRequest) + } + + /** + * logout Notification + */ + logoutNotification() { + Notification.cancel(NOTIFY_ID) + } +} \ No newline at end of file diff --git a/feature/recorder/src/main/ets/controller/StateController.ets b/feature/recorder/src/main/ets/controller/StateController.ets new file mode 100644 index 0000000000000000000000000000000000000000..b8c1ea6f3e12f2cdfe000c6174faea17018a9e45 --- /dev/null +++ b/feature/recorder/src/main/ets/controller/StateController.ets @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { LogUtil } from '@ohos/common'; + +const TAG = 'ControlState : '; + +export enum ControlState { + START, + END, +} + +export class StateController { + private state: ControlState = ControlState.END; + private stateUpdateListener: (RecordState) => void = undefined; + + /** + * Set the status update listener + * + * @param listener + */ + public setStateUpdateListener(listener: (RecordState) => void) { + this.stateUpdateListener = listener; + } + + /** + * Notification of status updates + */ + private async notifyStateChanges() { + if (this.stateUpdateListener) { + this.stateUpdateListener(this.state); + } + } + + /** + * Announce the start of recording + */ + public start() { + LogUtil.info(`${TAG} record start.`); + this.state = ControlState.START; + this.notifyStateChanges(); + } + + /** + * Announce the end of recording + */ + public end() { + LogUtil.info(`${TAG} record end.`); + this.state = ControlState.END; + this.notifyStateChanges(); + } +} \ No newline at end of file diff --git a/feature/recorder/src/main/ets/model/RecordData.ets b/feature/recorder/src/main/ets/model/RecordData.ets new file mode 100644 index 0000000000000000000000000000000000000000..60e96623cfaa5b9537d20eacafe8b215a76aee66 --- /dev/null +++ b/feature/recorder/src/main/ets/model/RecordData.ets @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { default as MediaController } from '../controller/MediaController'; +import mediaLibrary from '@ohos.multimedia.mediaLibrary'; + +export class RecordData { + fileAsset: mediaLibrary.FileAsset; + title: string; + duration: number; + date: number; + + constructor(fileAsset: mediaLibrary.FileAsset) { + this.fileAsset = fileAsset; + if (fileAsset) { + if (fileAsset.duration > 0) { + this.duration = fileAsset.duration; + } else { + this.duration = 0; + } + this.date = fileAsset.dateAdded; + this.title = fileAsset.title; + } else { + this.duration = 0; + this.title = ''; + } + } + + async getDuration() { + this.duration = Number(await MediaController.getFileDuration(this.fileAsset.title)); + } +} \ No newline at end of file diff --git a/feature/recorder/src/main/module.json5 b/feature/recorder/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ed1e0395a095843abbd6047df13d59d682c9c980 --- /dev/null +++ b/feature/recorder/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "recorder", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ], + "uiSyntax": "ets" + } +} diff --git a/feature/recorder/src/main/resources/base/element/string.json b/feature/recorder/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/feature/recorder/src/main/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/figures/buildHap.png b/figures/buildHap.png new file mode 100644 index 0000000000000000000000000000000000000000..7b2bf97258248c2e28a44150b129965e1a4d71d5 Binary files /dev/null and b/figures/buildHap.png differ diff --git a/figures/install.png b/figures/install.png new file mode 100644 index 0000000000000000000000000000000000000000..938d678caa5ca490f39607eca80f55d46836f6d5 Binary files /dev/null and b/figures/install.png differ diff --git a/figures/record.png b/figures/record.png new file mode 100644 index 0000000000000000000000000000000000000000..5fdd630e7d12482e0d01b1df78b61b1cf05858f8 Binary files /dev/null and b/figures/record.png differ diff --git a/figures/signature.png b/figures/signature.png new file mode 100644 index 0000000000000000000000000000000000000000..401a2e346be5237989fdb4880fbf53a76c76a8ee Binary files /dev/null and b/figures/signature.png differ diff --git a/hvigorfile.js b/hvigorfile.js new file mode 100644 index 0000000000000000000000000000000000000000..5f2735e3deeaf655828407544bbed9365c258278 --- /dev/null +++ b/hvigorfile.js @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').appTasks \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..5694510c41deffd4f7a9c13b7ba9ce20e3190e33 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "license": "ISC", + "devDependencies": {}, + "name": "ohosrecorder", + "ohos": { + "org": "huawei", + "directoryLevel": "project", + "buildTool": "hvigor" + }, + "description": "example description", + "repository": {}, + "version": "1.0.0", + "dependencies": { + "@ohos/hypium": "1.0.2", + "@ohos/hvigor-ohos-plugin": "1.2.2", + "@ohos/hvigor": "1.2.2" + } +} diff --git a/product/phone/.gitignore b/product/phone/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5a6ba80fa3d9498a23ae8ae7d9518f8743fa8a96 --- /dev/null +++ b/product/phone/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/.preview +/build +/.cxx \ No newline at end of file diff --git a/product/phone/build-profile.json5 b/product/phone/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f8f03407f77914b43168aeca6bb0929efd6700b4 --- /dev/null +++ b/product/phone/build-profile.json5 @@ -0,0 +1,13 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/product/phone/hvigorfile.js b/product/phone/hvigorfile.js new file mode 100644 index 0000000000000000000000000000000000000000..d7720ee6a7aad5c617d1fd2f6fc8c87067bfa32c --- /dev/null +++ b/product/phone/hvigorfile.js @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').hapTasks diff --git a/product/phone/package-lock.json b/product/phone/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..0b4f8dc111bbf1ecc45dc631e7ad3bf42e596677 --- /dev/null +++ b/product/phone/package-lock.json @@ -0,0 +1,22 @@ +{ + "name": "phone", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@ohos/common": { + "version": "file:../../common" + }, + "@ohos/recorder": { + "version": "file:../../feature/recorder", + "requires": { + "@ohos/common": "file:../../common" + }, + "dependencies": { + "@ohos/common": { + "version": "file:../../common" + } + } + } + } +} diff --git a/product/phone/package.json b/product/phone/package.json new file mode 100644 index 0000000000000000000000000000000000000000..9c0ae7b1545ebb53266c0a066a6a60a5007fe4b9 --- /dev/null +++ b/product/phone/package.json @@ -0,0 +1,17 @@ +{ + "license": "ISC", + "devDependencies": {}, + "name": "phone", + "ohos": { + "org": "huawei", + "directoryLevel": "module", + "buildTool": "hvigor" + }, + "description": "example description", + "repository": {}, + "version": "1.0.0", + "dependencies": { + "@ohos/recorder": "file:../../feature/recorder", + "@ohos/common": "file:../../common" + } +} diff --git a/product/phone/src/main/ets/Application/MyAbilityStage.ts b/product/phone/src/main/ets/Application/MyAbilityStage.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a9efc1ed476ee2dfdca792a1632b37b99922670 --- /dev/null +++ b/product/phone/src/main/ets/Application/MyAbilityStage.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 AbilityStage from "@ohos.application.AbilityStage" + +export default class MyAbilityStage extends AbilityStage { + onCreate() { + console.log("[Demo] MyAbilityStage onCreate") + } +} \ No newline at end of file diff --git a/product/phone/src/main/ets/MainAbility/MainAbility.ts b/product/phone/src/main/ets/MainAbility/MainAbility.ts new file mode 100644 index 0000000000000000000000000000000000000000..67501c1826d9d010967d72a2428c02442e5d1423 --- /dev/null +++ b/product/phone/src/main/ets/MainAbility/MainAbility.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 Ability from '@ohos.application.Ability' +import Notification from '@ohos.notification'; + +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("[Demo] MainAbility onCreate") + globalThis.abilityWant = want; + globalThis.abilityContext = this.context; + } + + onDestroy() { + Notification.cancelAll() + console.log("[Demo] MainAbility onDestroy") + } + + onWindowStageCreate(windowStage) { + // Main window is created, set main page for this ability + console.log("[Demo] MainAbility onWindowStageCreate") + this.context.requestPermissionsFromUser(['ohos.permission.MICROPHONE', 'ohos.permission.WRITE_MEDIA', 'ohos.permission.READ_MEDIA']).then(() => { + Notification.requestEnableNotification().then(() => { + windowStage.loadContent("pages/index", (err, data) => { + if (err.code) { + console.error('Failed to load the content. Cause:' + JSON.stringify(err)); + return; + } + console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data)) + }); + }) + }) + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + console.log("[Demo] MainAbility onWindowStageDestroy") + } + + onForeground() { + // Ability has brought to foreground + console.log("[Demo] MainAbility onForeground") + } + + onBackground() { + // Ability has back to background + console.log("[Demo] MainAbility onBackground") + } +}; diff --git a/product/phone/src/main/ets/pages/RecordingPage.ets b/product/phone/src/main/ets/pages/RecordingPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..a99deb48a0189c59e7c02649a64753eaf836d043 --- /dev/null +++ b/product/phone/src/main/ets/pages/RecordingPage.ets @@ -0,0 +1,252 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { ConfigData, Util } from '@ohos/common' +import { RecordController, MediaController, RecordState } from '@ohos/recorder' +import router from '@ohos.router'; +import mediaLibrary from '@ohos.multimedia.mediaLibrary' +import prompt from '@ohos.prompt'; +import call from '@ohos.telephony.call'; + +const TIME_INTERVAL_S = 2; +const TIME_COUNT = 2 * 2 + 1; +const VOICE_FREQUENCY = 25; + +@Entry +@Component +struct RecordingPage { + @State currentTime: number = 0 + @State state: RecordState = RecordState.IDLE + private recordController: RecordController = new RecordController() + @State fileAsset: mediaLibrary.FileAsset = undefined + heightList: Array = [] + mIntervalId = -1 + + async aboutToAppear() { + let that = this + this.recordController.setTimeUpdateListener((currentTimeMs: number) => { + if (Math.floor(currentTimeMs / (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY)) - Math.floor(this.currentTime / (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY)) == 1) { + that.heightList.splice(0, 1) + that.heightList.push(this.recordController.getSoundVolume()) + } + this.currentTime = currentTimeMs + }) + this.recordController.setStateUpdateListener((state) => { + this.state = state + }) + this.recordController.initAudioRecorder() + this.fileAsset = await MediaController.createAudioFile() + let fd = await this.fileAsset.open('Rw') + this.recordController.startRecorder(`fd://${fd}`) + this.initHeightList() + } + + aboutToDisappear() { + if (this.state != RecordState.IDLE) { + this.recordController.stop() + } + } + + initHeightList() { + this.heightList = [] + for (let index = 0; index < VOICE_FREQUENCY; index++) { + this.heightList.push(0) + } + } + + build() { + Column() { + Row() { + Row() { + Image($r("app.media.ic_back")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + .margin({ left: $r("app.float.distance_32"), right: $r("app.float.distance_16") }) + Text($r("app.string.title")) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_40")) + .lineHeight($r("app.float.wh_value_37_5")) + } + .onClick(() => { + router.back() + }) + + Image($r("app.media.ic_clock_enhance")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + .margin({ right: $r("app.float.distance_32") }) + } + .height($r("app.float.wh_value_56")) + .width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.SpaceBetween) + + Stack({ alignContent: Alignment.Top }) { + Column() { + Row() { + ForEach(Array.from(Array(TIME_COUNT + 1).keys()), (item) => { + Column() { + Text(Util.timeFormat(((item - (TIME_COUNT - 1) / 2) * 1000 * TIME_INTERVAL_S + Math.floor(this.currentTime / (1000 * TIME_INTERVAL_S)) * (1000 * TIME_INTERVAL_S)), false)) + .textAlign(TextAlign.Center) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_20")) + .opacity($r("app.float.opacity_6")) + Row() { + ForEach(Array.from(Array(5).keys()), (scaleItem) => { + Rect() + .width($r("app.float.wh_value_2_5")) + .fill($r("app.color.text_color")) + .height(scaleItem == 2 ? $r("app.float.wh_value_26_5") : $r("app.float.wh_value_13_5")) + .opacity(scaleItem == 2 ? $r("app.float.opacity_4") : $r("app.float.opacity_2")) + }, scaleItem => scaleItem) + }.width(ConfigData.WH_100_100) + .alignItems(VerticalAlign.Top) + .height($r("app.float.wh_value_26_5")) + .justifyContent(FlexAlign.SpaceAround) + }.width((100 / TIME_COUNT) + "%") + }, item => item) + } + .offset({ + x: (-(100 / TIME_COUNT) * (this.currentTime % (TIME_INTERVAL_S * 1000) / (TIME_INTERVAL_S * 1000))) + "%" + }) + + Row() { + Row() { + Row() { + ForEach(Array.from(Array(VOICE_FREQUENCY).keys()), (item) => { + Rect() + .width($r("app.float.wh_value_2_5")) + .fill($r("app.color.text_color")) + .height(this.heightList[item]) + .opacity($r("app.float.opacity_2")) + }, item => item) + }.width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.SpaceAround) + .offset({ + x: (-(100 / VOICE_FREQUENCY) * (this.currentTime % (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY) / (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY) - 1)) + "%" + }) + } + .clip(true) + .width(ConfigData.WH_50_100) + .height(ConfigData.WH_100_100) + } + .justifyContent(FlexAlign.Start) + .width(ConfigData.WH_100_100) + .height($r("app.float.wh_value_266_5")) + .backgroundColor($r("app.color.shadow_color")) + } + + Column() { + Rect() + .height($r("app.float.wh_value_304")) + .width($r("app.float.wh_value_2_5")) + .fill($r("app.color.pointer_color")) + Circle() + .height($r("app.float.wh_value_10_5")) + .width($r("app.float.wh_value_10_5")) + .fill($r("app.color.pointer_color")) + }.margin({ top: $r("app.float.distance_16") }) + + }.width(ConfigData.WH_100_100) + .height($r("app.float.wh_value_346_5")) + + + Text(Util.timeFormatMs(this.currentTime)) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_96")) + .lineHeight($r("app.float.wh_value_85_5")) + Text($r("app.string.predict_hint")) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_28")) + .opacity($r("app.float.opacity_6")) + .visibility(this.currentTime > (10 * 1000) ? Visibility.Hidden : Visibility.Visible) + Blank() + Row() { + Column() { + Image($r("app.media.ic_clock_mark")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + Text($r("app.string.mark")) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_20")) + .lineHeight($r("app.float.wh_value_18_5")) + } + .justifyContent(FlexAlign.Center) + .height($r("app.float.wh_value_64")) + .width($r("app.float.wh_value_64")) + .borderRadius($r("app.float.wh_value_64")) + + Column() { + Image(this.state == RecordState.IDLE ? $r("app.media.ic_clock_record") : $r("app.media.ic_clock_stop")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + } + .justifyContent(FlexAlign.Center) + .height($r("app.float.wh_value_64")) + .width($r("app.float.wh_value_64")) + .borderRadius($r("app.float.wh_value_64")) + .backgroundColor($r("app.color.white")) + .onClick(async () => { + if (this.state == RecordState.IDLE) { + let hasCall = await call.hasCall() + if (hasCall) { + prompt.showToast({ message: $r('app.string.has_call_hint') }) + } else { + this.recordController.initAudioRecorder() + this.fileAsset = await MediaController.createAudioFile() + let fd = await this.fileAsset.open('Rw') + this.recordController.startRecorder(`fd://${fd}`) + } + } else { + this.recordController.stop(() => { + this.initHeightList() + MediaController.saveFileDuration(this.fileAsset.title, this.currentTime) + router.replace({ + url: "pages/index" + }) + }) + } + }) + + Column() { + Image(this.state == RecordState.RUNNING ? $r("app.media.ic_clock_suspend") : $r("app.media.ic_clock_play")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + Text(this.state == RecordState.RUNNING ? $r("app.string.pause") : $r("app.string.continue_record")) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_20")) + .lineHeight($r("app.float.wh_value_18_5")) + } + .justifyContent(FlexAlign.Center) + .height($r("app.float.wh_value_64")) + .width($r("app.float.wh_value_64")) + .borderRadius($r("app.float.wh_value_64")) + .onClick(() => { + if (this.state == RecordState.RUNNING) { + this.recordController.pause() + } else { + this.recordController.resume() + } + }) + } + .margin({ bottom: $r("app.float.wh_value_32") }) + .width(ConfigData.WH_100_100) + .padding({ left: $r("app.float.distance_80"), right: $r("app.float.distance_80") }) + .justifyContent(FlexAlign.SpaceBetween) + } + .height(ConfigData.WH_100_100) + .width(ConfigData.WH_100_100) + .backgroundColor($r("app.color.background_color")) + } +} \ No newline at end of file diff --git a/product/phone/src/main/ets/pages/index.ets b/product/phone/src/main/ets/pages/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..fa199ba7c4baee87b7bb8e3af2af72110c18eac2 --- /dev/null +++ b/product/phone/src/main/ets/pages/index.ets @@ -0,0 +1,305 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { ConfigData, Util } from '@ohos/common' +import router from '@ohos.router'; +import { AudioController, AudioState, MediaController, RecordData } from '@ohos/recorder' +import call from '@ohos.telephony.call'; +import prompt from '@ohos.prompt'; + +@Entry +@Component +struct Index { + @State recordList: Array = [] + @State selectedIndex: number = -1 + @State currentTime: number = 0 + private audioController: AudioController = new AudioController() + @State state: AudioState = AudioState.IDLE; + @State filterText: string = ""; + private deleteRecord: RecordData = undefined + confirm: () => void = () => { + MediaController.deleteRecord(this.deleteRecord.fileAsset.uri, () => { + for (let i = this.recordList.length - 1;i >= 0; i--) { + if (this.deleteRecord.fileAsset.uri == this.recordList[i].fileAsset.uri) { + this.recordList.splice(i, 1) + } + } + }) + } + dialogController: CustomDialogController = new CustomDialogController({ + builder: DeleteRecordDialog({ deleteRecordTitle: this.deleteRecord.title, confirm: this.confirm }), + autoCancel: true, + alignment: DialogAlignment.Bottom, + offset: { dy: -64, dx: 0 } + }) + + async getAudios() { + this.recordList = await MediaController.queryAllAudios() + } + + aboutToAppear() { + this.getAudios() + this.audioController.setTimeUpdateListener((currentTime: number) => { + this.currentTime = currentTime + }) + this.audioController.setStateUpdateListener((state: AudioState) => { + if (state == AudioState.IDLE) { + this.selectedIndex = -1 + } + this.state = state + }) + } + + build() { + Column() { + Row() { + Image($r("app.media.ic_clock_add")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + .margin({ right: $r("app.float.distance_32") }) + + Image($r("app.media.public_more")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + .margin({ right: $r("app.float.distance_32") }) + } + .height($r("app.float.wh_value_56")) + .width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.End) + + Row() { + Text($r("app.string.title")) + .fontSize($r("app.float.font_48")) + .fontColor($r("app.color.text_color")) + .fontWeight(500) + .margin({ left: $r("app.float.distance_32") }) + } + .height($r("app.float.wh_value_56")) + .width(ConfigData.WH_100_100) + + Column() { + Stack({ alignContent: Alignment.Start }) { + TextInput({ placeholder: $r("app.string.search_recording_files") }) + .width(ConfigData.WH_100_100) + .height($r("app.float.wh_value_53_5")) + .backgroundColor($r("app.color.white")) + .borderRadius($r("app.float.distance_53_5")) + .padding({ left: $r("app.float.distance_53_5") }) + .onChange((value) => { + this.filterText = value + }) + Image($r("app.media.ic_record_search")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + .margin({ left: $r("app.float.wh_value_16") }) + }.width(ConfigData.WH_100_100) + .height($r("app.float.wh_value_53_5")) + + Column() { + List() { + ForEach([...this.recordList].reverse().map((value, index) => { + return { i: index, data: value } + }), (item) => { + ListItem() { + Column() { + Row() { + Column() { + Text(item.data.title) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_32")) + .width($r("app.float.wh_value_280")) + .lineHeight($r("app.float.wh_value_28")) + Text(Util.getDateFormat(item.data.date)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_28")) + .width($r("app.float.wh_value_280")) + .lineHeight($r("app.float.wh_value_25_5")) + } + + Row() { + Text(Util.timeFormat(item.data.duration)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_28")) + .margin({ right: $r("app.float.distance_2_5") }) + Image((item.i == this.selectedIndex && this.state == AudioState.RUNNING) ? $r("app.media.ic_record_stop") : $r("app.media.ic_record_play")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + } + } + .height($r("app.float.wh_value_85_5")) + .width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.SpaceBetween) + .padding({ left: $r("app.float.distance_21_5"), right: $r("app.float.distance_21_5") }) + .onClick(() => { + if (this.selectedIndex != item.i || this.state == AudioState.IDLE) { + this.selectedIndex = item.i + this.audioController.initAudioPlayer(item.data.fileAsset, () => { + this.audioController.play() + }) + } else if (this.state == AudioState.RUNNING) { + this.audioController.pause() + } else if (this.state == AudioState.PAUSED) { + this.audioController.continuePlay() + } + }) + .gesture( + LongPressGesture() + .onAction(() => { + this.audioController.stop() + this.deleteRecord = item.data + this.dialogController.open() + }) + ) + + if (this.selectedIndex == item.i) { + Column() { + Slider({ + value: this.currentTime, + min: 0, + max: item.data.duration, + style: SliderStyle.OutSet + }) + .width($r("app.float.wh_value_433")) + .onChange((value: number, mode: SliderChangeMode) => { + if (mode == SliderChangeMode.End || mode == 3) { + this.currentTime = value + this.audioController.seek(value) + } + }) + + Row() { + Text(Util.timeFormat(this.currentTime)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_20")) + .margin({ right: $r("app.float.distance_2_5") }) + Text(Util.timeFormat(item.data.duration)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_20")) + .margin({ right: $r("app.float.distance_2_5") }) + } + .width($r("app.float.wh_value_405")) + .justifyContent(FlexAlign.SpaceBetween) + + }.height($r("app.float.wh_value_85_5")) + .width(ConfigData.WH_100_100) + } + } + }.visibility(item.data.title.indexOf(this.filterText) >= 0 ? Visibility.Visible : Visibility.None) + }, item => item.i) + + }.width(ConfigData.WH_100_100) + .backgroundColor($r("app.color.white")) + .borderRadius($r("app.float.wh_value_32")) + .divider({ + strokeWidth: $r("app.float.wh_value_1"), + color: $r("app.color.shadow_color"), + startMargin: $r("app.float.distance_16"), + endMargin: $r("app.float.distance_16") + }) + }.margin({ top: $r("app.float.distance_22_5") }) + .flexShrink(1) + + Blank() + Row() { + Image($r("app.media.ic_clock_record")) + .width($r("app.float.wh_value_32")) + .height($r("app.float.wh_value_32")) + } + .width($r("app.float.wh_value_64")) + .height($r("app.float.wh_value_64")) + .justifyContent(FlexAlign.Center) + .borderRadius($r("app.float.wh_value_32")) + .backgroundColor($r("app.color.white")) + .shadow({ radius: $r("app.float.distance_16"), color: $r("app.color.shadow_color") }) + .margin({ bottom: $r("app.float.distance_32"), top: $r("app.float.distance_16") }) + .onClick(async() => { + this.audioController.stop() + let hasCall = await call.hasCall() + if (hasCall) { + prompt.showToast({ message: $r('app.string.has_call_hint') }) + } else { + router.replace({ + url: "pages/RecordingPage" + }) + } + }) + } + .height(ConfigData.WH_100_100) + .flexShrink(1) + .justifyContent(FlexAlign.Center) + .padding({ + left: $r("app.float.distance_16"), + right: $r("app.float.distance_16"), + top: $r("app.float.distance_6_5") + }) + + } + .height(ConfigData.WH_100_100) + .width(ConfigData.WH_100_100) + .backgroundColor($r("app.color.background_color")) + } +} + +@CustomDialog +struct DeleteRecordDialog { + private controller: CustomDialogController + private deleteRecordTitle: string + private confirm: () => void + + build() { + Column() { + Text(this.deleteRecordTitle) + .fontSize($r("app.float.font_28")) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .width(ConfigData.WH_100_100) + .margin({ left: $r("app.float.distance_16") }) + Text($r("app.string.delete_hint")) + .fontSize($r("app.float.font_32")) + .margin($r("app.float.distance_16")) + .fontColor($r("app.color.text_color")) + Row() { + Text($r("app.string.cancel")) + .fontSize($r("app.float.font_32")) + .fontColor($r("app.color.text_color")) + .width($r("app.float.wh_value_64")) + .textAlign(TextAlign.Center) + .onClick(() => { + this.controller.close() + }) + .fontColor($r("app.color.hint_color")) + Divider().vertical(true).opacity($r("app.float.opacity_4")) + Text($r("app.string.delete")) + .fontSize($r("app.float.font_32")) + .textAlign(TextAlign.Center) + .width($r("app.float.wh_value_64")) + .onClick(() => { + if (this.confirm) { + this.confirm() + this.controller.close() + } + }) + .fontColor($r("app.color.warn_color")) + }.height($r("app.float.wh_value_32")) + .width(ConfigData.WH_100_100) + .padding({ left: $r("app.float.wh_value_32"), right: $r("app.float.wh_value_32") }) + .justifyContent(FlexAlign.SpaceAround) + }.padding($r("app.float.distance_24")) + } +} \ No newline at end of file diff --git a/product/phone/src/main/module.json5 b/product/phone/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4bd4b498d60cc1c008a6ae0a718dcc857f6cd850 --- /dev/null +++ b/product/phone/src/main/module.json5 @@ -0,0 +1,49 @@ +{ + "module": { + "name": "phone", + "type": "entry", + "srcEntrance": "./ets/Application/MyAbilityStage.ts", + "description": "$string:phone_desc", + "mainElement": "MainAbility", + "deviceTypes": [ + "default" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "uiSyntax": "ets", + "abilities": [ + { + "name": "MainAbility", + "srcEntrance": "./ets/MainAbility/MainAbility.ts", + "description": "$string:MainAbility_desc", + "icon": "$media:icon", + "label": "$string:MainAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:white", + "visible": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.MICROPHONE" + }, + { + "name": "ohos.permission.WRITE_MEDIA" + }, + { + "name": "ohos.permission.READ_MEDIA" + } + ] + } +} \ No newline at end of file diff --git a/product/phone/src/main/resources/base/element/color.json b/product/phone/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..1bbc9aa9617e97c45440e1d3d66afc1154837012 --- /dev/null +++ b/product/phone/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/product/phone/src/main/resources/base/element/float.json b/product/phone/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..de34807f00ebc18ce411b6180014a8929c02ea8f --- /dev/null +++ b/product/phone/src/main/resources/base/element/float.json @@ -0,0 +1,164 @@ +{ + "float": [ + { + "name": "wh_value_1", + "value": "1vp" + }, + { + "name": "wh_value_2_5", + "value": "2.5vp" + }, + { + "name": "wh_value_10_5", + "value": "10.5vp" + }, + { + "name": "wh_value_13_5", + "value": "13.5vp" + }, + { + "name": "wh_value_16", + "value": "16vp" + }, + { + "name": "wh_value_18_5", + "value": "18.5vp" + }, + { + "name": "wh_value_21_5", + "value": "21.5vp" + }, + { + "name": "wh_value_25_5", + "value": "25.5vp" + }, + { + "name": "wh_value_26_5", + "value": "26.5vp" + }, + { + "name": "wh_value_28", + "value": "28vp" + }, + { + "name": "wh_value_32", + "value": "32vp" + }, + { + "name": "wh_value_37_5", + "value": "37.5vp" + }, + { + "name": "wh_value_53_5", + "value": "53.5vp" + }, + { + "name": "wh_value_56", + "value": "56vp" + }, + { + "name": "wh_value_64", + "value": "64vp" + }, + { + "name": "wh_value_85_5", + "value": "85.5vp" + }, + { + "name": "wh_value_266_5", + "value": "266.5vp" + }, + { + "name": "wh_value_280", + "value": "280vp" + }, + { + "name": "wh_value_304", + "value": "304vp" + }, + { + "name": "wh_value_346_5", + "value": "346.5vp" + }, + { + "name": "wh_value_405", + "value": "405vp" + }, + { + "name": "wh_value_433", + "value": "433vp" + }, + { + "name": "font_20", + "value": "20px" + }, + { + "name": "font_28", + "value": "28px" + }, + { + "name": "font_32", + "value": "32px" + }, + { + "name": "font_40", + "value": "40px" + }, + { + "name": "font_48", + "value": "48px" + }, + { + "name": "font_96", + "value": "96px" + }, + { + "name": "distance_2_5", + "value": "2.5vp" + }, + { + "name": "distance_6_5", + "value": "6.5vp" + }, + { + "name": "distance_16", + "value": "16vp" + }, + { + "name": "distance_21_5", + "value": "21.5vp" + }, + { + "name": "distance_22_5", + "value": "22.5vp" + }, + { + "name": "distance_24", + "value": "24vp" + }, + { + "name": "distance_32", + "value": "32vp" + }, + { + "name": "distance_80", + "value": "80vp" + }, + { + "name": "distance_53_5", + "value": "53.5vp" + }, + { + "name": "opacity_2", + "value": "0.2" + }, + { + "name": "opacity_4", + "value": "0.4" + }, + { + "name": "opacity_6", + "value": "0.6" + } + ] +} \ No newline at end of file diff --git a/product/phone/src/main/resources/base/element/string.json b/product/phone/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..a93532c0e4937cd663f2b45f3fd9ab4c86a92e97 --- /dev/null +++ b/product/phone/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "phone_desc", + "value": "description" + }, + { + "name": "MainAbility_desc", + "value": "description" + }, + { + "name": "MainAbility_label", + "value": "录音机" + } + ] +} \ No newline at end of file diff --git a/product/phone/src/main/resources/base/media/icon.png b/product/phone/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b186c704fd1345ae527fabc8a479fc92470a8c6b Binary files /dev/null and b/product/phone/src/main/resources/base/media/icon.png differ diff --git a/product/phone/src/main/resources/base/profile/main_pages.json b/product/phone/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..8b9f2c28b5bfe9a27358b083d8d6ede9543648de --- /dev/null +++ b/product/phone/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,6 @@ +{ + "src": [ + "pages/index", + "pages/RecordingPage" + ] +} diff --git a/product/phone/src/ohosTest/ets/Application/TestAbilityStage.ts b/product/phone/src/ohosTest/ets/Application/TestAbilityStage.ts new file mode 100644 index 0000000000000000000000000000000000000000..8b2b4c83f77af465031d452094ce40d8b7d8f887 --- /dev/null +++ b/product/phone/src/ohosTest/ets/Application/TestAbilityStage.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 AbilityStage from "@ohos.application.AbilityStage" + +export default class TestAbilityStage extends AbilityStage { + onCreate() { + console.log("[Demo] TestAbilityStage onCreate") + globalThis.abilityContext = this.context + } +} \ No newline at end of file diff --git a/product/phone/src/ohosTest/ets/TestAbility/TestAbility.ts b/product/phone/src/ohosTest/ets/TestAbility/TestAbility.ts new file mode 100644 index 0000000000000000000000000000000000000000..440afc42383533e67be796608cdb600d915fd4b4 --- /dev/null +++ b/product/phone/src/ohosTest/ets/TestAbility/TestAbility.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 Ability from '@ohos.application.Ability' +import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry' +import { Hypium } from '@ohos/hypium' +import testsuite from '../test/List.test' + +export default class TestAbility extends Ability { + onCreate(want, launchParam) { + console.log('TestAbility onCreate') + var abilityDelegator: any + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var abilityDelegatorArguments: any + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + console.info('start run testcase!!!') + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + console.log('TestAbility onDestroy') + } + + onWindowStageCreate(windowStage) { + console.log('TestAbility onWindowStageCreate') + windowStage.loadContent("TestAbility/pages/index", (err, data) => { + if (err.code) { + console.error('Failed to load the content. Cause:' + JSON.stringify(err)); + return; + } + console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data)) + }); + + globalThis.abilityContext = this.context; + } + + onWindowStageDestroy() { + console.log('TestAbility onWindowStageDestroy') + } + + onForeground() { + console.log('TestAbility onForeground') + } + + onBackground() { + console.log('TestAbility onBackground') + } +}; \ No newline at end of file diff --git a/product/phone/src/ohosTest/ets/TestAbility/pages/index.ets b/product/phone/src/ohosTest/ets/TestAbility/pages/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..2a02d2b4125e012d3b57589d2305193ad8aa202b --- /dev/null +++ b/product/phone/src/ohosTest/ets/TestAbility/pages/index.ets @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 router from '@ohos.router'; + +@Entry +@Component +struct Index { + aboutToAppear() { + console.info('TestAbility index aboutToAppear') + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/product/phone/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts b/product/phone/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..66154a400bdc3c79f82d0a396f190713e7a5919f --- /dev/null +++ b/product/phone/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 TestRunner from '@ohos.application.testRunner' +import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry' + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +function translateParamsToString(parameters) { + const keySet = new Set([ + '-s class', '-s notClass', '-s suite', '-s it', + '-s level', '-s testType', '-s size', '-s timeout', + '-s dryRun' + ]) + let targetParams = ''; + for (const key in parameters) { + if (keySet.has(key)) { + targetParams = `${targetParams} ${key} ${parameters[key]}` + } + } + return targetParams.trim() +} + +async function onAbilityCreateCallback() { + console.log("onAbilityCreateCallback"); +} + +async function addAbilityMonitorCallback(err: any) { + console.info("addAbilityMonitorCallback : " + JSON.stringify(err)) +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + console.info("OpenHarmonyTestRunner OnPrepare ") + } + + async onRun() { + console.log('OpenHarmonyTestRunner onRun run') + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + cmd += ' '+translateParamsToString(abilityDelegatorArguments.parameters) + var debug = abilityDelegatorArguments.parameters["-D"] + if (debug == 'true') + { + cmd += ' -D' + } + console.info('cmd : '+cmd) + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + console.info('executeShellCommand : err : ' + JSON.stringify(err)); + console.info('executeShellCommand : data : ' + d.stdResult); + console.info('executeShellCommand : data : ' + d.exitCode); + }) + console.info('OpenHarmonyTestRunner onRun end') + } +}; \ No newline at end of file diff --git a/product/phone/src/ohosTest/ets/test/Ability.test.ets b/product/phone/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..a18de15cf90def60208f3046e0a20bec16fd0831 --- /dev/null +++ b/product/phone/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,359 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; +import { Util } from '@ohos/common'; +import { RecordController, RecordState, StateController, ControlState } from '@ohos/recorder'; + +export default function abilityTest() { + describe('ActsAbilityTest', function () { + + it('StateControllerStart', 0, async function () { + let mStateController = new StateController(); + let testState = false; + mStateController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(ControlState.START); + testState = false; + } + }); + testState = true; + mStateController.start(); + }) + + it('StateControllerEnd', 0, async function () { + let mStateController = new StateController(); + let testState = false; + mStateController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(ControlState.END); + testState = false; + } + }); + mStateController.start(); + testState = true; + setTimeout(() => { + mStateController.end(); + }, 100); + }) + + it('addZeroMin', 0, async function () { + let num = 0; + let res = Util.addZero(num); + expect(res).assertEqual('00'); + }) + + it('addZeroMinus', 0, async function () { + let num = -1; + let res = Util.addZero(num); + expect(res).assertEqual('0-1'); + }) + + it('addZeroMax', 0, async function () { + let num = 9; + let res = Util.addZero(num); + expect(res).assertEqual('09'); + }) + + it('addZeroOver', 0, async function () { + let num = 10; + let res = Util.addZero(num); + expect(res).assertEqual('10'); + }) + + it('addZeroNaN', 0, async function () { + let num = NaN; + let res = Util.addZero(num); + expect(res).assertEqual('NaN'); + }) + + it('getTime', 0, async function () { + let nowDate = new Date(); + let time = Util.getTime(); + let res = false; + if (parseInt(time.substring(0, 2)) == nowDate.getHours() && parseInt(time.substring(2, 4)) == nowDate.getMinutes() && parseInt(time.substring(4, 6)) == nowDate.getSeconds()) { + res = true; + } + expect(res).assertTrue(); + }) + + it('getDate', 0, async function () { + let nowDate = new Date(); + let time = Util.getDate(); + let res = false; + if (parseInt(time.substring(0, 4)) == nowDate.getFullYear() && parseInt(time.substring(4, 6)) == nowDate.getMonth() + 1 && parseInt(time.substring(6, 8)) == nowDate.getDate()) { + res = true; + } + expect(res).assertTrue(); + }) + + it('getTimeFormatZero', 0, async function () { + let time = 0; + let res = Util.getTimeFormat(time); + expect(res).assertEqual('00:00:00'); + }) + + it('getTimeFormat', 0, async function () { + let time = new Date('2022-11-11T11:11:11').getTime(); + let res = Util.getTimeFormat(time); + expect(res).assertEqual('11:11:11'); + }) + + it('getDateFormat', 0, async function () { + let time = new Date('2022-11-11T11:11:11').getTime(); + let res = Util.getDateFormat(time); + expect(res).assertEqual('2022/11/11'); + }) + + it('getDateFormatZero', 0, async function () { + let time = 0; + let res = Util.getDateFormat(time); + expect(res).assertEqual('1970/01/01'); + }) + + it('timeFormatMsZero', 0, async function () { + let time = 0; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMsMinus', 0, async function () { + let time = -1; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMsMin', 0, async function () { + let time = 9; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMsMs', 0, async function () { + let time = 10; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.01'); + }) + + it('timeFormatMsSecond', 0, async function () { + let time = 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:01.00'); + }) + + it('timeFormatMsMinute', 0, async function () { + let time = 60 * 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('01:00.00'); + }) + + it('timeFormatMsHour', 0, async function () { + let time = 60 * 60 * 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('01:00:00'); + }) + + it('timeFormatMsDay', 0, async function () { + let time = 24 * 60 * 60 * 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMinus', 0, async function () { + let time = -1; + let res = Util.timeFormat(time); + expect(res).assertEqual(''); + }) + + it('timeFormatZeroH', 0, async function () { + let time = 0; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:00:00`); + }) + + it('timeFormatZero', 0, async function () { + let time = 0; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:00`); + }) + + it('timeFormatMinH', 0, async function () { + let time = 999; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:00:00`); + }) + + it('timeFormatMin', 0, async function () { + let time = 999; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:00`); + }) + + it('timeFormatSecondH', 0, async function () { + let time = 1000; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:00:01`); + }) + + it('timeFormatSecond', 0, async function () { + let time = 1000; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:01`); + }) + + it('timeFormatMinuteH', 0, async function () { + let time = 60 * 1000; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:01:00`); + }) + + it('timeFormatMinute', 0, async function () { + let time = 60 * 1000; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`01:00`); + }) + + it('timeFormatHour', 0, async function () { + let time = 60 * 60 * 1000; + let res = Util.timeFormat(time); + expect(res).assertEqual(`01:00:00`); + }) + + it('timeFormatDay', 0, async function () { + let time = 24 * 60 * 60 * 1000; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:00`); + }) + + it('RecordControllerStartTime', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.RUNNING); + testState = false; + } + }); + testState = true; + mRecordController.startTime(); + }) + + it('RecordStartTime', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setTimeUpdateListener((time) => { + if (testState) { + expect(time > 0).assertTrue(); + testState = false; + } + }); + testState = true; + mRecordController.startTime(); + }) + + it('RecordControllerPause', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.PAUSED); + testState = false; + } + }); + mRecordController.startTime(); + testState = true; + setTimeout(() => { + mRecordController.pause(); + }, 100); + }) + + it('RecordControllerResume', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.RUNNING); + testState = false; + } + }); + mRecordController.startTime(); + mRecordController.pause(); + testState = true; + setTimeout(() => { + mRecordController.resume(); + }, 100); + }) + + it('RecordControllerPauseStop', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.IDLE); + testState = false; + } + }); + mRecordController.startTime(); + mRecordController.pause(); + setTimeout(() => { + mRecordController.stop(() => { + testState = true; + }); + }, 100); + }) + + it('RecordControllerStop', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.IDLE); + testState = false; + } + }); + mRecordController.startTime(); + setTimeout(() => { + mRecordController.stop(() => { + testState = true; + }); + }, 100); + }) + + it('RecordStop', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setTimeUpdateListener((time) => { + if (testState) { + expect(time).assertEqual(0); + testState = false; + } + }); + mRecordController.startTime(); + setTimeout(() => { + mRecordController.stop(() => { + testState = true; + }); + }, 100); + }) + + }) +} \ No newline at end of file diff --git a/product/phone/src/ohosTest/ets/test/List.test.ets b/product/phone/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..fd9764189c525a7ae769839735e6e775c3778077 --- /dev/null +++ b/product/phone/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 abilityTest from './Ability.test' + +export default function testsuite() { + abilityTest() +} \ No newline at end of file diff --git a/product/phone/src/ohosTest/module.json5 b/product/phone/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..809f37570e1045d13f747149ba5d7ae859b34346 --- /dev/null +++ b/product/phone/src/ohosTest/module.json5 @@ -0,0 +1,38 @@ +{ + "module": { + "name": "phone_test", + "type": "feature", + "srcEntrance": "./ets/Application/TestAbilityStage.ts", + "description": "$string:entry_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "default" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "uiSyntax": "ets", + "abilities": [ + { + "name": "TestAbility", + "srcEntrance": "./ets/TestAbility/TestAbility.ts", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "visible": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:white", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/product/phone/src/ohosTest/resources/base/element/color.json b/product/phone/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..1bbc9aa9617e97c45440e1d3d66afc1154837012 --- /dev/null +++ b/product/phone/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/product/phone/src/ohosTest/resources/base/element/string.json b/product/phone/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..36d4230c53e9f5a07ae343ad8dc9808341975e3b --- /dev/null +++ b/product/phone/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "entry_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/product/phone/src/ohosTest/resources/base/media/icon.png b/product/phone/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/product/phone/src/ohosTest/resources/base/media/icon.png differ diff --git a/product/phone/src/ohosTest/resources/base/profile/test_pages.json b/product/phone/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..fcef82b4dfc18e28106ff9ecd1c8b48ec74d18a4 --- /dev/null +++ b/product/phone/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "TestAbility/pages/index" + ] +} diff --git a/product/tablet/.gitignore b/product/tablet/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5a6ba80fa3d9498a23ae8ae7d9518f8743fa8a96 --- /dev/null +++ b/product/tablet/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/.preview +/build +/.cxx \ No newline at end of file diff --git a/product/tablet/build-profile.json5 b/product/tablet/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f8f03407f77914b43168aeca6bb0929efd6700b4 --- /dev/null +++ b/product/tablet/build-profile.json5 @@ -0,0 +1,13 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/product/tablet/hvigorfile.js b/product/tablet/hvigorfile.js new file mode 100644 index 0000000000000000000000000000000000000000..d7720ee6a7aad5c617d1fd2f6fc8c87067bfa32c --- /dev/null +++ b/product/tablet/hvigorfile.js @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').hapTasks diff --git a/product/tablet/package-lock.json b/product/tablet/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..a45b41de70ebfabae4fa1f8b882a81d993137fd3 --- /dev/null +++ b/product/tablet/package-lock.json @@ -0,0 +1,22 @@ +{ + "name": "tablet", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@ohos/common": { + "version": "file:../../common" + }, + "@ohos/recorder": { + "version": "file:../../feature/recorder", + "requires": { + "@ohos/common": "file:../../common" + }, + "dependencies": { + "@ohos/common": { + "version": "file:../../common" + } + } + } + } +} diff --git a/product/tablet/package.json b/product/tablet/package.json new file mode 100644 index 0000000000000000000000000000000000000000..5195036991e533f6949ea0822aa8ffe0098035cc --- /dev/null +++ b/product/tablet/package.json @@ -0,0 +1,17 @@ +{ + "license": "ISC", + "devDependencies": {}, + "name": "tablet", + "ohos": { + "org": "huawei", + "directoryLevel": "module", + "buildTool": "hvigor" + }, + "description": "example description", + "repository": {}, + "version": "1.0.0", + "dependencies": { + "@ohos/recorder": "file:../../feature/recorder", + "@ohos/common": "file:../../common" + } +} diff --git a/product/tablet/src/main/ets/Application/MyAbilityStage.ts b/product/tablet/src/main/ets/Application/MyAbilityStage.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a9efc1ed476ee2dfdca792a1632b37b99922670 --- /dev/null +++ b/product/tablet/src/main/ets/Application/MyAbilityStage.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 AbilityStage from "@ohos.application.AbilityStage" + +export default class MyAbilityStage extends AbilityStage { + onCreate() { + console.log("[Demo] MyAbilityStage onCreate") + } +} \ No newline at end of file diff --git a/product/tablet/src/main/ets/MainAbility/MainAbility.ts b/product/tablet/src/main/ets/MainAbility/MainAbility.ts new file mode 100644 index 0000000000000000000000000000000000000000..67501c1826d9d010967d72a2428c02442e5d1423 --- /dev/null +++ b/product/tablet/src/main/ets/MainAbility/MainAbility.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 Ability from '@ohos.application.Ability' +import Notification from '@ohos.notification'; + +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("[Demo] MainAbility onCreate") + globalThis.abilityWant = want; + globalThis.abilityContext = this.context; + } + + onDestroy() { + Notification.cancelAll() + console.log("[Demo] MainAbility onDestroy") + } + + onWindowStageCreate(windowStage) { + // Main window is created, set main page for this ability + console.log("[Demo] MainAbility onWindowStageCreate") + this.context.requestPermissionsFromUser(['ohos.permission.MICROPHONE', 'ohos.permission.WRITE_MEDIA', 'ohos.permission.READ_MEDIA']).then(() => { + Notification.requestEnableNotification().then(() => { + windowStage.loadContent("pages/index", (err, data) => { + if (err.code) { + console.error('Failed to load the content. Cause:' + JSON.stringify(err)); + return; + } + console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data)) + }); + }) + }) + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + console.log("[Demo] MainAbility onWindowStageDestroy") + } + + onForeground() { + // Ability has brought to foreground + console.log("[Demo] MainAbility onForeground") + } + + onBackground() { + // Ability has back to background + console.log("[Demo] MainAbility onBackground") + } +}; diff --git a/product/tablet/src/main/ets/pages/RecordingPage.ets b/product/tablet/src/main/ets/pages/RecordingPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..f352c8b972a6eaf623dd618f9447fc647f984cb6 --- /dev/null +++ b/product/tablet/src/main/ets/pages/RecordingPage.ets @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { ConfigData, Util } from '@ohos/common' +import { RecordController, MediaController, RecordState } from '@ohos/recorder' +import mediaLibrary from '@ohos.multimedia.mediaLibrary' +import { StateController } from '@ohos/recorder' +import prompt from '@ohos.prompt'; +import call from '@ohos.telephony.call'; + +const TIME_INTERVAL_S = 2; +const TIME_COUNT = 2 * 6 + 1; +const VOICE_FREQUENCY = 60; + +@Component +export struct RecordingPage { + @State currentTime: number = 0 + @State state: RecordState = RecordState.IDLE + private recordController: RecordController = new RecordController() + @State fileAsset: mediaLibrary.FileAsset = undefined + private stateController = new StateController() + heightList: Array = [] + + async aboutToAppear() { + let that = this + this.recordController.setTimeUpdateListener((currentTimeMs: number) => { + if (Math.floor(currentTimeMs / (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY)) - Math.floor(this.currentTime / (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY)) == 1) { + that.heightList.splice(0, 1) + that.heightList.push(this.recordController.getSoundVolume()) + } + this.currentTime = currentTimeMs + }) + this.recordController.setStateUpdateListener((state) => { + this.state = state + }) + this.initHeightList() + } + + initHeightList() { + this.heightList = [] + for (let index = 0; index < VOICE_FREQUENCY; index++) { + this.heightList.push(0) + } + } + + aboutToDisappear() { + if (this.state != RecordState.IDLE) { + this.recordController.stop() + } + } + + build() { + Column() { + Column() { + Row() { + Image($r("app.media.ic_clock_enhance")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + .margin({ right: $r("app.float.distance_12") }) + } + .height($r("app.float.wh_value_28")) + .width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.End) + + Stack({ alignContent: Alignment.Top }) { + Column() { + Row() { + ForEach(Array.from(Array(TIME_COUNT + 1).keys()), (item) => { + Column() { + Text(Util.timeFormat(((item - (TIME_COUNT - 1) / 2) * 1000 * TIME_INTERVAL_S + Math.floor(this.currentTime / (1000 * TIME_INTERVAL_S)) * (1000 * TIME_INTERVAL_S)), false)) + .textAlign(TextAlign.Center) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_10")) + .opacity($r("app.float.opacity_6")) + Row() { + ForEach(Array.from(Array(5).keys()), (scaleItem) => { + Rect() + .width($r("app.float.wh_value_1")) + .fill($r("app.color.text_color")) + .height(scaleItem == 2 ? $r("app.float.wh_value_10") : $r("app.float.wh_value_5")) + .opacity(scaleItem == 2 ? $r("app.float.opacity_4") : $r("app.float.opacity_2")) + }, scaleItem => scaleItem) + }.width(ConfigData.WH_100_100) + .alignItems(VerticalAlign.Top) + .height($r("app.float.wh_value_10")) + .justifyContent(FlexAlign.SpaceAround) + }.width((100 / TIME_COUNT) + "%") + }, item => item) + } + .offset({ + x: (-(100 / TIME_COUNT) * (this.currentTime % (TIME_INTERVAL_S * 1000) / (TIME_INTERVAL_S * 1000))) + "%" + }) + + Row() { + Row() { + Row() { + ForEach(Array.from(Array(VOICE_FREQUENCY).keys()), (item) => { + Rect() + .width($r("app.float.wh_value_1")) + .fill($r("app.color.text_color")) + .height(this.heightList[item] * 0.5) + .opacity($r("app.float.opacity_2")) + }, item => item) + }.width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.SpaceAround) + .offset({ + x: (-(100 / VOICE_FREQUENCY) * (this.currentTime % (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY) / (TIME_COUNT / 2 * 1000 * TIME_INTERVAL_S / VOICE_FREQUENCY) - 1)) + "%" + }) + } + .clip(true) + .width(ConfigData.WH_50_100) + .height(ConfigData.WH_100_100) + } + .justifyContent(FlexAlign.Start) + .width(ConfigData.WH_100_100) + .height($r("app.float.wh_value_100")) + .backgroundColor($r("app.color.shadow_color")) + } + + Column() { + Rect() + .height($r("app.float.wh_value_114")) + .width($r("app.float.wh_value_1")) + .fill($r("app.color.pointer_color")) + Circle() + .height($r("app.float.wh_value_4")) + .width($r("app.float.wh_value_4")) + .fill($r("app.color.pointer_color")) + } + .margin({ top: $r("app.float.distance_6") }) + } + .width(ConfigData.WH_100_100) + .clip(true) + .height($r("app.float.wh_value_128")) + .margin({ top: $r("app.float.wh_value_72") }) + + Text(Util.timeFormatMs(this.currentTime)) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_48")) + .lineHeight($r("app.float.wh_value_32")) + Text($r("app.string.predict_hint")) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_14")) + .opacity($r("app.float.opacity_6")) + .visibility(this.currentTime > (10 * 1000) ? Visibility.Hidden : Visibility.Visible) + } + .visibility(this.state == RecordState.IDLE ? Visibility.Hidden : Visibility.Visible) + + Blank() + Row() { + Column() { + Image($r("app.media.ic_clock_mark")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + Text($r("app.string.mark")) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_10")) + .lineHeight($r("app.float.wh_value_8")) + } + .justifyContent(FlexAlign.Center) + .height($r("app.float.wh_value_24")) + .width($r("app.float.wh_value_24")) + .borderRadius($r("app.float.wh_value_24")) + .visibility(this.state == RecordState.IDLE ? Visibility.Hidden : Visibility.Visible) + + Column() { + Image(this.state == RecordState.IDLE ? $r("app.media.ic_clock_record") : $r("app.media.ic_clock_stop")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + } + .justifyContent(FlexAlign.Center) + .height($r("app.float.wh_value_24")) + .width($r("app.float.wh_value_24")) + .borderRadius($r("app.float.wh_value_24")) + .backgroundColor($r("app.color.white")) + .onClick(async () => { + if (this.state == RecordState.IDLE) { + let hasCall = await call.hasCall() + if (hasCall) { + prompt.showToast({ message: $r('app.string.has_call_hint') }) + } else { + this.stateController.start() + this.recordController.initAudioRecorder() + this.fileAsset = await MediaController.createAudioFile() + let fd = await this.fileAsset.open('Rw') + this.recordController.startRecorder(`fd://${fd}`) + } + } else { + this.recordController.stop(() => { + this.initHeightList() + MediaController.saveFileDuration(this.fileAsset.title, this.currentTime) + this.stateController.end() + }) + } + }) + + Column() { + Image(this.state == RecordState.RUNNING ? $r("app.media.ic_clock_suspend") : $r("app.media.ic_clock_play")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + Text(this.state == RecordState.RUNNING ? $r("app.string.pause") : $r("app.string.continue_record")) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_10")) + .lineHeight($r("app.float.wh_value_8")) + } + .justifyContent(FlexAlign.Center) + .height($r("app.float.wh_value_24")) + .width($r("app.float.wh_value_24")) + .borderRadius($r("app.float.wh_value_24")) + .onClick(() => { + if (this.state == RecordState.RUNNING) { + this.recordController.pause() + } else { + this.recordController.resume() + } + }) + .visibility(this.state == RecordState.IDLE ? Visibility.Hidden : Visibility.Visible) + } + .margin({ bottom: $r("app.float.wh_value_12") }) + .width(ConfigData.WH_100_100) + .padding({ left: $r("app.float.distance_135"), right: $r("app.float.distance_135") }) + .justifyContent(FlexAlign.SpaceBetween) + } + .height(ConfigData.WH_100_100) + .width(ConfigData.WH_100_100) + } +} diff --git a/product/tablet/src/main/ets/pages/index.ets b/product/tablet/src/main/ets/pages/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..8fa920ff4ceab54d393a1a90f49c00a1a2e3f68d --- /dev/null +++ b/product/tablet/src/main/ets/pages/index.ets @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { ConfigData } from '@ohos/common' +import { recordList } from './recordList' +import { RecordingPage } from './RecordingPage' +import { StateController } from '@ohos/recorder' + +@Entry +@Component +struct Index { + private stateController = new StateController() + + build() { + Row() { + Column() { + recordList({ stateController: this.stateController }) + } + .padding({ + left: $r("app.float.distance_12"), + right: $r("app.float.distance_12"), + top: $r("app.float.distance_8"), + bottom: $r("app.float.distance_8"), + }) + .width($r("app.float.wh_value_256")) + .height(ConfigData.WH_100_100) + + Divider().vertical(true).opacity($r("app.float.opacity_2")) + Column() { + RecordingPage({ stateController: this.stateController }) + } + .height(ConfigData.WH_100_100) + .width($r("app.float.wh_value_383")) + + } + .backgroundColor($r("app.color.background_color")) + .width(ConfigData.WH_100_100) + .height(ConfigData.WH_100_100) + } +} + diff --git a/product/tablet/src/main/ets/pages/recordList.ets b/product/tablet/src/main/ets/pages/recordList.ets new file mode 100644 index 0000000000000000000000000000000000000000..9a5f73b760f40c0d643f874bd108680b470394d2 --- /dev/null +++ b/product/tablet/src/main/ets/pages/recordList.ets @@ -0,0 +1,292 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { ConfigData, Util } from '@ohos/common' +import { AudioController, AudioState, MediaController, RecordData } from '@ohos/recorder' +import { StateController, ControlState } from '@ohos/recorder' + +@Component +export struct recordList { + @State recordList: Array = [] + @State selectedIndex: number = -1 + @State currentTime: number = 0 + private audioController: AudioController = new AudioController() + @State state: AudioState = AudioState.IDLE; + @State filterText: string = ""; + private deleteRecord: RecordData = undefined + private stateController = new StateController() + @State disable: boolean = false + confirm: () => void = () => { + MediaController.deleteRecord(this.deleteRecord.fileAsset.uri, () => { + for (let i = this.recordList.length - 1;i >= 0; i--) { + if (this.deleteRecord.fileAsset.uri == this.recordList[i].fileAsset.uri) { + this.recordList.splice(i, 1) + } + } + }) + } + dialogController: CustomDialogController = new CustomDialogController({ + builder: DeleteRecordDialog({ deleteRecordTitle: this.deleteRecord.title, confirm: this.confirm }), + autoCancel: true, + alignment: DialogAlignment.Bottom, + offset: { dy: -16, dx: 0 } + }) + + async getAudios() { + this.recordList = await MediaController.queryAllAudios() + } + + aboutToAppear() { + this.getAudios() + this.stateController.setStateUpdateListener((state) => { + if (state == ControlState.START) { + this.disable = true + this.audioController.stop() + } else { + this.disable = false + this.getAudios() + } + }) + this.audioController.setTimeUpdateListener((currentTime: number) => { + this.currentTime = currentTime + }) + this.audioController.setStateUpdateListener((state: AudioState) => { + if (state == AudioState.IDLE) { + this.selectedIndex = -1 + } + this.state = state + }) + } + + build() { + Column() { + Row() { + Image($r("app.media.ic_clock_add")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + .margin({ right: $r("app.float.distance_12") }) + + Image($r("app.media.public_more")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + .margin({ right: $r("app.float.distance_12") }) + } + .height($r("app.float.wh_value_28")) + .width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.End) + + Row() { + Text($r("app.string.title")) + .fontSize($r("app.float.font_30")) + .fontColor($r("app.color.text_color")) + .fontWeight(500) + .margin({ left: $r("app.float.distance_12") }) + } + .height($r("app.float.wh_value_21")) + .width(ConfigData.WH_100_100) + + Column() { + Stack({ alignContent: Alignment.Start }) { + TextInput({ placeholder: $r("app.string.search_recording_files") }) + .placeholderFont({ size: $r("app.float.font_16") }) + .width(ConfigData.WH_100_100) + .height($r("app.float.wh_value_21")) + .backgroundColor($r("app.color.white")) + .borderRadius($r("app.float.distance_21")) + .padding({ left: $r("app.float.distance_21") }) + .onChange((value) => { + this.filterText = value + }) + Image($r("app.media.ic_record_search")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + .margin({ left: $r("app.float.distance_6") }) + }.width(ConfigData.WH_100_100) + .height($r("app.float.wh_value_21")) + + Column() { + List() { + ForEach([...this.recordList].reverse().map((value, index) => { + return { i: index, data: value } + }), (item) => { + ListItem() { + Column() { + Row() { + Column() { + Text(item.data.title) + .fontColor($r("app.color.text_color")) + .fontSize($r("app.float.font_16")) + .width($r("app.float.wh_value_105")) + .lineHeight($r("app.float.wh_value_10_5")) + Text(Util.getDateFormat(item.data.date)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_14")) + .width($r("app.float.wh_value_105")) + .lineHeight($r("app.float.wh_value_9_5")) + } + + Row() { + Text(Util.timeFormat(item.data.duration)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_14")) + .margin({ right: $r("app.float.distance_2") }) + Image((item.i == this.selectedIndex && this.state == AudioState.RUNNING) ? $r("app.media.ic_record_stop") : $r("app.media.ic_record_play")) + .width($r("app.float.wh_value_12")) + .height($r("app.float.wh_value_12")) + } + } + .height($r("app.float.wh_value_32")) + .width(ConfigData.WH_100_100) + .justifyContent(FlexAlign.SpaceBetween) + .padding({ left: $r("app.float.distance_12"), right: $r("app.float.distance_12") }) + .onClick(() => { + if (this.selectedIndex != item.i || this.state == AudioState.IDLE) { + this.selectedIndex = item.i + this.audioController.initAudioPlayer(item.data.fileAsset, () => { + this.audioController.play() + }) + } else if (this.state == AudioState.RUNNING) { + this.audioController.pause() + } else if (this.state == AudioState.PAUSED) { + this.audioController.continuePlay() + } + }) + .gesture( + LongPressGesture() + .onAction(() => { + this.audioController.stop() + this.deleteRecord = item.data + this.dialogController.open() + }) + ) + + if (this.selectedIndex == item.i) { + Column() { + Slider({ + value: this.currentTime, + min: 0, + max: item.data.duration, + style: SliderStyle.OutSet + }) + .trackThickness($r("app.float.wh_value_2")) + .height($r("app.float.wh_value_8")) + .width($r("app.float.wh_value_232")) + .onChange((value: number, mode: SliderChangeMode) => { + if (mode == SliderChangeMode.End || mode == 3) { + this.currentTime = value + this.audioController.seek(value) + } + }) + .offset({ y: $r("app.float.wh_value_minus_19") }) + + Row() { + Text(Util.timeFormat(this.currentTime)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_10")) + .margin({ right: $r("app.float.distance_2") }) + Text(Util.timeFormat(item.data.duration)) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .fontSize($r("app.float.font_10")) + .margin({ right: $r("app.float.distance_2") }) + } + .margin({ top: $r("app.float.distance_6") }) + .width($r("app.float.wh_value_208")) + .justifyContent(FlexAlign.SpaceBetween) + } + .padding({ top: $r("app.float.distance_4") }) + .height($r("app.float.wh_value_32")) + .width(ConfigData.WH_100_100) + } + } + }.visibility(item.data.title.indexOf(this.filterText) >= 0 ? Visibility.Visible : Visibility.None) + }, item => item.i) + + } + .width(ConfigData.WH_100_100) + .backgroundColor($r("app.color.white")) + .borderRadius($r("app.float.wh_value_12")) + .divider({ + strokeWidth: $r("app.float.wh_value_0_5"), + color: $r("app.color.shadow_color"), + startMargin: $r("app.float.distance_12"), + endMargin: $r("app.float.distance_12") + }) + .enabled(!this.disable) + .opacity(this.disable ? $r("app.float.opacity_6") : $r("app.float.opacity_full")) + } + .flexShrink(1) + .margin({ top: $r("app.float.distance_8") }) + } + .flexShrink(1) + .margin({ top: $r("app.float.distance_6"), bottom: $r("app.float.distance_12") }) + } + .width(ConfigData.WH_100_100) + .height(ConfigData.WH_100_100) + } +} + +@CustomDialog +struct DeleteRecordDialog { + private controller: CustomDialogController + private deleteRecordTitle: string + private confirm: () => void + + build() { + Column() { + Text(this.deleteRecordTitle) + .fontSize($r("app.float.font_14")) + .fontColor($r("app.color.text_color")) + .opacity($r("app.float.opacity_6")) + .width(ConfigData.WH_100_100) + .margin({ left: $r("app.float.distance_6") }) + Text($r("app.string.delete_hint")) + .fontSize($r("app.float.font_16")) + .margin($r("app.float.distance_6")) + .fontColor($r("app.color.text_color")) + Row() { + Text($r("app.string.cancel")) + .fontSize($r("app.float.font_16")) + .fontColor($r("app.color.text_color")) + .width($r("app.float.wh_value_24")) + .textAlign(TextAlign.Center) + .onClick(() => { + this.controller.close() + }) + .fontColor($r("app.color.hint_color")) + Divider().vertical(true).opacity($r("app.float.opacity_4")) + Text($r("app.string.delete")) + .fontSize($r("app.float.font_16")) + .textAlign(TextAlign.Center) + .width($r("app.float.wh_value_24")) + .onClick(() => { + if (this.confirm) { + this.confirm() + this.controller.close() + } + }) + .fontColor($r("app.color.warn_color")) + } + .height($r("app.float.wh_value_12")) + .width(ConfigData.WH_100_100) + .padding({ left: $r("app.float.wh_value_12"), right: $r("app.float.wh_value_12") }) + .justifyContent(FlexAlign.SpaceAround) + } + .padding($r("app.float.distance_12")) + } +} \ No newline at end of file diff --git a/product/tablet/src/main/module.json5 b/product/tablet/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..6a30a379752a06bb87b982e16b0c00d685259c05 --- /dev/null +++ b/product/tablet/src/main/module.json5 @@ -0,0 +1,49 @@ +{ + "module": { + "name": "tablet", + "type": "entry", + "srcEntrance": "./ets/Application/MyAbilityStage.ts", + "description": "$string:tablet_desc", + "mainElement": "MainAbility", + "deviceTypes": [ + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "uiSyntax": "ets", + "abilities": [ + { + "name": "MainAbility", + "srcEntrance": "./ets/MainAbility/MainAbility.ts", + "description": "$string:MainAbility_desc", + "icon": "$media:icon", + "label": "$string:MainAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:white", + "visible": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.MICROPHONE" + }, + { + "name": "ohos.permission.WRITE_MEDIA" + }, + { + "name": "ohos.permission.READ_MEDIA" + } + ] + } +} \ No newline at end of file diff --git a/product/tablet/src/main/resources/base/element/color.json b/product/tablet/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..1bbc9aa9617e97c45440e1d3d66afc1154837012 --- /dev/null +++ b/product/tablet/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/product/tablet/src/main/resources/base/element/float.json b/product/tablet/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..85e2fd0a48f5ac42fa88572828eeb0f27377a392 --- /dev/null +++ b/product/tablet/src/main/resources/base/element/float.json @@ -0,0 +1,164 @@ +{ + "float": [ + { + "name": "wh_value_0_5", + "value": "0.5vp" + }, + { + "name": "wh_value_1", + "value": "1vp" + }, + { + "name": "wh_value_2", + "value": "2vp" + }, + { + "name": "wh_value_4", + "value": "4vp" + }, + { + "name": "wh_value_5", + "value": "5vp" + }, + { + "name": "wh_value_8", + "value": "8vp" + }, + { + "name": "wh_value_10", + "value": "10vp" + }, + { + "name": "wh_value_9_5", + "value": "9.5vp" + }, + { + "name": "wh_value_10_5", + "value": "10.5vp" + }, + { + "name": "wh_value_12", + "value": "12vp" + }, + { + "name": "wh_value_minus_19", + "value": "-19vp" + }, + { + "name": "wh_value_21", + "value": "21vp" + }, + { + "name": "wh_value_24", + "value": "24vp" + }, + { + "name": "wh_value_28", + "value": "28vp" + }, + { + "name": "wh_value_32", + "value": "32vp" + }, + { + "name": "wh_value_72", + "value": "72vp" + }, + { + "name": "wh_value_100", + "value": "100vp" + }, + { + "name": "wh_value_105", + "value": "105vp" + }, + { + "name": "wh_value_114", + "value": "114vp" + }, + { + "name": "wh_value_128", + "value": "128vp" + }, + { + "name": "wh_value_208", + "value": "208vp" + }, + { + "name": "wh_value_232", + "value": "232vp" + }, + { + "name": "wh_value_256", + "value": "256vp" + }, + { + "name": "wh_value_383", + "value": "383vp" + }, + { + "name": "distance_2", + "value": "2vp" + }, + { + "name": "distance_4", + "value": "4vp" + }, + { + "name": "distance_6", + "value": "6vp" + }, + { + "name": "distance_8", + "value": "8vp" + }, + { + "name": "font_10", + "value": "10px" + }, + { + "name": "font_14", + "value": "14px" + }, + { + "name": "font_16", + "value": "16px" + }, + { + "name": "font_30", + "value": "30px" + }, + { + "name": "font_48", + "value": "48px" + }, + { + "name": "distance_12", + "value": "12vp" + }, + { + "name": "distance_21", + "value": "21vp" + }, + { + "name": "distance_135", + "value": "135vp" + }, + { + "name": "opacity_2", + "value": "0.2" + }, + { + "name": "opacity_4", + "value": "0.4" + }, + { + "name": "opacity_6", + "value": "0.6" + }, + { + "name": "opacity_full", + "value": "1" + } + ] +} \ No newline at end of file diff --git a/product/tablet/src/main/resources/base/element/string.json b/product/tablet/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f52f06094e3cf5ef9e7c03278701f438456a19ee --- /dev/null +++ b/product/tablet/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "tablet_desc", + "value": "description" + }, + { + "name": "MainAbility_desc", + "value": "description" + }, + { + "name": "MainAbility_label", + "value": "录音机" + } + ] +} \ No newline at end of file diff --git a/product/tablet/src/main/resources/base/media/icon.png b/product/tablet/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b186c704fd1345ae527fabc8a479fc92470a8c6b Binary files /dev/null and b/product/tablet/src/main/resources/base/media/icon.png differ diff --git a/product/tablet/src/main/resources/base/profile/main_pages.json b/product/tablet/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..feec276e105eeb8d621c20aaf838f318b0a94150 --- /dev/null +++ b/product/tablet/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/index" + ] +} diff --git a/product/tablet/src/ohosTest/ets/Application/TestAbilityStage.ts b/product/tablet/src/ohosTest/ets/Application/TestAbilityStage.ts new file mode 100644 index 0000000000000000000000000000000000000000..fedbf385606de8abde4622b8e3da4539b9a173ff --- /dev/null +++ b/product/tablet/src/ohosTest/ets/Application/TestAbilityStage.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 AbilityStage from "@ohos.application.AbilityStage" + +export default class TestAbilityStage extends AbilityStage { + onCreate() { + console.log("[Demo] TestAbilityStage onCreate") + globalThis.abilityContext = this.context; + } +} \ No newline at end of file diff --git a/product/tablet/src/ohosTest/ets/TestAbility/TestAbility.ts b/product/tablet/src/ohosTest/ets/TestAbility/TestAbility.ts new file mode 100644 index 0000000000000000000000000000000000000000..440afc42383533e67be796608cdb600d915fd4b4 --- /dev/null +++ b/product/tablet/src/ohosTest/ets/TestAbility/TestAbility.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 Ability from '@ohos.application.Ability' +import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry' +import { Hypium } from '@ohos/hypium' +import testsuite from '../test/List.test' + +export default class TestAbility extends Ability { + onCreate(want, launchParam) { + console.log('TestAbility onCreate') + var abilityDelegator: any + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var abilityDelegatorArguments: any + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + console.info('start run testcase!!!') + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + console.log('TestAbility onDestroy') + } + + onWindowStageCreate(windowStage) { + console.log('TestAbility onWindowStageCreate') + windowStage.loadContent("TestAbility/pages/index", (err, data) => { + if (err.code) { + console.error('Failed to load the content. Cause:' + JSON.stringify(err)); + return; + } + console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data)) + }); + + globalThis.abilityContext = this.context; + } + + onWindowStageDestroy() { + console.log('TestAbility onWindowStageDestroy') + } + + onForeground() { + console.log('TestAbility onForeground') + } + + onBackground() { + console.log('TestAbility onBackground') + } +}; \ No newline at end of file diff --git a/product/tablet/src/ohosTest/ets/TestAbility/pages/index.ets b/product/tablet/src/ohosTest/ets/TestAbility/pages/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..2a02d2b4125e012d3b57589d2305193ad8aa202b --- /dev/null +++ b/product/tablet/src/ohosTest/ets/TestAbility/pages/index.ets @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 router from '@ohos.router'; + +@Entry +@Component +struct Index { + aboutToAppear() { + console.info('TestAbility index aboutToAppear') + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/product/tablet/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts b/product/tablet/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..66154a400bdc3c79f82d0a396f190713e7a5919f --- /dev/null +++ b/product/tablet/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 TestRunner from '@ohos.application.testRunner' +import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry' + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +function translateParamsToString(parameters) { + const keySet = new Set([ + '-s class', '-s notClass', '-s suite', '-s it', + '-s level', '-s testType', '-s size', '-s timeout', + '-s dryRun' + ]) + let targetParams = ''; + for (const key in parameters) { + if (keySet.has(key)) { + targetParams = `${targetParams} ${key} ${parameters[key]}` + } + } + return targetParams.trim() +} + +async function onAbilityCreateCallback() { + console.log("onAbilityCreateCallback"); +} + +async function addAbilityMonitorCallback(err: any) { + console.info("addAbilityMonitorCallback : " + JSON.stringify(err)) +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + console.info("OpenHarmonyTestRunner OnPrepare ") + } + + async onRun() { + console.log('OpenHarmonyTestRunner onRun run') + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + cmd += ' '+translateParamsToString(abilityDelegatorArguments.parameters) + var debug = abilityDelegatorArguments.parameters["-D"] + if (debug == 'true') + { + cmd += ' -D' + } + console.info('cmd : '+cmd) + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + console.info('executeShellCommand : err : ' + JSON.stringify(err)); + console.info('executeShellCommand : data : ' + d.stdResult); + console.info('executeShellCommand : data : ' + d.exitCode); + }) + console.info('OpenHarmonyTestRunner onRun end') + } +}; \ No newline at end of file diff --git a/product/tablet/src/ohosTest/ets/test/Ability.test.ets b/product/tablet/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..a18de15cf90def60208f3046e0a20bec16fd0831 --- /dev/null +++ b/product/tablet/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,359 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; +import { Util } from '@ohos/common'; +import { RecordController, RecordState, StateController, ControlState } from '@ohos/recorder'; + +export default function abilityTest() { + describe('ActsAbilityTest', function () { + + it('StateControllerStart', 0, async function () { + let mStateController = new StateController(); + let testState = false; + mStateController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(ControlState.START); + testState = false; + } + }); + testState = true; + mStateController.start(); + }) + + it('StateControllerEnd', 0, async function () { + let mStateController = new StateController(); + let testState = false; + mStateController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(ControlState.END); + testState = false; + } + }); + mStateController.start(); + testState = true; + setTimeout(() => { + mStateController.end(); + }, 100); + }) + + it('addZeroMin', 0, async function () { + let num = 0; + let res = Util.addZero(num); + expect(res).assertEqual('00'); + }) + + it('addZeroMinus', 0, async function () { + let num = -1; + let res = Util.addZero(num); + expect(res).assertEqual('0-1'); + }) + + it('addZeroMax', 0, async function () { + let num = 9; + let res = Util.addZero(num); + expect(res).assertEqual('09'); + }) + + it('addZeroOver', 0, async function () { + let num = 10; + let res = Util.addZero(num); + expect(res).assertEqual('10'); + }) + + it('addZeroNaN', 0, async function () { + let num = NaN; + let res = Util.addZero(num); + expect(res).assertEqual('NaN'); + }) + + it('getTime', 0, async function () { + let nowDate = new Date(); + let time = Util.getTime(); + let res = false; + if (parseInt(time.substring(0, 2)) == nowDate.getHours() && parseInt(time.substring(2, 4)) == nowDate.getMinutes() && parseInt(time.substring(4, 6)) == nowDate.getSeconds()) { + res = true; + } + expect(res).assertTrue(); + }) + + it('getDate', 0, async function () { + let nowDate = new Date(); + let time = Util.getDate(); + let res = false; + if (parseInt(time.substring(0, 4)) == nowDate.getFullYear() && parseInt(time.substring(4, 6)) == nowDate.getMonth() + 1 && parseInt(time.substring(6, 8)) == nowDate.getDate()) { + res = true; + } + expect(res).assertTrue(); + }) + + it('getTimeFormatZero', 0, async function () { + let time = 0; + let res = Util.getTimeFormat(time); + expect(res).assertEqual('00:00:00'); + }) + + it('getTimeFormat', 0, async function () { + let time = new Date('2022-11-11T11:11:11').getTime(); + let res = Util.getTimeFormat(time); + expect(res).assertEqual('11:11:11'); + }) + + it('getDateFormat', 0, async function () { + let time = new Date('2022-11-11T11:11:11').getTime(); + let res = Util.getDateFormat(time); + expect(res).assertEqual('2022/11/11'); + }) + + it('getDateFormatZero', 0, async function () { + let time = 0; + let res = Util.getDateFormat(time); + expect(res).assertEqual('1970/01/01'); + }) + + it('timeFormatMsZero', 0, async function () { + let time = 0; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMsMinus', 0, async function () { + let time = -1; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMsMin', 0, async function () { + let time = 9; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMsMs', 0, async function () { + let time = 10; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.01'); + }) + + it('timeFormatMsSecond', 0, async function () { + let time = 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:01.00'); + }) + + it('timeFormatMsMinute', 0, async function () { + let time = 60 * 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('01:00.00'); + }) + + it('timeFormatMsHour', 0, async function () { + let time = 60 * 60 * 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('01:00:00'); + }) + + it('timeFormatMsDay', 0, async function () { + let time = 24 * 60 * 60 * 1000; + let res = Util.timeFormatMs(time); + expect(res).assertEqual('00:00.00'); + }) + + it('timeFormatMinus', 0, async function () { + let time = -1; + let res = Util.timeFormat(time); + expect(res).assertEqual(''); + }) + + it('timeFormatZeroH', 0, async function () { + let time = 0; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:00:00`); + }) + + it('timeFormatZero', 0, async function () { + let time = 0; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:00`); + }) + + it('timeFormatMinH', 0, async function () { + let time = 999; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:00:00`); + }) + + it('timeFormatMin', 0, async function () { + let time = 999; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:00`); + }) + + it('timeFormatSecondH', 0, async function () { + let time = 1000; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:00:01`); + }) + + it('timeFormatSecond', 0, async function () { + let time = 1000; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:01`); + }) + + it('timeFormatMinuteH', 0, async function () { + let time = 60 * 1000; + let res = Util.timeFormat(time); + expect(res).assertEqual(`00:01:00`); + }) + + it('timeFormatMinute', 0, async function () { + let time = 60 * 1000; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`01:00`); + }) + + it('timeFormatHour', 0, async function () { + let time = 60 * 60 * 1000; + let res = Util.timeFormat(time); + expect(res).assertEqual(`01:00:00`); + }) + + it('timeFormatDay', 0, async function () { + let time = 24 * 60 * 60 * 1000; + let res = Util.timeFormat(time, false); + expect(res).assertEqual(`00:00`); + }) + + it('RecordControllerStartTime', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.RUNNING); + testState = false; + } + }); + testState = true; + mRecordController.startTime(); + }) + + it('RecordStartTime', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setTimeUpdateListener((time) => { + if (testState) { + expect(time > 0).assertTrue(); + testState = false; + } + }); + testState = true; + mRecordController.startTime(); + }) + + it('RecordControllerPause', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.PAUSED); + testState = false; + } + }); + mRecordController.startTime(); + testState = true; + setTimeout(() => { + mRecordController.pause(); + }, 100); + }) + + it('RecordControllerResume', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.RUNNING); + testState = false; + } + }); + mRecordController.startTime(); + mRecordController.pause(); + testState = true; + setTimeout(() => { + mRecordController.resume(); + }, 100); + }) + + it('RecordControllerPauseStop', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.IDLE); + testState = false; + } + }); + mRecordController.startTime(); + mRecordController.pause(); + setTimeout(() => { + mRecordController.stop(() => { + testState = true; + }); + }, 100); + }) + + it('RecordControllerStop', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setStateUpdateListener((state) => { + if (testState) { + expect(state).assertEqual(RecordState.IDLE); + testState = false; + } + }); + mRecordController.startTime(); + setTimeout(() => { + mRecordController.stop(() => { + testState = true; + }); + }, 100); + }) + + it('RecordStop', 0, async function () { + let mRecordController = new RecordController(); + mRecordController.initAudioRecorder(); + let testState = false; + mRecordController.setTimeUpdateListener((time) => { + if (testState) { + expect(time).assertEqual(0); + testState = false; + } + }); + mRecordController.startTime(); + setTimeout(() => { + mRecordController.stop(() => { + testState = true; + }); + }, 100); + }) + + }) +} \ No newline at end of file diff --git a/product/tablet/src/ohosTest/ets/test/List.test.ets b/product/tablet/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..fd9764189c525a7ae769839735e6e775c3778077 --- /dev/null +++ b/product/tablet/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2022 HiHope Open Source Organization. + * 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 abilityTest from './Ability.test' + +export default function testsuite() { + abilityTest() +} \ No newline at end of file diff --git a/product/tablet/src/ohosTest/module.json5 b/product/tablet/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1e6c0dc1a9e06b7c4eb24cbe797d74fe26d3ba3d --- /dev/null +++ b/product/tablet/src/ohosTest/module.json5 @@ -0,0 +1,38 @@ +{ + "module": { + "name": "tablet_test", + "type": "feature", + "srcEntrance": "./ets/Application/TestAbilityStage.ts", + "description": "$string:entry_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "uiSyntax": "ets", + "abilities": [ + { + "name": "TestAbility", + "srcEntrance": "./ets/TestAbility/TestAbility.ts", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "visible": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:white", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/product/tablet/src/ohosTest/resources/base/element/color.json b/product/tablet/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..1bbc9aa9617e97c45440e1d3d66afc1154837012 --- /dev/null +++ b/product/tablet/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/product/tablet/src/ohosTest/resources/base/element/string.json b/product/tablet/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..36d4230c53e9f5a07ae343ad8dc9808341975e3b --- /dev/null +++ b/product/tablet/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "entry_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/product/tablet/src/ohosTest/resources/base/media/icon.png b/product/tablet/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/product/tablet/src/ohosTest/resources/base/media/icon.png differ diff --git a/product/tablet/src/ohosTest/resources/base/profile/test_pages.json b/product/tablet/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..fcef82b4dfc18e28106ff9ecd1c8b48ec74d18a4 --- /dev/null +++ b/product/tablet/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "TestAbility/pages/index" + ] +}