diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e58308304579840f49fa53ef0854845f1b0cc103 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.continueprogress", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..0a4975d1c29719b0d87de955643b6301af924057 --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "ContinueProgress" + } + ] +} diff --git a/AppScope/resources/base/media/app_icon.png b/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 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..338e5b0bc22082e0ffcc7121c2ed3897a3ddccb0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,78 @@ + Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + 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. + +Apache License, Version 2.0 +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: +1.You must give any other recipients of the Work or Derivative Works a copy of this License; and +2.You must cause any modified files to carry prominent notices stating that You changed the files; and +3.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 +4.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 \ No newline at end of file diff --git a/README.en.md b/README.en.md deleted file mode 100644 index abb5d0f8c3bc800e08108b4f3fa010d708e2a024..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# ContinueProgress - -#### 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 index a871d40a18f2617f55c0c1758f65bab7c3ac0af3..9d6fa40c7eac9bda2126afe3ba8fbcc4a3d7f705 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,124 @@ -# ContinueProgress +# 应用接续(浏览进度) -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +### 介绍 -#### 软件架构 -软件架构说明 +本示例基于应用接续和分布式能力实现了长列表浏览、媒体浏览和web页面浏览的进度接续。长列表是List嵌套WaterFlow组件,需要将currentOffset接续; 媒体进度接续使用Avplayer实现,接续其集数和播放时间,使用seek()接口实现;Web进度使用js语句获取进度。 +### 效果预览 -#### 安装教程 +| 首页 | 长列表 | 媒体 | web | +|---------------------------------|------------------------------------|---------------------------------|-------------------------------| +| ![image](screenshots/index.png) | ![image](screenshots/longlist.png) | ![image](screenshots/video.png) | ![image](screenshots/web.png) | -1. xxxx -2. xxxx -3. xxxx +### 使用说明: -#### 使用说明 +本示例提供3个按钮触发使用不同场景的进度接续: +1. 长列表进度:点击该按钮完成进入长列表浏览页面,并可以完成进度接续。 +2. 媒体浏览进度接续:可以观看视频切换视频,并可以完成进度接续。 +3. Web页面浏览进度接续:可以查看web页面,并可以完成进度接续。 -1. xxxx -2. xxxx -3. xxxx +### 工程目录 -#### 参与贡献 +``` +├──entry/src/main/ets/ +│ ├──entryability +│ │ └──EntryAbility.ets // 程序入口类 +│ ├──entrybackupability +│ │ └──EntryBackupAbility.ets // 备份恢复类 +│ └──pages +│ └──Index.ets // 首页 +├──entry/src/main/resources // 应用资源目录 +├──features/longList/src/main/ets/ +│ ├──constants +│ │ ├──BreakpointConstants.ets // 断点相关常量 +│ │ ├──CommonConstants.ets // 一般常量 +│ │ └──HomeConstants.ets // 主页的常量 +│ ├──longlistability +│ │ └──LongListAbility.ets +│ ├──model +│ │ ├──FooterTabData.ets // 底部导航栏 +│ │ ├──FunctionEntryData.ets // 功能区 +│ │ ├──FunctionEntryListData.ets // 功能区数据列表 +│ │ ├──WaterFlowData.ets // 瀑布流数据 +│ │ ├──WaterFlowDescriptionData.ets // 瀑布流item底部描述信息 +│ │ ├──WaterFlowHeadData.ets // 瀑布流item媒体信息 +│ │ └──WaterFlowListData.ets // 瀑布流数据列表 +│ ├──pages +│ │ └──Index.ets // 滑动页面入口 +│ ├──utils +│ │ ├──BreakpointSystem.ets // 一多断点监听 +│ │ ├──BreakpointType.ets // 一多断点类型 +│ │ ├──Logger.ets // 日志 +│ │ └──NetworkUtil.ets // 网络请求 +│ └──view +│ ├──FunctionView.ets // 功能区页面 +│ ├──HomeContent.ets // 主页 +│ ├──IndexNavDestination.ets // 导航栏入口 +│ ├──NavigationBarView.ets // 导航栏页面 +│ ├──SearchBarView.ets // 搜索栏页面 +│ ├──WaterFlowDescriptionView.ets // 瀑布流item底部描述页面 +│ ├──WaterFlowImageView.ets // 瀑布流item图片描述页面 +│ ├──WaterFlowLivingView.ets // 瀑布流item直播描述页面 +│ ├──WaterFlowVideoView.ets // 瀑布流item视频描述页面 +│ └──WaterFlowView.ets // 瀑布流页面 +├──features/longList/src/main/resources // 资源类 +├──features/video/src/main/ets/ // 代码区 +│ ├──components +│ │ ├──ExitVideo.ets // 退出应用组件 +│ │ ├──SpeedDialog.ets // 播放倍速弹窗 +│ │ └──VideoOperate.ets // 视频操作组件 +│ ├──pages +│ │ └──Index.ets // 首页视频界面 +│ ├──utils +│ │ ├──GlobalContext.ets // 公共工具类 +│ │ ├──Logger.ets // 日志帮助类 +│ │ ├──ResourceUtil.ets // 公共工具类 +│ │ └──TimeUtils.ets // 视频时间帮助类 +│ ├──videoability +│ │ └──VideoAbility.ets +│ └──videomanager +│ └──AvPlayManager.ets // 视频管理接口,统一封装了对外提供的功能接口 +├──features/video/src/main/resources // 应用资源目录 +├──features/web/src/main/ets // 代码区 +│ ├──common +│ │ ├──constants +│ │ │ └──CommonConstants.ets // 常量 +│ │ └──utils +│ │ └──Logger.ets // 日志打印 +│ ├──model +│ │ └──ProductModel.ets // 产品模型 +│ ├──pages +│ │ ├──IndexPage.ets // 首页页面 +│ │ └──OrderConfirmPage.ets // 订单确认页面 +│ └──webability +│ └──WebAbility.ets +└──features/web/src/main/resources // 资源文件夹 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +``` +### 具体实现 +1. 长列表是List嵌套WaterFlow组件,需要接续currentOffset。 +2. 媒体进度接续使用Avplayer实现,接续其集数和播放进度。 +3. Web进度使用js语句获取进度。 -#### 特技 +### 相关权限 +不涉及 -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/) +### 依赖 +不涉及 + +### 约束与限制 + +1.本示例仅支持标准系统上运行,支持设备:华为手机。 + +2.HarmonyOS系统:HarmonyOS 5.0.0 Release及以上。 + +3.DevEco Studio版本:DevEco Studio 5.0.0 Release及以上。 + +4.HarmonyOS SDK版本:HarmonyOS 5.0.0 Release SDK及以上。 + +5.双端设备需要登录同一华为账号。 + +6.双端设备需要打开Wi-Fi和蓝牙开关。条件允许时,建议双端设备接入同一个局域网,可提升数据传输的速度。 + +7.应用接续只能在同应用(UIAbility)之间触发,双端设备都需要有该应用。 \ No newline at end of file diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43a7247350247d49a0c9ca9c2966349ab585d8d8 --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,91 @@ +{ + "app": { + "signingConfigs": [ + { + "name": "default", + "type": "HarmonyOS", + "material": { + "certpath": "C:\\Users\\lucy\\.ohos\\config\\default_continue-progress_CBJNtw2wsaMQMk9bRBMBsNa-pj2mvBAZP9BSDUtE6zQ=.cer", + "storePassword": "0000001B401BE0DAC302C909B9DB5EC720FC0F8040FFE2210D3A48F7EB8A09ACA7903B98D887340BCEEDCE", + "keyAlias": "debugKey", + "keyPassword": "0000001BE4242FA48D3462E59F1BBE7CBEE41A4187C2DB6CE4F5C72449EAA450AA5C78F4F02557C1DD1BDC", + "profile": "C:\\Users\\lucy\\.ohos\\config\\default_continue-progress_CBJNtw2wsaMQMk9bRBMBsNa-pj2mvBAZP9BSDUtE6zQ=.p7b", + "signAlg": "SHA256withECDSA", + "storeFile": "C:\\Users\\lucy\\.ohos\\config\\default_continue-progress_CBJNtw2wsaMQMk9bRBMBsNa-pj2mvBAZP9BSDUtE6zQ=.p12" + } + } + ], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS", + "buildOption": { + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "web", + "srcPath": "./features/web", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "video", + "srcPath": "./features/video", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "longList", + "srcPath": "./features/longList", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/code-linter.json5 b/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..77b31b517a3e5c2f34c3ae1bf44083c0c06cbd6d --- /dev/null +++ b/code-linter.json5 @@ -0,0 +1,20 @@ +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + } +} \ No newline at end of file diff --git a/entry/.gitignore b/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/entry/build-profile.json5 b/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4d611879c7913fb0610c686e2399258ab3a6dad1 --- /dev/null +++ b/entry/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/entry/hvigorfile.ts b/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6edcd90486dd5a853cf7d34c8647f08414ca7a3 --- /dev/null +++ b/entry/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/entry/obfuscation-rules.txt b/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/entry/oh-package-lock.json5 b/entry/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..22f9d347866ad25369f3795f6e63fffe832878b5 --- /dev/null +++ b/entry/oh-package-lock.json5 @@ -0,0 +1,9 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": {}, + "packages": {} +} \ No newline at end of file diff --git a/entry/oh-package.json5 b/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ad39a3934251fcccb72eaf9abedc2e6ed007a8ca --- /dev/null +++ b/entry/oh-package.json5 @@ -0,0 +1,9 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", +} + diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..3afb1a4200f749101d06d43e832cc4c662bc49c3 --- /dev/null +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..be794f48ccffc46278e76ce3493088a647b0d470 --- /dev/null +++ b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..dd18aedd94d45f4d9113a0a75e043e73308a9c1b --- /dev/null +++ b/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { common, Want } from '@kit.AbilityKit'; + +@Entry +@Component +struct Index { + private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; + + build() { + Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) { + Text($r('app.string.title')) + .fontSize(30) + .textAlign(TextAlign.Start) + .width('100%') + .fontWeight(FontWeight.Bold) + .padding({ + top: 56, + left: 16, + right: 16 + }) + + Column() { + Button($r('app.string.button1')) + .width('100%') + .margin({ top: 12 }) + .onClick(async () => { + let want: Want = { + deviceId: '', + bundleName: 'com.example.continueprogress', + abilityName: 'LongListAbility', + } + this.context.startAbility(want); + }) + + Button($r('app.string.button2')) + .width('100%') + .margin({ top: 12 }) + .onClick(async () => { + let want: Want = { + deviceId: '', + bundleName: 'com.example.continueprogress', + abilityName: 'VideoAbility', + } + this.context.startAbility(want); + }) + + Button($r('app.string.button3')) + .width('100%') + .margin({ + top: 12, + bottom: 16 + }) + .onClick(async () => { + let want: Want = { + deviceId: '', + bundleName: 'com.example.continueprogress', + abilityName: 'WebAbility', + } + this.context.startAbility(want); + }) + + } + .width('100%') + .padding({ + left: 16, + right: 16 + }) + .alignSelf(ItemAlign.End) + } + .width('100%') + .height('100%') + + } +} \ No newline at end of file diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..313aa6132b97bb2b003cc1cac0b3a5f34c7372f3 --- /dev/null +++ b/entry/src/main/module.json5 @@ -0,0 +1,50 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home", + ] + } + ] + }, + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ], + } + ] + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..cc60ce1199ee3f6d4e9c6d868b2e5e107178295a --- /dev/null +++ b/entry/src/main/resources/base/element/string.json @@ -0,0 +1,32 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Continue Progress" + }, + { + "name": "title", + "value": "Continue Progress" + }, + { + "name": "button1", + "value": "Long List Continue" + }, + { + "name": "button2", + "value": "Video Continue" + }, + { + "name": "button3", + "value": "Web Continue" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/media/background.png b/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/entry/src/main/resources/base/media/background.png differ diff --git a/entry/src/main/resources/base/media/foreground.png b/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/entry/src/main/resources/base/media/foreground.png differ diff --git a/entry/src/main/resources/base/media/layered_image.json b/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/media/startIcon.png b/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/entry/src/main/resources/base/media/startIcon.png differ diff --git a/entry/src/main/resources/base/profile/backup_config.json b/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/entry/src/main/resources/dark/element/color.json b/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..79b11c2747aec33e710fd3a7b2b3c94dd9965499 --- /dev/null +++ b/entry/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#000000" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..cc60ce1199ee3f6d4e9c6d868b2e5e107178295a --- /dev/null +++ b/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,32 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Continue Progress" + }, + { + "name": "title", + "value": "Continue Progress" + }, + { + "name": "button1", + "value": "Long List Continue" + }, + { + "name": "button2", + "value": "Video Continue" + }, + { + "name": "button3", + "value": "Web Continue" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..34cb9b17f7ba3c65500d5f36d081811fc218b17c --- /dev/null +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,32 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "浏览进度接续" + }, + { + "name": "title", + "value": "浏览进度接续" + }, + { + "name": "button1", + "value": "长列表进度接续" + }, + { + "name": "button2", + "value": "媒体播放进度接续" + }, + { + "name": "button3", + "value": "web页面浏览进度接续" + } + ] +} \ No newline at end of file diff --git a/features/longList/build-profile.json5 b/features/longList/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4d611879c7913fb0610c686e2399258ab3a6dad1 --- /dev/null +++ b/features/longList/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/features/longList/hvigorfile.ts b/features/longList/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6edcd90486dd5a853cf7d34c8647f08414ca7a3 --- /dev/null +++ b/features/longList/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/features/longList/obfuscation-rules.txt b/features/longList/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/features/longList/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/features/longList/oh-package.json5 b/features/longList/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0ee9ff49782fd80b3cdafcd58b1c4f491331a964 --- /dev/null +++ b/features/longList/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "name": "longlist", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/features/longList/src/main/ets/constants/BreakpointConstants.ets b/features/longList/src/main/ets/constants/BreakpointConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..a4b3c02e0ab81433b6af2e3b9ae63d46c1da3451 --- /dev/null +++ b/features/longList/src/main/ets/constants/BreakpointConstants.ets @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Constants for breakpoint. + */ +export class BreakpointConstants { + /** + * Breakpoints that represent small device types. + */ + public static readonly BREAKPOINT_SM: string = 'sm'; + + /** + * Breakpoints that represent middle device types. + */ + public static readonly BREAKPOINT_MD: string = 'md'; + + /** + * Breakpoints that represent large device types. + */ + public static readonly BREAKPOINT_LG: string = 'lg'; + + /** + * Breakpoint range sm + */ + public static readonly RANGE_SM: string = '(320vp<=width<600vp)'; + + /** + * Breakpoint range md + */ + public static readonly RANGE_MD: string = '(600vp<=width<840vp)'; + + /** + * Breakpoint range lg + */ + public static readonly RANGE_LG: string = '(840vp<=width)'; + + /** + * Breakpoint value sm/md/lg + */ + public static readonly BREAKPOINT_VALUE: Array = ['320vp', '600vp', '840vp']; + + /** + * AppStorage key breakpoint + */ + public static readonly BREAKPOINT_NAME: string = 'breakpoint'; + + /** + * Grid num two + */ + public static readonly GRID_NUM_TWO = '1fr 1fr'; + + /** + * Grid num three + */ + public static readonly GRID_NUM_THREE = '1fr 1fr 1fr'; + + /** + * Grid num four + */ + public static readonly GRID_NUM_FOUR = '1fr 1fr 1fr 1fr'; + + /** + * SearchBar and WATER_FLOW margin left sm + */ + public static readonly SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_SM: number = 16; + + /** + * SearchBar and WATER_FLOW margin left md + */ + public static readonly SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_MD: number = 24; + + /** + * SearchBar and WATER_FLOW margin left LG + */ + public static readonly SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_LG: number = 32; + + /** + * SearchBar and WATER_FLOW margin right sm + */ + public static readonly SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_SM: number = 16; + + /** + * SearchBar and WATER_FLOW margin right md + */ + public static readonly SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_MD: number = 24; + + /** + * SearchBar and WATER_FLOW margin right lg + */ + public static readonly SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_LG: number = 32; + + /** + * Function margin right sm + */ + public static readonly FUNCTION_MARGIN_RIGHT_SM: number = 10; + + /** + * Function margin right md + */ + public static readonly FUNCTION_MARGIN_RIGHT_MD: number = 24; + + /** + * Function margin right lg + */ + public static readonly FUNCTION_MARGIN_RIGHT_LG: number = 134; + + /** + * Function margin left sm + */ + public static readonly FUNCTION_MARGIN_LEFT_SM: number = 10; + + /** + * Function margin left md + */ + public static readonly FUNCTION_MARGIN_LEFT_MD: number = 24; + + /** + * Function margin left lg + */ + public static readonly FUNCTION_MARGIN_LEFT_LG: number = 134; +} \ No newline at end of file diff --git a/features/longList/src/main/ets/constants/CommonConstants.ets b/features/longList/src/main/ets/constants/CommonConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..c865911808d8389dbf234445e0d8a0974632a743 --- /dev/null +++ b/features/longList/src/main/ets/constants/CommonConstants.ets @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum NetConnectionState { + UNKNOWN_STATE = 0, + SUCCEED_STATE, + LOADING_STATE, + FAIL_STATE +} + +export class CommonConstants { + /** + * Full percent. + */ + public static readonly FULL_PERCENT: string = '100%'; + + /** + * 20 percent. + */ + public static readonly TWENTY_PERCENT: string = '20%'; + + /** + * Interface Path + */ + public static readonly MOCK_INTERFACE_PATH_NAME = 'smoothSlide'; + + /** + * the api fileName of function area + */ + public static readonly MOCK_INTERFACE_FUNCTION_FILE_NAME: string = 'entry.json'; + + /** + * the api fileName of waterFlow area + */ + public static readonly MOCK_INTERFACE_WATER_FLOW_FILE_NAME: string = 'waterFlow.json'; + + /** + * Waterflow cached count. + */ + public static readonly WATER_FLOW_CACHED_COUNT: number = 10; + + /** + * Function page number + */ + public static readonly FUNCTION_PAGE_NUMBER: number = 1; + + /** + * Function page size + */ + public static readonly FUNCTION_PAGE_SIZE: number = 25; + + /** + * waterFlow page size + */ + public static readonly WATER_FLOW_PAGE_SIZE: number = 20; + + /** + * waterFlow page start index + */ + public static readonly WATER_FLOW_PAGE_START_INDEX: number = 1; + + /** + * Function page one + */ + public static readonly FUNCTION_FIRST_INDEX: number = 0; + + /** + * Function page one + */ + public static readonly FUNCTION_SECOND_INDEX: number = 1; + + /** + * Function page one + */ + public static readonly FUNCTION_FIRST_COUNT: number = 10; + + /** + * Function page one + */ + public static readonly FUNCTION_SECOND_COUNT: number = 15; + + /** + * water flow default page count + */ + public static readonly WATER_FLOW_DEFAULT_PAGE_COUNT: number = 6; + + /** + * waterFlow image card reuseId + */ + public static readonly WATER_FLOW_IMAGE_REUSE_ID: string = 'image'; + + /** + * waterFlow image card type + */ + public static readonly WATER_FLOW_IMAGE_TYPE: string = 'image'; + + /** + * waterFlow video card reuseId + */ + public static readonly WATER_FLOW_VIDEO_REUSE_ID: string = 'video'; + + /** + * waterFlow image card type + */ + public static readonly WATER_FLOW_VIDEO_TYPE: string = 'video'; + + /** + * + * waterFlow living card reuseId + */ + public static readonly WATER_FLOW_LIVING_REUSE_ID: string = 'living'; + + /** + * waterFlow living card type + */ + public static readonly WATER_FLOW_LIVING_TYPE: string = 'living'; + + /** + * waterFlow living card struct id + */ + public static readonly WATER_FLOW_LIVING_STRUCT_ID: string = 'livingCard'; + + /** + * waterFlow card max count + */ + public static readonly WATER_FLOW_MAX_COUNT: number = 20; + + /** + * string default value: '' + */ + public static readonly STRING_DEFAULT_VALUE: string = ''; + + /** + * number default value: 0 + */ + public static readonly NUMBER_DEFAULT_VALUE: number = 0; + + /** + * toast show time + */ + public static readonly TOAST_SHOW_TIME: number = 3000; + + /** + * toast show time + */ + public static readonly TOAST_SHOW_MARGIN_BOTTOM: number = 108; + + /** + * the height of pull to refresh + */ + public static readonly PULL_TO_REFRESH_HEIGHT: number = 64; + + /** + * toast show time + */ + public static readonly ANIMATION_DURATION_TIME: number = 300; + + /** + * vip sign + */ + public static readonly VIP_SIGN: string = '1'; + + /** + * space + */ + public static readonly SPACE_FOUR: number = 4; + + /** + * space + */ + public static readonly SPACE_EIGHT: number = 8; + + /** + * font weight + */ + public static readonly TEXT_FONT_WEIGHT_500: number = 500; + + /** + * font weight + */ + public static readonly TEXT_FONT_WEIGHT_400: number = 400; + + /** + * text max lines + */ + public static readonly TEXT_MAX_LINES: number = 2; + + /** + * zIndex + */ + public static readonly LARGE_INDEX: number = 1; + + /** + * the first index of arr + */ + public static readonly ARR_FIRST_INDEX: number = 0; + + /** + * the count 1 of arr that should be deal + */ + public static readonly DEAL_COUNT_ONE: number = 1; + + /** + * phone critical value + */ + public static readonly CRITICAL_VALUE: number = 500; + + /** + * water flow two columns + */ + public static readonly WATER_FLOW_TWO_COLUMNS: number = 2; + + /** + * water flow three columns + */ + public static readonly WATER_FLOW_THREE_COLUMNS: number = 3; + + /** + * water flow column gap + */ + public static readonly WATER_FLOW_COLUMN_GAP: number = 8; + + /** + * description margin left + */ + public static readonly DESCRIPTION_MARGIN_LEFT: number = 12; + + /** + * description two lines height + */ + public static readonly DESCRIPTION_TWO_LINES_HEIGHT: number = 65; + + /** + * description three lines height + */ + public static readonly DESCRIPTION_THREE_LINES_HEIGHT: number = 80; + + /** + * language + */ + public static readonly LANGUAGE: string = 'language'; + + /** + * chinese language + */ + public static readonly CHINESE_LANGUAGE: string = 'zh'; +} \ No newline at end of file diff --git a/features/longList/src/main/ets/constants/HomeConstants.ets b/features/longList/src/main/ets/constants/HomeConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..ae8b7c82f9f2168e53160b6fe0219d7886db2def --- /dev/null +++ b/features/longList/src/main/ets/constants/HomeConstants.ets @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class HomeConstants { + /** + * Footer tab topics. + */ + public static readonly FOOTER_TOPIC_LIST: string[] = ['首页', '分类', '发现', '购物袋', '我的']; + + /** + * Footer tab english topics. + */ + public static readonly FOOTER_TOPIC_LIST_EN: string[] = ['home', 'categorize', 'found', 'shopping', 'mine']; + + /** + * Footer topic icons. + */ + public static readonly FOOTER_TOPIC_ICONS: Resource[] = + [$r('app.media.ic_public_home_filled'), $r('app.media.ic_public_class'), + $r('app.media.ic_public_discover'), $r('app.media.ic_public_shopping'), $r('app.media.ic_public_mine')]; + + /** + * Footer topic icons. + */ + public static readonly FOOTER_TOPIC_ICONS_SELECTED: Resource[] = + [$r('app.media.ic_public_home_filled'), $r('app.media.ic_public_class'), + $r('app.media.ic_public_discover'), $r('app.media.ic_public_shopping'), $r('app.media.ic_public_mine')]; + + /** + * Function default icons. + */ + public static readonly FUNCTION_DEFAULT_ICONS: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + /** + * Function area two lines height + */ + public static readonly FUNCTION_TWO_LINES_HEIGHT: number = 176; + + /** + * Function area three lines height + */ + public static readonly FUNCTION_THREE_LINES_HEIGHT: number = 254; + + /** + * Function rows page one. + */ + public static readonly FUNCTION_ROWS_PAGE_ONE: string = '1fr 1fr'; + + /** + * Function rows page two. + */ + public static readonly FUNCTION_ROWS_PAGE_TWO: string = '1fr 1fr 1fr'; + + /** + * Function column page two. + */ + public static readonly FUNCTION_COLUMN: string = '1fr 1fr 1fr 1fr 1fr'; + + /** + * index page. + */ + public static readonly INDEX_PAGE: string = '首页'; + + /** + * english index page. + */ + public static readonly INDEX_PAGE_EN: string = 'home'; +} \ No newline at end of file diff --git a/features/longList/src/main/ets/longlistability/LongListAbility.ets b/features/longList/src/main/ets/longlistability/LongListAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..ad058eab2ed07c8fa10d7e194ed905d3613c367c --- /dev/null +++ b/features/longList/src/main/ets/longlistability/LongListAbility.ets @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; +import { distributedDataObject } from '@kit.ArkData'; + +class ContinueData { + continueIndex: number | undefined; + currentOffset: number | undefined; + + constructor(continueIndex: number | undefined, currentOffset: number | undefined) { + this.continueIndex = continueIndex; + this.currentOffset = currentOffset; + } +} + +export default class LongListAbility extends UIAbility { + continueRestore(want: Want) { + if (!want.parameters || !want.parameters.distributedSessionId) { + return; + } + + let continueData: ContinueData = new ContinueData(undefined,undefined); + let dataObject = distributedDataObject.create(this.context, continueData); + dataObject.on('status', (sessionId: string, networkId: string, status: string) => { + if (status === 'restored') { + AppStorage.setOrCreate('continueIndex', dataObject['continueIndex']); + AppStorage.setOrCreate('currentOffset', dataObject['currentOffset']); + AppStorage.setOrCreate('continueEntry', true); + AppStorage.setOrCreate('setCurrentOffset', true); + } + }); + let sessionId = want.parameters.distributedSessionId as string; + dataObject.setSessionId(sessionId); + this.context.restoreWindowStage(new LocalStorage()); + } + + async onContinue(wantParam: Record): Promise { + let continueIndex = AppStorage.get('continueIndex') as number; + let currentOffset = AppStorage.get('currentOffset') as number; + let continueData: ContinueData = new ContinueData(continueIndex, currentOffset); + let dataObject = distributedDataObject.create(this.context, continueData); + let sessionId = distributedDataObject.genSessionId(); + dataObject.setSessionId(sessionId); + wantParam.distributedSessionId = sessionId; + let deviceId = wantParam.targetDevice as string; + dataObject.save(deviceId); + return AbilityConstant.OnContinueResult.AGREE; + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onCreate'); + if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) { + if (want.parameters && want.parameters.distributedSessionId) { + this.continueRestore(want); + } + } + } + + onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { + if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) { + if (want.parameters && want.parameters.distributedSessionId) { + this.continueRestore(want); + } + } + } + + onDestroy(): void { + hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage){ + // Main window is created, set main page for this ability + hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'EntryAbility', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'EntryAbility', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onBackground'); + } +} diff --git a/features/longList/src/main/ets/model/FooterTabData.ets b/features/longList/src/main/ets/model/FooterTabData.ets new file mode 100644 index 0000000000000000000000000000000000000000..ed16d627cf0606ec12fe8b1511838bdf428ada81 --- /dev/null +++ b/features/longList/src/main/ets/model/FooterTabData.ets @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { HomeConstants } from '../constants/HomeConstants'; + +export class FooterTabData { + public tabList: FooterTab[] = []; + + constructor() { + HomeConstants.FOOTER_TOPIC_LIST.forEach((item: string, index: number) => { + this.tabList.push(new FooterTab(item, HomeConstants.FOOTER_TOPIC_ICONS[index], + HomeConstants.FOOTER_TOPIC_ICONS_SELECTED[index])); + }); + } +} + +export class FooterTabDataEn { + public tabList: FooterTab[] = []; + + constructor() { + HomeConstants.FOOTER_TOPIC_LIST_EN.forEach((item: string, index: number) => { + this.tabList.push(new FooterTab(item, HomeConstants.FOOTER_TOPIC_ICONS[index], + HomeConstants.FOOTER_TOPIC_ICONS_SELECTED[index])); + }); + } +} + +export class FooterTab { + /** + * Name of the tab. + */ + public name: string; + /** + * Icon of the tab. + */ + public icon: Resource; + /** + * Icon selected. + */ + public iconSelected: Resource; + + constructor(name: string, icon: Resource, iconSelected: Resource) { + this.name = name; + this.icon = icon; + this.iconSelected = iconSelected; + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/model/FunctionEntryData.ets b/features/longList/src/main/ets/model/FunctionEntryData.ets new file mode 100644 index 0000000000000000000000000000000000000000..ed58a4a6ece74966fe6ffcc8bcd38af0106c7195 --- /dev/null +++ b/features/longList/src/main/ets/model/FunctionEntryData.ets @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class FunctionEntryData { + public icon: string; + public name: string; + public nameEn: string; + public url: string; + public index: number; + + constructor(icon: string, name: string, nameEn: string, url: string, index: number) { + this.icon = icon; + this.name = name; + this.nameEn = nameEn; + this.url = url; + this.index = index; + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/model/FunctionEntryListData.ets b/features/longList/src/main/ets/model/FunctionEntryListData.ets new file mode 100644 index 0000000000000000000000000000000000000000..093eaed9edb7c8ad8c680f5d289534082a0d96f5 --- /dev/null +++ b/features/longList/src/main/ets/model/FunctionEntryListData.ets @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NetworkUtil } from '../utils/NetworkUtil'; +import { FunctionEntryData } from './FunctionEntryData'; +import { CommonConstants } from '../constants/CommonConstants'; + +export class FunctionEntryListData { + public netWorkUtil: NetworkUtil = new NetworkUtil(); + + async getData(): Promise { + let tmp: FunctionEntryData[] = + await this.netWorkUtil.getFunctionEntryData(CommonConstants.MOCK_INTERFACE_PATH_NAME, + CommonConstants.MOCK_INTERFACE_FUNCTION_FILE_NAME, CommonConstants.FUNCTION_PAGE_NUMBER, + CommonConstants.FUNCTION_PAGE_SIZE); + return tmp; + } +} + diff --git a/features/longList/src/main/ets/model/WaterFlowData.ets b/features/longList/src/main/ets/model/WaterFlowData.ets new file mode 100644 index 0000000000000000000000000000000000000000..60669971eaa6ae39a7d11e3c4cceff8f1537be8e --- /dev/null +++ b/features/longList/src/main/ets/model/WaterFlowData.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { WaterFlowDescriptionData } from './WaterFlowDescriptionData'; +import { WaterFlowHeadData } from './WaterFlowHeadData'; + +export class WaterFlowData { + public waterFlowHead: WaterFlowHeadData; + public waterFlowDescription: WaterFlowDescriptionData; + + constructor(waterFlowHead: WaterFlowHeadData, waterFlowDescription: WaterFlowDescriptionData) { + this.waterFlowHead = waterFlowHead; + this.waterFlowDescription = waterFlowDescription; + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/model/WaterFlowDescriptionData.ets b/features/longList/src/main/ets/model/WaterFlowDescriptionData.ets new file mode 100644 index 0000000000000000000000000000000000000000..71b83d3f3e38401c51313b7337121aef63a8725d --- /dev/null +++ b/features/longList/src/main/ets/model/WaterFlowDescriptionData.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class WaterFlowDescriptionData { + public title: string; + public titleEn: string; + public userImage: string; + public vipSign: string; + public userName: string; + public collectionsCount: number; + public url: string; + public index: number; + + constructor(title: string, titleEn: string, userImage: string, vipSign: string, userName: string, + collectionsCount: number, url: string, index: number) { + this.title = title; + this.titleEn = titleEn; + this.userImage = userImage; + this.vipSign = vipSign; + this.userName = userName; + this.collectionsCount = collectionsCount; + this.url = url; + this.index = index; + } + +} \ No newline at end of file diff --git a/features/longList/src/main/ets/model/WaterFlowHeadData.ets b/features/longList/src/main/ets/model/WaterFlowHeadData.ets new file mode 100644 index 0000000000000000000000000000000000000000..12b967a5ac38b46a7affa96afd6181298fc53d97 --- /dev/null +++ b/features/longList/src/main/ets/model/WaterFlowHeadData.ets @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class WaterFlowHeadData { + public thumbnails: string; + public source: string; + public width: number; + public height: number; + public type: string; + + constructor(thumbnails: string, source: string, width: number, height: number, type: string) { + this.thumbnails = thumbnails; + this.source = source; + this.width = width; + this.height = height; + this.type = type; + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/model/WaterFlowListData.ets b/features/longList/src/main/ets/model/WaterFlowListData.ets new file mode 100644 index 0000000000000000000000000000000000000000..7dc475e297a2bafc619f7a6d2d82cf0a2a771ab7 --- /dev/null +++ b/features/longList/src/main/ets/model/WaterFlowListData.ets @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NetworkUtil } from '../utils/NetworkUtil'; +import { WaterFlowData } from './WaterFlowData'; +import { CommonConstants } from '../constants/CommonConstants'; + +export class WaterFlowListData { + public dataSource: BasicDataSource = new BasicDataSource(); + public netWorkUtil: NetworkUtil = new NetworkUtil(); + + getData(): BasicDataSource { + return this.dataSource; + } + + async addData(fileName: string, pageNo: number, pageSize: number): Promise { + let tmp: WaterFlowData[] = + await this.netWorkUtil.getWaterFlowData(CommonConstants.MOCK_INTERFACE_PATH_NAME, fileName, pageNo, pageSize); + this.dataSource.addData(tmp); + } +} + +class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private originDataArray: WaterFlowData[] = []; + + totalCount(): number { + return this.originDataArray.length; + } + + getData(index: number): WaterFlowData { + return this.originDataArray[index]; + } + + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < CommonConstants.ARR_FIRST_INDEX) { + this.listeners.push(listener); + } + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= CommonConstants.ARR_FIRST_INDEX) { + this.listeners.splice(pos, CommonConstants.DEAL_COUNT_ONE); + } + } + + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }); + } + + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }); + } + + public addData(data: WaterFlowData[]): void { + let len = this.originDataArray.length; + this.originDataArray = this.originDataArray.concat(data); + this.notifyDataAdd(len); + } + + public clearData(): void { + this.originDataArray = []; + this.notifyDataReload(); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/pages/Index.ets b/features/longList/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..d183dd1b783764d9aae43a38e15b77521f6cc1b6 --- /dev/null +++ b/features/longList/src/main/ets/pages/Index.ets @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { display, window } from '@kit.ArkUI'; +import { i18n } from '@kit.LocalizationKit'; +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import { BreakpointSystem } from '../utils/BreakpointSystem'; +import { IndexNavDestination } from '../view/IndexNavDestination'; +import { HomeConstants } from '../constants/HomeConstants'; +import { CommonConstants } from '../constants/CommonConstants'; +import Logger from '../utils/Logger'; + +@Entry +@Component +struct Index { + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink(CommonConstants.LANGUAGE) language: string = CommonConstants.CHINESE_LANGUAGE; + @Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack(); + @State windowsHeight: number = CommonConstants.NUMBER_DEFAULT_VALUE; + private breakpointSystem: BreakpointSystem = new BreakpointSystem(); + private indexPage: string = CommonConstants.CHINESE_LANGUAGE; + + onBackPress() { + return true; + } + + @Builder + PageMap(name: string) { + if (name === this.indexPage) { + IndexNavDestination({ windowsHeight: this.windowsHeight }); + } else { + NavDestination() + .hideTitleBar(true); + } + } + + aboutToAppear(): void { + try { + let systemLanguage: string = i18n.System.getSystemLanguage(); + let local = new Intl.Locale(systemLanguage); + this.language = local.language; + } catch (err) { + Logger.error(`call System.getSystemLanguage() failed`); + } + if (this.language === CommonConstants.CHINESE_LANGUAGE) { + this.indexPage = HomeConstants.INDEX_PAGE; + } else { + this.indexPage = HomeConstants.INDEX_PAGE_EN; + } + this.pageInfos.pushPath({ name: this.indexPage }, false); + this.windowsHeight = px2vp(display.getDefaultDisplaySync().height); + this.breakpointSystem.register(); + window.getLastWindow(getContext()).then(win => { + win.setWindowLayoutFullScreen(true); + let area = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + let height = area.topRect.height; + let vpHeight = px2vp(height); + AppStorage.setOrCreate('topHeight', vpHeight); + }); + } + + aboutToDisappear(): void { + this.breakpointSystem.unregister(); + } + + build() { + Navigation(this.pageInfos) { + } + .hideNavBar(true) + .mode(NavigationMode.Split) + .hideToolBar(true) + .hideTitleBar(true) + .navDestination(this.PageMap); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/utils/BreakpointSystem.ets b/features/longList/src/main/ets/utils/BreakpointSystem.ets new file mode 100644 index 0000000000000000000000000000000000000000..f0ede609789bdd86c37617a7955bbd17378044c7 --- /dev/null +++ b/features/longList/src/main/ets/utils/BreakpointSystem.ets @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import mediaQuery from '@ohos.mediaquery'; +import { BreakpointConstants } from '../constants/BreakpointConstants'; + +declare interface BreakPointTypeOption { + sm?: T, + md?: T, + lg?: T, +} + +export class BreakpointType { + public options: BreakPointTypeOption; + + constructor(option: BreakPointTypeOption) { + this.options = option; + } + + getValue(currentPoint: string): T { + let point: T; + switch (currentPoint) { + case BreakpointConstants.BREAKPOINT_SM: + point = this.options.sm as T; + break; + case BreakpointConstants.BREAKPOINT_MD: + point = this.options.md as T; + break; + case BreakpointConstants.BREAKPOINT_LG: + point = this.options.lg as T; + break; + default: + point = this.options.sm as T; + break; + } + return point; + } +} + +export class BreakpointSystem { + private readonly listenerKey = 'change'; + private currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + private smListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_SM); + private mdListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_MD); + private lgListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_LG); + + private updateCurrentBreakpoint(breakpoint: string): void { + if (this.currentBreakpoint !== breakpoint) { + this.currentBreakpoint = breakpoint; + AppStorage.setOrCreate(BreakpointConstants.BREAKPOINT_NAME, this.currentBreakpoint); + } + } + + private isBreakpointSM = (mediaQueryResult: mediaQuery.MediaQueryResult): void => { + if (mediaQueryResult.matches) { + this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_SM); + } + }; + + private isBreakpointMD = (mediaQueryResult: mediaQuery.MediaQueryResult): void => { + if (mediaQueryResult.matches) { + this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_MD); + } + }; + + private isBreakpointLG = (mediaQueryResult: mediaQuery.MediaQueryResult): void => { + if (mediaQueryResult.matches) { + this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_LG); + } + }; + + public register(): void { + this.smListener.on(this.listenerKey, this.isBreakpointSM); + this.mdListener.on(this.listenerKey, this.isBreakpointMD); + this.lgListener.on(this.listenerKey, this.isBreakpointLG); + } + + public unregister(): void { + this.smListener.off(this.listenerKey, this.isBreakpointSM); + this.mdListener.off(this.listenerKey, this.isBreakpointMD); + this.lgListener.off(this.listenerKey, this.isBreakpointLG); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/utils/BreakpointType.ets b/features/longList/src/main/ets/utils/BreakpointType.ets new file mode 100644 index 0000000000000000000000000000000000000000..e5082132676e8825a6db51225a643f70ea890356 --- /dev/null +++ b/features/longList/src/main/ets/utils/BreakpointType.ets @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BreakpointConstants } from '../constants/BreakpointConstants'; + +export class BreakpointType { + public sm: T; + public md: T; + public lg: T; + + constructor(sm: T, md: T, lg: T) { + this.sm = sm; + this.md = md; + this.lg = lg; + } + + getValue(currentBreakpoint: string): T { + if (currentBreakpoint === BreakpointConstants.BREAKPOINT_MD) { + return this.md; + } + if (currentBreakpoint === BreakpointConstants.BREAKPOINT_LG) { + return this.lg; + } else { + return this.sm; + } + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/utils/Logger.ets b/features/longList/src/main/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..56419b8f96945271afd594241cda6c99c1a7af0a --- /dev/null +++ b/features/longList/src/main/ets/utils/Logger.ets @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + } + + /** + * Outputs debug-level logs. + * @param args Indicates the log parameters. + */ + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + /** + * Outputs info-level logs. + * @param args Indicates the log parameters. + */ + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + /** + * Outputs warning-level logs. + * @param args Indicates the log parameters. + */ + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + /** + * Outputs error-level logs. + * @param args Indicates the log parameters. + */ + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('[PageSlip]'); + diff --git a/features/longList/src/main/ets/utils/NetworkUtil.ets b/features/longList/src/main/ets/utils/NetworkUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..789c9f65f54cb74c2ed724e320710aa159b46d5d --- /dev/null +++ b/features/longList/src/main/ets/utils/NetworkUtil.ets @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { http } from '@kit.NetworkKit'; +import { systemDateTime } from '@kit.BasicServicesKit'; +import { FunctionEntryData } from '../model/FunctionEntryData'; +import { WaterFlowData } from '../model/WaterFlowData'; +import Logger from './Logger'; +import { WaterFlowHeadData } from '../model/WaterFlowHeadData'; +import { WaterFlowDescriptionData } from '../model/WaterFlowDescriptionData'; +import { CommonConstants } from '../constants/CommonConstants'; + +const URL: string = 'https://devecostudio-drcn.op.hicloud.com/solution/v1/getSceneMockData' + + '?scene={scene}&fileName={fileName}&pageNum={pageNum}&pageSize={pageSize}'; + +export class NetworkUtil { + private waterFlowIndex: number = CommonConstants.NUMBER_DEFAULT_VALUE; + private functionIndex: number = CommonConstants.NUMBER_DEFAULT_VALUE; + + static async getResponse(url: string, params: string[]): Promise | null> { + try { + const beginTime = systemDateTime.getTime(); + url = url.replace('{scene}', params[0]) + .replace('{fileName}', params[1]) + .replace('{pageNum}', params[2]) + .replace('{pageSize}', params[3]); + let data: http.HttpResponse = await http.createHttp().request( + url, + { + method: http.RequestMethod.GET, + header: { + 'Content-Type': 'application/json' + }, + expectDataType: http.HttpDataType.STRING, + usingCache: true, + }); + const endTime = systemDateTime.getTime() - beginTime; + Logger.info('getResponse success, url:' + url + ', cost: ' + endTime); + if (data && data.responseCode === http.ResponseCode.OK && typeof data.result === 'string') { + let arr: Record = JSON.parse(data.result); + return arr; + } else { + return null; + } + } catch (err) { + Logger.error('getResponse err:', JSON.stringify(err)); + return null; + } + } + + async getFunctionEntryData(scene: string, fileName: string, pageNum: number, + pageSize: number): Promise { + let result: FunctionEntryData[] = []; + let params: string[] = [scene, fileName, pageNum + '', pageSize + '']; + try { + let arr = await NetworkUtil.getResponse(URL, params); + if (arr) { + let tempData = arr.data as object[]; + let result: FunctionEntryData[] = []; + for (let i = 0; i < tempData.length; i++) { + let tmp = tempData[i] as Record; + let temp = new FunctionEntryData(tmp.icon, tmp.name, tmp.name_en, tmp.url, this.functionIndex); + this.functionIndex++; + result.push(temp); + } + return result; + } + } catch (err) { + Logger.error('getFunctionEntryData err:', JSON.stringify(err)); + } + return result; + } + + async getWaterFlowData(scene: string, fileName: string, pageNum: number, + pageSize: number): Promise { + let result: WaterFlowData[] = []; + let params: string[] = [scene, fileName, pageNum + '', pageSize + '']; + try { + let arr = await NetworkUtil.getResponse(URL, params); + if (arr) { + let tempData = arr['data'] as object[]; + let result: WaterFlowData[] = []; + for (let i = 0; i < tempData.length; i++) { + let tmp = tempData[i] as Record; + let temWaterFlowHead = + new WaterFlowHeadData(tmp.thumbnails, tmp.source, parseInt(tmp.width), parseInt(tmp.height), tmp.type); + let temWaterFlowDescription = + new WaterFlowDescriptionData(tmp.title, tmp.title_en, tmp.user_image, tmp.vip_sign, + tmp.nick_name, parseInt(tmp.collections_count), tmp.url, this.waterFlowIndex); + let temWaterFlow = new WaterFlowData(temWaterFlowHead, temWaterFlowDescription); + this.waterFlowIndex++; + result.push(temWaterFlow); + } + return result; + } + } catch (err) { + Logger.error('getWaterFlowData err: ', JSON.stringify(err)); + } + return result; + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/FunctionView.ets b/features/longList/src/main/ets/view/FunctionView.ets new file mode 100644 index 0000000000000000000000000000000000000000..055510baa182a85c975bec8ba09cde7dba22e5a7 --- /dev/null +++ b/features/longList/src/main/ets/view/FunctionView.ets @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import { BreakpointType } from '../utils/BreakpointSystem'; +import { FunctionEntryListData } from '../model/FunctionEntryListData'; +import { FunctionEntryData } from '../model/FunctionEntryData'; +import { HomeConstants } from '../constants/HomeConstants'; +import { CommonConstants } from '../constants/CommonConstants'; + +@Component +export struct FunctionView { + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; + @StorageLink(CommonConstants.LANGUAGE) language:string = CommonConstants.CHINESE_LANGUAGE; + @Watch('aboutToAppear') @Link freshFlag: boolean; + @State functionEntryListData: FunctionEntryListData = new FunctionEntryListData(); + @State functionEntryData: FunctionEntryData[] = []; + @State functionEntryFirstData: FunctionEntryData[] = []; + @State functionEntrySecondData: FunctionEntryData[] = []; + @State pageSizeList: number[] = [CommonConstants.FUNCTION_FIRST_INDEX, CommonConstants.FUNCTION_SECOND_INDEX]; + @State currentIndex: number = CommonConstants.FUNCTION_FIRST_INDEX; + @State gridHeight: number = HomeConstants.FUNCTION_TWO_LINES_HEIGHT; + + async aboutToAppear(): Promise { + let arr: FunctionEntryData[] = await this.functionEntryListData.getData(); + this.functionEntryData = arr; + this.functionEntryFirstData = arr.slice(CommonConstants.NUMBER_DEFAULT_VALUE, CommonConstants.FUNCTION_FIRST_COUNT); + this.functionEntrySecondData = arr.slice(CommonConstants.FUNCTION_FIRST_COUNT, + CommonConstants.FUNCTION_FIRST_COUNT + CommonConstants.FUNCTION_SECOND_COUNT); + } + + build() { + Swiper() { + Grid() { + if (this.functionEntryData.length === CommonConstants.NUMBER_DEFAULT_VALUE) { + this.DefaultGrid(); + } else { + this.FunctionGrid(this.functionEntryFirstData); + } + } + .rowsGap($r('app.float.function_row_gap')) + .padding({ bottom: $r('app.float.function_padding_bottom') }) + .height(this.gridHeight) + .rowsTemplate(HomeConstants.FUNCTION_ROWS_PAGE_ONE) + .columnsTemplate(HomeConstants.FUNCTION_COLUMN); + + Grid() { + if (this.functionEntryData.length === CommonConstants.NUMBER_DEFAULT_VALUE) { + this.DefaultGrid(); + } else { + this.FunctionGrid(this.functionEntrySecondData); + } + } + .rowsGap($r('app.float.function_row_gap')) + .padding({ bottom: $r('app.float.function_padding_bottom') }) + .height(this.gridHeight) + .rowsTemplate(HomeConstants.FUNCTION_ROWS_PAGE_TWO) + .columnsTemplate(HomeConstants.FUNCTION_COLUMN); + } + .onChange((index: number) => { + this.currentIndex = index; + }) + .onContentDidScroll((selectedIndex: number, index: number, position: number) => { + if (selectedIndex === CommonConstants.FUNCTION_FIRST_INDEX && index === CommonConstants.FUNCTION_FIRST_INDEX) { + this.gridHeight = HomeConstants.FUNCTION_TWO_LINES_HEIGHT + + (HomeConstants.FUNCTION_TWO_LINES_HEIGHT - HomeConstants.FUNCTION_THREE_LINES_HEIGHT) * position; + } + if (selectedIndex === CommonConstants.FUNCTION_SECOND_INDEX && index === CommonConstants.FUNCTION_SECOND_INDEX) { + this.gridHeight = HomeConstants.FUNCTION_THREE_LINES_HEIGHT + + (HomeConstants.FUNCTION_TWO_LINES_HEIGHT - HomeConstants.FUNCTION_THREE_LINES_HEIGHT) * position; + } + }) + .loop(false) + .margin({ + left: new BreakpointType({ + sm: BreakpointConstants.FUNCTION_MARGIN_LEFT_SM, + md: BreakpointConstants.FUNCTION_MARGIN_LEFT_MD, + lg: BreakpointConstants.FUNCTION_MARGIN_LEFT_LG + }).getValue(this.currentBreakpoint), + right: new BreakpointType({ + sm: BreakpointConstants.FUNCTION_MARGIN_RIGHT_SM, + md: BreakpointConstants.FUNCTION_MARGIN_RIGHT_MD, + lg: BreakpointConstants.FUNCTION_MARGIN_RIGHT_LG + }).getValue(this.currentBreakpoint) + }); + } + + @Builder + DefaultGrid() { + ForEach(HomeConstants.FUNCTION_DEFAULT_ICONS, + () => { + GridItem() { + Column() { + Column() { + } + .height($r('app.float.function_gridItem_image_height')) + .width($r('app.float.function_gridItem_image_width')) + .borderRadius($r('app.float.function_gridItem_border_radius')) + .backgroundColor($r('app.color.function_default_color')); + + Column() { + } + .height($r('app.float.function_gridItem_gray_height')) + .width($r('app.float.function_gridItem_gray_width')) + .backgroundColor($r('app.color.function_default_color')) + .margin({ top: $r('app.float.function_gridItem_gray_marge_top') }); + } + .margin({ bottom: $r('app.float.function_gridItem_marge_bottom') }) + .height($r('app.float.function_gridItem_height')); + }; + }, (item: FunctionEntryData): string => item.index.toString()); + } + + @Builder + FunctionGrid(data: FunctionEntryData[]) { + ForEach(data, (item: FunctionEntryData) => { + GridItem() { + Column() { + Image(item.icon) + .height($r('app.float.function_gridItem_image_height')) + .width($r('app.float.function_gridItem_image_width')) + .borderRadius($r('app.float.function_gridItem_border_radius')); + Text(this.language === CommonConstants.CHINESE_LANGUAGE ? item.name : item.nameEn) + .fontSize($r('app.float.category_text_font')) + .lineHeight($r('app.float.function_gridItem_text_line_height')) + .margin({ top: $r('app.float.function_gridItem_text_margin_top') }); + }; + } + .width($r('app.float.function_gridItem_width')) + .height($r('app.float.function_gridItem_height')); + }, (item: FunctionEntryData): string => item.index.toString()); + } +} + diff --git a/features/longList/src/main/ets/view/HomeContent.ets b/features/longList/src/main/ets/view/HomeContent.ets new file mode 100644 index 0000000000000000000000000000000000000000..b12e9d823b68798eaf53924223ffa2b3781e4eda --- /dev/null +++ b/features/longList/src/main/ets/view/HomeContent.ets @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { connection } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { promptAction } from '@kit.ArkUI'; +import { WaterFlowView } from './WaterFlowView'; +import { WaterFlowListData } from '../model/WaterFlowListData'; +import { CommonConstants, NetConnectionState } from '../constants/CommonConstants'; +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import { SearchBarView } from './SearchBarView'; +import { FunctionView } from './FunctionView'; +import { HomeConstants } from '../constants/HomeConstants'; +import { BreakpointType } from '../utils/BreakpointSystem'; +import { common, Want } from '@kit.AbilityKit'; +import Logger from '../utils/Logger'; + +@Component +export struct HomeContent { + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; + @StorageProp('topHeight') topHeight: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @StorageLink('currentOffset') currentOffset: number = 0; + @StorageLink('setCurrentOffset') @Watch('watchCurrentOffset')setCurrentOffset: boolean = false; + @Link windowsHeight: number; + @State isShowFoot: boolean = false; + @State isRefreshing: boolean = false; + @State waterFlowListData: WaterFlowListData = new WaterFlowListData(); + @State freshFlag: boolean = false; + @State netConnectState: NetConnectionState = NetConnectionState.UNKNOWN_STATE; + public scroller: Scroller = new Scroller(); + public waterFlowScroller: Scroller = new Scroller(); + private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; + + watchCurrentOffset(){ + if(this.setCurrentOffset){ + this.scroller.scrollTo({xOffset:0, yOffset:this.currentOffset}); + this.setCurrentOffset = false; + } + } + + listenNetworkEvent(firstLoading: boolean) { + let netCon: connection.NetConnection = connection.createNetConnection(); + let loading: boolean = false; + netCon.register((error: BusinessError) => { + Logger.info(JSON.stringify(error)); + }); + netCon.on('netUnavailable', () => { + if (!loading) { + loading = true; + if (this.netConnectState !== NetConnectionState.SUCCEED_STATE) { + this.netConnectState = NetConnectionState.LOADING_STATE; + setTimeout(() => { + this.netConnectState = NetConnectionState.FAIL_STATE; + }, 3000); + } else { + try { + promptAction.showToast({ + message: $r('app.string.net_connection_description'), + bottom: 120, + duration: 3000 + }); + } catch (error) { + let message = (error as BusinessError).message; + let code = (error as BusinessError).code; + Logger.error('showToast args error code is ', code.toString(), 'message is ', message.toString()); + } + } + } + }); + netCon.on('netAvailable', () => { + if (!loading) { + loading = true; + this.netConnectState = NetConnectionState.SUCCEED_STATE; + if (!firstLoading) { + this.isShowFoot = false; + setTimeout(() => { + this.waterFlowListData.dataSource.clearData(); + this.waterFlowListData.addData(CommonConstants.MOCK_INTERFACE_WATER_FLOW_FILE_NAME, + Math.ceil(Math.random() * 20), CommonConstants.WATER_FLOW_PAGE_SIZE); + this.freshFlag = !this.freshFlag; + this.isRefreshing = false; + }, 1000); + } + } + }); + setTimeout(() => { + this.isRefreshing = false; + netCon.unregister((error: BusinessError) => { + Logger.error('unregister err:' + JSON.stringify(error)); + }); + }, 1000); + } + + aboutToAppear(): void { + this.listenNetworkEvent(true); + } + + onDidBuild(): void { + if(this.setCurrentOffset){ + this.scroller.scrollTo({xOffset:0, yOffset:this.currentOffset}); + this.setCurrentOffset = false; + } + } + build() { + Column() { + Row(){ + Image($r('app.media.back')) + .width(40) + .height(40) + .margin({ + right:8 + }) + .onClick(()=>{ + let want: Want = { + deviceId: '', + bundleName: 'com.example.continueprogress', + abilityName: 'EntryAbility', + } + this.context.startAbility(want) + }) + Text('长列表进度接续') + .fontSize(20) + .fontWeight(700) + .lineHeight(27) + } + .height(56) + .width('100%') + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Start) + .margin({ + top: this.topHeight + }) + .padding({ + left:19, + right:19 + }) + SearchBarView(); + Column() { + Scroll(this.scroller) { + if (this.netConnectState === NetConnectionState.SUCCEED_STATE) { + Column() { + FunctionView({ freshFlag: this.freshFlag }); + WaterFlowView({ + waterFlowListData: this.waterFlowListData, + waterFlowScroller: this.waterFlowScroller, + scroller: this.scroller, + windowsHeight: this.windowsHeight, + isShowFoot: this.isShowFoot + }); + } + .justifyContent(FlexAlign.Start); + } else if (this.netConnectState === NetConnectionState.LOADING_STATE) { + Column() { + this.loadingData(); + } + .justifyContent(FlexAlign.Start); + } else if (this.netConnectState === NetConnectionState.FAIL_STATE) { + Column() { + this.netUnavailable(); + } + .justifyContent(FlexAlign.Start); + } + } + .onAppear(()=>{ + if(this.setCurrentOffset){ + this.scroller.scrollTo({xOffset:0, yOffset:this.currentOffset}); + this.setCurrentOffset = false; + } + this.currentOffset = this.scroller.currentOffset().yOffset; + }) + .onDidScroll((xOffset: number, yOffset: number, scrollState: ScrollState)=>{ + if(!this.setCurrentOffset){ + this.currentOffset = this.scroller.currentOffset().yOffset; + } + }) + .onDisAppear(()=>{ + this.currentOffset = this.scroller.currentOffset().yOffset; + }) + .width(CommonConstants.FULL_PERCENT) + .scrollBar(BarState.Off); + } + } + .backgroundColor($r('app.color.start_window_background')) + .height(CommonConstants.FULL_PERCENT) + .width(CommonConstants.FULL_PERCENT); + } + + @Builder + loadingData() { + Stack() { + Column() { + Grid() { + ForEach(HomeConstants.FUNCTION_DEFAULT_ICONS.slice(CommonConstants.NUMBER_DEFAULT_VALUE, + CommonConstants.FUNCTION_FIRST_COUNT), + () => { + GridItem() { + Column() { + Column() { + } + .height($r('app.float.function_gridItem_image_height')) + .width($r('app.float.function_gridItem_image_width')) + .backgroundColor($r('app.color.function_default_color')); + + Column() { + } + .height($r('app.float.function_gridItem_gray_height')) + .width($r('app.float.function_gridItem_gray_width')) + .backgroundColor($r('app.color.function_default_color')) + .margin({ top: $r('app.float.function_gridItem_gray_marge_top') }); + }; + } + .width($r('app.float.function_gridItem_width')) + .height($r('app.float.function_gridItem_height')); + }, (index: number): string => index.toString()); + } + .rowsGap($r('app.float.function_row_gap')) + .padding({ + bottom: $r('app.float.function_padding_default_bottom'), + left: $r('app.float.function_padding_left'), + right: $r('app.float.function_padding_right'), + }) + .height($r('app.float.function_two_lines_default_height')) + .rowsTemplate(HomeConstants.FUNCTION_ROWS_PAGE_ONE) + .columnsTemplate(HomeConstants.FUNCTION_COLUMN); + + Grid() { + ForEach(HomeConstants.FUNCTION_DEFAULT_ICONS.slice(CommonConstants.NUMBER_DEFAULT_VALUE, + CommonConstants.WATER_FLOW_DEFAULT_PAGE_COUNT), + () => { + GridItem() { + Column() { + } + .height($r('app.float.water_flow_gridItem_image_height')) + .width(CommonConstants.FULL_PERCENT) + .backgroundColor($r('app.color.function_default_color')) + .borderRadius($r('app.float.rounded_size_16')); + }; + }, (index: number): string => index.toString()); + } + .rowsGap($r('app.float.water_flow_default_item_gap')) + .columnsGap($r('app.float.water_flow_default_item_gap')) + .padding({ + bottom: $r('app.float.function_padding_bottom'), + left: $r('app.float.function_padding_left'), + right: $r('app.float.function_padding_right'), + }) + .height($r('app.float.water_flow_default_height')) + .rowsTemplate(BreakpointConstants.GRID_NUM_TWO) + .columnsTemplate(new BreakpointType({ + sm: BreakpointConstants.GRID_NUM_TWO, + md: BreakpointConstants.GRID_NUM_THREE, + lg: BreakpointConstants.GRID_NUM_THREE + }).getValue(this.currentBreakpoint)); + }; + + LoadingProgress() + .color(Color.Black) + .opacity($r('app.float.net_unavailable_opacity')) + .width($r('app.float.net_unavailable_loading_width')) + .height($r('app.float.net_unavailable_loading_height')); + }; + } + + @Builder + netUnavailable() { + Column() { + Image($r('app.media.moon')) + .width($r('app.float.net_unavailable_moon_width')) + .height($r('app.float.net_unavailable_moon_height')) + .margin({ + top: $r('app.float.net_unavailable_moon_margin_top'), + bottom: $r('app.float.net_unavailable_moon_margin_bottom') + }); + Text($r('app.string.net_connection_description')) + .fontSize($r('app.float.net_unavailable_text_fontsize')) + .opacity($r('app.float.net_unavailable_opacity')); + } + .width(CommonConstants.FULL_PERCENT) + .height(CommonConstants.FULL_PERCENT); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/IndexNavDestination.ets b/features/longList/src/main/ets/view/IndexNavDestination.ets new file mode 100644 index 0000000000000000000000000000000000000000..fb6b3d8c0cbda2fca2aa7fe723e5c9c6c4a5df7c --- /dev/null +++ b/features/longList/src/main/ets/view/IndexNavDestination.ets @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import { BreakpointType } from '../utils/BreakpointSystem'; +import { NavigationBarView } from '../view/NavigationBarView'; +import { HomeContent } from '../view/HomeContent'; + +@Component +export struct IndexNavDestination { + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @Link windowsHeight: number ; + private swiperController: SwiperController = new SwiperController(); + private scroller: Scroller = new Scroller(); + private waterFlowScroller: Scroller = new Scroller(); + + build() { + NavDestination() { + Flex({ + direction: new BreakpointType( + { + sm: FlexDirection.Column, + md: FlexDirection.Column, + lg: FlexDirection.Row, + } + ).getValue(this.currentBreakpoint), + wrap: FlexWrap.NoWrap, + justifyContent: FlexAlign.Start, + alignItems: ItemAlign.Start, + alignContent: FlexAlign.Start + }) { + Column() { + HomeContent({ + scroller: this.scroller, + waterFlowScroller: this.waterFlowScroller, + windowsHeight: this.windowsHeight + }); + } + .width($r('app.string.full_screen')) + .height($r('app.string.full_screen')); + + NavigationBarView({ + swiperController: this.swiperController, + scroller: this.scroller, + waterFlowScroller: this.waterFlowScroller + }); + } + .direction( + new BreakpointType( + { + sm: Direction.Auto, + md: Direction.Auto, + lg: Direction.Rtl, + } + ).getValue(this.currentBreakpoint) + ) + .width($r('app.string.full_screen')) + .height($r('app.string.full_screen')) + .backgroundColor($r('app.color.start_window_background')); + }; + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/NavigationBarView.ets b/features/longList/src/main/ets/view/NavigationBarView.ets new file mode 100644 index 0000000000000000000000000000000000000000..8d3300c6f79328b23d7c017492b4966a002e5342 --- /dev/null +++ b/features/longList/src/main/ets/view/NavigationBarView.ets @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import { BreakpointType } from '../utils/BreakpointSystem'; +import { FooterTab, FooterTabData, FooterTabDataEn } from '../model/FooterTabData'; +import { CommonConstants } from '../constants/CommonConstants'; + +@Component +export struct NavigationBarView { + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink(CommonConstants.LANGUAGE) language: string = CommonConstants.CHINESE_LANGUAGE; + @State selectIndex: number = CommonConstants.NUMBER_DEFAULT_VALUE; + public swiperController: SwiperController | undefined = undefined; + public scroller: Scroller = new Scroller(); + public waterFlowScroller: Scroller = new Scroller(); + private iconArr: FooterTab[] = []; + + aboutToAppear(): void { + if (this.language === CommonConstants.CHINESE_LANGUAGE) { + this.iconArr = new FooterTabData().tabList; + } else { + this.iconArr = new FooterTabDataEn().tabList; + } + } + + build() { + Row() { + ForEach(this.iconArr, (item: FooterTab, index: number) => { + Column() { + this.setNavigationItem(item, index); + }; + }, (index: number) => JSON.stringify(index)); + } + .width($r('app.string.full_screen')) + .height($r('app.float.navigation_height')) + .justifyContent(FlexAlign.SpaceBetween) + .backgroundColor($r('app.color.start_window_background')); + } + + @Builder + setNavigationItem(item: FooterTab, index: number) { + Column({ space: CommonConstants.SPACE_FOUR }) { + Image(item.icon) + .objectFit(ImageFit.Fill) + .width($r('app.float.navigation_icon_size')) + .height($r('app.float.navigation_icon_size')) + .margin({ bottom: $r('app.float.margin_4') }); + + Text(item.name) + .fontSize($r('app.float.font_size_10')) + .fontWeight(FontWeight.Medium); + } + .onClick(() => { + if (item.name === this.iconArr[0].name) { + this.scroller.scrollTo({ xOffset: 0, yOffset: 0 }); + this.waterFlowScroller.scrollToIndex(0); + } + }) + .width(new BreakpointType( + { + sm: $r('app.string.full_screen_20'), + md: $r('app.string.full_screen_20'), + lg: $r('app.string.full_screen'), + } + ).getValue(this.currentBreakpoint)) + .height(new BreakpointType( + { + sm: $r('app.string.full_screen'), + md: $r('app.string.full_screen'), + lg: $r('app.float.navigation_height_lg'), + } + ).getValue(this.currentBreakpoint)) + .opacity(index === 0 ? 1 : $r('app.float.opacity_percent_40')) + .padding({ + top: new BreakpointType( + { + sm: $r('app.float.margin_4'), + md: $r('app.float.margin_4'), + lg: $r('app.float.zero'), + } + ).getValue(this.currentBreakpoint) + }); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/SearchBarView.ets b/features/longList/src/main/ets/view/SearchBarView.ets new file mode 100644 index 0000000000000000000000000000000000000000..9d92a62a1351c951a68aa10f51518e48cfb563db --- /dev/null +++ b/features/longList/src/main/ets/view/SearchBarView.ets @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonConstants } from '../constants/CommonConstants'; +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import { BreakpointType } from '../utils/BreakpointSystem'; + +@Component +export struct SearchBarView { + @StorageProp('topHeight') topHeight: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; + + build() { + Row() { + Stack({ alignContent: Alignment.Start }) { + TextInput({ placeholder: $r('app.string.search') }) + .backgroundColor($r('app.color.common_background_3')) + .height($r('app.float.search_input_height')) + .fontSize($r('app.float.search_input_font')) + .padding({ + left: $r('app.float.search_input_left'), + right: $r('app.float.search_input_right') + }) + .width(CommonConstants.FULL_PERCENT); + + Image($r('app.media.ic_public_search')) + .width($r('app.float.search_img_size')) + .height($r('app.float.search_img_size')) + .margin({ left: $r('app.float.search_img_left') }); + } + .alignSelf(ItemAlign.Center) + .layoutWeight(1); + } + .justifyContent(FlexAlign.Center) + .height($r('app.float.search_bar_height')) + .margin({ + left: new BreakpointType({ + sm: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_SM, + md: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_MD, + lg: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_LG + }).getValue(this.currentBreakpoint), + right: new BreakpointType({ + sm: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_SM, + md: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_MD, + lg: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_LG + }).getValue(this.currentBreakpoint), + bottom:16 + }); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/WaterFlowDescriptionView.ets b/features/longList/src/main/ets/view/WaterFlowDescriptionView.ets new file mode 100644 index 0000000000000000000000000000000000000000..cd0b96a02aa84fe40366abee21c71d1ce06ec087 --- /dev/null +++ b/features/longList/src/main/ets/view/WaterFlowDescriptionView.ets @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { intl } from '@kit.LocalizationKit'; +import { CommonConstants } from '../constants/CommonConstants'; + +@Component +export struct WaterFlowDescriptionView { + @Link title: string; + @Link userImage: string; + @Link userName: string; + @Link type: string; + @Link collectionsCount: number; + @Link vipSign: string; + private numberFormat: intl.NumberFormat = new intl.NumberFormat('collectionsCount', { maximumFractionDigits: 1 }); + + build() { + Column() { + Text(this.title) + .fontSize($r('app.float.font_size_14')) + .fontWeight(CommonConstants.TEXT_FONT_WEIGHT_500) + .width($r('app.string.full_screen')) + .maxLines(CommonConstants.TEXT_MAX_LINES) + .textOverflow({ overflow: TextOverflow.Ellipsis }); + Row() { + Row() { + Stack({ alignContent: Alignment.BottomEnd }) { + Image(this.userImage) + .width($r('app.float.photo_size_16')) + .height($r('app.float.photo_size_16')) + .borderRadius($r('app.float.user_image_photo_radius')); + if (this.vipSign === CommonConstants.VIP_SIGN) { + Image($r('app.media.V')).zIndex(CommonConstants.LARGE_INDEX) + .width($r('app.float.photo_size_8')) + .height($r('app.float.photo_size_8')) + .borderRadius($r('app.float.user_image_vip_radius')); + } + }; + + Text(this.userName) + .fontSize($r('app.float.font_size_10')) + .fontWeight(CommonConstants.TEXT_FONT_WEIGHT_400) + .fontColor(Color.Black) + .opacity($r('app.float.opacity_percent_60')) + .margin({ left: 4 }); + } + .justifyContent(FlexAlign.Center); + + + Blank(); + + Row() { + Stack() { + Image('living' === this.type ? $r('app.media.people') : $r('app.media.ic_public_heart')) + .fillColor(Color.Black) + .objectFit(ImageFit.Fill) + .width($r('app.float.photo_size_width')) + .height($r('app.float.photo_size_height')) + .onClick(() => { + this.collectionsCount++; + }); + }; + + Text(this.collectionsCount < 1000 ? this.collectionsCount + '' : + this.numberFormat.format(this.collectionsCount / 1000) + 'k') + .fontSize($r('app.float.font_size_12')) + .fontFamily($r('sys.string.ohos_id_text_font_family_regular')) + .fontColor(Color.Black) + .opacity($r('app.float.opacity_percent_60')) + .margin({ left: $r('app.float.margin_4') }); + }; + } + .justifyContent(FlexAlign.SpaceBetween) + .width($r('app.string.full_screen')) + .margin({ top: $r('app.float.margin_8') }); + } + .padding({ + left: $r('app.float.margin_12'), + right: $r('app.float.margin_12'), + top: $r('app.float.margin_12'), + bottom: $r('app.float.margin_10') + }) + .width($r('app.string.full_screen')); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/WaterFlowImageView.ets b/features/longList/src/main/ets/view/WaterFlowImageView.ets new file mode 100644 index 0000000000000000000000000000000000000000..286f67b5bc328af6e99b123da46df9f2aa6b4c34 --- /dev/null +++ b/features/longList/src/main/ets/view/WaterFlowImageView.ets @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import { CommonConstants } from '../constants/CommonConstants'; +import { WaterFlowDescriptionView } from './WaterFlowDescriptionView'; + +@Component +@Reusable +export struct WaterFlowImageView { + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @Link waterFlowItemWidth: number; + @State source: string = CommonConstants.STRING_DEFAULT_VALUE; + @State title: string = CommonConstants.STRING_DEFAULT_VALUE; + @State userImage: string = CommonConstants.STRING_DEFAULT_VALUE; + @State userName: string = CommonConstants.STRING_DEFAULT_VALUE; + @State collectionsCount: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @State type: string = CommonConstants.WATER_FLOW_IMAGE_TYPE; + @State vipSign: string = CommonConstants.STRING_DEFAULT_VALUE; + @State imageWidth: number = 0; + @State imageHeight: number = 0; + + aboutToReuse(params: Record): void { + this.source = params.source; + this.title = params.title; + this.userImage = params.userImage; + this.userName = params.userName; + this.type = params.type; + this.vipSign = params.vipSign; + this.collectionsCount = parseInt(params.collectionsCount, CommonConstants.NUMBER_DEFAULT_VALUE); + this.imageWidth = parseInt(params.imageWidth, CommonConstants.NUMBER_DEFAULT_VALUE); + this.imageHeight = parseInt(params.imageHeight, CommonConstants.NUMBER_DEFAULT_VALUE); + } + + build() { + Column() { + Image(this.source) + .alt($r('app.media.default_image')) + .width($r('app.string.full_screen')) + .height(this.imageHeight / this.imageWidth * this.waterFlowItemWidth); + WaterFlowDescriptionView({ + title: this.title, + userImage: this.userImage, + userName: this.userName, + type: this.type, + collectionsCount: this.collectionsCount, + vipSign: this.vipSign + }); + } + .width($r('app.string.full_screen')) + .height($r('app.string.full_screen')); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/WaterFlowLivingView.ets b/features/longList/src/main/ets/view/WaterFlowLivingView.ets new file mode 100644 index 0000000000000000000000000000000000000000..bffc3449184ad07dbcffd8aff28dcba7707227ec --- /dev/null +++ b/features/longList/src/main/ets/view/WaterFlowLivingView.ets @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { componentUtils } from '@kit.ArkUI'; +import { CommonConstants } from '../constants/CommonConstants'; +import { WaterFlowDescriptionView } from './WaterFlowDescriptionView'; + +@Component +@Reusable +export struct WaterFlowLivingView { + @State source: string = CommonConstants.STRING_DEFAULT_VALUE; + @State thumbnails: string = CommonConstants.STRING_DEFAULT_VALUE; + @State currentBreakpoint: string = CommonConstants.STRING_DEFAULT_VALUE; + @State windowsHeight: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @State title: string = CommonConstants.STRING_DEFAULT_VALUE; + @State userImage: string = CommonConstants.STRING_DEFAULT_VALUE; + @State userName: string = CommonConstants.STRING_DEFAULT_VALUE; + @State collectionsCount: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @State type: string = CommonConstants.WATER_FLOW_LIVING_TYPE; + @State vipSign: string = CommonConstants.STRING_DEFAULT_VALUE; + @State itemIndex: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @Link waterFlowItemWidth: number; + @State livingWidth: number = 0; + @State livingHeight: number = 0; + videoController: VideoController = new VideoController(); + + aboutToReuse(params: Record): void { + if (typeof params.itemIndex === 'number') { + this.itemIndex = params.itemIndex; + } + if (typeof params.source === 'string') { + this.source = params.source; + } + if (typeof params.thumbnails === 'string') { + this.thumbnails = params.thumbnails; + } + if (typeof params.title === 'string') { + this.title = params.title; + } + if (typeof params.userImage === 'string') { + this.userImage = params.userImage; + } + if (typeof params.vipSign === 'string') { + this.vipSign = params.vipSign; + } + if (typeof params.userName === 'string') { + this.userName = params.userName; + } + if (typeof params.type === 'string') { + this.type = params.type; + } + if (typeof params.collectionsCount === 'number') { + this.collectionsCount = params.collectionsCount; + } + if (typeof params.livingWidth === 'number') { + this.livingWidth = params.livingWidth; + } + if (typeof params.livingHeight === 'number') { + this.livingHeight = params.livingHeight; + } + } + + build() { + Column() { + Stack({ alignContent: Alignment.TopEnd }) { + Video({ src: this.source, previewUri: this.thumbnails, controller: this.videoController }) + .id(CommonConstants.WATER_FLOW_LIVING_STRUCT_ID + this.itemIndex) + .controls(false) + .muted(true) + .loop(true) + .width($r('app.string.full_screen')) + .height(this.livingHeight / this.livingWidth * this.waterFlowItemWidth) + .onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => { + if (isVisible && currentRatio >= 1.0) { + this.videoController.start(); + } else { + this.videoController.pause(); + } + }) + .onAppear(() => { + let videoPos = componentUtils.getRectangleById(CommonConstants.WATER_FLOW_LIVING_STRUCT_ID + this.itemIndex); + if (px2vp(videoPos.windowOffset.y) + px2vp(videoPos.size.height) < this.windowsHeight) { + setTimeout(() => { + this.videoController.start(); + }, 1000); + } + }); + Row() { + Image($r('app.media.arrow_right_living_play')) + .width($r('app.float.live_view_image_size')) + .height($r('app.float.live_view_image_size')); + } + .margin($r('app.float.live_view_image_margin')) + .zIndex(1); + }; + + WaterFlowDescriptionView({ + title: this.title, + userImage: this.userImage, + userName: this.userName, + type: this.type, + collectionsCount: this.collectionsCount, + vipSign: this.vipSign + }) + } + .width($r('app.string.full_screen')) + .height($r('app.string.full_screen')); + } +} diff --git a/features/longList/src/main/ets/view/WaterFlowVideoView.ets b/features/longList/src/main/ets/view/WaterFlowVideoView.ets new file mode 100644 index 0000000000000000000000000000000000000000..680d0d3adb16750b2d18d63b7688cdbe0f09efc2 --- /dev/null +++ b/features/longList/src/main/ets/view/WaterFlowVideoView.ets @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonConstants } from '../constants/CommonConstants'; +import { WaterFlowDescriptionView } from './WaterFlowDescriptionView'; + +@Component +@Reusable +export struct WaterFlowVideoView { + @State source: string = CommonConstants.STRING_DEFAULT_VALUE; + @State thumbnails: string = CommonConstants.STRING_DEFAULT_VALUE; + @State playStatus: boolean = false; + @State currentBreakpoint: string = CommonConstants.STRING_DEFAULT_VALUE; + @State title: string = CommonConstants.STRING_DEFAULT_VALUE; + @State userImage: string = CommonConstants.STRING_DEFAULT_VALUE; + @State userName: string = CommonConstants.STRING_DEFAULT_VALUE; + @State collectionsCount: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @State type: string = CommonConstants.WATER_FLOW_VIDEO_TYPE; + @State vipSign: string = CommonConstants.STRING_DEFAULT_VALUE; + @Link waterFlowItemWidth: number; + @State videoWidth: number = 0; + @State videoHeight: number = 0; + + aboutToReuse(params: Record): void { + this.thumbnails = params.thumbnails; + this.title = params.title; + this.videoWidth = parseInt(params.videoWidth, CommonConstants.NUMBER_DEFAULT_VALUE); + this.videoHeight = parseInt(params.videoHeight, CommonConstants.NUMBER_DEFAULT_VALUE); + this.userImage = params.userImage; + this.userName = params.userName; + this.type = params.type; + this.vipSign = params.vipSign; + this.collectionsCount = parseInt(params.collectionsCount, CommonConstants.NUMBER_DEFAULT_VALUE); + } + + build() { + Column() { + Stack({ alignContent: Alignment.TopEnd }) { + Image(this.thumbnails) + .alt($r('app.media.default_image')) + .width($r('app.string.full_screen')) + .height(this.videoHeight / this.videoWidth * this.waterFlowItemWidth); + Row() { + if (!this.playStatus) { + Image($r('app.media.arrow_right_play')) + .width($r('app.float.arrow_size')) + .height($r('app.float.arrow_size')); + } + } + .margin($r('app.float.margin_10')) + .zIndex(CommonConstants.LARGE_INDEX); + }; + + WaterFlowDescriptionView({ + title: this.title, + userImage: this.userImage, + userName: this.userName, + type: this.type, + collectionsCount: this.collectionsCount, + vipSign: this.vipSign + }) + } + .width($r('app.string.full_screen')) + .height($r('app.string.full_screen')); + } +} \ No newline at end of file diff --git a/features/longList/src/main/ets/view/WaterFlowView.ets b/features/longList/src/main/ets/view/WaterFlowView.ets new file mode 100644 index 0000000000000000000000000000000000000000..b708005c79609b52d7f995fd2610572586a89cfb --- /dev/null +++ b/features/longList/src/main/ets/view/WaterFlowView.ets @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { display, MeasureText, promptAction } from '@kit.ArkUI'; +import { connection } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { BreakpointType } from '../utils/BreakpointSystem'; +import { WaterFlowListData } from '../model/WaterFlowListData'; +import { WaterFlowData } from '../model/WaterFlowData'; +import { WaterFlowImageView } from './WaterFlowImageView'; +import { WaterFlowVideoView } from './WaterFlowVideoView'; +import { WaterFlowLivingView } from './WaterFlowLivingView'; +import { CommonConstants } from '../constants/CommonConstants'; +import { BreakpointConstants } from '../constants/BreakpointConstants'; +import Logger from '../utils/Logger'; + +@Component +export struct WaterFlowView { + @StorageLink(BreakpointConstants.BREAKPOINT_NAME) currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink(CommonConstants.LANGUAGE) language: string = CommonConstants.CHINESE_LANGUAGE; + @StorageLink('continueIndex') continueIndex: number = 0; + @StorageLink('continueEntry') @Watch('watchContinueEntry') continueEntry: boolean = false; + @State @Watch('watchLoadFinish') loadFinish:boolean = false; + @State listDataCount: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @State waterFlowItemWidth: number = CommonConstants.NUMBER_DEFAULT_VALUE; + @Link isShowFoot: boolean; + @Link waterFlowListData: WaterFlowListData; + @Link windowsHeight: number; + public waterFlowScroller: Scroller = new Scroller(); + public scroller: Scroller = new Scroller(); + private pageNo: number = CommonConstants.WATER_FLOW_PAGE_START_INDEX; + private pageSize: number = CommonConstants.WATER_FLOW_PAGE_SIZE; + + watchLoadFinish(){ + if (this.continueEntry && this.loadFinish){ + this.waterFlowScroller.scrollTo({xOffset:0, yOffset:this.continueIndex}); + this.continueEntry = false; + } + } + + watchContinueEntry(){ + if (this.continueEntry && this.loadFinish){ + this.waterFlowScroller.scrollTo({xOffset:0, yOffset:this.continueIndex}); + this.continueEntry = false; + } + } + listenNetworkEvent() { + let netCon: connection.NetConnection = connection.createNetConnection(); + netCon.register((error: BusinessError) => { + Logger.info('register info:' + JSON.stringify(error)); + }); + netCon.on('netUnavailable', () => { + if (this.isShowFoot === true) { + this.isShowFoot = false; + } + try { + promptAction.showToast({ + message: $r('app.string.net_connection_description'), + bottom: CommonConstants.TOAST_SHOW_MARGIN_BOTTOM, + duration: CommonConstants.TOAST_SHOW_TIME + }); + } catch (error) { + let message = (error as BusinessError).message; + let code = (error as BusinessError).code; + Logger.error('showToast args error code is ', code.toString(), 'message is ', message.toString()); + } + }); + netCon.on('netAvailable', () => { + if (this.isShowFoot === false) { + this.isShowFoot = true; + } + }); + setTimeout(() => { + netCon.unregister((error: BusinessError) => { + Logger.info('unregister info:' + JSON.stringify(error)); + }); + }, 500); + } + + updateWaterFlowItemWidth() { + let windowWidth: number = px2vp(display.getDefaultDisplaySync().width); + let columns = windowWidth > CommonConstants.CRITICAL_VALUE ? CommonConstants.WATER_FLOW_THREE_COLUMNS : + CommonConstants.WATER_FLOW_TWO_COLUMNS; + let marginLeft = windowWidth > CommonConstants.CRITICAL_VALUE ? + BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_MD : + BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_SM; + this.waterFlowItemWidth = + (px2vp(display.getDefaultDisplaySync().width) - marginLeft * 2 - + (columns - 1) * CommonConstants.WATER_FLOW_COLUMN_GAP) / columns; + } + + getTitleHeight(title: string) { + let textWidth: number = MeasureText.measureText({ + textContent: title, + fontSize: $r('app.float.font_size_14') + }); + return textWidth > (this.waterFlowItemWidth - CommonConstants.DESCRIPTION_MARGIN_LEFT * 2) ? + CommonConstants.DESCRIPTION_THREE_LINES_HEIGHT : CommonConstants.DESCRIPTION_TWO_LINES_HEIGHT; + } + + aboutToAppear(): void { + this.waterFlowListData.addData(CommonConstants.MOCK_INTERFACE_WATER_FLOW_FILE_NAME, this.pageNo, this.pageSize); + + this.updateWaterFlowItemWidth(); + let callback: Callback = () => { + this.updateWaterFlowItemWidth(); + this.scroller.scrollTo({ xOffset: 0, yOffset: 0 }); + this.waterFlowScroller.scrollToIndex(0); + }; + display.on('foldDisplayModeChange', callback); + } + + build() { + Column({ space: CommonConstants.SPACE_EIGHT }) { + Column() { + WaterFlow({ footer: this.footStyle, scroller: this.waterFlowScroller }) { + LazyForEach(this.waterFlowListData.getData(), (item: WaterFlowData, index: number) => { + FlowItem() { + if (item.waterFlowHead.type === CommonConstants.WATER_FLOW_IMAGE_TYPE) { + WaterFlowImageView({ + source: item.waterFlowHead.source, + title: this.language === CommonConstants.CHINESE_LANGUAGE ? item.waterFlowDescription.title : + item.waterFlowDescription.titleEn, + userImage: item.waterFlowDescription.userImage, + userName: item.waterFlowDescription.userName, + collectionsCount: item.waterFlowDescription.collectionsCount, + waterFlowItemWidth: this.waterFlowItemWidth, + imageWidth: item.waterFlowHead.width, + vipSign: item.waterFlowDescription.vipSign, + imageHeight: item.waterFlowHead.height + }) + .reuseId(CommonConstants.WATER_FLOW_IMAGE_REUSE_ID); + } else if (item.waterFlowHead.type === CommonConstants.WATER_FLOW_VIDEO_TYPE) { + WaterFlowVideoView({ + source: item.waterFlowHead.source, + thumbnails: item.waterFlowHead.thumbnails, + currentBreakpoint: this.currentBreakpoint, + title: this.language === CommonConstants.CHINESE_LANGUAGE ? item.waterFlowDescription.title : + item.waterFlowDescription.titleEn, + userImage: item.waterFlowDescription.userImage, + userName: item.waterFlowDescription.userName, + collectionsCount: item.waterFlowDescription.collectionsCount, + waterFlowItemWidth: this.waterFlowItemWidth, + vipSign: item.waterFlowDescription.vipSign, + videoWidth: item.waterFlowHead.width, + videoHeight: item.waterFlowHead.height + }) + .reuseId(CommonConstants.WATER_FLOW_VIDEO_REUSE_ID); + } else { + WaterFlowLivingView({ + source: item.waterFlowHead.source, + thumbnails: item.waterFlowHead.thumbnails, + currentBreakpoint: this.currentBreakpoint, + windowsHeight: this.windowsHeight, + title: this.language === CommonConstants.CHINESE_LANGUAGE ? item.waterFlowDescription.title : + item.waterFlowDescription.titleEn, + userImage: item.waterFlowDescription.userImage, + userName: item.waterFlowDescription.userName, + collectionsCount: item.waterFlowDescription.collectionsCount, + itemIndex: item.waterFlowDescription.index, + waterFlowItemWidth: this.waterFlowItemWidth, + livingWidth: item.waterFlowHead.width, + livingHeight: item.waterFlowHead.height, + vipSign: item.waterFlowDescription.vipSign, + }) + .reuseId(CommonConstants.WATER_FLOW_LIVING_REUSE_ID); + } + } + .height(item.waterFlowHead.height / item.waterFlowHead.width * this.waterFlowItemWidth + + this.getTitleHeight(item.waterFlowDescription.title)) + .backgroundColor(Color.White) + .width($r('app.string.full_screen')) + .clip(true) + .borderRadius($r('app.float.rounded_size_16')) + .onAppear(() => { + if (index + 20 === this.waterFlowListData.dataSource.totalCount() && + this.waterFlowListData.dataSource.totalCount() < 15) { + this.pageNo = this.pageNo + 1 > 25 ? this.pageNo - 24 : this.pageNo + 1; + this.waterFlowListData.addData(CommonConstants.MOCK_INTERFACE_WATER_FLOW_FILE_NAME, this.pageNo, + this.pageSize); + } + this.listDataCount = this.waterFlowListData.dataSource.totalCount(); + this.loadFinish = true; + }); + }, (item: WaterFlowData) => { + return item.waterFlowDescription.index.toString(); + }); + } + .onDisAppear(()=>{ + this.continueIndex = this.waterFlowScroller.currentOffset().yOffset; + }) + .onScrollIndex((first:number, last:number)=>{ + if (!this.continueEntry){ + this.continueIndex = this.waterFlowScroller.currentOffset().yOffset; + } + }) + .flingSpeedLimit(4800) + .onReachEnd(() => { + this.listenNetworkEvent(); + if (this.waterFlowListData.dataSource.totalCount() < CommonConstants.WATER_FLOW_MAX_COUNT) { + this.pageNo = this.pageNo + 1 > 25 ? this.pageNo - 24 : this.pageNo + 1; + this.waterFlowListData.addData(CommonConstants.MOCK_INTERFACE_WATER_FLOW_FILE_NAME, this.pageNo, + this.pageSize); + } + this.listDataCount = this.waterFlowListData.dataSource.totalCount(); + }) + .cachedCount(CommonConstants.WATER_FLOW_CACHED_COUNT) + .nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.SELF_FIRST }) + .columnsTemplate(new BreakpointType({ + sm: BreakpointConstants.GRID_NUM_TWO, + md: BreakpointConstants.GRID_NUM_THREE, + lg: BreakpointConstants.GRID_NUM_FOUR + }).getValue(this.currentBreakpoint)) + .columnsGap($r('app.float.water_flow_column_gap')) + .rowsGap($r('app.float.water_flow_row_gap')) + .layoutDirection(FlexDirection.Column) + .itemConstraintSize({ + minWidth: $r('app.string.zero_screen'), + maxWidth: $r('app.string.full_screen'), + minHeight: $r('app.string.zero_screen'), + }); + } + .width($r('app.string.full_screen')) + .height($r('app.string.full_screen')); + } + .height($r('app.string.full_screen')) + .margin({ + top: $r('app.float.margin_8'), + bottom: $r('app.float.navigation_height'), + left: new BreakpointType({ + sm: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_SM, + md: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_MD, + lg: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_LEFT_LG + }).getValue(this.currentBreakpoint), + right: new BreakpointType({ + sm: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_SM, + md: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_MD, + lg: BreakpointConstants.SEARCHBAR_AND_WATER_FLOW_MARGIN_RIGHT_LG + }).getValue(this.currentBreakpoint) + }) + .animation({ + duration: CommonConstants.ANIMATION_DURATION_TIME, + curve: Curve.EaseOut, + playMode: PlayMode.Normal + }); + } + + @Builder + footStyle() { + Row() { + if (this.isShowFoot && this.listDataCount >= CommonConstants.WATER_FLOW_MAX_COUNT) { + Text($r('app.string.footer_text_max_count')) + .fontWeight(CommonConstants.TEXT_FONT_WEIGHT_400) + .fontSize($r('app.float.font_size_14')) + .height($r('app.float.text_height')) + .margin({ bottom: $r('app.float.margin_10') }) + .opacity($r('app.float.opacity_percent_40')); + } else if (this.isShowFoot) { + Row() { + LoadingProgress() + .color(Color.Black) + .opacity($r('app.float.net_unavailable_opacity')) + .width($r('app.float.net_request_loading_width')) + .height($r('app.float.net_request_loading_height')); + + Text($r('app.string.footer_text_loading')) + .fontWeight(CommonConstants.TEXT_FONT_WEIGHT_400) + .fontSize($r('app.float.font_size_14')) + .height($r('app.float.text_height')) + .opacity($r('app.float.opacity_percent_40')); + } + .height($r('app.float.net_request_loading_width')) + .width(CommonConstants.FULL_PERCENT) + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Center); + } + } + .width(CommonConstants.FULL_PERCENT) + .height(CommonConstants.TWENTY_PERCENT) + .margin({ bottom: $r('app.float.margin_30') }) + .alignItems(VerticalAlign.Bottom) + .justifyContent(FlexAlign.Center); + } +} \ No newline at end of file diff --git a/features/longList/src/main/module.json5 b/features/longList/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f4be63708f93c52662777a59aab1027fee8cef4f --- /dev/null +++ b/features/longList/src/main/module.json5 @@ -0,0 +1,27 @@ +{ + "module": { + "name": "longList", + "type": "feature", + "description": "$string:module_desc", + "mainElement": "LongListAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "LongListAbility", + "srcEntry": "./ets/longlistability/LongListAbility.ets", + "description": "$string:LongListAbility_desc", + "icon": "$media:layered_image", + "label": "$string:LongListAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "continuable": true + } + ] + } +} \ No newline at end of file diff --git a/features/longList/src/main/resources/base/element/color.json b/features/longList/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..0ebc8753a0578e85c153bff40003880781db9011 --- /dev/null +++ b/features/longList/src/main/resources/base/element/color.json @@ -0,0 +1,20 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#F1F3F5" + }, + { + "name": "common_background_2", + "value": "#F7F7F7" + }, + { + "name": "common_background_3", + "value": "#E5E7E9" + }, + { + "name": "function_default_color", + "value": "#CCCCCC" + } + ] +} \ No newline at end of file diff --git a/features/longList/src/main/resources/base/element/float.json b/features/longList/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..cf223dd4e456f1825e2069725e8b304dda57e2f5 --- /dev/null +++ b/features/longList/src/main/resources/base/element/float.json @@ -0,0 +1,452 @@ +{ + "float": [ + { + "name": "page_col_padding_sm", + "value": "16vp" + }, + { + "name": "page_col_padding_md", + "value": "24vp" + }, + { + "name": "page_col_padding_lg", + "value": "32vp" + }, + { + "name": "common_image_size_1", + "value": "24vp" + }, + { + "name": "common_image_size_2", + "value": "30vp" + }, + { + "name": "common_image_size_3", + "value": "36vp" + }, + { + "name": "common_image_size_4", + "value": "48vp" + }, + { + "name": "common_image_size_5", + "value": "60vp" + }, + { + "name": "common_image_size_6", + "value": "32vp" + }, + { + "name": "common_image_size_7", + "value": "12vp" + }, + { + "name": "common_image_size_8", + "value": "72vp" + }, + { + "name": "common_image_size_9", + "value": "10vp" + }, + { + "name": "common_image_size_10", + "value": "12vp" + }, + { + "name": "common_image_size_11", + "value": "40vp" + }, + { + "name": "common_border_radius_1", + "value": "2vp" + }, + { + "name": "common_border_radius_2", + "value": "4vp" + }, + { + "name": "common_border_radius_3", + "value": "5vp" + }, + { + "name": "common_border_radius_4", + "value": "8vp" + }, + { + "name": "common_border_radius_5", + "value": "12vp" + }, + { + "name": "common_border_radius_6", + "value": "14vp" + }, + { + "name": "common_border_radius_7", + "value": "16vp" + }, + { + "name": "common_border_radius_8", + "value": "20vp" + }, + { + "name": "common_border_radius_9", + "value": "24vp" + }, + { + "name": "common_border_radius_10", + "value": "10vp" + }, + { + "name": "tab_font_size", + "value": "10fp" + }, + { + "name": "tab_top_margin", + "value": "4vp" + }, + { + "name": "side_tab_bar_height", + "value": "80vp" + }, + { + "name": "bottom_tab_bar_width_lg", + "value": "96vp" + }, + { + "name": "search_input_font", + "value": "16fp" + }, + { + "name": "search_input_height", + "value": "40vp" + }, + { + "name": "search_input_left", + "value": "36vp" + }, + { + "name": "search_input_right", + "value": "12vp" + }, + { + "name": "search_img_size", + "value": "16vp" + }, + { + "name": "search_img_left", + "value": "12vp" + }, + { + "name": "category_text_font", + "value": "12fp" + }, + { + "name": "category_text_line", + "value": "17vp" + }, + { + "name": "navigation_width_sm", + "value": "72" + }, + { + "name": "navigation_height", + "value": "84" + }, + { + "name": "navigation_height_lg", + "value": "100" + }, + { + "name": "window_navigation_bar_height", + "value": "28" + }, + { + "name": "navigation_width", + "value": "96" + }, + { + "name": "window_status_bar_height", + "value": "40" + }, + { + "name": "window_status_bar_padding_top", + "value": "40" + }, + { + "name": "space_4", + "value": "4" + }, + { + "name": "navigation_icon_size", + "value": "24" + }, + { + "name": "margin_4", + "value": "4" + }, + { + "name": "zero", + "value": "0" + }, + { + "name": "rounded_size_16", + "value": "16" + }, + { + "name": "margin_8", + "value": "8" + }, + { + "name": "font_size_14", + "value": "14" + }, + { + "name": "font_size_16", + "value": "16" + }, + { + "name": "line_height_16", + "value": "16" + }, + { + "name": "margin_24", + "value": "24" + }, + { + "name": "margin_30", + "value": "30" + }, + { + "name": "font_size_10", + "value": "10" + }, + { + "name": "font_size_12", + "value": "12" + }, + { + "name": "font_size_15", + "value": "15" + }, + { + "name": "line_height_12", + "value": "12" + }, + { + "name": "line_height_14", + "value": "14" + }, + { + "name": "opacity_percent_60", + "value": "0.6" + }, + { + "name": "opacity_percent_40", + "value": "0.4" + }, + { + "name": "margin_12", + "value": "12" + }, + { + "name": "circular_size_16", + "value": "16" + }, + { + "name": "photo_size_8", + "value": "8" + }, + { + "name": "photo_size_16", + "value": "16" + }, + { + "name": "photo_size_width", + "value": "14.78" + }, + { + "name": "photo_size_height", + "value": "12.87" + }, + { + "name": "user_image_photo_radius", + "value": "8" + }, + { + "name": "user_image_vip_radius", + "value": "4" + }, + { + "name": "arrow_size", + "value": "24" + }, + { + "name": "margin_10", + "value": "10" + }, + { + "name": "live_view_image_size", + "value": "24" + }, + { + "name": "live_view_image_margin", + "value": "10" + }, + { + "name": "function_row_gap", + "value": "16" + }, + { + "name": "water_flow_default_item_gap", + "value": "8" + }, + { + "name": "function_padding_bottom", + "value": "36" + }, + { + "name": "function_padding_default_bottom", + "value": "20" + }, + { + "name": "function_padding_left", + "value": "10" + }, + { + "name": "function_padding_right", + "value": "10" + }, + { + "name": "function_two_lines_default_height", + "value": "160" + }, + { + "name": "water_flow_default_height", + "value": "526" + }, + { + "name": "function_gridItem_image_height", + "value": "40" + }, + { + "name": "function_gridItem_image_width", + "value": "40" + }, + { + "name": "function_gridItem_border_radius", + "value": "12" + }, + { + "name": "function_gridItem_text_line_height", + "value": "18" + }, + { + "name": "function_gridItem_text_image_height", + "value": "8" + }, + { + "name": "function_gridItem_text_image_margin_top", + "value": "8" + }, + { + "name": "function_gridItem_text_margin_top", + "value": "5" + }, + { + "name": "function_gridItem_height", + "value": "62" + }, + { + "name": "function_gridItem_width", + "value": "68" + }, + { + "name": "function_gridItem_gray_width", + "value": "40" + }, + { + "name": "function_gridItem_gray_height", + "value": "8" + }, + { + "name": "function_gridItem_gray_marge_top", + "value": "8" + }, + { + "name": "function_gridItem_marge_bottom", + "value": "16" + }, + { + "name": "water_flow_gridItem_image_height", + "value": "240" + }, + { + "name": "search_bar_height", + "value": "56" + }, + { + "name": "text_height", + "value": "19" + }, + { + "name": "image_width", + "value": "18" + }, + { + "name": "net_unavailable_opacity", + "value": "0.6" + }, + { + "name": "net_unavailable_text_fontsize", + "value": "14" + }, + { + "name": "net_unavailable_moon_width", + "value": "113" + }, + { + "name": "net_unavailable_moon_height", + "value": "90" + }, + { + "name": "net_unavailable_moon_margin_top", + "value": "220" + }, + { + "name": "net_unavailable_moon_margin_bottom", + "value": "30" + }, + { + "name": "net_unavailable_loading_height", + "value": "92" + }, + { + "name": "net_unavailable_loading_little_width", + "value": "32" + }, + { + "name": "net_unavailable_loading_little_height", + "value": "32" + }, + { + "name": "net_unavailable_loading_width", + "value": "92" + }, + { + "name": "net_request_loading_width", + "value": "24" + }, + { + "name": "net_request_loading_height", + "value": "24" + }, + { + "name": "water_flow_row_gap", + "value": "8" + }, + { + "name": "water_flow_column_gap", + "value": "8" + } + ] +} \ No newline at end of file diff --git a/features/longList/src/main/resources/base/element/string.json b/features/longList/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6d86060ee7b1008a207aa608bac1c9cf5b1fe741 --- /dev/null +++ b/features/longList/src/main/resources/base/element/string.json @@ -0,0 +1,52 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "LongListAbility_label", + "value": "description" + }, + { + "name": "LongListAbility_desc", + "value": "pageSlip" + }, + { + "name": "internet_reason", + "value": "Used to access the Internet for the scenario of retrieving network list data" + }, + { + "name": "network_info_reason", + "value": "Used to retrieve network information for the scenario of retrieving network list data" + }, + { + "name": "full_screen", + "value": "100%" + }, + { + "name": "zero_screen", + "value": "0%" + }, + { + "name": "full_screen_20", + "value": "20%" + }, + { + "name": "net_connection_description", + "value": "Unable to connect to the network, please check your network settings" + }, + { + "name": "footer_text_max_count", + "value": "Reached the bottom" + }, + { + "name": "footer_text_loading", + "value": "Loading, please wait a moment" + }, + { + "name": "search", + "value": "search..." + } + ] +} \ No newline at end of file diff --git a/features/longList/src/main/resources/base/media/V.svg b/features/longList/src/main/resources/base/media/V.svg new file mode 100644 index 0000000000000000000000000000000000000000..77ae07e6e6de79658a8b11460e181c77f0f8649a --- /dev/null +++ b/features/longList/src/main/resources/base/media/V.svg @@ -0,0 +1,14 @@ + + + Created with Pixso. + + + + + + + + + + + diff --git a/features/longList/src/main/resources/base/media/arrow_right.svg b/features/longList/src/main/resources/base/media/arrow_right.svg new file mode 100644 index 0000000000000000000000000000000000000000..5283fd327effeee1bd23d4543e865a9cee4bfff6 --- /dev/null +++ b/features/longList/src/main/resources/base/media/arrow_right.svg @@ -0,0 +1,15 @@ + + + + Public/ic_public_arrow_right + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/features/longList/src/main/resources/base/media/arrow_right_living_play.svg b/features/longList/src/main/resources/base/media/arrow_right_living_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..b9c512ef9ce38df504074c6110c15d51850b8c91 --- /dev/null +++ b/features/longList/src/main/resources/base/media/arrow_right_living_play.svg @@ -0,0 +1,10 @@ + + + Created with Pixso. + + + + + + + diff --git a/features/longList/src/main/resources/base/media/arrow_right_play.svg b/features/longList/src/main/resources/base/media/arrow_right_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..6abe41bca2b91f569dab9532d432da2c42238e6c --- /dev/null +++ b/features/longList/src/main/resources/base/media/arrow_right_play.svg @@ -0,0 +1,10 @@ + + + Created with Pixso. + + + + + + + diff --git a/features/longList/src/main/resources/base/media/back.png b/features/longList/src/main/resources/base/media/back.png new file mode 100644 index 0000000000000000000000000000000000000000..4caa54288a72d7dd733539035ae21ada0a3eb05d Binary files /dev/null and b/features/longList/src/main/resources/base/media/back.png differ diff --git a/features/longList/src/main/resources/base/media/background.png b/features/longList/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/features/longList/src/main/resources/base/media/background.png differ diff --git a/features/longList/src/main/resources/base/media/default_image.png b/features/longList/src/main/resources/base/media/default_image.png new file mode 100644 index 0000000000000000000000000000000000000000..8e79ec0008b7cdd90b1900105c4f1560739f34ed Binary files /dev/null and b/features/longList/src/main/resources/base/media/default_image.png differ diff --git a/features/longList/src/main/resources/base/media/foreground.png b/features/longList/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/features/longList/src/main/resources/base/media/foreground.png differ diff --git a/features/longList/src/main/resources/base/media/ic_public_class.png b/features/longList/src/main/resources/base/media/ic_public_class.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ba651d49baf7005dc46c308f64225b701de774 Binary files /dev/null and b/features/longList/src/main/resources/base/media/ic_public_class.png differ diff --git a/features/longList/src/main/resources/base/media/ic_public_discover.png b/features/longList/src/main/resources/base/media/ic_public_discover.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c829610f62e0919763a8825f2c9dd2286ef483 Binary files /dev/null and b/features/longList/src/main/resources/base/media/ic_public_discover.png differ diff --git a/features/longList/src/main/resources/base/media/ic_public_heart.png b/features/longList/src/main/resources/base/media/ic_public_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..c85e2685d775bc46981080676777f047194c62ca Binary files /dev/null and b/features/longList/src/main/resources/base/media/ic_public_heart.png differ diff --git a/features/longList/src/main/resources/base/media/ic_public_home_filled.png b/features/longList/src/main/resources/base/media/ic_public_home_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..d28b7c991308933cf5e2290aeca96ffff41d8927 Binary files /dev/null and b/features/longList/src/main/resources/base/media/ic_public_home_filled.png differ diff --git a/features/longList/src/main/resources/base/media/ic_public_input_search.svg b/features/longList/src/main/resources/base/media/ic_public_input_search.svg new file mode 100644 index 0000000000000000000000000000000000000000..0d4b36b2b2f8b69509e8b88c37a7518994bdec7b --- /dev/null +++ b/features/longList/src/main/resources/base/media/ic_public_input_search.svg @@ -0,0 +1,12 @@ + + + Created with Pixso. + + + + + + + + + diff --git a/features/longList/src/main/resources/base/media/ic_public_mine.png b/features/longList/src/main/resources/base/media/ic_public_mine.png new file mode 100644 index 0000000000000000000000000000000000000000..297fdd0d44606f1e7df12b7d7e2589208fee5a99 Binary files /dev/null and b/features/longList/src/main/resources/base/media/ic_public_mine.png differ diff --git a/features/longList/src/main/resources/base/media/ic_public_search.svg b/features/longList/src/main/resources/base/media/ic_public_search.svg new file mode 100644 index 0000000000000000000000000000000000000000..423182ce01e353a362155bdb443b9686672631d9 --- /dev/null +++ b/features/longList/src/main/resources/base/media/ic_public_search.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_search + + + + + + + + + + \ No newline at end of file diff --git a/features/longList/src/main/resources/base/media/ic_public_shopping.png b/features/longList/src/main/resources/base/media/ic_public_shopping.png new file mode 100644 index 0000000000000000000000000000000000000000..b1afd59335f88d7a43e3d51b37dfffea55689ca4 Binary files /dev/null and b/features/longList/src/main/resources/base/media/ic_public_shopping.png differ diff --git a/features/longList/src/main/resources/base/media/layered_image.json b/features/longList/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/features/longList/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/features/longList/src/main/resources/base/media/moon.png b/features/longList/src/main/resources/base/media/moon.png new file mode 100644 index 0000000000000000000000000000000000000000..c9036a669dd0b1fc6a5808419b49c58ff4025cf0 Binary files /dev/null and b/features/longList/src/main/resources/base/media/moon.png differ diff --git a/features/longList/src/main/resources/base/media/people.svg b/features/longList/src/main/resources/base/media/people.svg new file mode 100644 index 0000000000000000000000000000000000000000..59de5ea50bc4491b5a3b2da45142edd5373fdd81 --- /dev/null +++ b/features/longList/src/main/resources/base/media/people.svg @@ -0,0 +1,9 @@ + + + Created with Pixso. + + + + + + diff --git a/features/longList/src/main/resources/base/media/startIcon.png b/features/longList/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/features/longList/src/main/resources/base/media/startIcon.png differ diff --git a/features/longList/src/main/resources/base/media/tab_home.svg b/features/longList/src/main/resources/base/media/tab_home.svg new file mode 100644 index 0000000000000000000000000000000000000000..ff026a99092323be572eb3f9e08aade115048a00 --- /dev/null +++ b/features/longList/src/main/resources/base/media/tab_home.svg @@ -0,0 +1,13 @@ + + + BottomNavigationBar/item/icon_actived首页-2 + + + + + + + + + + \ No newline at end of file diff --git a/features/longList/src/main/resources/base/media/tab_home_selected.svg b/features/longList/src/main/resources/base/media/tab_home_selected.svg new file mode 100644 index 0000000000000000000000000000000000000000..a14f5eeb73edceaaa586e00cc4c4615a4e041d92 --- /dev/null +++ b/features/longList/src/main/resources/base/media/tab_home_selected.svg @@ -0,0 +1,24 @@ + + + 编组 11 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/features/longList/src/main/resources/base/media/tab_item_normal.svg b/features/longList/src/main/resources/base/media/tab_item_normal.svg new file mode 100644 index 0000000000000000000000000000000000000000..43a4f4f29c47ac31cb70941137c8c4ee086ff52e --- /dev/null +++ b/features/longList/src/main/resources/base/media/tab_item_normal.svg @@ -0,0 +1,12 @@ + + + Created with Pixso. + + + + + + + + + diff --git a/features/longList/src/main/resources/base/profile/backup_config.json b/features/longList/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/features/longList/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/features/longList/src/main/resources/base/profile/main_pages.json b/features/longList/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/features/longList/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/features/longList/src/main/resources/en_US/element/string.json b/features/longList/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..cdd719bc60818376cbc4c44df1ed8e867bdf66d9 --- /dev/null +++ b/features/longList/src/main/resources/en_US/element/string.json @@ -0,0 +1,52 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "pageSlip" + }, + { + "name": "full_screen", + "value": "100%" + }, + { + "name": "full_screen_20", + "value": "20%" + }, + { + "name": "internet_reason", + "value": "Used to access the Internet for the scenario of retrieving network list data" + }, + { + "name": "network_info_reason", + "value": "Used to retrieve network information for the scenario of retrieving network list data" + }, + { + "name": "zero_screen", + "value": "0%" + }, + { + "name": "search", + "value": "search..." + }, + { + "name": "footer_text_loading", + "value": "Loading, please wait a moment" + }, + { + "name": "net_connection_description", + "value": "Unable to connect to the network, please check your network settings" + }, + { + "name": "footer_text_max_count", + "value": "Reached the bottom" + } + ] +} \ No newline at end of file diff --git a/features/longList/src/main/resources/zh_CN/element/string.json b/features/longList/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..7c2743031dec047919838435cc184b1a6e28cc36 --- /dev/null +++ b/features/longList/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,52 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "描述" + }, + { + "name": "EntryAbility_label", + "value": "流畅页面" + }, + { + "name": "full_screen", + "value": "100%" + }, + { + "name": "full_screen_20", + "value": "20%" + }, + { + "name": "internet_reason", + "value": "用于获取网络列表数据场景使用Internet网络" + }, + { + "name": "network_info_reason", + "value": "用于获取网络列表数据场景获取网络信息" + }, + { + "name": "zero_screen", + "value": "0%" + }, + { + "name": "net_connection_description", + "value": "无法连接网络,请检查网络设置" + }, + { + "name": "footer_text_max_count", + "value": "—已到达底部—" + }, + { + "name": "footer_text_loading", + "value": "—加载中,请稍后—" + }, + { + "name": "search", + "value": "搜索..." + } + ] +} \ No newline at end of file diff --git a/features/video/build-profile.json5 b/features/video/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4d611879c7913fb0610c686e2399258ab3a6dad1 --- /dev/null +++ b/features/video/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/features/video/hvigorfile.ts b/features/video/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6edcd90486dd5a853cf7d34c8647f08414ca7a3 --- /dev/null +++ b/features/video/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/features/video/obfuscation-rules.txt b/features/video/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/features/video/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/features/video/oh-package.json5 b/features/video/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..57a6dfde51626aa36acbbdce03488af8c1f0cd36 --- /dev/null +++ b/features/video/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "name": "video", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/features/video/src/main/ets/components/ExitVideo.ets b/features/video/src/main/ets/components/ExitVideo.ets new file mode 100644 index 0000000000000000000000000000000000000000..8ea99b3d98838dcf9d51292a10f57a089cbc182e --- /dev/null +++ b/features/video/src/main/ets/components/ExitVideo.ets @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { common, Want } from '@kit.AbilityKit'; +import { connection } from '@kit.NetworkKit'; +import { promptAction } from '@kit.ArkUI'; +import avPlayManage from '../videomanager/AvPlayManager'; +import { GlobalContext } from '../utils/GlobalContext'; + +@Component +export struct ExitVideo { + @StorageLink('videoName') videoName: Resource = $r('app.string.video_res_1'); + @StorageLink('show') show: boolean = false; + @StorageLink('videoSelect') videoSelect: number = 0; + @Link avPlayManage: avPlayManage; + private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; + private videoList: Resource[] = [$r('app.string.video_res_1'), $r('app.string.video_res_2')]; + + async isInternet(): Promise { + if (connection.getAllNetsSync().length <= 0) { + this.toast(); + return false; + } + return true; + } + + async toast() { + promptAction.showToast({ + message: $r('app.string.video_warn'), + duration: 2000, + }); + } + @Builder + listBuilder() { + Column() { + List() { + ForEach(this.videoList, (item: Resource, index) => { + ListItem() { + Column() { + Row() { + Text(item) + .fontSize(16) + .fontWeight(500) + .textAlign(TextAlign.Center) + + Blank() + + if (this.videoSelect === index) { + Text($r('app.string.playing')) + .fontSize(14) + .opacity($r('app.float.size_zero_six')) + .fontWeight(400) + .textAlign(TextAlign.Center) + .fontColor('rgb(10, 89, 247)') + }else { + Text($r('app.string.click_change')) + .fontSize(14) + .opacity($r('app.float.size_zero_six')) + .fontColor('rgba(0,0,0,0.6)') + .fontWeight(400) + .textAlign(TextAlign.Center) + } + } + .width('90%') + } + .width('100%') + } + .borderRadius(16) + .backgroundColor(Color.White) + .width('100%') + .height(58) + .margin({ + bottom:10 + }) + .onClick(async () => { + if (index === 2) { + try { + if (!await this.isInternet()) { + return; + } + } catch (err) { + this.toast(); + return; + } + } + this.videoSelect = index; + AppStorage.setOrCreate('videoName', this.videoList[this.videoSelect]); + AppStorage.setOrCreate('videoIndex', this.videoSelect); + AppStorage.setOrCreate('speedName', $r('app.string.video_speed_1_0X')); + AppStorage.setOrCreate('speedIndex', 0); + AppStorage.setOrCreate('currentTime', 0); + this.show = false; + let str: string = await (GlobalContext.getContext().getObject('context') as (common.UIAbilityContext)) + .resourceManager.getStringValue(this.videoList[this.videoSelect]); + this.avPlayManage.videoChoose(str); + }) + }, (index: number) => JSON.stringify(index)) + } + .scrollBar(BarState.Off) + .width('100%') + .height('200vp') + } + .padding({ + left:16, + right:16, + bottom:28 + }) + .onClick(() => { + this.show = false; + }) + } + + build() { + Flex({ + direction:FlexDirection.Row, + justifyContent:FlexAlign.SpaceBetween + }) { + // Exit + Row(){ + Image($r('app.media.back')) + .id('Exit') + .width(40) + .height(40) + .onClick(() => { + let want: Want = { + deviceId: '', + bundleName: 'com.example.continueprogress', + abilityName: 'EntryAbility', + } + this.context.startAbility(want); + }) + + Text($r('app.string.title')) + .fontColor(Color.White) + .fontWeight(FontWeight.Medium) + .fontSize($r('app.float.size_24')) + .margin({ left: 8 }) + } + Image($r('app.media.list')) + .width(40) + .height(40) + .onClick(()=>{ + this.show = true; + }) + .bindSheet($$this.show, this.listBuilder, { + height: SheetSize.FIT_CONTENT, + backgroundColor: '#F1F3F5', + title: { title: this.videoList[this.videoSelect] }, + detents: [SheetSize.FIT_CONTENT, SheetSize.FIT_CONTENT, 200], + }) + } + .margin({ top: $r('app.float.size_20'), left: 16, right:16}) + } +} \ No newline at end of file diff --git a/features/video/src/main/ets/components/SpeedDialog.ets b/features/video/src/main/ets/components/SpeedDialog.ets new file mode 100644 index 0000000000000000000000000000000000000000..e860def64978012c38798bc93ac27fcffbfd6098 --- /dev/null +++ b/features/video/src/main/ets/components/SpeedDialog.ets @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import avPlayManage from '../videomanager/AvPlayManager'; + +// Index of the playback rate list. +const ZERO = 0; +const ONE = 1; +const TWO = 2; +const THREE = 3; + +@CustomDialog +export struct SpeedDialog { + @State speedList: Resource[] = + [$r('app.string.video_speed_1_0X'), $r('app.string.video_speed_1_25X'), $r('app.string.video_speed_1_75X'), + $r('app.string.video_speed_2_0X')]; + @Link speedSelect: number; // Index of the current selection + @StorageLink('avPlayManage') avPlayManage: avPlayManage = new avPlayManage(); + private controller: CustomDialogController; + + build() { + Column() { + Text($r('app.string.dialog_play_speed')) + .fontSize($r('app.float.size_20')) + .width('90%') + .fontColor(Color.Black) + .textAlign(TextAlign.Start) + .margin({ top: $r('app.float.size_20'), bottom: $r('app.float.size_12') }) + + List() { + ForEach(this.speedList, (item: Resource, index) => { + ListItem() { + Column() { + Row() { + Text(item) + .fontSize($r('app.float.size_16')) + .fontColor(Color.Black) + .fontWeight(FontWeight.Medium) + .textAlign(TextAlign.Center) + Blank() + Image(this.speedSelect === index ? $r('app.media.ic_radio_selected') : $r('app.media.ic_radio')) + .width($r('app.float.size_24')) + .height($r('app.float.size_24')) + .objectFit(ImageFit.Contain) + } + .width('100%') + + if (index !== this.speedList.length - ONE) { + Divider() + .vertical(false) + .strokeWidth(1) + .margin({ top: $r('app.float.size_10') }) + .color($r('app.color.speed_dialog')) + .width('100%') + } + } + .width('90%') + } + .width('100%') + .height($r('app.float.size_48')) + .onClick(() => { + this.speedSelect = index; + AppStorage.setOrCreate('speedName', this.speedList[this.speedSelect]); + AppStorage.setOrCreate('speedIndex', this.speedSelect); + this.controller.close(); + switch (this.speedSelect) { + case ZERO: + this.avPlayManage.videoSpeedOne(); + break; + case ONE: + this.avPlayManage.videoSpeedOnePointTwentyFive(); + break; + case TWO: + this.avPlayManage.videoSpeedOnePointSeventyFive(); + break; + case THREE: + this.avPlayManage.videoSpeedTwo(); + break; + } + }) + }, (index: number) => JSON.stringify(index)) + } + .width('100%') + .height('192vp') + .margin({ + top: $r('app.float.size_12') + }) + + Row() { + Text($r('app.string.dialog_cancel')) + .fontSize($r('app.float.size_16')) + .fontColor('#0A59F7') + .fontWeight(FontWeight.Medium) + .layoutWeight(1) + .textAlign(TextAlign.Center) + .onClick(() => { + this.controller.close() + }) + } + .alignItems(VerticalAlign.Center) + .height($r('app.float.size_50')) + .padding({ bottom: $r('app.float.size_5') }) + .width('100%') + } + .alignItems(HorizontalAlign.Center) + .width('90%') + .borderRadius($r('app.float.size_24')) + .backgroundColor(Color.White) + } +} \ No newline at end of file diff --git a/features/video/src/main/ets/components/VideoOperate.ets b/features/video/src/main/ets/components/VideoOperate.ets new file mode 100644 index 0000000000000000000000000000000000000000..cc838f34bc843ea760b90b51c6b1879c82768d3c --- /dev/null +++ b/features/video/src/main/ets/components/VideoOperate.ets @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { timeConvert } from '../utils/TimeUtils'; +import avPlayManage from '../videomanager/AvPlayManager'; +import { SpeedDialog } from '../components/SpeedDialog'; + +@Component +export struct VideoOperate { + @StorageLink('currentTime') currentTime: number = 0; + @StorageLink('flag') flag: boolean = true; + @StorageLink('speedIndex') speedIndex: number = 0; // Index of the playback rate list. + @StorageLink('sliderWidth') sliderWidth: string = ''; + @StorageLink('speedName') speedName: Resource = $r('app.string.video_speed_1_0X'); + @Link durationTime: number; + @Link isSwiping: boolean; + @Link avPlayManage: avPlayManage; + @Link XComponentFlag: boolean; + @State videoList: Resource[] = [$r('app.string.video_res_1'), $r('app.string.video_res_2')]; + @State speedSelect: number = 0; // Speed Magnification Selection + private dialogController: CustomDialogController = new CustomDialogController({ + builder: SpeedDialog({ speedSelect: $speedSelect }), + alignment: DialogAlignment.Bottom, + offset: { dx: $r('app.float.size_zero'), dy: $r('app.float.size_down_20') } + }); + + build() { + Row() { + Row() { + Image(this.flag ? $r('app.media.ic_video_play') : $r('app.media.ic_video_pause'))// Pause/Play + .id('play') + .width($r('app.float.size_40')) + .height($r('app.float.size_40')) + .onClick(() => { + if (this.flag) { + this.avPlayManage.videoPause(); + this.flag = false; + } else { + this.avPlayManage.videoPlay(); + this.flag = true; + } + }) + + // Left side time + Text(timeConvert(this.currentTime)) + .fontColor(Color.White) + .textAlign(TextAlign.End) + .fontWeight(FontWeight.Regular) + .margin({ left: $r('app.float.size_10') }) + } + + Row() { + Slider({ + value: this.currentTime, + min: 0, + max: this.durationTime, + style: SliderStyle.OutSet + }) + .id('Slider') + .blockColor(Color.White) + .trackColor(Color.Gray) + .selectedColor($r('app.color.slider_selected')) + .showTips(false) + .onChange((value: number, mode: SliderChangeMode) => { + if (mode === SliderChangeMode.Begin) { + this.isSwiping = true; + this.avPlayManage.videoPause(); + this.flag = false; + } + this.avPlayManage.videoSeek(value); + let timeInterval = AppStorage.get('timeInterval') as number; + clearInterval(timeInterval); + timeInterval = setInterval(() => { // Update the current time. + this.currentTime = this.avPlayManage.getCurrentTime(); + }, 100); + AppStorage.setOrCreate('timeInterval', timeInterval); + if (mode === SliderChangeMode.End) { + this.isSwiping = false; + this.flag = true; + this.avPlayManage.videoPlay(); + } + }) + } + .layoutWeight(1) + + Row() { + // Right side time + Text(timeConvert(this.durationTime)) + .fontColor(Color.White) + .fontWeight(FontWeight.Regular) + + Button(this.speedName, { type: ButtonType.Normal }) + .border({ width: $r('app.float.size_1'), color: Color.White }) + .width($r('app.float.size_75')) + .height($r('app.float.size_40')) + .fontSize($r('app.float.size_15')) + .borderRadius($r('app.float.size_24')) + .fontColor(Color.White) + .backgroundColor(Color.Black) + .opacity($r('app.float.size_1')) + .margin({ left: $r('app.float.size_10') }) + .id('Speed') + .onClick(() => { + this.speedSelect = this.speedIndex; + this.dialogController.open(); + }) + } + } + .justifyContent(FlexAlign.Center) + .padding({ left: $r('app.float.size_25'), right: $r('app.float.size_30') }) + .width('100%') + } +} \ No newline at end of file diff --git a/features/video/src/main/ets/pages/Index.ets b/features/video/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..5882d51de77778e482c6a522b6c5048ebe4d8689 --- /dev/null +++ b/features/video/src/main/ets/pages/Index.ets @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { media } from '@kit.MediaKit'; +import { connection } from '@kit.NetworkKit'; +import { common } from '@kit.AbilityKit'; +import { display, promptAction } from '@kit.ArkUI'; +import { emitter } from '@kit.BasicServicesKit'; +import avPlayManage from '../videomanager/AvPlayManager'; +import { VideoOperate } from '../components/VideoOperate'; +import { ExitVideo } from '../components/ExitVideo'; +import { timeConvert } from '../utils/TimeUtils'; +import { GlobalContext } from '../utils/GlobalContext'; + +const PROPORTION = 0.99; // Screen Percentage +const SURFACEW = 0.9; // Surface width ratio +const SURFACEH = 1.78; // Surface height ratio +const TIMEOUT = 0; // Timer ID +const SET_TIME_OUT = 8000; // Interval: 8s +const SET_INTERVAL = 100; + +class innerInfo { + eventId: number = 0 + priority: emitter.EventPriority = 0 +} + +let innerEventFalse: innerInfo = { + eventId: 1, + priority: emitter.EventPriority.HIGH +}; + +let innerEventTrue: innerInfo = { + eventId: 2, + priority: emitter.EventPriority.HIGH +}; + +let innerEventWH: innerInfo = { + eventId: 3, + priority: emitter.EventPriority.HIGH +}; + +@Entry +@Component +struct Index { + @StorageLink('flag') flag: boolean = true; // Pause Playback + @StorageLink('currentTime') currentTime: number = 0; + @StorageLink('show') show: boolean = false; // Indicates whether the videoPanel component is displayed. + @StorageLink('videoSelect') videoSelect: number = 0; + @StorageLink('videoState') @Watch('continueChange')videoState: string = ''; + @StorageLink('videoName') videoName: Resource = $r('app.string.video_res_1'); + @StorageLink('videoIndex') videoIndex: number = 0; + @StorageLink('continue') @Watch('continueChange')continue: boolean = false; + @StorageLink('timeInterval') timeInterval: number = 0; + @State avPlayManage: avPlayManage = new avPlayManage(); + @State isSwiping: boolean = false; + @State isClickScreen: boolean = false; + @State XComponentFlag: boolean = false; + @State speedSelect: number = 0; + @State videoListSelect: number = 0; + @State durationTime: number = 0; + @State surfaceW: number = 0; + @State surfaceH: number = 0; + @State percent: number = 0; + @State windowWidth: number = 300; + @State windowHeight: number = 300; + @State isCalcWHFinished: boolean = false; + private videoList: Resource[] = [$r('app.string.video_res_1'), $r('app.string.video_res_2')]; + private videoNameList: string[] = ['test1.mp4', 'test2.mp4'] + private surfaceId: string = ''; + private timeout: number = 0; // Timer ID + private xComponentController: XComponentController = new XComponentController(); + + continueChange(){ + if (this.videoState === 'playing' && this.continue) { + this.avPlayManage.videoSeek(this.currentTime); + AppStorage.set('continue', false); + } + } + setTimer(): void { + let that = this; + this.timeout = setTimeout(() => { + that.isClickScreen = false; // Hide the operation panel + }, SET_TIME_OUT); // Hide in 8 seconds + } + + clearTimer(): void { + if (this.timeout !== TIMEOUT) { + clearTimeout(this.timeout); + this.timeout = TIMEOUT; + } + } + + aboutToAppear() { + this.windowWidth = display.getDefaultDisplaySync().width; + this.windowHeight = display.getDefaultDisplaySync().height; + this.surfaceW = (GlobalContext.getContext().getObject('windowWidth') as number) * SURFACEW; + this.surfaceH = this.surfaceW / SURFACEH; + this.flag = true; + AppStorage.setOrCreate('avPlayManage', this.avPlayManage); + } + + aboutToDisappear() { + this.avPlayManage.videoRelease(); + emitter.off(innerEventFalse.eventId); + } + + onPageHide() { + this.avPlayManage.videoPause(); + this.flag = false; + } + + onPageShow() { + emitter.on(innerEventTrue, (res) => { + if (res.data) { + this.flag = res.data.flag; + this.XComponentFlag = res.data.flag; + } + }); + emitter.on(innerEventFalse, (res) => { + if (res.data) { + this.flag = res.data.flag; + } + }); + emitter.on(innerEventWH, (res) => { + if (res.data) { + this.windowWidth = res.data.width; + this.windowHeight = res.data.height; + this.setVideoWH(); + } + }); + + if (this.flag == false) { + this.clearTimer(); + } + } + + setVideoWH(): void { + if (this.percent >= 1) { // Horizontal video + this.surfaceW = Math.round(this.windowWidth * PROPORTION); + this.surfaceH = Math.round(this.surfaceW / this.percent); + } else { // Vertical video + this.surfaceH = Math.round(this.windowHeight * PROPORTION); + this.surfaceW = Math.round(this.surfaceH * this.percent); + } + } + + async isInternet(): Promise { + if (connection.getAllNetsSync().length <= 0) { + this.toast(); + return false + } + return true; + } + + async toast() { + promptAction.showToast({ + message: $r('app.string.video_warn'), + duration: 2000, + }); + } + + @Builder + CoverXComponent() { + XComponent({ + // Loading the video container + id: 'xComponent', + type: XComponentType.SURFACE, + controller: this.xComponentController + }) + .visibility(this.XComponentFlag ? Visibility.Visible : Visibility.Hidden) + .onLoad(() => { + this.surfaceId = this.xComponentController.getXComponentSurfaceId(); + this.avPlayManage.initPlayer(this.surfaceId, this.videoNameList[this.videoIndex],this.continue,this.currentTime, + (avPlayer: media.AVPlayer) => { + this.percent = avPlayer.width / avPlayer.height; + this.setVideoWH(); + this.isCalcWHFinished = true; + this.durationTime = this.avPlayManage.getDurationTime(); + this.clearTimer(); + clearInterval(this.timeInterval); + if(!this.flag){ + this.avPlayManage.videoPause() + } + this.timeInterval = setInterval(() => { // Update the current time. + this.currentTime = this.avPlayManage.getCurrentTime(); + }, SET_INTERVAL); + }) + + }) + .height(this.isCalcWHFinished ? `${this.surfaceH}px` : '100%') + .width(this.isCalcWHFinished ? `${this.surfaceW}px` : '100%') + } + + build() { + Stack() { + Column() { + this.CoverXComponent() + } + .align(Alignment.TopStart) + .margin({ top: $r('app.float.size_80') }) + .id('Video') + .justifyContent(FlexAlign.Center) + + Text() + .height(`${this.surfaceH}px`) + .width(`${this.surfaceW}px`) + .margin({ top: $r('app.float.size_80') }) + .backgroundColor(Color.Black) + .opacity($r('app.float.size_zero_five')) + .visibility(this.isSwiping ? Visibility.Visible : Visibility.Hidden) + + Row() { + Text(timeConvert(this.currentTime)) + .fontSize($r('app.float.size_24')) + .opacity($r('app.float.size_1')) + .fontColor($r('app.color.slider_selected')) + Text('/' + timeConvert(this.durationTime)) + .fontSize($r('app.float.size_24')) + .opacity($r('app.float.size_1')) + .fontColor(Color.White) + } + .margin({ top: $r('app.float.size_80') }) + .visibility(this.isSwiping ? Visibility.Visible : Visibility.Hidden) + + Column() { + Row() { + ExitVideo({ + avPlayManage: $avPlayManage, + }) + } + .width('100%') + .justifyContent(FlexAlign.Start) + + Blank() + + Column() { + // Progress bar + VideoOperate({ + avPlayManage: $avPlayManage, + durationTime: $durationTime, + isSwiping: $isSwiping, + XComponentFlag: $XComponentFlag + }) + .width('100%') + } + .justifyContent(FlexAlign.Center) + } + .onTouch((event: TouchEvent) => { + if (event.type === TouchType.Down) { + this.isClickScreen = true; + this.clearTimer(); + } else if (event.type === TouchType.Up) { + this.setTimer(); + } else if (event.type === TouchType.Move) { + this.isClickScreen = true; + this.clearTimer(); + } + }) + .visibility(this.isClickScreen ? Visibility.Visible : Visibility.Hidden) + .width('100%') + .height('100%') + } + .onClick(() => { + this.isClickScreen = !this.isClickScreen; + if (this.isClickScreen) { + this.setTimer(); + } else { + this.clearTimer(); + } + }) + .backgroundColor(Color.Black) + .height('100%') + .width('100%') + .padding({ top: '36vp', bottom: '28vp' }) + } +} \ No newline at end of file diff --git a/features/video/src/main/ets/utils/GlobalContext.ets b/features/video/src/main/ets/utils/GlobalContext.ets new file mode 100644 index 0000000000000000000000000000000000000000..63ac2c38e5a8017dc554bafcbf4c4a962f002251 --- /dev/null +++ b/features/video/src/main/ets/utils/GlobalContext.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class GlobalContext { + private constructor() { + } + + private static instance: GlobalContext; + private _objects = new Map(); + + public static getContext(): GlobalContext { + if (!GlobalContext.instance) { + GlobalContext.instance = new GlobalContext(); + } + return GlobalContext.instance; + } + + getObject(value: string): Object | undefined { + return this._objects.get(value); + } + + setObject(key: string, objectClass: Object): void { + this._objects.set(key, objectClass); + } +} \ No newline at end of file diff --git a/features/video/src/main/ets/utils/Logger.ets b/features/video/src/main/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..b2d262127f7e5bfa39b7b9fa8f5513eb7e0f55af --- /dev/null +++ b/features/video/src/main/ets/utils/Logger.ets @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + } + + debug(...args: string[]): void { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]): void { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]): void { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]): void { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('[Samples_VideoPlay]'); \ No newline at end of file diff --git a/features/video/src/main/ets/utils/ResourceUtil.ets b/features/video/src/main/ets/utils/ResourceUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..b84f7b34f769cde67e813c91035335500e00fa0d --- /dev/null +++ b/features/video/src/main/ets/utils/ResourceUtil.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { abilityDelegatorRegistry } from '@kit.TestKit' + +let context: Context + +export async function getString(str: Resource) { + if (context === null) { + const abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator() + context = abilityDelegator.getAppContext() + } + let manager = context.resourceManager + return await manager.getStringValue(str); +} \ No newline at end of file diff --git a/features/video/src/main/ets/utils/TimeUtils.ets b/features/video/src/main/ets/utils/TimeUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..ce11beaca551af32daa5cf9970fbf4c8abce3cf8 --- /dev/null +++ b/features/video/src/main/ets/utils/TimeUtils.ets @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const TIME_ONE = 60000; +const TIME_TWO = 1000; +const TIME_THREE = 10; + +export function timeConvert(time: number): string { + let min: number = Math.floor(time / TIME_ONE); + let second: string = ((time % TIME_ONE) / TIME_TWO).toFixed(0); + return `${min}:${(Number(second) < TIME_THREE ? '0' : '') + second}`; +} + diff --git a/features/video/src/main/ets/videoability/VideoAbility.ets b/features/video/src/main/ets/videoability/VideoAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..9609c0071a5ce78b25f5ede90d978be82ed5d568 --- /dev/null +++ b/features/video/src/main/ets/videoability/VideoAbility.ets @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; +import { distributedDataObject } from '@kit.ArkData'; +import { emitter } from '@kit.BasicServicesKit'; +import { GlobalContext } from '../utils/GlobalContext'; + +class ContinueData { + continueTime: number | undefined; + continueItem: number | undefined; + flag: boolean | undefined; + + constructor(continueTime: number | undefined, continueItem: number | undefined, flag: boolean | undefined) { + this.continueTime = continueTime; + this.continueItem = continueItem; + this.flag = flag; + } +} + +export default class VideoAbility extends UIAbility { + continueRestore(want: Want) { + if (!want.parameters || !want.parameters.distributedSessionId) { + return; + } + let continueData: ContinueData = new ContinueData(undefined, undefined, undefined); + let dataObject = distributedDataObject.create(this.context, continueData); + dataObject.on('status', (sessionId: string, networkId: string, status: string) => { + if (status === 'restored') { + AppStorage.setOrCreate('currentTime', dataObject['continueTime']); + AppStorage.setOrCreate('videoIndex', dataObject['continueItem']); + AppStorage.setOrCreate('flag', dataObject['flag']); + AppStorage.setOrCreate('continue', true); + } + }); + let sessionId = want.parameters.distributedSessionId as string; + dataObject.setSessionId(sessionId); + this.context.restoreWindowStage(new LocalStorage()); + } + + async onContinue(wantParam: Record): Promise { + let currentTime = AppStorage.get('currentTime') as number; + let videoIndex = AppStorage.get('videoIndex') as number; + let flag = AppStorage.get('flag') as boolean; + let continueData: ContinueData = new ContinueData(currentTime, videoIndex, flag); + let dataObject = distributedDataObject.create(this.context, continueData); + let sessionId = distributedDataObject.genSessionId(); + dataObject.setSessionId(sessionId); + wantParam.distributedSessionId = sessionId; + + let deviceId = wantParam.targetDevice as string; + dataObject.save(deviceId); + return AbilityConstant.OnContinueResult.AGREE; + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + GlobalContext.getContext().setObject('abilityWant', want); + GlobalContext.getContext().setObject('context', this.context) + if (want.parameters) { + if (want.parameters.currentTime) { + GlobalContext.getContext().setObject('currentTime', want.parameters.currentTime); + } + } + if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) { + if (want.parameters && want.parameters.distributedSessionId) { + this.continueRestore(want); + } + } + } + + onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { + GlobalContext.getContext().setObject('abilityWant', want); + GlobalContext.getContext().setObject('context', this.context); + if (want.parameters) { + if (want.parameters.currentTime) { + GlobalContext.getContext().setObject('currentTime', want.parameters.currentTime); + } + } + if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) { + if (want.parameters && want.parameters.distributedSessionId) { + this.continueRestore(want); + } + } + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + windowStage.getMainWindow().then((win: window.Window) => { + win.setWindowKeepScreenOn(true); + win.setWindowSystemBarProperties({ + statusBarColor: '#000000', + statusBarContentColor: '#FFFFFF' + }); + win.setWindowLayoutFullScreen(true); + win.on('windowSizeChange', (newSize: window.Size) => { + let eventWHData: emitter.EventData = { + data: { + 'width': newSize.width, + 'height': newSize.height + } + }; + let innerEventWH: emitter.InnerEvent = { + eventId: 3, + priority: emitter.EventPriority.HIGH + }; + emitter.emit(innerEventWH, eventWHData); + }); + }); + windowStage.loadContent('pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/features/video/src/main/ets/videomanager/AvPlayManager.ets b/features/video/src/main/ets/videomanager/AvPlayManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..3db1e8dcb9a7ce1d23164fe1fed82a566ac805f7 --- /dev/null +++ b/features/video/src/main/ets/videomanager/AvPlayManager.ets @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { media } from '@kit.MediaKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { BusinessError, emitter } from '@kit.BasicServicesKit'; +import { common } from '@kit.AbilityKit'; +import Logger from '../utils/Logger'; +import { GlobalContext } from '../utils/GlobalContext'; + +const CASE_ZERO = 0; +const CASE_ONE = 1; +const CASE_TWO = 2; +const CASE_THREE = 3; + +export default class AvPlayManage { + public state: string = ''; + public avPlayer: media.AVPlayer | null = null; + private tag: string = 'AVPlayManage'; + private surfaceID: string = ''; + private mgr: resourceManager.ResourceManager = {} as resourceManager.ResourceManager; + // Current time of the video. + private currentTime: number = 0; + // Total video length. + private durationTime: number = 0; + // Playback rate selection. + private speedSelect: number = 0; + private fileDescriptor: resourceManager.RawFileDescriptor | null = null; + private videoSrc: string = ''; + private fileSrc: string = ''; + private continue:boolean = false; + + async initPlayer(surfaceId: string, file: string, isContinue: boolean, continueTime: number, + callback: (avPlayer: media.AVPlayer) => void): Promise { + this.surfaceID = surfaceId; + try { + // Creates the avPlayer instance object. + this.avPlayer = await media.createAVPlayer(); + // Creates a callback function for state machine changes. + this.continue = isContinue; + await this.setAVPlayerCallback(isContinue, continueTime, callback); + this.mgr = (GlobalContext.getContext().getObject('context') as (common.UIAbilityContext)).resourceManager; + if (file === 'network.mp4') { + this.fileSrc = + 'https:\/\/vd3.bdstatic.com\/mda-pdc2kmwtd2vxhiy4\/cae_h264\/1681502407203843413\/mda-pdc2kmwtd2vxhiy4.mp4'; + } else { + this.fileSrc = file; + } + let regex: RegExp = new RegExp('^(http|https)', 'i'); + let bool = regex.test(this.fileSrc); + if (bool) { + this.avPlayer.url = this.fileSrc; + } else { + this.fileDescriptor = await this.mgr.getRawFd(this.fileSrc); + this.avPlayer.fdSrc = this.fileDescriptor; + } + }catch(e){ + Logger.error(this.tag, + `setAVPlayerCallback Invoke avPlayer failed, code is ${e.code}, message is ${e.message}`); + } + } + + async setAVPlayerCallback(isContinue: boolean, continueTime: number, + callback: (avPlayer: media.AVPlayer) => void): Promise { + if (this.avPlayer === null) { + return; + } + this.avPlayer.on('seekDone', (seekDoneTime) => { + Logger.info(this.tag, `setAVPlayerCallback AVPlayer seek succeeded, seek time is ${seekDoneTime}`); + }); + this.avPlayer.on('error', (err) => { + if (this.avPlayer === null) { + return; + } + Logger.error(this.tag, + `setAVPlayerCallback Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); + this.avPlayer.reset(); + }); + this.avPlayer.on('stateChange', async (state, reason) => { + if (this.avPlayer === null) { + return; + } + switch (state) { + case 'idle': // This state machine is triggered after the reset interface is successfully invoked. + this.state = 'idle'; + AppStorage.set('videoState', this.state); + this.avPlayer.release(); + this.avPlayerChoose(callback); + break; + case 'initialized': // This status is reported after the playback source is set on the AVPlayer. + this.state = 'initialized'; + AppStorage.set('videoState', this.state); + this.avPlayer.surfaceId = this.surfaceID; + this.avPlayer.prepare(); + break; + case 'prepared': // This state machine is reported after the prepare interface is successfully invoked. + this.state = 'prepared'; + AppStorage.set('videoState', this.state); + this.durationTime = this.avPlayer.duration; + this.currentTime = this.avPlayer.currentTime; + this.avPlayer.play(); // Invoke the playback interface to start playback. + switch (this.speedSelect) { + case CASE_ZERO: + this.videoSpeedOne(); + break; + case CASE_ONE: + this.videoSpeedOnePointTwentyFive(); + break; + case CASE_TWO: + this.videoSpeedOnePointSeventyFive(); + break; + case CASE_THREE: + this.videoSpeedTwo(); + break; + } + callback(this.avPlayer); + break; + case 'playing': // After the play interface is successfully invoked, the state machine is reported. + this.state = 'playing'; + AppStorage.set('videoState', this.state); + if (this.continue) { + this.videoSeek(continueTime); + this.continue = false; + AppStorage.set('continue', false); + } + let eventDataTrue: emitter.EventData = { + data: { + 'flag': true + } + }; + let innerEventTrue: emitter.InnerEvent = { + eventId: 2, + priority: emitter.EventPriority.HIGH + }; + emitter.emit(innerEventTrue, eventDataTrue); + break; + case 'paused': + this.state = 'paused'; + AppStorage.set('videoState', this.state); + break; + case 'stopped': + this.state = 'stopped'; + AppStorage.set('videoState', this.state); + break; + case 'completed': // This state machine is triggered to report when the playback ends. + this.state = 'completed'; + AppStorage.set('videoState', this.state); + let eventDataFalse: emitter.EventData = { + data: { + 'flag': false + } + }; + let innerEvent: emitter.InnerEvent = { + eventId: 1, + priority: emitter.EventPriority.HIGH + }; + emitter.emit(innerEvent, eventDataFalse); + break; + default: + this.state = 'unknown'; + AppStorage.set('videoState', this.state); + break; + } + }); + // Listening function for reporting time + this.avPlayer.on('timeUpdate', (time: number) => { + this.currentTime = time; + }); + } + + getDurationTime(): number { + return this.durationTime; + } + + getCurrentTime(): number { + return this.currentTime; + } + + videoPlay(): void { + if (this.avPlayer) { + try { + this.avPlayer.play(); + } catch (e) { + Logger.error(this.tag, `videoPlay = ${JSON.stringify(e)}`); + } + } + } + + videoPause(): void { + if (this.avPlayer) { + try { + this.avPlayer.pause(); + } catch (e) { + Logger.info(this.tag, `videoPause== ${JSON.stringify(e)}`); + } + } + } + + videoSpeedOne(): void { + if (this.avPlayer) { + try { + this.avPlayer.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_00_X); + Logger.info(this.tag, 'videoSpeed_1_00'); + } catch (e) { + Logger.info(this.tag, `videoSpeed_1_00== ${JSON.stringify(e)}`); + } + } + } + + videoSpeedOnePointTwentyFive(): void { + if (this.avPlayer) { + try { + this.avPlayer.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_25_X); + Logger.info(this.tag, 'videoSpeed_1_25'); + } catch (e) { + Logger.info(this.tag, `videoSpeed_1_25== ${JSON.stringify(e)}`); + } + } + } + + videoSpeedOnePointSeventyFive(): void { + if (this.avPlayer) { + try { + this.avPlayer.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_75_X); + Logger.info(this.tag, 'videoSpeed_1_75'); + } catch (e) { + Logger.info(this.tag, 'videoSpeed_1_75==' + JSON.stringify(e)); + } + } + } + + videoSpeedTwo(): void { + if (this.avPlayer) { + try { + this.avPlayer.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_2_00_X); + Logger.info(this.tag, `videoSpeed_2_0`); + } catch (e) { + Logger.info(this.tag, `videoSpeed_2_0== ${JSON.stringify(e)}`); + } + } + } + + videoSeek(seekTime: number): void { + if ( this.state === 'playing' || this.state === 'paused' || this.state ==='prepared' || this.state ==='completed') { + try { + this.avPlayer!.seek(seekTime, media.SeekMode.SEEK_CLOSEST); + Logger.info(this.tag, `videoSeek== ${seekTime}`); + } catch (e) { + Logger.info(this.tag, `videoSeek== ${JSON.stringify(e)}`); + } + } + } + + async videoReset(): Promise { + if (this.avPlayer === null) { + return; + } + this.avPlayer.reset(); + } + + async videoRelease(): Promise { + if (this.avPlayer === null) { + return; + } + this.avPlayer.release((err) => { + if (err === null) { + Logger.info(this.tag, 'videoRelease release success'); + } + }); + } + + async videoChoose(videoSrc: string): Promise { + try { + this.videoSrc = videoSrc; + let timeInterval = AppStorage.get('timeInterval') as number; + clearInterval(timeInterval); + Logger.info(this.tag, `videoChoose this.videoSrc = ${this.videoSrc}`); + this.videoReset(); + } catch (e) { + Logger.info(this.tag, `videoChoose== ${JSON.stringify(e)}`); + } + } + + async avPlayerChoose(callback: (avPlayer: media.AVPlayer) => void): Promise { + try { + this.avPlayer = await media.createAVPlayer(); + this.fileDescriptor = null; + if (this.videoSrc === 'network.mp4') { + this.fileSrc = + 'https:\/\/vd3.bdstatic.com\/mda-pdc2kmwtd2vxhiy4\/cae_h264\/1681502407203843413\/mda-pdc2kmwtd2vxhiy4.mp4'; + } else { + this.fileSrc = this.videoSrc; + } + let regex: RegExp = new RegExp('^(http|https)', 'i'); + let bool = regex.test(this.fileSrc); + if (bool) { + this.avPlayer.url = this.fileSrc; + } else { + this.fileDescriptor = await this.mgr.getRawFd(this.fileSrc); + this.avPlayer.fdSrc = this.fileDescriptor; + } + await this.setAVPlayerCallback(this.continue, 0, callback); + } catch (e) { + e = e as BusinessError; + this.videoReset(); + } + } +} \ No newline at end of file diff --git a/features/video/src/main/module.json5 b/features/video/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..67fe0b4218ea80a21e6da5f46ddc5afa723f11c9 --- /dev/null +++ b/features/video/src/main/module.json5 @@ -0,0 +1,50 @@ +{ + "module": { + "name": "video", + "type": "feature", + "description": "$string:module_desc", + "mainElement": "VideoAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "VideoAbility", + "srcEntry": "./ets/videoability/VideoAbility.ets", + "description": "$string:VideoAbility_desc", + "icon": "$media:layered_image", + "label": "$string:VideoAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "continuable": true, + "launchType": "singleton" + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:reason_internet", + "usedScene": { + "abilities": [ + "VideoAbility" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.GET_NETWORK_INFO", + "reason": "$string:reason_get_network_info", + "usedScene": { + "abilities": [ + "VideoAbility" + ], + "when": "always" + } + } + ] + } +} \ No newline at end of file diff --git a/features/video/src/main/resources/base/element/color.json b/features/video/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..419aa49d53413d68acdd73edc8d2b80d01ef60e9 --- /dev/null +++ b/features/video/src/main/resources/base/element/color.json @@ -0,0 +1,24 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "slider_selected", + "value": "#007DFF" + }, + { + "name": "speed_dialog", + "value": "#33bab4b4" + }, + { + "name": "video_play", + "value": "#333333" + }, + { + "name": "video_play_selected", + "value": "#5c5c5c" + } + ] +} \ No newline at end of file diff --git a/features/video/src/main/resources/base/element/float.json b/features/video/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..1ea100d17b3e64467b32d74ac46263d8846b277b --- /dev/null +++ b/features/video/src/main/resources/base/element/float.json @@ -0,0 +1,104 @@ +{ + "float": [ + { + "name": "size_zero_five", + "value": "0.5" + }, + { + "name": "size_zero_six", + "value": "0.6" + }, + { + "name": "size_zero", + "value": "0" + }, + { + "name": "size_1", + "value": "1" + }, + { + "name": "size_5", + "value": "5" + }, + { + "name": "size_10", + "value": "10" + }, + { + "name": "size_12", + "value": "12" + }, + { + "name": "size_15", + "value": "15" + }, + { + "name": "size_16", + "value": "16" + }, + { + "name": "size_18", + "value": "18" + }, + { + "name": "size_20", + "value": "20" + }, + { + "name": "size_down_20", + "value": "-20" + }, + { + "name": "size_24", + "value": "24" + }, + { + "name": "size_25", + "value": "25" + }, + { + "name": "size_30", + "value": "30" + }, + { + "name": "size_32", + "value": "32" + }, + { + "name": "size_35", + "value": "35" + }, + { + "name": "size_40", + "value": "40" + }, + { + "name": "size_45", + "value": "45" + }, + { + "name": "size_48", + "value": "48" + }, + { + "name": "size_50", + "value": "50" + }, + { + "name": "size_64", + "value": "64" + }, + { + "name": "size_75", + "value": "75" + }, + { + "name": "size_80", + "value": "-80" + }, + { + "name": "size_254", + "value": "254" + } + ] +} \ No newline at end of file diff --git a/features/video/src/main/resources/base/element/string.json b/features/video/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c9b08acb818916507d9327a512823d5c996ce96d --- /dev/null +++ b/features/video/src/main/resources/base/element/string.json @@ -0,0 +1,76 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "VideoAbility_desc", + "value": "description" + }, + { + "name": "VideoAbility_label", + "value": "label" + }, + { + "name": "video_res_1", + "value": "test1.mp4" + }, + { + "name": "video_res_2", + "value": "test2.mp4" + }, + { + "name": "video_res_3", + "value": "network.mp4" + }, + { + "name": "video_speed_1_0X", + "value": "1.0X" + }, + { + "name": "video_speed_1_25X", + "value": "1.25X" + }, + { + "name": "video_speed_1_75X", + "value": "1.75X" + }, + { + "name": "video_speed_2_0X", + "value": "2.0X" + }, + { + "name": "video_warn", + "value": "Please check if the network is connected or available!" + }, + { + "name": "dialog_cancel", + "value": "Cancel" + }, + { + "name": "dialog_play_speed", + "value": "Playback speed" + }, + { + "name": "playing", + "value": "Playing" + }, + { + "name": "reason_internet", + "value": "Used for accessing the Internet during video playback" + }, + { + "name": "reason_get_network_info", + "value": "Used for retrieving network information during video playback" + }, + { + "name": "click_change", + "value": "Click Change" + }, + { + "name": "title", + "value": "Video Continue" + } + ] +} \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/back.png b/features/video/src/main/resources/base/media/back.png new file mode 100644 index 0000000000000000000000000000000000000000..12870ebe39daf8b31918f433d596eeaf5440f8f9 Binary files /dev/null and b/features/video/src/main/resources/base/media/back.png differ diff --git a/features/video/src/main/resources/base/media/background.png b/features/video/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/features/video/src/main/resources/base/media/background.png differ diff --git a/features/video/src/main/resources/base/media/foreground.png b/features/video/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/features/video/src/main/resources/base/media/foreground.png differ diff --git a/features/video/src/main/resources/base/media/ic_radio.png b/features/video/src/main/resources/base/media/ic_radio.png new file mode 100644 index 0000000000000000000000000000000000000000..9c5f71f9c655cca2f4b3d05606e3841a372e813c Binary files /dev/null and b/features/video/src/main/resources/base/media/ic_radio.png differ diff --git a/features/video/src/main/resources/base/media/ic_radio_selected.svg b/features/video/src/main/resources/base/media/ic_radio_selected.svg new file mode 100644 index 0000000000000000000000000000000000000000..f115d62baa097b60a6b7764edd5ea81295cb12d4 --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_radio_selected.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/features/video/src/main/resources/base/media/ic_screen_switch.svg b/features/video/src/main/resources/base/media/ic_screen_switch.svg new file mode 100644 index 0000000000000000000000000000000000000000..64bda6d74d09bbae5c3a96d593852fb279c7e20a --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_screen_switch.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_shuffle + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/ic_video_back.svg b/features/video/src/main/resources/base/media/ic_video_back.svg new file mode 100644 index 0000000000000000000000000000000000000000..7da6964ff75d09ab7434afcc9c451311184ca863 --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_video_back.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_back + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/ic_video_list.svg b/features/video/src/main/resources/base/media/ic_video_list.svg new file mode 100644 index 0000000000000000000000000000000000000000..f56a83eff6b0680fc6ddd8f5cd5654f50b14125b --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_video_list.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_view_list + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/ic_video_list_down.svg b/features/video/src/main/resources/base/media/ic_video_list_down.svg new file mode 100644 index 0000000000000000000000000000000000000000..c74821d7a7b0b888d0de4c76e532a34531bf7670 --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_video_list_down.svg @@ -0,0 +1,15 @@ + + + + 画板 + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/ic_video_list_up.svg b/features/video/src/main/resources/base/media/ic_video_list_up.svg new file mode 100644 index 0000000000000000000000000000000000000000..b56a7c5aef39d028bb6cd16e1cf159a2bba1647f --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_video_list_up.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_arrow_up_0 + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/ic_video_pause.svg b/features/video/src/main/resources/base/media/ic_video_pause.svg new file mode 100644 index 0000000000000000000000000000000000000000..b18a4a3d00cf2f80fe90c55faef56b39ed485a1b --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_video_pause.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_play + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/ic_video_play.svg b/features/video/src/main/resources/base/media/ic_video_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..675bf7f87b66ed5011191b77c1f3f678625be68a --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_video_play.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_pause + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/ic_video_view_list.svg b/features/video/src/main/resources/base/media/ic_video_view_list.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a7c8392a5ce10f74a34578415041614a8bce1d8 --- /dev/null +++ b/features/video/src/main/resources/base/media/ic_video_view_list.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_view_list_filled + + + + + + + + + + \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/icon.png b/features/video/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/features/video/src/main/resources/base/media/icon.png differ diff --git a/features/video/src/main/resources/base/media/layered_image.json b/features/video/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/features/video/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/features/video/src/main/resources/base/media/list.png b/features/video/src/main/resources/base/media/list.png new file mode 100644 index 0000000000000000000000000000000000000000..b87e35abe95cafb866f909b6a642c13c05cd537e Binary files /dev/null and b/features/video/src/main/resources/base/media/list.png differ diff --git a/features/video/src/main/resources/base/media/startIcon.png b/features/video/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/features/video/src/main/resources/base/media/startIcon.png differ diff --git a/features/video/src/main/resources/base/profile/main_pages.json b/features/video/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/features/video/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/features/video/src/main/resources/en_US/element/string.json b/features/video/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..24e29315e3f3bf1e3e87a4ba4b81d7803275ea22 --- /dev/null +++ b/features/video/src/main/resources/en_US/element/string.json @@ -0,0 +1,77 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "VideoAbility_desc", + "value": "description" + }, + { + "name": "VideoAbility_label", + "value": "label" + }, + { + "name": "video_res_1", + "value": "test1.mp4" + }, + { + "name": "video_res_2", + "value": "test2.mp4" + }, + { + "name": "video_res_3", + "value": "network.mp4" + }, + { + "name": "video_speed_1_0X", + "value": "1.0X" + }, + { + "name": "video_speed_1_25X", + "value": "1.25X" + }, + { + "name": "video_speed_1_75X", + "value": "1.75X" + }, + { + "name": "video_speed_2_0X", + "value": "2.0X" + }, + { + "name": "video_warn", + "value": "Please check if the network is connected or available!" + }, + { + "name": "dialog_cancel", + "value": "Cancel" + }, + { + "name": "dialog_play_speed", + "value": "Playback speed" + }, + { + "name": "playing", + "value": "playing" + }, + { + "name": "reason_internet", + "value": "Used for accessing the Internet during video playback" + }, + { + "name": "reason_get_network_info", + "value": "Used for retrieving network information during video playback" + }, + { + "name": "click_change", + "value": "Click Change" + }, + { + "name": "title", + "value": "Video Continue" + } + + ] +} \ No newline at end of file diff --git a/features/video/src/main/resources/rawfile/test1.mp4 b/features/video/src/main/resources/rawfile/test1.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..e89bdff923a22a778a39edce405bb1092a14449d Binary files /dev/null and b/features/video/src/main/resources/rawfile/test1.mp4 differ diff --git a/features/video/src/main/resources/rawfile/test2.mp4 b/features/video/src/main/resources/rawfile/test2.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..587eef0060b3beec8102ac1030c92beeb34ccb24 Binary files /dev/null and b/features/video/src/main/resources/rawfile/test2.mp4 differ diff --git a/features/video/src/main/resources/zh_CN/element/string.json b/features/video/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..b38b7b047f7a236a5aa3eb09ec359b4a593e60fa --- /dev/null +++ b/features/video/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,76 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "VideoAbility_desc", + "value": "description" + }, + { + "name": "VideoAbility_label", + "value": "适配播放" + }, + { + "name": "video_warn", + "value": "请检查网络是否连接或可用!" + }, + { + "name": "video_speed_1_0X", + "value": "1.0X" + }, + { + "name": "video_speed_1_25X", + "value": "1.25X" + }, + { + "name": "video_speed_1_75X", + "value": "1.75X" + }, + { + "name": "video_res_1", + "value": "test1.mp4" + }, + { + "name": "video_res_2", + "value": "test2.mp4" + }, + { + "name": "video_res_3", + "value": "network.mp4" + }, + { + "name": "video_speed_2_0X", + "value": "2.0X" + }, + { + "name": "dialog_cancel", + "value": "取消" + }, + { + "name": "dialog_play_speed", + "value": "播放倍速" + }, + { + "name": "playing", + "value": "当前播放" + }, + { + "name": "reason_internet", + "value": "用于视频播放场景使用Internet网络" + }, + { + "name": "reason_get_network_info", + "value": "用于视频播放场景获取网络信息" + }, + { + "name": "click_change", + "value": "点击切换" + }, + { + "name": "title", + "value": "媒体播放进度接续" + } + ] +} \ No newline at end of file diff --git a/features/web/build-profile.json5 b/features/web/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4d611879c7913fb0610c686e2399258ab3a6dad1 --- /dev/null +++ b/features/web/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/features/web/hvigorfile.ts b/features/web/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6edcd90486dd5a853cf7d34c8647f08414ca7a3 --- /dev/null +++ b/features/web/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/features/web/obfuscation-rules.txt b/features/web/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/features/web/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/features/web/oh-package.json5 b/features/web/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ce84c78232e1f42c270125897440feef24fa5b3f --- /dev/null +++ b/features/web/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "name": "web", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/features/web/src/main/ets/common/constants/CommonConstants.ets b/features/web/src/main/ets/common/constants/CommonConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..3d90d079ef8cc4df8f47e947f61627b54d20b310 --- /dev/null +++ b/features/web/src/main/ets/common/constants/CommonConstants.ets @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common constants for common component. + */ +export class CommonConstants { + /** + * Font family. + */ + static readonly HARMONY_HEI_TI_FONT_FAMILY = 'HarmonyHeiTi'; + + /** + * 100 percent. + */ + static readonly FULL_PERCENT: string = '100%'; + + /** + * 50 percent. + */ + static readonly PERCENT_50: string = '50%'; + + /** + * Duration. + */ + static readonly TOAST_DURATION: number = 2000; + + /** + * Space. + */ + static readonly NORMAL_SPACE: number = 12; +} \ No newline at end of file diff --git a/features/web/src/main/ets/common/utils/Logger.ets b/features/web/src/main/ets/common/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..9013ef6daa5049cb6d73fb8fa2bfdd56387f026f --- /dev/null +++ b/features/web/src/main/ets/common/utils/Logger.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + /** + * constructor. + * + * @param Prefix Identifies the log tag. + * @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF. + */ + constructor(prefix: string = 'MyApp', domain: number = 0xFF00) { + this.prefix = prefix; + this.domain = domain; + } + + debug(...args: string[]): void { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]): void { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]): void { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]): void { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('PageRedirection', 0xFF00); \ No newline at end of file diff --git a/features/web/src/main/ets/model/ProductModel.ets b/features/web/src/main/ets/model/ProductModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..a12969f92f43e708871e981154ee236032f06666 --- /dev/null +++ b/features/web/src/main/ets/model/ProductModel.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default interface ProductModel { + id: number; + name: string; + img: string; + price: number; + sku: string; +} + +export interface ArkTSFunModel { + jumpOrderConfirm: (detailStr: string) => void; +} \ No newline at end of file diff --git a/features/web/src/main/ets/pages/IndexPage.ets b/features/web/src/main/ets/pages/IndexPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..81f19ecf3b657bc288df17baa63678abba71189f --- /dev/null +++ b/features/web/src/main/ets/pages/IndexPage.ets @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BusinessError } from '@kit.BasicServicesKit'; +import { router, promptAction } from '@kit.ArkUI'; +import { webview } from '@kit.ArkWeb'; +import { window } from '@kit.ArkUI'; +import { common, Want } from '@kit.AbilityKit'; +import Logger from '../common/utils/Logger'; +import { CommonConstants } from '../common/constants/CommonConstants'; +import { ArkTSFunModel } from '../model/ProductModel'; + +const TAG: string = '[IndexPage]'; + +@Entry +@Component +struct IndexPage { + @StorageLink('continueRestore') continueRestore:boolean = false; + @StorageLink('pageUrl') @Watch('watchPageUrl')pageUrl:string = 'resource://rawfile/product_list.html'; + @StorageLink('scrollDistance')scrollDistance:number = 0; + @State webCanBack: boolean = false; + @State onPageEnd: boolean = false; + @State webCanForward: boolean = false; + @State controller: webview.WebviewController = new webview.WebviewController(); + @State statusBarHeight: number = 0; + @State sliderBarHeight: number = 56; + private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; + + arkTSObj: ArkTSFunModel = { + jumpOrderConfirm: (detailStr: string) => this.jumpOrderConfirm(detailStr) + }; + watchPageUrl(){ + if (this.continueRestore){ + this.controller.loadUrl(this.pageUrl); + this.continueRestore = false; + } + } + + aboutToAppear() { + webview.WebviewController.setWebDebuggingAccess(true); + window.getLastWindow(getContext(this), (err: BusinessError, windowClass: window.Window) => { + if (err.code) { + Logger.error(TAG, 'Failed to obtain the main window. Cause: ' + JSON.stringify(err)); + return; + } + Logger.info(TAG, 'Succeeded in obtaining the main window. Data: ' + JSON.stringify(windowClass)); + + // Realize the immersive effect. + try { + let type = window.AvoidAreaType.TYPE_SYSTEM; + // Get status bar height. + let area: window.AvoidArea = windowClass.getWindowAvoidArea(type); + let statusBarHeight = px2vp(area.topRect.height); + let sliderBarHeight = px2vp(area.bottomRect.height); + this.statusBarHeight = statusBarHeight; + this.sliderBarHeight = sliderBarHeight; + if (statusBarHeight > 0) { + windowClass.setWindowLayoutFullScreen(true); + } + } catch (exception) { + Logger.error(TAG, 'Failed to set the system bar properties. Cause: ' + JSON.stringify(exception)); + } + }); + } + + onBackPress() { + return true; + } + + jumpOrderConfirm(detailStr: string): void { + router.pushUrl({ + url: 'pages/OrderConfirmPage', + params: { statusBarHeight: this.statusBarHeight, sliderBarHeight: this.sliderBarHeight, detailStr } + }); + } + + build() { + Stack({ + alignContent:Alignment.TopStart + }) { + Web({ src: this.pageUrl, controller: this.controller }) + .layoutWeight(1) + .javaScriptProxy({ + object: this.arkTSObj, + name: 'arkTSFunObj', + methodList: ['jumpOrderConfirm'], + controller: this.controller + }) + .onConfirm(() => { + promptAction.showToast({ + message: $r('app.string.toast_msg'), + duration: CommonConstants.TOAST_DURATION + }); + return false; + }) + .width('100%') + .height(this.pageUrl.includes('product_list')?'90%':'100%') + .onPageEnd(async () => { + this.webCanBack = this.controller.accessBackward(); + this.webCanForward = this.controller.accessForward(); + this.onPageEnd = true; + if(this.pageUrl.includes('product_list') && this.continueRestore){ + this.controller.runJavaScript('javascript:document.getElementById("productList").scrollTop = ' + this.scrollDistance); + } + this.pageUrl = this.controller.getUrl(); + let result = await this.controller.runJavaScript('javascript:document.getElementById("productList").scrollTop'); + this.scrollDistance = Number(result); + }) + .zoomAccess(false) + .onTouch(async (event: TouchEvent) => { + if (event.type === TouchType.Up) { + if(this.pageUrl.includes('product_list')){ + let result = await this.controller.runJavaScript('javascript:document.getElementById("productList").scrollTop'); + this.scrollDistance = Number(result); + } + } + }) + .margin({ + top:this.pageUrl.includes('product_detail')?0:92, + }) + Row(){ + Image($r('app.media.back')) + .width(40) + .height(40) + .margin({ + right:8 + }) + .onClick(()=>{ + if(this.pageUrl.includes('product_list')){ + let want: Want = { + deviceId: '', + bundleName: 'com.example.continueprogress', + abilityName: 'EntryAbility', + } + this.context.startAbility(want); + }else if (this.pageUrl.includes('product_detail')){ + this.controller.loadUrl('resource://rawfile/product_list.html'); + } + }) + Text('Web页面浏览进度接续') + .fontSize(20) + .fontWeight(700) + .lineHeight(27) + } + .backgroundColor(this.pageUrl.includes('product_list')?'#FFF1F3F5':'#00000000') + .height(56) + .width('100%') + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Start) + .padding({ + left:19, + right:19 + }) + .margin({ + top:36, + }) + } + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP]) + .width(CommonConstants.FULL_PERCENT) + .height(CommonConstants.FULL_PERCENT) + .backgroundColor($r('app.color.common_bg')) + .padding({ + bottom:28 + }) + } +} \ No newline at end of file diff --git a/features/web/src/main/ets/pages/OrderConfirmPage.ets b/features/web/src/main/ets/pages/OrderConfirmPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..fa532c785c701a7ed7938780475f1149cafd6df3 --- /dev/null +++ b/features/web/src/main/ets/pages/OrderConfirmPage.ets @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { router, promptAction } from '@kit.ArkUI'; +import { CommonConstants } from '../common/constants/CommonConstants'; +import ProductModel from '../model/ProductModel'; + +@Extend(Text) +function titleStyle() { + .fontSize($r('app.float.font_size_mm')) + .lineHeight($r('app.float.line_height_mm')) + .fontWeight(FontWeight.Normal) + .fontFamily('HarmonyHeiTi') +} + +@Entry +@Component +struct OrderConfirmPage { + statusBarHeight: number = (router.getParams() as Record)['statusBarHeight']; + sliderBarHeight: number = (router.getParams() as Record)['sliderBarHeight']; + productDetail: ProductModel = JSON.parse((router.getParams() as Record)['detailStr']) as ProductModel; + + build() { + Column() { + Row() { + Image($r('app.media.ic_back_on')) + .width($r('app.float.img_size')) + .margin({ left: $r('app.float.md_padding_margin'), right: $r('app.float.lg_padding_margin') }) + .onClick(() => { + router.back(); + }) + Text($r('app.string.confirm_order')) + .fontSize($r('app.float.font_size_lg')) + .fontWeight(FontWeight.Medium) + } + .width(CommonConstants.FULL_PERCENT) + .height($r('app.float.navi_height')) + + Column() { + Row() { + Image($r('app.media.ic_location')) + .width($r('app.float.location_img_size')) + .aspectRatio(1) + Column() { + Row() { + Text($r('app.string.user_name')) + .titleStyle() + Text($r('app.string.user_phone')) + .titleStyle() + .margin({ left: $r('app.float.md_padding_margin') }) + } + + Text($r('app.string.user_address')) + .titleStyle() + .fontSize($r('app.float.font_size_sm')) + .fontColor($r('app.color.desc_font')) + .margin({ top: $r('app.float.ss_padding_margin') }) + } + .alignItems(HorizontalAlign.Start) + .margin({ left: $r('app.float.md_padding_margin') }) + .layoutWeight(1) + } + .padding($r('app.float.md_padding_margin')) + .margin({ top: $r('app.float.sm_padding_margin') }) + .backgroundColor(Color.White) + .width(CommonConstants.FULL_PERCENT) + .borderRadius($r('app.float.border_radius')) + + Column({ space: CommonConstants.NORMAL_SPACE }) { + Row() { + Image($r('app.media.ic_avatar')) + .width($r('app.float.img_size')) + .aspectRatio(1) + Text($r('app.string.self_operated')) + .titleStyle() + .margin({ left: $r('app.float.sm_padding_margin') }) + } + .height($r('app.float.ext_item_height')) + .margin({ top: $r('app.float.md_padding_margin') }) + + Divider() + .vertical(false) + .width(CommonConstants.FULL_PERCENT) + .color($r('app.color.common_bg')) + + Row({ space: CommonConstants.NORMAL_SPACE }) { + Image($rawfile(this.productDetail.img)) + .width($r('app.float.product_size')) + .aspectRatio(1) + .borderRadius($r('app.float.border_radius')) + Column() { + Text(this.productDetail.name) + .titleStyle() + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(2) + Text(this.productDetail.sku) + .titleStyle() + .fontSize($r('app.float.font_size_sm')) + .lineHeight($r('app.float.line_height_sm')) + .fontColor($r('app.color.desc_font')) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(2) + } + .justifyContent(FlexAlign.SpaceBetween) + .alignItems(HorizontalAlign.Start) + .layoutWeight(1) + .height($r('app.float.product_size')) + + Column() { + Text($r('app.string.dollar', this.productDetail.price)) + .titleStyle() + .fontWeight(FontWeight.Medium) + Text('x1') + .fontSize($r('app.float.font_size_sm')) + .fontColor($r('app.color.desc_font')) + .fontWeight(FontWeight.Medium) + .fontFamily('HarmonyHeiTi-Medium') + .margin({ top: $r('app.float.ss_padding_margin') }) + } + .margin({ left: $r('app.float.lg_padding_margin') }) + .alignItems(HorizontalAlign.End) + } + .alignItems(VerticalAlign.Top) + + Row() { + Text($r('app.string.delivery')) + .titleStyle() + Text($r('app.string.standard_delivery')) + .titleStyle() + } + .height($r('app.float.ext_item_height')) + .margin({ top: $r('app.float.md_padding_margin') }) + .width(CommonConstants.FULL_PERCENT) + .justifyContent(FlexAlign.SpaceBetween) + + Divider() + .vertical(false) + .width(CommonConstants.FULL_PERCENT) + .color($r('app.color.common_bg')) + Row() { + Text($r('app.string.invoice')) + .titleStyle() + Row() { + Text($r('app.string.personal_invoice')) + .titleStyle() + Image($r('app.media.ic_more')) + .width($r('app.float.more_img_width')) + .height($r('app.float.more_img_height')) + .margin({ left: $r('app.float.md_padding_margin') }) + } + } + .height($r('app.float.ext_item_height')) + .margin({ bottom: $r('app.float.md_padding_margin') }) + .width(CommonConstants.FULL_PERCENT) + .justifyContent(FlexAlign.SpaceBetween) + } + .alignItems(HorizontalAlign.Start) + .margin({ top: $r('app.float.md_padding_margin') }) + .padding({ left: $r('app.float.md_padding_margin'), right: $r('app.float.md_padding_margin') }) + .backgroundColor(Color.White) + .width(CommonConstants.FULL_PERCENT) + .borderRadius($r('app.float.border_radius')) + } + .layoutWeight(1) + + Row() { + Row() { + Text($r('app.string.dollar', '')) + .fontColor($r('app.color.price_red')) + .fontSize($r('app.float.font_size_md')) + .fontWeight(FontWeight.Medium) + Text(this.productDetail.price.toString()) + .fontColor($r('app.color.price_red')) + .fontSize($r('app.float.font_size_llg')) + .fontWeight(FontWeight.Medium) + } + + Button($r('app.string.confirm_order')) + .width(CommonConstants.PERCENT_50) + .linearGradient({ + angle: 90, + colors: [[$r('app.color.button_start'), 0.11], [$r('app.color.button_end'), 0.89]] + }) + .onClick(() => { + promptAction.showToast({ + message: $r('app.string.toast_msg'), + duration: CommonConstants.TOAST_DURATION + }); + }) + } + .margin({ bottom: $r('app.float.confirm_button_margin_bottom') }) + .justifyContent(FlexAlign.SpaceBetween) + .width(CommonConstants.FULL_PERCENT) + .height($r('app.float.navi_height')) + } + .width(CommonConstants.FULL_PERCENT) + .height(CommonConstants.FULL_PERCENT) + .backgroundColor($r('app.color.common_bg')) + .padding({ + top: this.statusBarHeight, + left: $r('app.float.md_padding_margin'), + right: $r('app.float.md_padding_margin'), + bottom: this.sliderBarHeight + }) + } +} \ No newline at end of file diff --git a/features/web/src/main/ets/webability/WebAbility.ets b/features/web/src/main/ets/webability/WebAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..593fec780f66867b04b8500ffa55c7ae3d48f2fd --- /dev/null +++ b/features/web/src/main/ets/webability/WebAbility.ets @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { window } from '@kit.ArkUI'; +import { distributedDataObject } from '@kit.ArkData'; +import Logger from '../common/utils/Logger'; + +const TAG = '[EntryAbility]'; + +class ContinueData { + pageUrl: string | undefined; + scrollDistance: number | undefined; + + constructor(pageUrl: string | undefined, scrollDistance: number | undefined) { + this.pageUrl = pageUrl; + this.scrollDistance = scrollDistance; + } +} + +export default class WebAbility extends UIAbility { + continueRestore(want: Want) { + if (!want.parameters || !want.parameters.distributedSessionId) { + return; + } + + let continueData: ContinueData = new ContinueData(undefined, undefined); + let dataObject = distributedDataObject.create(this.context, continueData); + dataObject.on('status', (sessionId: string, networkId: string, status: string) => { + if (status === 'restored') { + AppStorage.setOrCreate('continueRestore', true); + AppStorage.setOrCreate('pageUrl', dataObject['pageUrl']); + AppStorage.setOrCreate('scrollDistance', dataObject['scrollDistance']); + } + }); + let sessionId = want.parameters.distributedSessionId as string; + dataObject.setSessionId(sessionId); + this.context.restoreWindowStage(new LocalStorage()); + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + Logger.info(TAG, 'Ability onCreate'); + if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { + if (want.parameters && want.parameters.distributedSessionId) { + this.continueRestore(want); + } + } + } + + onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { + if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { + if (want.parameters && want.parameters.distributedSessionId) { + this.continueRestore(want); + } + } + } + + onDestroy(): void { + Logger.info(TAG, 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability. + Logger.info(TAG, 'Ability onWindowStageCreate'); + windowStage.loadContent('pages/IndexPage', (err, data) => { + if (err.code) { + Logger.error(TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + Logger.info(TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources. + Logger.info(TAG, 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground. + Logger.info(TAG, 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background. + Logger.info(TAG, 'Ability onBackground'); + } + + async onContinue(wantParam: Record): Promise { + let url = AppStorage.get('pageUrl') as string; + let distance = AppStorage.get('scrollDistance') as number; + AppStorage.setOrCreate('continue', false); + let continueData: ContinueData = new ContinueData(url, distance); + let dataObject = distributedDataObject.create(this.context, continueData); + let sessionId = distributedDataObject.genSessionId(); + dataObject.setSessionId(sessionId); + wantParam.distributedSessionId = sessionId; + let deviceId = wantParam.targetDevice as string; + dataObject.save(deviceId); + return AbilityConstant.OnContinueResult.AGREE; + } +} diff --git a/features/web/src/main/module.json5 b/features/web/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..163e667a19ecaab93ca0006d81f0736cded8d7ca --- /dev/null +++ b/features/web/src/main/module.json5 @@ -0,0 +1,27 @@ +{ + "module": { + "name": "web", + "type": "feature", + "description": "$string:module_desc", + "mainElement": "WebAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "WebAbility", + "srcEntry": "./ets/webability/WebAbility.ets", + "description": "$string:WebAbility_desc", + "icon": "$media:layered_image", + "label": "$string:WebAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "continuable": true + } + ] + } +} \ No newline at end of file diff --git a/features/web/src/main/resources/base/element/color.json b/features/web/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..60db317867ac3807694220886e6a1f3b74a7b521 --- /dev/null +++ b/features/web/src/main/resources/base/element/color.json @@ -0,0 +1,32 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "common_bg", + "value": "#F1F3F5" + }, + { + "name": "price_red", + "value": "#E92F4F" + }, + { + "name": "item_title_font", + "value": "#E6000000" + }, + { + "name": "desc_font", + "value": "#99000000" + }, + { + "name": "button_start", + "value": "#F64D43" + }, + { + "name": "button_end", + "value": "#EB344D" + } + ] +} \ No newline at end of file diff --git a/features/web/src/main/resources/base/element/float.json b/features/web/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..32d8fb17bfe41802d43ffd830e6ab55ff0d284aa --- /dev/null +++ b/features/web/src/main/resources/base/element/float.json @@ -0,0 +1,92 @@ +{ + "float": [ + { + "name": "font_size_sm", + "value": "12fp" + }, + { + "name": "line_height_sm", + "value": "16vp" + }, + { + "name": "font_size_mm", + "value": "14fp" + }, + { + "name": "line_height_mm", + "value": "19vp" + }, + { + "name": "font_size_md", + "value": "18fp" + }, + { + "name": "font_size_lg", + "value": "20fp" + }, + { + "name": "font_size_llg", + "value": "24fp" + }, + { + "name": "more_img_width", + "value": "6vp" + }, + { + "name": "more_img_height", + "value": "12vp" + }, + { + "name": "location_img_size", + "value": "18vp" + }, + { + "name": "ext_item_height", + "value": "20vp" + }, + { + "name": "img_size", + "value": "24vp" + }, + { + "name": "btn_size", + "value": "30vp" + }, + { + "name": "navi_height", + "value": "56vp" + }, + { + "name": "location_info_height", + "value": "64vp" + }, + { + "name": "product_size", + "value": "80vp" + }, + { + "name": "ss_padding_margin", + "value": "4vp" + }, + { + "name": "sm_padding_margin", + "value": "8vp" + }, + { + "name": "md_padding_margin", + "value": "12vp" + }, + { + "name": "lg_padding_margin", + "value": "16vp" + }, + { + "name": "border_radius", + "value": "16vp" + }, + { + "name": "confirm_button_margin_bottom", + "value": "10vp" + } + ] +} \ No newline at end of file diff --git a/features/web/src/main/resources/base/element/string.json b/features/web/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6e26287597a7708016ef9388bb3411253199709f --- /dev/null +++ b/features/web/src/main/resources/base/element/string.json @@ -0,0 +1,64 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "WebAbility_desc", + "value": "description" + }, + { + "name": "WebAbility_label", + "value": "Page Redirection" + }, + { + "name": "toast_msg", + "value": "This function is not developed yet. Please look forward to it." + }, + { + "name": "confirm_order", + "value": "Confirm the order" + }, + { + "name": "dollar", + "value": "¥%d" + }, + { + "name": "invoice", + "value": "Invoice" + }, + { + "name": "personal_invoice", + "value": "Electronic Free Text Invoice - Personal" + }, + { + "name": "delivery", + "value": "Delivery" + }, + { + "name": "standard_delivery", + "value": "Standard Delivery" + }, + { + "name": "self_operated", + "value": "Mall self-operated" + }, + { + "name": "user_name", + "value": "Zhang XX" + }, + { + "name": "user_phone", + "value": "185 xxxx xxxx" + }, + { + "name": "user_address", + "value": "Room XX, Unit XX, XX Building, XXX Community, Haidian District, Beijing" + }, + { + "name": "tittle", + "value": "Web Continue" + } + ] +} \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/back.png b/features/web/src/main/resources/base/media/back.png new file mode 100644 index 0000000000000000000000000000000000000000..21f734f35ead6027de252373c3b8c5880b1b6269 Binary files /dev/null and b/features/web/src/main/resources/base/media/back.png differ diff --git a/features/web/src/main/resources/base/media/background.png b/features/web/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/features/web/src/main/resources/base/media/background.png differ diff --git a/features/web/src/main/resources/base/media/foreground.png b/features/web/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/features/web/src/main/resources/base/media/foreground.png differ diff --git a/features/web/src/main/resources/base/media/ic_avatar.svg b/features/web/src/main/resources/base/media/ic_avatar.svg new file mode 100644 index 0000000000000000000000000000000000000000..74eea36644ceb52eeb916e31070f7831c786c76d --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_avatar.svg @@ -0,0 +1,9 @@ + + + ic_avatar + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/ic_back_off.svg b/features/web/src/main/resources/base/media/ic_back_off.svg new file mode 100644 index 0000000000000000000000000000000000000000..97d6f1aef3454e4dea3b478b6538e5bd87263ae8 --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_back_off.svg @@ -0,0 +1,13 @@ + + + ic_back_off + + + + + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/ic_back_on.svg b/features/web/src/main/resources/base/media/ic_back_on.svg new file mode 100644 index 0000000000000000000000000000000000000000..f0e4c462424e636d34ebb3f4128cc4813e91ec29 --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_back_on.svg @@ -0,0 +1,13 @@ + + + ic_back_on + + + + + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/ic_home.svg b/features/web/src/main/resources/base/media/ic_home.svg new file mode 100644 index 0000000000000000000000000000000000000000..3f292af0ebbc610399d773320449bd6b0140ff95 --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_home.svg @@ -0,0 +1,20 @@ + + + 画板备份 2 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/ic_location.svg b/features/web/src/main/resources/base/media/ic_location.svg new file mode 100644 index 0000000000000000000000000000000000000000..6e988c1f0eae082768ed49597a890811475c41f1 --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_location.svg @@ -0,0 +1,7 @@ + + + ic_location + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/ic_more.svg b/features/web/src/main/resources/base/media/ic_more.svg new file mode 100644 index 0000000000000000000000000000000000000000..3eddb9d5da3695df1f4ebb9bf2682b30f2f5ff6e --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_more.svg @@ -0,0 +1,9 @@ + + + ic_more + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/ic_next_off.svg b/features/web/src/main/resources/base/media/ic_next_off.svg new file mode 100644 index 0000000000000000000000000000000000000000..b131c5543325ef12caf61bf3f9bead48465cc0f8 --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_next_off.svg @@ -0,0 +1,13 @@ + + + ic_next_off + + + + + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/ic_next_on.svg b/features/web/src/main/resources/base/media/ic_next_on.svg new file mode 100644 index 0000000000000000000000000000000000000000..b3a4c2453c4e55ea75343ecac338b003c362e35b --- /dev/null +++ b/features/web/src/main/resources/base/media/ic_next_on.svg @@ -0,0 +1,13 @@ + + + ic_next_on + + + + + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/layered_image.json b/features/web/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/features/web/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/features/web/src/main/resources/base/media/startIcon.png b/features/web/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/features/web/src/main/resources/base/media/startIcon.png differ diff --git a/features/web/src/main/resources/base/profile/main_pages.json b/features/web/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..41f761d4a1c9943174ef77e665d57dca5f148164 --- /dev/null +++ b/features/web/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,6 @@ +{ + "src": [ + "pages/IndexPage", + "pages/OrderConfirmPage" + ] +} diff --git a/features/web/src/main/resources/en_US/element/string.json b/features/web/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6e26287597a7708016ef9388bb3411253199709f --- /dev/null +++ b/features/web/src/main/resources/en_US/element/string.json @@ -0,0 +1,64 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "WebAbility_desc", + "value": "description" + }, + { + "name": "WebAbility_label", + "value": "Page Redirection" + }, + { + "name": "toast_msg", + "value": "This function is not developed yet. Please look forward to it." + }, + { + "name": "confirm_order", + "value": "Confirm the order" + }, + { + "name": "dollar", + "value": "¥%d" + }, + { + "name": "invoice", + "value": "Invoice" + }, + { + "name": "personal_invoice", + "value": "Electronic Free Text Invoice - Personal" + }, + { + "name": "delivery", + "value": "Delivery" + }, + { + "name": "standard_delivery", + "value": "Standard Delivery" + }, + { + "name": "self_operated", + "value": "Mall self-operated" + }, + { + "name": "user_name", + "value": "Zhang XX" + }, + { + "name": "user_phone", + "value": "185 xxxx xxxx" + }, + { + "name": "user_address", + "value": "Room XX, Unit XX, XX Building, XXX Community, Haidian District, Beijing" + }, + { + "name": "tittle", + "value": "Web Continue" + } + ] +} \ No newline at end of file diff --git a/features/web/src/main/resources/rawfile/imgs/ic_1.png b/features/web/src/main/resources/rawfile/imgs/ic_1.png new file mode 100644 index 0000000000000000000000000000000000000000..466fbb0f0531b372d844f99f47e21196039c8e7f Binary files /dev/null and b/features/web/src/main/resources/rawfile/imgs/ic_1.png differ diff --git a/features/web/src/main/resources/rawfile/imgs/ic_2.png b/features/web/src/main/resources/rawfile/imgs/ic_2.png new file mode 100644 index 0000000000000000000000000000000000000000..334a2d5bbc94ab5a0e34843ae2e1ac4d6a5c8652 Binary files /dev/null and b/features/web/src/main/resources/rawfile/imgs/ic_2.png differ diff --git a/features/web/src/main/resources/rawfile/imgs/ic_3.png b/features/web/src/main/resources/rawfile/imgs/ic_3.png new file mode 100644 index 0000000000000000000000000000000000000000..71cb48b4334d3c9c7feed44d04d756faf75a7368 Binary files /dev/null and b/features/web/src/main/resources/rawfile/imgs/ic_3.png differ diff --git a/features/web/src/main/resources/rawfile/imgs/ic_4.png b/features/web/src/main/resources/rawfile/imgs/ic_4.png new file mode 100644 index 0000000000000000000000000000000000000000..96193e49d583410bbdd2f7cb9a185089dc19bec0 Binary files /dev/null and b/features/web/src/main/resources/rawfile/imgs/ic_4.png differ diff --git a/features/web/src/main/resources/rawfile/imgs/ic_5.png b/features/web/src/main/resources/rawfile/imgs/ic_5.png new file mode 100644 index 0000000000000000000000000000000000000000..9dd34ce4c53280e0f0092ea5a77a180ca11255df Binary files /dev/null and b/features/web/src/main/resources/rawfile/imgs/ic_5.png differ diff --git a/features/web/src/main/resources/rawfile/imgs/ic_6.png b/features/web/src/main/resources/rawfile/imgs/ic_6.png new file mode 100644 index 0000000000000000000000000000000000000000..6a5278d71861fc1f74daa7622ef501731173c9fd Binary files /dev/null and b/features/web/src/main/resources/rawfile/imgs/ic_6.png differ diff --git a/features/web/src/main/resources/rawfile/imgs/ic_service.svg b/features/web/src/main/resources/rawfile/imgs/ic_service.svg new file mode 100644 index 0000000000000000000000000000000000000000..89c6228feb23dbb501e64f1e0471f859658e9095 --- /dev/null +++ b/features/web/src/main/resources/rawfile/imgs/ic_service.svg @@ -0,0 +1,13 @@ + + + ic_service + + + + + + + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/rawfile/imgs/ic_shopping.svg b/features/web/src/main/resources/rawfile/imgs/ic_shopping.svg new file mode 100644 index 0000000000000000000000000000000000000000..354899b22174db024fca0bf70e2ce357af60abaf --- /dev/null +++ b/features/web/src/main/resources/rawfile/imgs/ic_shopping.svg @@ -0,0 +1,7 @@ + + + ic_Shopping + + + + \ No newline at end of file diff --git a/features/web/src/main/resources/rawfile/js/product_detail.js b/features/web/src/main/resources/rawfile/js/product_detail.js new file mode 100644 index 0000000000000000000000000000000000000000..02ffbc36d8dc53b921b386ccda7a12c99e1fa74a --- /dev/null +++ b/features/web/src/main/resources/rawfile/js/product_detail.js @@ -0,0 +1,100 @@ +let productList = [ + { + 'id': 1, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_1.png', + 'price': 4488 + }, + { + 'id': 2, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_2.png', + 'price': 4488 + }, + { + 'id': 3, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_3.png', + 'price': 4488 + }, + { + 'id': 4, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_4.png', + 'price': 4488 + }, + { + 'id': 5, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_5.png', + 'price': 4488 + }, + { + 'id': 6, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_6.png', + 'price': 4488 + }, + { + 'id': 7, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_1.png', + 'price': 4488 + }, + { + 'id': 8, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_2.png', + 'price': 4488 + }, + { + 'id': 9, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_3.png', + 'price': 4488 + }, + { + 'id': 10, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_4.png', + 'price': 4488 + }, + { + 'id': 11, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_5.png', + 'price': 4488 + } +]; + +let urlSearchParams = new URLSearchParams(window.location.search); +let productIndex = urlSearchParams.get('index'); +let productDetail = productList[productIndex]; +productDetail.sku = '冰晶蓝,8GB+128GB,4G网网通,官电视剧还是快点好4G网网通'; +let swiperStr = ''; +for (let i = 0; i < 3; i++) { + swiperStr += '' +} +document.getElementById('swiper').innerHTML = swiperStr; +document.getElementById('price').innerHTML = '¥ ' + productDetail.price; +document.getElementById('product-name').innerHTML = productDetail.name; + +let currentIndex = 0; +let images = document.querySelectorAll('#swiper img'); + +function showNext() { + var currentImg = document.querySelector('.showing'); + if (currentImg) { + currentImg.classList.remove('showing'); + + currentIndex += 1; + if (currentIndex >= images.length) { + currentIndex = 0; + } + images[currentIndex].classList.add('showing'); + } else { + images[0].classList.add('showing'); + } +} + +setInterval(showNext, 2000); diff --git a/features/web/src/main/resources/rawfile/js/product_list.js b/features/web/src/main/resources/rawfile/js/product_list.js new file mode 100644 index 0000000000000000000000000000000000000000..e2a96f4cb9b3a81dbbd586f29c6e6ea1e57f478a --- /dev/null +++ b/features/web/src/main/resources/rawfile/js/product_list.js @@ -0,0 +1,90 @@ +let productList = [ + { + 'id': 1, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_1.png', + 'price': 4488 + }, + { + 'id': 2, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_2.png', + 'price': 4488 + }, + { + 'id': 3, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_3.png', + 'price': 4488 + }, + { + 'id': 4, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_4.png', + 'price': 4488 + }, + { + 'id': 5, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_5.png', + 'price': 4488 + }, + { + 'id': 6, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_6.png', + 'price': 4488 + }, + { + 'id': 7, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_1.png', + 'price': 4488 + }, + { + 'id': 8, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_2.png', + 'price': 4488 + }, + { + 'id': 9, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_3.png', + 'price': 4488 + }, + { + 'id': 10, + 'name': 'XX设备 新品优惠!新品优惠!新品优惠!', + 'img': 'imgs/ic_4.png', + 'price': 4488 + }, + { + 'id': 11, + 'name': 'HW nove9 新品手机【官方标配】8+128G全网通', + 'img': 'imgs/ic_5.png', + 'price': 4488 + } +]; + +function addDocument() { + let list = ''; + for (let i = 0; i < productList.length; i++) { + list += `
  • `; + list += ``; + list += '
    '; + list += `

    ${productList[i].name}

    `; + list += `¥ ${productList[i].price}`; + list += '
    '; + list += '
  • '; + + let tmp = document.getElementById('productList'); + tmp.innerHTML = list; // 添加到div里 + } +} + +addDocument(); + +function jumpDetail(index) { + window.location.href = 'product_detail.html?index=' + index; +} diff --git a/features/web/src/main/resources/rawfile/product_detail.html b/features/web/src/main/resources/rawfile/product_detail.html new file mode 100644 index 0000000000000000000000000000000000000000..a9cc358639b2d26eb110c3e057ef7aea3126a784 --- /dev/null +++ b/features/web/src/main/resources/rawfile/product_detail.html @@ -0,0 +1,45 @@ + + + + + + + The Canavas Danmu Wall + + +
    +
    + +
    +
    +
    +
    +
    +
    HW nove9 新品手机【官方标配】8+128G全网通
    +
    商品详情:
    +
    商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情

    + 商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情商品详情

    +
    +
    +
    + +
    + + + diff --git a/features/web/src/main/resources/rawfile/product_list.html b/features/web/src/main/resources/rawfile/product_list.html new file mode 100644 index 0000000000000000000000000000000000000000..3c17a93267b6e9ac5189bae04c7cad93daca42d8 --- /dev/null +++ b/features/web/src/main/resources/rawfile/product_list.html @@ -0,0 +1,16 @@ + + + + + + + The Canavas Danmu Wall + + +
    +
      +
    +
    + + + diff --git a/features/web/src/main/resources/rawfile/style/product_detail.css b/features/web/src/main/resources/rawfile/style/product_detail.css new file mode 100644 index 0000000000000000000000000000000000000000..a653c3d902757c2f0cd0a280a0df44b83f9ce381 --- /dev/null +++ b/features/web/src/main/resources/rawfile/style/product_detail.css @@ -0,0 +1,158 @@ +body { + margin: 0; +} + +#detail { + width: 100%; + height: 100vh; + background-color: #F1F3F5; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +#detail-info { + flex: 1; + overflow: scroll; +} + +#slider { + width: 100vw; + height:100vw; + background-color: #000000; + overflow: hidden; +} +#swiper { + width: 100vw; + height:100vw; + background-color: #000000; +} + +#swiper > img { + height: 100%; + aspect-ratio: 1.42; + margin: 0px 0 24px 0; + object-fit: cover; + display: none; +} + +.showing { + display: block !important; +} +#dot { + position: absolute; + right: 18px; + top: 68vw; + z-index: 2; + width: 36px; + height: 18px; + border-radius: 9px; + background-color: rgba(0,0,0,0.40); + color: white; + text-align: center; + font-size: 12px; + line-height: 18px; + font-weight: 400; +} + +#info { + margin: 12px; + background-color: #FFFFFF; + border-radius: 16px; + padding: 12px; + color: #182431; +} + +#price { + font-size: 20px; + color: #E92F4F; + line-height: 28px; + font-weight: 500; + margin-top: 4px; +} + +#price > span { + font-size: 14px; +} + +#product-name { + font-size: 16px; + line-height: 21px; + font-weight: 400; + margin-top: 12px; +} + +#guide { + font-size: 14px; + line-height: 20px; + font-weight: 400; + margin-top: 26px; +} + +#product-info { + font-size: 14px; + opacity: 0.6; + text-align: left; + line-height: 20px; + font-weight: 400; + margin-top: 8px; +} + +br { + display: inline; + line-height: 8px; +} + +#menu { + height: 56px; + padding: 0 12px; + align-items: center; + display: flex; + flex-direction: row; +} + +#menu > div { + display: flex; +} + +#menu > div:first-of-type { + flex: 1; + justify-content: space-around; +} + +.btn { + display: flex; + flex-direction: column; +} + +.btn > img { + width: 24px; + height: 24px; +} + +.btn > text { + margin-top: 4px; + font-size: 10px; + text-align: center; + color: #182431; +} + +button { + color: #FFFFFF; + border: none; + height: 40px; + width: 96px +} + +button:first-of-type { + margin-left: 8px; + border-bottom-left-radius: 20px; + border-top-left-radius: 20px; + background: linear-gradient(270deg, #FF914C 52%, #FEA748 96%); +} + +button:last-of-type { + border-bottom-right-radius: 20px; + border-top-right-radius: 20px; + background: linear-gradient(90deg, #F64D43 11%, #EB344D 89%); +} \ No newline at end of file diff --git a/features/web/src/main/resources/rawfile/style/product_list.css b/features/web/src/main/resources/rawfile/style/product_list.css new file mode 100644 index 0000000000000000000000000000000000000000..400a4965bd579b0ea515e7f572e7f4666634e68e --- /dev/null +++ b/features/web/src/main/resources/rawfile/style/product_list.css @@ -0,0 +1,56 @@ +body { + margin: 0; +} + +#container { + width: 100vw; + height: 100vh; + display: flex; + flex-direction: column; +} +#nav { + height: 56px; + font-size: 24px; + color: #182431; + text-align: left; + line-height: 56px; + font-weight: 700; + padding: 0 24px; +} + +ul { + overflow: scroll; + list-style-type: none; + padding: 0; + margin: 0; +} +li { + display: flex; + padding: 8px 24px; +} +li > img { + width: 106px; + aspect-ratio: 1; + border-radius: 18px; +} +li > div { + margin-left: 12px; + flex: 1; +} +li > div > p { + font-size: 14px; + line-height: 19px; + color: #182431; + font-weight: 400; + text-align: justify; +} + +li > div > span { + font-size: 21px; + color: #E92F4F; + line-height: 28px; + font-weight: 500; +} +li > div > span > span { + font-size: 14px; +} \ No newline at end of file diff --git a/features/web/src/main/resources/zh_CN/element/string.json b/features/web/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..ef3cc17e2e6c1ac7f592756bc28a5d939a030532 --- /dev/null +++ b/features/web/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,64 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "WebAbility_desc", + "value": "description" + }, + { + "name": "WebAbility_label", + "value": "H5页面跳转" + }, + { + "name": "toast_msg", + "value": "该功能暂未开发,敬请期待" + }, + { + "name": "confirm_order", + "value": "确认订单" + }, + { + "name": "dollar", + "value": "¥%d" + }, + { + "name": "invoice", + "value": "发票" + }, + { + "name": "personal_invoice", + "value": "电子普通发票-个人" + }, + { + "name": "delivery", + "value": "配送" + }, + { + "name": "standard_delivery", + "value": "标准配送" + }, + { + "name": "self_operated", + "value": "商城自营" + }, + { + "name": "user_name", + "value": "张XX" + }, + { + "name": "user_phone", + "value": "185 xxxx xxxx" + }, + { + "name": "user_address", + "value": "北京市 海淀区 XXX小区XX楼XX单元XX室" + }, + { + "name": "tittle", + "value": "web页面浏览进度接续" + } + ] +} \ No newline at end of file diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1e473e424320d4e68b16737b289f5c851bb19d36 --- /dev/null +++ b/hvigor/hvigor-config.json5 @@ -0,0 +1,22 @@ +{ + "modelVersion": "5.0.1", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3cb9f1a87a81687554a76283af8df27d8bda775 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/oh-package-lock.json5 b/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..205c6cfd4d8cc03127a8a61d9c533f230e8ad80b --- /dev/null +++ b/oh-package-lock.json5 @@ -0,0 +1,27 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0", + "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" + }, + "packages": { + "@ohos/hamock@1.0.0": { + "name": "@ohos/hamock", + "version": "1.0.0", + "integrity": "sha512-K6lDPYc6VkKe6ZBNQa9aoG+ZZMiwqfcR/7yAVFSUGIuOAhPvCJAo9+t1fZnpe0dBRBPxj2bxPPbKh69VuyAtDg==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hamock/-/hamock-1.0.0.har", + "registryType": "ohpm" + }, + "@ohos/hypium@1.0.19": { + "name": "@ohos/hypium", + "version": "1.0.19", + "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..bb12751b97530b9a3504e56e8ed09d8c9ba1fa52 --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} diff --git a/screenshots/index.png b/screenshots/index.png new file mode 100644 index 0000000000000000000000000000000000000000..03fb8ace3279910f8f1a8f53f0bd16d7add5ec29 Binary files /dev/null and b/screenshots/index.png differ diff --git a/screenshots/longlist.png b/screenshots/longlist.png new file mode 100644 index 0000000000000000000000000000000000000000..1d18201def824a029ddf0e2d605c0632f992ad63 Binary files /dev/null and b/screenshots/longlist.png differ diff --git a/screenshots/video.png b/screenshots/video.png new file mode 100644 index 0000000000000000000000000000000000000000..07057d43260855bb082643413c74ab989bb18b99 Binary files /dev/null and b/screenshots/video.png differ diff --git a/screenshots/web.png b/screenshots/web.png new file mode 100644 index 0000000000000000000000000000000000000000..8bfe4f0fc35d4f577de6981f3b4b9e4a93f1ae3b Binary files /dev/null and b/screenshots/web.png differ