diff --git a/OAT.xml b/OAT.xml index f6c0e68811854c22a172ab359a3736ed121febea..fb0abc67f4e5ad64472a38046e530aa86c618989 100644 --- a/OAT.xml +++ b/OAT.xml @@ -147,6 +147,10 @@ Note:If the text contains special characters, please escape them according to th + + + + diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/.gitignore b/code/BasicFeature/Connectivity/MultipleFilesDownload/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/app.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0c62f4255e9b6a55af2e7153ef781af502a7b6b8 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/app.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "app": { + "bundleName": "multiple.files.myapplication", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/resources/base/element/string.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f5b165b8435a9f736e04891cf68477b44f7d5eec --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "MultipleFilesDownload" + } + ] +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/resources/base/media/app_icon.png b/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/AppScope/resources/base/media/app_icon.png differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/README.md b/code/BasicFeature/Connectivity/MultipleFilesDownload/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9f861b390b63a0129733e14b731edceccc1190fc --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/README.md @@ -0,0 +1,199 @@ + + +# 多文件下载监听案例 + +### 介绍 + +多文件下载监听在应用开发中是一个非常常见的需求。本示例将介绍如何使用request上传下载模块实现多文件下载监听,如监听每个文件下载任务的进度,任务暂停,下载完成等下载情况。下载历史页面展示的是下载完成的文件,可对页面中已删除的文件进行重新下载 。每个应用至多支持创建10个未完成的任务,相关规格说明请参考[request.agent.create](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-basic-services-kit/js-apis-request.md#requestagentcreate10)。 + +### 效果图预览 + +![](./casesfeature/multiplefilesdownload/multiple_files_download.gif) + +**使用说明** + +1. 连接网络。进入页面,点击“全部开始”,启动所有文件的下载任务。点击“全部暂停”,暂停所有文件下载任务。再次点击“全部开始”,可重新启动未完成的下载任务。如出现下载失败,一般是网络不稳定,点击“全部开始”即可重新下载。 +2. 下载完成的文件会保存在应用缓存路径下,出现在下载历史页面中。 +3. 可点击删除按钮,在应用缓存路径下删除该文件存储的数据。 +4. 点击文件,若文件中在应用缓存路径下存在,弹窗提示用户“文件已存在”;否则弹窗提示用户“文件不存在,是否重新下载?”,点击确认按钮,该条文件数据会出现在下载列表中,可点击“全部开始“按钮重新下载。 + +### 实现思路 + +1. 配置下载参数。一个下载任务需要配置对应一套下载参数request.agent.Config。本例中使用downloadConfig方法简单配置了下载文件的url,实际业务中请按实际情况按需配置。源码参考[MultipleFilesDownload.ets](./casesfeature/multiplefilesdownload/src/main/ets/view/MultipleFilesDownload.ets)。 + + ```typescript + let config: request.agent.Config = { + action: request.agent.Action.DOWNLOAD, // 配置任务选项,这里配置为下载任务 + url: downloadUrl, // 配置下载任务url + overwrite: true, // 下载过程中路径已存在时的解决方案选择。true表示覆盖已存在的文件 + method: 'GET', // HTTP标准方法。下载时,使用GET或POST。 + saveas: './', // 这里'./'表示下载至应用当前缓存路径下。 + mode: request.agent.Mode.BACKGROUND, // 任务模式设置后台任务。 + gauge: true // 后台任务的过程进度通知策略,仅应用于后台任务。true表示发出每个进度已完成或失败的通知。 + }; + ``` + +2. 创建多个文件下载监听实例。单个文件下载监听只需要配置下载参数,创建下载任务,注册下载任务相关监听,启动下载任务即可实现。而要实现多文件下载监听,需要每个下载任务注册独立的下载监听回调。本例通过封装自定义组件FileDownloadItem,在每个FileDownloadItem中创建各自的下载任务和监听回调,从而实现多文件下载监听。源码参考[MultipleFilesDownload.ets](./casesfeature/multiplefilesdownload/src/main/ets/view/MultipleFilesDownload.ets)。 + + ```typescript + ForEach(this.downloadConfigArray, (item: request.agent.Config) => { + ListItem() { + // 创建文件下载监听实例 + FileDownloadItem({ + downloadConfig: item, // 文件下载配置 + isStartAllDownload: this.isStartAllDownload, // 是否全部开始下载 + downloadCount: this.downloadCount // 待下载任务数量 + downloadFailCount: this.downloadFailCount // 下载失败任务数量 + }) + } + }, (item: request.agent.Config) => JSON.stringify(item)) + ``` + +3. 创建下载任务,并注册下载任务相关监听。本例在每个FileDownloadItem中使用request.agent.create创建下载任务。然后在下载任务创建成功后,注册各自下载任务相关监听。本例中注册了下载任务完成回调,下载任务失败回调,下载进度更新回调,暂停任务回调,重新启动任务回调。源码参考[FileDownloadItem.ets](./casesfeature/multiplefilesdownload/src/main/ets/view/FileDownloadItem.ets)。 + + ```typescript + request.agent.create(context, this.downloadConfig).then((task: request.agent.Task) => { + // 注册下载任务相关回调 + task.on('completed', this.completedCallback); // 下载任务完成回调 + task.on('failed', this.failedCallback); // 下载任务失败回调 + task.on('pause', this.pauseCallback); // 暂停任务回调 + task.on('resume', this.resumeCallback); // 重新启动任务回调 + task.on('progress', this.progressCallback); // 下载进度更新回调 + }).catch((err: BusinessError) => { + logger.error(TAG, `Failed to task create with error message: ${err.message}, error code: ${err.code}`); + }); + ``` + +4. 启动下载任务。本例在每个FileDownloadItem中使用task.start方法启动各自的下载任务。源码参考[FileDownloadItem.ets](./casesfeature/multiplefilesdownload/src/main/ets/view/FileDownloadItem.ets)。 + + ```typescript + task.start((err: BusinessError) => { + if (err) { + logger.error(TAG, `Failed to task start with error message: ${err.message}, error code: ${err.code}`); + return; + } + this.downloadTask = task; + }) + ``` + +5. 删除文件。使用fs.unlink删除该条数据应用文件路径 + + ```typescript + // TODO:知识点:删除该条数据应用文件路径 + fs.unlink(filePath).then(() => { + // 文件状态变为已删除 + this.FileInfo.fileStatus = 3; + promptAction.showToast({ + message: $r('app.string.multiple_files_download_history_toast_text_del'), + duration: 2000, + bottom: '50%', + }) + }).catch((error: BusinessError) => { + logger.error("remove file failed with error:" + error); + }); + ``` + +6. 重新下载已删除的文件。使用fs.access判断文件是否已经存在,若不存在弹窗提示用户是否重新下载。 + + ```typescript + // 获取应用文件路径 + const cacheDir = context.cacheDir; + const FilePath: string = `${cacheDir}/${this.fileName}`; + + // TODO:知识点:使用fs.access判断目录是否已经存在。 + fs.access(FilePath).then((res: boolean) => { + if (!res) { + // 文件已删除,弹窗提示是否重新下载 + AlertDialog.show({ + message: `${this.fileName}文件不存在,是否重新下载?`, + alignment: DialogAlignment.Center, // 弹窗在竖直方向上的对齐方式。 + autoCancel: false, // 点击遮罩层时,是否关闭弹窗。默认值:true + primaryButton: { + value: $r('app.string.multiple_files_download_history_button_text_cancel'), + fontColor: $r('app.color.multiple_files_download_text_color'), + action: () => { + logger.info('已取消'); + } + }, + secondaryButton: { + value: $r('app.string.multiple_files_download_history_button_text_confirm'), + fontColor: $r('app.color.multiple_files_download_del_bgc'), + action: () => { + // 更改该条数据下载状态 + this.FileInfo.fileStatus = 0; + // 该条数据加入下载列表数组中 + this.downloadFileArray.push(this.FileInfo); + // 下载历史列表删除该条数据 + this.historyArray = this.historyArray.filter( ( item: downloadFilesData ) => { + return item.id !== this.FileInfo.id; + }); + // 更新待下载任务数量 + this.downloadCount = this.downloadFileArray.length; + } + }, + cornerRadius: $r('app.integer.multiple_files_download_borderRadius_twelve'), // 弹窗边框弧度 + width: $r('app.string.multiple_files_download_list_height'), // 弹窗宽度 + cancel: () => { + logger.info('点击关闭按钮'); + } + }); + } else { + // 文件已下载到本地,弹窗提示文件已存在 + promptAction.showToast({ + message: $r('app.string.multiple_files_download_history_toast_text_exist'), + duration: 1000, + bottom: '50%', + }) + } + ``` + +### 高性能知识点 + +本示例中注册了progress下载进度更新监听,不建议在下载进度更新回调中加日志打印,以减少不必要的性能损耗。 + +### FAQ + +1. 无网络时,点击”全部开始“,下载队列中的任务状态却显示”已暂停“。 + + 答:request.agent.Config有一个参数retry默认是true,如果没有网络或者网络不满足时,会自动暂停waiting,所以在没网络的时候下载底层逻辑会走暂停回调,应用侧在暂停回调里设置了显示当前下载状态,所以无网络时,点击”全部开始“,下载队列中的任务状态显示”已暂停“。目前本例中已经把retry设置为false,没网络时,开启下载任务会直接走入失败回调,直接显示”下载失败“。 + +### 工程结构&模块类型 + + ``` + multiplefilesdownload // har类型 + |---view + | |---MultipleFilesDownload.ets // 视图层-文件下载页面 + | |---FileDownloadItem.ets // 视图层-单个文件下载组件 + | |---HistoryItem.ets // 视图层-单个文件历史组件 + ``` + +### 参考资料 + +[上传下载](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-basic-services-kit/js-apis-request.md) + +### 相关权限 + +不涉及。 + +### 依赖 + +不涉及。 +### 约束与限制 + +1.本示例仅支持标准系统上运行。 + +2.本示例为Stage模型,支持API12版本SDK,SDK版本号(API Version 12 Release)。 + +3.本示例需要使用DevEco Studio版本号(DevEco Studio 5.0.0 Release)及以上版本才可编译运行。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +```shell +git init +git config core.sparsecheckout true +echo code/UI/VideoListAutoPlay/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull origin master +``` \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/build-profile.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9ede792b42a3c1edacd109b5960fd7c826fd9d67 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/build-profile.json5 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "app": { + "signingConfigs": [], + "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": "multiplefilesdownload", + "srcPath": "./casesfeature/multiplefilesdownload" + } + ] +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/.gitignore b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/BuildProfile.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/BuildProfile.ets new file mode 100644 index 0000000000000000000000000000000000000000..941e827b6c5dcdfc74d13c1579e1ee4abc05dde6 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/BuildProfile.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 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. + */ + +/** + * Use these variables when you tailor your ArkTS code. They must be of the const type. + */ +export const HAR_VERSION = '1.0.0'; +export const BUILD_MODE_NAME = 'debug'; +export const DEBUG = true; +export const TARGET_NAME = 'default'; + +/** + * BuildProfile Class is used only for compatibility purposes. + */ +export default class BuildProfile { + static readonly HAR_VERSION = HAR_VERSION; + static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; + static readonly DEBUG = DEBUG; + static readonly TARGET_NAME = TARGET_NAME; +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/Index.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..d6d16d5c245d0f726a21f063c9ec4aed7b7b411a --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/Index.ets @@ -0,0 +1,15 @@ +/* + * 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 { MultipleFilesDownloadComponent } from './src/main/ets/view/MultipleFilesDownload'; diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/build-profile.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..327ec25811b642efb47540bc55764cdefa9c49ae --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + } + ] +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/consumer-rules.txt b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/consumer-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/hvigorfile.ts b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..44decacbebc37ddc7aa62d9549a2907d3e344d45 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/hvigorfile.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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 { harTasks } from '@ohos/hvigor-ohos-plugin'; +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins: [] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/multiple_files_download.gif b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/multiple_files_download.gif new file mode 100644 index 0000000000000000000000000000000000000000..993227bd9df995261cecef2a9394357363e8d1be Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/multiple_files_download.gif differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/obfuscation-rules.txt b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..985b2aeb7658286b17bd26eab8f217c3fe75ea8b --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# 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://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# 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 \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/oh-package.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b6a8c714facd51937a0a2f470bb24ecea17e4cd3 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/oh-package.json5 @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "name" : "multiplefilesdownload", + "version" : "1.0.0", + "description" : "Please describe the basic information.", + "main" : "Index.ets", + "author" : "", + "license" : "Apache-2.0", + "dependencies" : { } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/model/dataType.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/model/dataType.ets new file mode 100644 index 0000000000000000000000000000000000000000..5eeaaeb3bed8457aab2d9855af7615f2e182c67d --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/model/dataType.ets @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 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. + */ + +let nextId: number = 1; + +@Observed +export class downloadFilesData { + // 唯一标识 + id: number; + // 下载地址 + url: string; + // 文件下载状态: 0未下载, 1下载成功, 2下载失败, 3已删除 + fileStatus: number; + // 下载完成的时间戳 + downloadTime: number; + + constructor( + url: string = '', + fileStatus: number = 0, + downloadTime: number = 0, + ) { + this.id = nextId++; + this.url = url; + this.fileStatus = fileStatus; + this.downloadTime = downloadTime; + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/utils/Logger.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..0fea1d5abc6561dfceba2571025f6ccb509ef66d --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/utils/Logger.ets @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 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 '@ohos.hilog'; + +/** + * 日志打印类 + */ +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + this.format.toUpperCase(); + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export let logger = new Logger('[CommonAppDevelopment]') diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/utils/formatTime.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/utils/formatTime.ets new file mode 100644 index 0000000000000000000000000000000000000000..cb993fbd98916c1c790a88d6b1940ca2170acde2 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/utils/formatTime.ets @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 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 { logger } from '../utils/Logger'; + + +// 从下载链接获取文件名。这里以简单包含文件名的下载链接为例。 +export function getFileNameFromUrl(url: string): string { + // 将URL字符串按照'/'字符进行分割,返回一个数组。 + const segments = url.split('/'); + // 使用pop方法从数组的末尾返回最后一个元素。 + return segments.pop() || '未知文件名'; +} + + +/** + * 格式化日期时间字符串 + * + * @param value 待格式化的日期值,支持数字、字符串和Date类型,默认为当前时间戳 + * @param fmt 格式化字符串,默认为'YYYY-MM-DD HH:mm:ss' + * @returns 返回格式化后的日期时间字符串 + */ +export function formatTime (value: number | string | Date = Date.now(), fmt: string = 'YYYY-MM-DD HH:mm:ss'): string { + try { + let data: Date; + + if(typeof value === 'number' || typeof value === 'string') { + data = new Date(value); + if(isNaN(data.getTime())) { + throw new Error('Invalid date'); + } + } else { + data = value; + } + + // 左侧补零函数 + const padZero = (value: number, len: number = 2): string => { + return String(value).padStart(len, '0'); + } + + const replacement = (match: string) => { + switch (match) { + case 'YYYY': + return padZero(data.getFullYear()); + + case 'YY': + return padZero(data.getFullYear()).slice(2, 4); + + case 'MM': + return padZero(data.getMonth() + 1); + + case 'M': + return String(data.getMonth() + 1); + + case 'DD': + return padZero(data.getDate()); + + case 'D': + return String(data.getDate()); + + case 'HH': + return padZero(data.getHours()); + + case 'H': + return String(data.getHours()); + + case 'mm': + return padZero(data.getMinutes()); + + case 'm': + return String(data.getMinutes()); + + case 'ss': + return padZero(data.getSeconds()); + + case 's': + return String(data.getSeconds()); + + default: + return match; + } + } + + return fmt.replace(/(YYYY|YY|M{1,2}|D{1,2}|H{1,2}|m{1,2}|s{1,2})/g, replacement); + + } catch (error) { + logger.error('Error formatting date:', error); + return ''; + } +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/FileDownloadItem.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/FileDownloadItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..e71f1c8a0fdac115d6f0a8c3298b6161d4e1a512 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/FileDownloadItem.ets @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2025 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 { logger } from '../utils/Logger'; +import { BusinessError } from '@ohos.base'; +import common from '@ohos.app.ability.common'; // 导入依赖资源context模块 +import request from '@ohos.request'; // 导入上传下载模块 +import { getFileNameFromUrl } from '../utils/formatTime'; +import { downloadFilesData } from '../model/dataType'; + +const TAG = 'Multiple_Files_Download'; +const BYTE_CONVERSION: number = 1024; // 字节转换 +const INIT_PROGRESS: number = 0; // 进度条初始值 +const context = getContext(this) as common.UIAbilityContext; // 获取当前页面的上下文 + + +// 配置下载参数,这里以简单配置url为例 +function downloadConfig(downloadUrl: string): request.agent.Config { + // TODO 知识点:配置下载参数。一个下载任务需要配置对应一套下载参数request.agent.Config。本例中使用downloadConfig方法简单配置了下载文件的url,实际业务中请按实际情况按需配置。 + const config: request.agent.Config = { + action: request.agent.Action.DOWNLOAD, // 配置任务选项,这里配置为下载任务 + url: downloadUrl, // 配置下载任务url + overwrite: true, // 下载过程中路径已存在时的解决方案选择。true表示覆盖已存在的文件 + method: 'GET', // HTTP标准方法。下载时,使用GET或POST。 + saveas: './', // 这里'./'表示下载至应用当前缓存路径下。 + mode: request.agent.Mode.BACKGROUND, // 任务模式设置后台任务。 + gauge: true, // 后台任务的过程进度通知策略,仅应用于后台任务。true表示发出每个进度已完成或失败的通知。 + retry: false, // 默认为true,如果没有网络或者网络不满足时,会自动暂停waiting,等网络满足时进行一次重试。设置为false时,没网直接走失败回调 + }; + return config; +} + +// 单个下载任务 +@Component +export struct FileDownloadItem { + // 文件下载配置 + @State downloadConfig: request.agent.Config = { action: request.agent.Action.DOWNLOAD, url: '' }; + // 下载文件名 + @State fileName: string = ''; + // 下载任务状态 + @State state: string = ''; + // 监听是否全部开始下载 + @Link @Watch('onDownLoadUpdated') isStartAllDownload: boolean; + // 下载任务对象初始化。用于下载失败和下载过程中暂停和重新启动下载。 + private downloadTask: request.agent.Task | undefined; + // 待下载任务数量 + @Link downloadCount: number; + // 下载失败任务数量 + @Link downloadFailCount: number; + // 下载状态图标显隐控制。下载中显示图标,下载完成或者下载失败隐藏图标 + @State isShow: boolean = false; + // 是否正在下载标志位 + @State downloading: boolean = false; + // 下载文件大小。类型字符串 + @State sFileSize: string = '-'; + // 下载文件大小。类型数值 + @State nFileSize: number = 0; + // 当前已下载数据量。类型字符串 + @State sCurrentDownloadSize: string = '-'; + // 当前已下载数据量。类型数值 + @State nCurrentDownloadSize: number = 0; + // 下载文件数据 + @ObjectLink fileDataInfo: downloadFilesData; + // 下载历史列表 + @Link historyArray: downloadFilesData[]; + // 下载列表 + @Link downloadFileArray: downloadFilesData[]; + // 下载任务完成回调 + private completedCallback = (progress: request.agent.Progress) => { + // 下载状态设置为下载完成 + this.state = '下载完成'; + // 获取下载完成的时间 + const downloadTime = new Date().getTime(); + // 下载完成时更改对应数据源中的下载状态与下载时间 + this.getFileStatusAndTime(1, downloadTime); + + if (this.sFileSize === '未知大小') { + // 如果下载url文件的服务器采用chunk分块传输文件数据,是获取不到下载文件总大小的。对于这种下载文件大小无法获取到的情况, + // 本例中下载进度条展示效果是初始未下载时进度为0,总进度为1,当文件下载完成时下载进度值改成1,表示下载完成,同步更新显示到进度条上。 + this.nCurrentDownloadSize = 1; + } + + // 文件下载完成,待下载任务数量减1 + if (this.downloadCount > 0) { + this.downloadCount--; + } + + // 隐藏下载状态图标 + this.isShow = false; + } + // 下载任务失败回调。任务下载失败一般是由于网络不好,底层重试也失败后进入该下载失败回调。如果网络没问题,建议重新下载再试。 + private failedCallback = (progress: request.agent.Progress) => { + this.state = '下载失败'; + + // 下载失败时更改对应数据源中的下载状态 + this.getFileStatusAndTime(2); + + // 当所有任务下载失败时,"全部暂停"状态重置为"全部开始"。 + this.downloadFailCount++; + if (this.downloadFailCount === this.downloadCount) { + this.isStartAllDownload = false; + } + if (this.downloadTask) { + // show用于获取下载任务相关信息。 + request.agent.show(this.downloadTask.tid, (err: BusinessError, taskInfo: request.agent.TaskInfo) => { + if (err) { + logger.error(TAG, `Failed to show with error message: ${err.message}, error code: ${err.code}`); + return; + } + if (this.downloadTask) { + // 打印下载失败的任务id和任务状态 + logger.error(TAG, + `Failed to download with error downloadTask tid: ${this.downloadTask.tid} state: ${taskInfo.progress.state}`); + // 隐藏下载状态图标 + this.isShow = false; + } + }); + } + } + // 暂停任务回调 + private pauseCallback = (progress: request.agent.Progress) => { + this.state = '已暂停'; + // 切换下载状态图标 + this.downloading = false; + } + // 重新启动任务回调。如果下载url文件的服务器不支持分片传输,则文件将重新下载。如果服务器支持分片传输,则会基于之前暂停时的下载进度继续下载。 + private resumeCallback = (progress: request.agent.Progress) => { + // 切换下载状态图标 + this.downloading = true; + } + // 下载进度更新回调 + private progressCallback = (progress: request.agent.Progress) => { + // 性能知识点: 如果注册了progress下载进度更新监听,不建议在progress下载进度更新回调中加日志打印,减少不必要的性能损耗。 + this.state = '下载中'; + this.downloading = true; + // 显示下载状态图标 + this.isShow = true; + if (this.downloadTask) { + // 第一次开始下载 + if (this.sFileSize === '-') { + // 如果下载url文件的服务器采用chunk分块传输文件数据,是获取不到下载文件总大小的。传过来的值为-1,则在页面上显示'未知大小' + if (progress.sizes[0] === -1) { + this.sFileSize = '未知大小'; + // 文件大小无法获取的情况下,进度条的值设置为0,总进度设置为1 + this.nCurrentDownloadSize = 0; + this.nFileSize = 1; + } else { + // 能获取文件大小时,按实际下载数据量更新进度 + this.nFileSize = progress.sizes[0]; + this.sFileSize = (progress.sizes[0] / BYTE_CONVERSION).toFixed() + 'kb'; + this.nCurrentDownloadSize = progress.processed; + } + } else if (this.sFileSize === '未知大小') { + // 非首次下载(暂停过下载任务后重新启动下载时),文件大小未知情况时,下载时进度不做更新 + logger.info(TAG, `When the file size is unknown, the download progress will not be updated`); + } else { + // 非首次下载(暂停过下载任务后重新启动下载时),文件大小能获取到的情况,更新下载进度 + this.nCurrentDownloadSize = progress.processed; + } + // 用于显示已下载文件数据大小 + this.sCurrentDownloadSize = (progress.processed / BYTE_CONVERSION).toFixed() + 'kb'; + } + } + + aboutToAppear(): void { + // 初始化下载配置 + this.downloadConfig = downloadConfig(this.fileDataInfo.url); + + // 从下载链接获取文件名 + this.fileName = getFileNameFromUrl(this.fileDataInfo.url); + } + + // 文件下载成功时更改文件状态与时间;下载失败时更改文件的状态 + getFileStatusAndTime(status: number, time?: number) { + this.fileDataInfo.fileStatus = status; + if (time) { + this.fileDataInfo.downloadTime = time; + // 下载成功加入到下载历史列表 + this.historyArray.push(this.fileDataInfo); + // 下载列表删除下载成功的数据 + this.downloadFileArray = this.downloadFileArray.filter((item: downloadFilesData) => { + return item.id !== this.fileDataInfo.id; + }); + } + } + + // 监听是否开始下载/暂停下载 + onDownLoadUpdated(): void { + if (this.isStartAllDownload) { + // 如果下载失败,则重新下载。下载失败原因一般是网络原因导致。 + if (this.state === '下载失败') { + // 下载任务完成或者任务失败时,底层会自动销毁任务资源。所以如果需要重新下载,重新创建任务即可。这里只做了初始化task对象 + this.downloadTask = undefined; + // 隐藏下载状态图标 + this.isShow = false; + // 重置下载任务状态 + this.state = ''; + } + // 下载 + this.startDownload(); + } else { + if (this.downloadFailCount > 0 && this.downloadFailCount === this.downloadCount) { + // 如果是任务全部下载失败,重置isStartAllDownload为false的情况,重置downloadFailCount + this.downloadFailCount = 0; + } else { + // 暂停下载 + this.pauseDownload(); + } + } + } + + // 启动下载任务 + startDownload(): void { + // 首次下载,创建任务 + if (this.downloadTask === undefined) { + // TODO 知识点:创建下载任务,并注册下载任务相关监听。本例在每个FileDownloadItem中使用request.agent.create创建下载任务。然后在下载 + // 任务创建成功后,注册各自下载任务相关监听。本例中注册了下载任务完成回调,下载任务失败回调,下载进度更新回调,暂停任务回调,重新启动任务回调。 + request.agent.create(context, this.downloadConfig).then((task: request.agent.Task) => { + // 注册下载任务相关回调 + task.on('completed', this.completedCallback); // 下载任务完成回调 + task.on('failed', this.failedCallback); // 下载任务失败回调 + task.on('pause', this.pauseCallback); // 暂停任务回调 + task.on('resume', this.resumeCallback); // 重新启动任务回调 + task.on('progress', this.progressCallback); // 下载进度更新回调 + + // TODO 知识点:启动下载任务。本例在每个FileDownloadItem中使用task.start方法启动各自的下载任务。 + task.start((err: BusinessError) => { + if (err) { + logger.error(TAG, `Failed to task start with error message: ${err.message}, error code: ${err.code}`); + return; + } + this.downloadTask = task; + }) + }).catch((err: BusinessError) => { + logger.error(TAG, `Failed to task create with error message: ${err.message}, error code: ${err.code}`); + }); + } else { + // 任务已存在时,继续下载 + this.resumeDownload(); + } + } + + // 暂停下载任务 + pauseDownload(): void { + if (this.downloadTask) { + // TODO 知识点:使用request.agent.show,根据任务id可查询任务的详细信息。本处用于查询下载任务状态 + request.agent.show(this.downloadTask.tid, (err: BusinessError, taskInfo: request.agent.TaskInfo) => { + if (err) { + logger.error(TAG, `Failed to show with error message: ${err.message}, error code: ${err.code}`); + return; + } + // 判断当前下载任务状态是否满足暂停条件。 + if (this.downloadTask && (taskInfo.progress.state === request.agent.State.WAITING || taskInfo.progress.state === + request.agent.State.RUNNING || taskInfo.progress.state === request.agent.State.RETRYING)) { + // TODO 知识点:使用task.pause可以暂停正在等待WAITING/正在运行RUNNING/正在重试RETRYING的后台下载任务。 + this.downloadTask.pause().then(() => { + // 暂停任务成功 + }).catch((err: BusinessError) => { + logger.error(TAG, `Failed to pause with error message: ${err.message}, error code: ${err.code}`); + }); + } else { + if (this.downloadTask) { + // 不满足暂停任务条件 + logger.info(TAG, `Not meeting the pause task conditions,current task state: ${taskInfo.progress.state}`); + } + } + }); + } + } + + // 重新启动下载任务 + resumeDownload(): void { + if (this.downloadTask) { + // 查询任务状态 + request.agent.show(this.downloadTask.tid, (err: BusinessError, taskInfo: request.agent.TaskInfo) => { + if (err) { + logger.error(TAG, `Failed to show with error message: ${err.message}, error code: ${err.code}`); + return; + } + // 判断如果任务是暂停状态,则重新启动下载任务 + if (this.downloadTask && taskInfo.progress.state === request.agent.State.PAUSED) { + // TODO 知识点:使用task.resume可以重新启动任务,可恢复暂停的后台任务。 + this.downloadTask.resume((err: BusinessError) => { + if (err) { + logger.error(TAG, `Failed to resume with error message: ${err.message}, error code: ${err.code}`); + return; + } + // 重新启动下载任务成功 + }); + } + }); + } + } + + build() { + RelativeContainer() { + Image($r('app.media.multiple_files_download_file')) + .height($r('app.integer.multiple_files_download_image_size_fifty')) + .width($r('app.integer.multiple_files_download_image_size_fifty')) + .id('fileImage') + + Text(this.fileName) + .fontSize($r('app.integer.multiple_files_download_text_font_size_fourteen')) + .padding({ left: $r('app.integer.multiple_files_download_padding_twenty') }) + .alignRules({ + left: { anchor: 'fileImage', align: HorizontalAlign.End } + }) + .id('fileName') + + Image(this.downloading ? $r('app.media.multiple_files_download_start') : + $r('app.media.multiple_files_download_stop')) + .visibility(this.isShow ? Visibility.Visible : Visibility.Hidden) + .height($r('app.integer.multiple_files_download_image_size_twenty_five')) + .width($r('app.integer.multiple_files_download_image_size_twenty_five')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_seven') }) + .alignRules({ + right: { anchor: '__container__', align: HorizontalAlign.End } + }) + .id('downloadImage') + .onClick(() => { + // 这里未做实际功能,仅做展示 + AlertDialog.show({ + message: $r('app.string.multiple_files_download_function_only_display_purposes'), + alignment: DialogAlignment.Center + }); + }) + + Text(this.sCurrentDownloadSize + '/' + this.sFileSize) + .fontSize($r('app.integer.multiple_files_download_text_font_size_twelve')) + .width($r('app.string.multiple_files_download_text_width')) + .fontColor($r('app.color.multiple_files_download_text_font_color')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_seven') }) + .padding({ left: $r('app.integer.multiple_files_download_padding_twenty') }) + .alignRules({ + top: { anchor: 'downloadImage', align: VerticalAlign.Center }, + left: { anchor: 'fileImage', align: HorizontalAlign.End } + }) + .id('downloadVal') + + Text(this.state) + .fontSize($r('app.integer.multiple_files_download_text_font_size_twelve')) + .fontColor($r('app.color.multiple_files_download_text_font_color')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_seven') }) + .alignRules({ + top: { anchor: 'downloadImage', align: VerticalAlign.Center }, + left: { anchor: 'downloadVal', align: HorizontalAlign.End }, + right: { anchor: 'downloadImage', align: HorizontalAlign.Start } + }) + .id(this.fileName + 'state') + // 下载进度条,用于显示从下载进度更新回调中获取到的已下载数据大小 + Progress({ value: INIT_PROGRESS, total: this.nFileSize, type: ProgressType.Capsule }) + .alignRules({ + top: { anchor: 'fileImage', align: VerticalAlign.Bottom } + }) + .value(this.nCurrentDownloadSize) + .height($r('app.integer.multiple_files_download_progress_height')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_five') }) + .id('progress') + } + .width($r('app.string.multiple_files_download_relative_container_width')) + .height($r('app.string.multiple_files_download_relative_container_height')) + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/HistoryItem.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/HistoryItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..b52a3d705dfee95bed3da026f56809f299acc971 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/HistoryItem.ets @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2025 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 { getFileNameFromUrl, formatTime } from '../utils/formatTime'; +import { downloadFilesData } from '../model/dataType'; +import { fileIo as fs } from '@kit.CoreFileKit'; +import { BusinessError, emitter } from '@kit.BasicServicesKit'; +import { logger } from '../utils/Logger'; +import { promptAction } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +const TAG: string = 'HistoryFiles'; +const context = getContext(this) as common.UIAbilityContext; // 获取当前页面的上下文 + +@Component +export default struct HistoryItem { + // 下载历史的数据 + @ObjectLink fileInfo: downloadFilesData; + // 文件名称 + @State fileName: string = ''; + // 待下载任务数量 + @Link downloadCount: number; + // 下载历史列表 + @Link historyArray: downloadFilesData[]; + // 下载列表 + @Link downloadFileArray: downloadFilesData[]; + + + aboutToAppear(): void { + // 从下载链接获取文件名 + this.fileName = getFileNameFromUrl(this.fileInfo.url); + } + + build() { + Row() { + RelativeContainer() { + Image($r('app.media.multiple_files_download_file')) + .height($r('app.integer.multiple_files_download_image_size_fifty')) + .width($r('app.integer.multiple_files_download_image_size_fifty')) + .alignRules({ + top: { anchor: '__container__', align: VerticalAlign.Top }, + left: { anchor: '__container__', align: HorizontalAlign.Start } + }) + .id('fileImage') + + // 文件名文本 + Text(this.fileName) + .fontSize($r('app.integer.multiple_files_download_text_font_size_fourteen')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_seven'), left: $r('app.integer.multiple_files_download_padding_twenty') }) + .alignRules({ + left: { anchor: 'fileImage', align: HorizontalAlign.End } + }) + .decoration({ + type: this.fileInfo.fileStatus === 1 ? TextDecorationType.None : TextDecorationType.LineThrough + }) + .id('fileName') + + // 已下载文本 + Text(this.fileInfo.fileStatus === 1 ? $r('app.string.multiple_files_download_history_text_downloaded') : '') + .fontSize($r('app.integer.multiple_files_download_text_font_size_twelve')) + .fontColor($r('app.color.multiple_files_download_text_font_color')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_seven'), left: $r('app.integer.multiple_files_download_padding_twenty') }) + .alignRules({ + top: { anchor: 'fileName', align: VerticalAlign.Bottom }, + left: { anchor: 'fileImage', align: HorizontalAlign.End } + }) + .id(this.fileName + 'state') + + // 下载完成时间 + Text(this.fileInfo.fileStatus === 1 ? formatTime(this.fileInfo.downloadTime, 'YYYY/MM/DD HH:mm') : '') + .fontSize($r('app.integer.multiple_files_download_text_font_size_twelve')) + .fontColor($r('app.color.multiple_files_download_text_font_color')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_seven'), left: $r('app.integer.multiple_files_download_margin_left_sixty') }) + .alignRules({ + top: { anchor: 'fileName', align: VerticalAlign.Bottom }, + left: { anchor: 'fileImage', align: HorizontalAlign.End } + }) + .id('downloadTime') + + // 删除按钮 + Button($r('app.string.multiple_files_download_history_button_del'), { type: ButtonType.Normal, stateEffect: true }) + .fontSize($r('app.integer.multiple_files_download_text_font_size_twelve')) + .padding({ top: $r('app.integer.multiple_files_download_padding_zero'), left: $r('app.integer.multiple_files_download_padding_five'), bottom: $r('app.integer.multiple_files_download_padding_zero'), right: $r('app.integer.multiple_files_download_padding_five') }) + .height($r('app.integer.multiple_files_download_height_twenty_six')) + .borderRadius($r('app.integer.multiple_files_download_borderRadius_eight')) + .enabled( this.fileInfo.fileStatus === 1 ? true : false) + .opacity(this.fileInfo.fileStatus === 1 ? 1 : 0.7) + .backgroundColor($r('app.color.multiple_files_download_del_bgc')) + .alignRules({ + bottom: { anchor: 'fileImage', align: VerticalAlign.Bottom }, + right: { anchor: '__container__', align: HorizontalAlign.End } + }) + .margin({bottom:10}) + .onClick( () => { + let filePath = getContext(this).cacheDir + '/' + this.fileName; + + // TODO:知识点:删除该条数据应用文件路径 + fs.unlink(filePath).then(() => { + // 文件状态变为已删除 + this.fileInfo.fileStatus = 3; + promptAction.showToast({ + message: $r('app.string.multiple_files_download_history_toast_text_del'), + duration: 2000, + bottom: '50%', + }) + }).catch((error: BusinessError) => { + logger.error('remove file failed with error:' + error); + }); + + emitter.emit({ eventId: 0, priority: 0 }, { + data: { + isDel: true + } + }) + }) + .id(this.fileName + 'delete') + } + } + .width($r('app.string.multiple_files_download_list_width')) + .height($r('app.string.multiple_files_download_relative_container_height')) + .id(this.fileName) + .onClick(() => { + // 获取应用文件路径 + const cacheDir = context.cacheDir; + const filePath: string = `${cacheDir}/${this.fileName}`; + + // TODO:知识点:使用fs.access判断目录是否已经存在。 + fs.access(filePath).then((res: boolean) => { + if (!res) { + // 文件已删除,弹窗提示是否重新下载 + AlertDialog.show({ + message: `${this.fileName}文件不存在,是否重新下载?`, + alignment: DialogAlignment.Center, // 弹窗在竖直方向上的对齐方式。 + autoCancel: false, // 点击遮罩层时,是否关闭弹窗。默认值:true + primaryButton: { + value: $r('app.string.multiple_files_download_history_button_text_cancel'), + fontColor: $r('app.color.multiple_files_download_text_color'), + action: () => { + logger.info('已取消'); + } + }, + secondaryButton: { + value: $r('app.string.multiple_files_download_history_button_text_confirm'), + fontColor: $r('app.color.multiple_files_download_del_bgc'), + action: () => { + // 更改该条数据下载状态 + this.fileInfo.fileStatus = 0; + // 该条数据加入下载列表数组中 + this.downloadFileArray.push(this.fileInfo); + // 下载历史列表删除该条数据 + this.historyArray = this.historyArray.filter( ( item: downloadFilesData ) => { + return item.id !== this.fileInfo.id; + }); + // 更新待下载任务数量 + this.downloadCount = this.downloadFileArray.length; + } + }, + cornerRadius: $r('app.integer.multiple_files_download_borderRadius_twelve'), // 弹窗边框弧度 + width: $r('app.string.multiple_files_download_list_height'), // 弹窗宽度 + cancel: () => { + logger.info('点击关闭按钮'); + } + }); + } else { + // 文件已下载到本地,弹窗提示文件已存在 + promptAction.showToast({ + message: $r('app.string.multiple_files_download_history_toast_text_exist'), + duration: 1000, + bottom: '50%', + }) + } + }).catch((err: BusinessError) => { + logger.error(TAG, `access failed with error message: ${err.message}, error code: ${err.code}`); + }); + }) + } +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/MultipleFilesDownload.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/MultipleFilesDownload.ets new file mode 100644 index 0000000000000000000000000000000000000000..b5f7bcbfb275dfb572973ee313723563dbff4785 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/ets/view/MultipleFilesDownload.ets @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2025 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 { FileDownloadItem } from './FileDownloadItem'; +import request from '@ohos.request'; // 导入上传下载模块。需要配置ohos.permission.INTERNET权限 +import { + ItemRestriction, + SegmentButton, + SegmentButtonOptions, + SegmentButtonTextItem +} from '@ohos.arkui.advanced.SegmentButton'; // 分段按钮,仅用于ux展示 +import { downloadFilesData } from '../model/dataType'; +import HistoryItem from './HistoryItem'; +import { Context } from '@kit.AbilityKit'; +import { JSON, util } from '@kit.ArkTS'; + +const NO_TASK: number = 0; // 0个下载任务 + + +/** + * 功能描述: 多文件下载监听在应用开发中是一个非常常见的需求。本示例将介绍如何使用request上传下载模块实现多文件下载监听,如监听每个文件下载任务的进度,任务暂停,下载完成等下载情况。 + * 每个应用最多支持创建10个未完成的任务,相关规格说明请参考request.agent.create。 + * + * 推荐场景: 多文件下载 + * + * 核心组件: + * 1. FileDownloadItem + * + * 实现步骤: + * 1.配置下载参数。一个下载任务需要配置对应一套下载参数request.agent.Config。本例中使用downloadConfig方法简单配置了下载文件的url,实际业务 + * 中请按实际情况按需配置。 + * 2.创建多个文件下载监听实例。单个文件下载监听只需要配置下载参数,创建下载任务,注册下载任务相关监听,启动下载任务即可实现。而要实现多文件下载监听, + * 需要每个下载任务注册独立的下载监听回调。本例通过封装自定义组件FileDownloadItem,在每个FileDownloadItem中创建各自的下载任务和监听回调,从 + * 而实现多文件下载监听。 + * 3.创建下载任务,并注册下载任务相关监听。本例在每个FileDownloadItem中使用request.agent.create创建下载任务。然后在下载任务创建成功后,注 + * 册各自下载任务相关监听。本例中注册了下载任务完成回调,下载任务失败回调,下载进度更新回调,暂停任务回调,重新启动任务回调。 + * 4.启动下载任务。本例在每个FileDownloadItem中使用task.start方法启动各自的下载任务。 + * 5.本例中下载任务用到的其他操作:使用request.agent.show,根据任务id可查询任务的详细信息。使用task.pause可以暂停正在等待WAITING/正在运行 + * RUNNING/正在重试RETRYING的后台下载任务。使用task.resume可以重新启动任务,可恢复暂停的后台任务。 + */ + +@Component +export struct MultipleFilesDownloadComponent { + // 是否全部开始下载 + @State isStartAllDownload: boolean = false; + // 待下载任务数量 + @State downloadCount: number = 0; + // 下载失败任务数量。当所有任务下载失败时,"全部暂停"状态重置为"全部开始"。 + @State downloadFailCount: number = 0; + // 分段按钮,仅用于ux展示 + @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({ + buttons: [{ text: $r('app.string.multiple_files_download_file_upload') }, + { text: $r('app.string.multiple_files_download_list') }, + { text: $r('app.string.multiple_files_download_album_backup') }] as ItemRestriction, + backgroundBlurStyle: BlurStyle.BACKGROUND_THICK, + selectedFontColor: Color.White, + selectedBackgroundColor: $r('app.color.multiple_files_download_selected_background_color'), + backgroundColor: $r('app.color.multiple_files_download_background_color') + }) + // 监听SegmentButton组件selectedIndexes值变化,[1]表示设置默认选中项编号为'下载列表'项 + @State @Watch('onSelectedChange') tabSelectedIndexes: number[] = [1]; + // 下载列表内容透明度。用于控制是否显示下载列表相应内容 + @State downloadPageOpacity: number = 1; + // 下载列表内容使能控制。避免隐藏后还能点击 + @State isDownloadPageEnabled: boolean = true; + // 历史列表的显隐控制。 + @State historyPageVisibility: Visibility = Visibility.None; + // 历史列表内容使其能控制。避免隐藏后还能点击 + @State isHistoryPageEnabled: boolean = false; + // 点击历史列表tab时,控制下载列表相应的内容的显隐 + @State downloadPageVisibility: Visibility = Visibility.Visible; + // 下载列表数据 + @State downloadFileArray: downloadFilesData[] = []; + // 下载历史列表数据 + @State historyList: downloadFilesData[] = []; + + // 当选择"下载列表"时,显示下载列表相应内容,选择其他选项时,不显示内容,仅用于ux展示 + onSelectedChange(): void { + // 显示下载列表内容 + if (this.tabSelectedIndexes[0] === 1) { + this.downloadPageOpacity = 1; + this.isDownloadPageEnabled = true; + this.downloadPageVisibility = Visibility.Visible; + this.historyPageVisibility = Visibility.None; + } else if (this.tabSelectedIndexes[0] === 2) { + // 不显示下载列表相应内容,并不占位 + this.isDownloadPageEnabled = false; + this.downloadPageVisibility = Visibility.None; + // 显示历史列表相应内容 + this.historyPageVisibility = Visibility.Visible; + this.isHistoryPageEnabled = true; + } else { + this.downloadPageOpacity = 0; + this.isDownloadPageEnabled = false; + this.historyPageVisibility = Visibility.None; + this.isHistoryPageEnabled = false; + } + // 点击分段按钮时,若下载数量为0,重置是否全部开始下载按钮 + if (this.downloadCount === 0) { + this.isStartAllDownload = false; + } + } + + // 下载信息初始化 + aboutToAppear(): void { + // 初始化下载列表 + this.loadInitializationDataSource(); + + // 下载数量,用于显示页面上下载队列数量 + this.downloadCount = this.downloadFileArray.length; + } + + // 读取historyDownloadResourceData.json中的数据源 + loadInitializationDataSource() { + const context: Context = getContext(this); + // 读取historyDownloadResourceData.json中的数据 + let jsonData: Uint8Array = context.resourceManager.getRawFileContentSync('historyDownloadResourceData.json'); + // 将数据解码,转成字符串 + let stringData: string = util.TextDecoder.create('utf-8').decodeToString(jsonData); + let data: [] = JSON.parse(stringData) as []; + + for (let i = 0; i < data.length; i++) { + const downloadItemData = data[i] as downloadFilesData; + let downloadData: downloadFilesData = new downloadFilesData(downloadItemData.url, downloadItemData.fileStatus, + downloadItemData.downloadTime); + // 将数据存入下载列表 + this.downloadFileArray.push(downloadData); + } + } + + build() { + Column() { + Text($r('app.string.multiple_files_download_transfer_list')) + .fontSize($r('app.integer.multiple_files_download_text_font_size_twenty_five')) + .textAlign(TextAlign.Center) + .fontWeight(FontWeight.Bold) + SegmentButton({ options: this.tabOptions, selectedIndexes: $tabSelectedIndexes }) + .margin({ + top: $r('app.integer.multiple_files_download_margin_top_twenty'), + bottom: $r('app.integer.multiple_files_download_margin_bottom_twelve') + }) + + Row() { + Row() { + Text($r('app.string.multiple_files_download_queue')) + .fontSize($r('app.integer.multiple_files_download_text_font_size_twenty')) + Text(this.downloadCount.toString()).fontSize($r('app.integer.multiple_files_download_text_font_size_twenty')) + }.width($r('app.string.multiple_files_download_row_width')) + + Row() { + // this.downloadCount > 0用于当存在未完成的下载任务时才可以暂停下载 + Text(this.isStartAllDownload && this.downloadCount > NO_TASK ? '全部暂停' : '全部开始') + .fontSize($r('app.integer.multiple_files_download_text_font_size_twenty')) + .fontColor($r('app.color.multiple_files_download_text_color')) + .textAlign(TextAlign.End) + .width($r('app.string.multiple_files_download_row_text_width')) + .id('download') + .onClick(() => { + if (this.downloadCount === NO_TASK) { + AlertDialog.show({ + message: $r('app.string.multiple_files_download_completed'), + alignment: DialogAlignment.Center + }); + return; + } + this.isStartAllDownload = !this.isStartAllDownload; + }) + }.width($r('app.string.multiple_files_download_row_width')) + } + .opacity(this.downloadPageOpacity) + .visibility(this.downloadPageVisibility) + .enabled(this.isDownloadPageEnabled) + .width($r('app.string.multiple_files_download_outer_row_width')) + + List() { + /** + * 本例只展示下载列表。由于每个应用request.agent.create最多支持创建10个未完成的任务,数量较小,所以这里使用ForEach展示下载列表。 + * 如果需要显示大量历史下载完成任务信息,建议使用LazyForEach提升性能。create相关限制说明请参考:https://docs.openharmony.cn + * /pages/v5.0/en/application-dev/reference/apis-basic-services-kit/js-apis-request.md。 + */ + ForEach(this.downloadFileArray, (item: downloadFilesData) => { + ListItem() { + // TODO 知识点:创建多个文件下载监听实例。单个文件下载监听只需要配置下载参数,创建下载任务,注册下载任务相关监听,启动下载任务即可实现。 + // 而要实现多文件下载监听,需要每个下载任务注册独立的下载监听回调。本例通过封装自定义组件FileDownloadItem, + // 在每个FileDownloadItem中创建各自的下载任务和监听回调,从而实现多文件下载监听。 + FileDownloadItem({ + fileDataInfo: item, // 文件下载配置 + isStartAllDownload: this.isStartAllDownload, // 是否全部开始下载 + downloadCount: this.downloadCount, // 待下载任务数量 + downloadFailCount: this.downloadFailCount, // 下载失败任务数量 + historyArray: this.historyList, + downloadFileArray: this.downloadFileArray // 下载文件数据源 + }) + } + .margin({ bottom: $r('app.integer.multiple_files_download_margin_bottom_eighteen') }) + }, (item: request.agent.Config) => JSON.stringify(item)) + } + .opacity(this.downloadPageOpacity) + .visibility(this.downloadPageVisibility) + .enabled(this.isDownloadPageEnabled) + .width($r('app.string.multiple_files_download_list_width')) + .height($r('app.string.multiple_files_download_list_height')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_thirty') }) + + // 下载历史列表 + Column() { + List() { + ForEach(this.historyList, (item: downloadFilesData) => { + ListItem() { + HistoryItem({ + fileInfo: item, + downloadCount: this.downloadCount, // 待下载任务数量 + historyArray: this.historyList, + downloadFileArray: this.downloadFileArray // 下载文件数据源 + }) + } + }) + } + } + .visibility(this.historyPageVisibility) + .enabled(this.isHistoryPageEnabled) + .width($r('app.string.multiple_files_download_outer_row_width')) + .height($r('app.string.multiple_files_download_list_height')) + .margin({ top: $r('app.integer.multiple_files_download_margin_top_thirty') }) + + }.padding($r('app.integer.multiple_files_download_padding_fifteen')) + .focusable(false) // 避免SegmentButton组件在Navigator路由页面中获焦显示多余的选中框 + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/module.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c771f31be9c5b6e52e7da3f8f1eadac2535f1cab --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/module.json5 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "module": { + "name": "multiplefilesdownload", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET" + }, + ] + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/color.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..a7cf350f4b88612efde9029babdd9b12b544e42c --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/color.json @@ -0,0 +1,28 @@ +{ + "color": [ + { + "name": "multiple_files_download_text_font_color", + "value": "#66182431" + }, + { + "name": "multiple_files_download_column_border_color", + "value": "#6fafee" + }, + { + "name": "multiple_files_download_selected_background_color", + "value": "#FF007DFF" + }, + { + "name": "multiple_files_download_background_color", + "value": "#FFFEFEFE" + }, + { + "name": "multiple_files_download_text_color", + "value": "#418AF3" + }, + { + "name": "multiple_files_download_del_bgc", + "value": "#F55A42" + } + ] +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/integer.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/integer.json new file mode 100644 index 0000000000000000000000000000000000000000..3b1936d6a96a00d707f9a14b4086f09e4b381961 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/integer.json @@ -0,0 +1,104 @@ +{ + "integer": [ + { + "name": "multiple_files_download_image_size_fifty", + "value": 50 + }, + { + "name": "multiple_files_download_image_size_twenty_five", + "value": 25 + }, + { + "name": "multiple_files_download_text_font_size_fourteen", + "value": 14 + }, + { + "name": "multiple_files_download_text_font_size_twelve", + "value": 12 + }, + { + "name": "multiple_files_download_text_font_size_twenty_five", + "value": 25 + }, + { + "name": "multiple_files_download_text_font_size_twenty", + "value": 20 + }, + { + "name": "multiple_files_download_padding_twenty", + "value": 20 + }, + { + "name": "multiple_files_download_padding_fifteen", + "value": 15 + }, + { + "name": "multiple_files_download_margin_bottom_eighteen", + "value": 18 + }, + { + "name": "multiple_files_download_margin_bottom_twelve", + "value": 12 + }, + { + "name": "multiple_files_download_margin_top_twenty", + "value": 20 + }, + { + "name": "multiple_files_download_margin_top_seven", + "value": 7 + }, + { + "name": "multiple_files_download_margin_top_five", + "value": 5 + }, + { + "name": "multiple_files_download_progress_height", + "value": 5 + }, + { + "name": "multiple_files_download_border_width", + "value": 5 + }, + { + "name": "multiple_files_download_border_radius", + "value": 10 + }, + { + "name": "multiple_files_download_margin_top_thirty", + "value": 30 + }, + { + "name": "multiple_files_download_margin_left_four", + "value": 4 + }, + { + "name": "multiple_files_download_padding_zero", + "value": 0 + }, + { + "name": "multiple_files_download_padding_five", + "value": 5 + }, + { + "name": "multiple_files_download_height_twenty_six", + "value": 26 + }, + { + "name": "multiple_files_download_borderRadius_eight", + "value": 8 + }, + { + "name": "multiple_files_download_borderRadius_twelve", + "value": 12 + }, + { + "name": "multiple_files_download_history_percentage_fifty", + "value": 50 + }, + { + "name": "multiple_files_download_margin_left_sixty", + "value": 60 + } + ] +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/string.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..a2bcb7fd23c4ecc8bed15401e41de3c633bb4371 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/element/string.json @@ -0,0 +1,92 @@ +{ + "string": [ + { + "name": "multiple_files_download_text_width", + "value": "40%" + }, + { + "name": "multiple_files_download_relative_container_width", + "value": "100%" + }, + { + "name": "multiple_files_download_relative_container_height", + "value": "15%" + }, + { + "name": "multiple_files_download_row_width", + "value": "50%" + }, + { + "name": "multiple_files_download_row_text_width", + "value": "100%" + }, + { + "name": "multiple_files_download_outer_row_width", + "value": "100%" + }, + { + "name": "multiple_files_download_transfer_list", + "value": "传输列表" + }, + { + "name": "multiple_files_download_queue", + "value": "下载队列 " + }, + { + "name": "multiple_files_download_completed", + "value": "下载已完成" + }, + { + "name": "multiple_files_download_function_only_display_purposes", + "value": "功能仅做展示" + }, + { + "name": "multiple_files_download_list", + "value": "下载列表" + }, + { + "name": "multiple_files_download_file_upload", + "value": "文件上传" + }, + { + "name": "multiple_files_download_album_backup", + "value": "下载历史" + }, + { + "name": "multiple_files_download_row_height", + "value": "30%" + }, + { + "name": "multiple_files_download_list_width", + "value": "100%" + }, + { + "name": "multiple_files_download_list_height", + "value": "80%" + }, + { + "name": "multiple_files_download_history_button_del", + "value": "删除" + }, + { + "name": "multiple_files_download_history_toast_text_del", + "value": "文件已删除" + }, + { + "name": "multiple_files_download_history_text_downloaded", + "value": "已下载" + }, + { + "name": "multiple_files_download_history_button_text_cancel", + "value": "取消" + }, + { + "name": "multiple_files_download_history_button_text_confirm", + "value": "确定" + }, + { + "name": "multiple_files_download_history_toast_text_exist", + "value": "文件已存在" + } + ] +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_file.png b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_file.png new file mode 100644 index 0000000000000000000000000000000000000000..a323e4d76acc869e00672162e540167576b93c71 Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_file.png differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_start.png b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_start.png new file mode 100644 index 0000000000000000000000000000000000000000..78100dcba58fcc7cc98d979fe5f025653d823ecb Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_start.png differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_stop.png b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_stop.png new file mode 100644 index 0000000000000000000000000000000000000000..5ba740178443c784ba961aec5dca4375b30f7f38 Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/base/media/multiple_files_download_stop.png differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/rawfile/historyDownloadResourceData.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/rawfile/historyDownloadResourceData.json new file mode 100644 index 0000000000000000000000000000000000000000..9cdf9be182d807b6e42f1b7d4bbc5c38ab2ee8c7 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/casesfeature/multiplefilesdownload/src/main/resources/rawfile/historyDownloadResourceData.json @@ -0,0 +1,32 @@ +[ + { + "id": 1, + "url": "https://gitee.com/openharmony/applications_app_samples/raw/master/code/Solutions/Shopping/OrangeShopping/feature/navigationHome/src/main/resources/base/media/favor_list_tea.png", + "fileStatus": 0, + "downloadTime": 1728529510880 + }, + { + "id": 2, + "url": "https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/product/entry/src/main/resources/base/media/after_cache.png", + "fileStatus": 0, + "downloadTime": 1728529510880 + }, + { + "id": 3, + "url": "https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/product/entry/src/main/resources/base/media/icon_main_color.png", + "fileStatus": 0, + "downloadTime": 1728529510880 + }, + { + "id": 4, + "url": "https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/product/entry/src/main/resources/base/media/image_resizable.png", + "fileStatus": 0, + "downloadTime": 1728529510880 + }, + { + "id": 5, + "url": "https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/product/entry/src/main/resources/base/media/text_overflow_detail.png", + "fileStatus": 0, + "downloadTime": 1728529510880 + } +] \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/code-linter.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..28586467ee7a761c737d8654a73aed6fddbc3c71 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/code-linter.json5 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/.gitignore b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/build-profile.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b4d65d490ef6cbe22d933b9231555210f1555786 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/hvigorfile.ts b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 { 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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/obfuscation-rules.txt b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/oh-package-lock.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c1d88b7cd0ab6c8848ac4d9b2af02239e7236c8c --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/oh-package-lock.json5 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "multiplefilesdownload@../casesfeature/multiplefilesdownload": "multiplefilesdownload@../casesfeature/multiplefilesdownload" + }, + "packages": { + "multiplefilesdownload@../casesfeature/multiplefilesdownload": { + "name": "multiplefilesdownload", + "version": "1.0.0", + "resolved": "../casesfeature/multiplefilesdownload", + "registryType": "local" + } + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/oh-package.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f24d0a9a5ec9e1d584e09fddc01b076e7336bf22 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/oh-package.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "multiplefilesdownload": "file:../casesfeature/multiplefilesdownload" + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/entryability/EntryAbility.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..3e77e4b9f7946d80aa77e6b2dcfe40b23ccf6178 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..1504a74f09dfdcfae408be979f99369a2c5affab --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/pages/Index.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..4bad3936cd364518af749d360530bcd7865c343e --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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 { MultipleFilesDownloadComponent } from 'multiplefilesdownload'; + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + RelativeContainer() { + /** + * 功能描述:多文件下载监听在应用开发中是一个非常常见的需求。本示例将介绍如何使用request上传下载模块实现多文件下载监听,如监听每个文件下载任务的进度,任务暂停,下载完成等下载情况。每个应用最多支持创建10个未完成的任务,相关规格说明请参考request.agent.create。 + * 参数介绍:无 + */ + MultipleFilesDownloadComponent(); + + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/module.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..dece7db447b686c5afc03a0ed853c0cf4c87c3a2 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/module.json5 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/element/color.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..d66f9a7d4ac61fb8d215239ab3620b7bcd77bf33 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/element/string.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/background.png b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/background.png differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/foreground.png b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/foreground.png differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/layered_image.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/startIcon.png b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/media/startIcon.png differ diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/profile/backup_config.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..d742c2f96e7dd0f406f499941f3147345e998f95 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/profile/main_pages.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/dark/element/color.json b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..438d5bc43bb23c59c210d586b96635a72da5b64a --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/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/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/ets/test/Ability.test.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f8ce9a2c012f8fe36114cef65216ef0b6254f41 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/ets/test/List.test.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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 abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/module.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c8bd24faff0ff0064a57435f74662f1db5865f88 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/ohosTest/module.json5 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/test/List.test.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1186b1f53c3a70930921c5dbd1417332bec56c9 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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 localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/test/LocalUnit.test.ets b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..841bfd77e56060e50ec0924302a5ae624e76e3aa --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/hvigor/hvigor-config.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..a653156fd914ed32a77644b64b6a0da0cc63c153 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/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/code/BasicFeature/Connectivity/MultipleFilesDownload/hvigorfile.ts b/code/BasicFeature/Connectivity/MultipleFilesDownload/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 { 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/code/BasicFeature/Connectivity/MultipleFilesDownload/oh-package-lock.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..312f72e2c553ccec4962bd317631512268a949ef --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/oh-package-lock.json5 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "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://repo.harmonyos.com/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://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.19.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/oh-package.json5 b/code/BasicFeature/Connectivity/MultipleFilesDownload/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..eaef52736f22c14e04fca386ba01611ab32a7e4a --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.1", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} diff --git a/code/BasicFeature/Connectivity/MultipleFilesDownload/ohosTest.md b/code/BasicFeature/Connectivity/MultipleFilesDownload/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..7691735123dfcf4320758d8d5074bb5bcbada342 --- /dev/null +++ b/code/BasicFeature/Connectivity/MultipleFilesDownload/ohosTest.md @@ -0,0 +1,13 @@ +# 多文件下载监听案例 测试用例归档 + +## 用例表 + +|测试功能|预置条件|输入|预期输出|是否自动|测试结果| +|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------| +|点击“全部开始”功能是否正常启动下载| 1.进入案例页面 2.正常连接网络 | 点击“全部开始” |点击“全部开始”,下载队列中两个任务能正常下载|是|Pass| +|点击“全部暂停”功能是否能正常暂停下载| 1.进入案例页面 2.正常连接网络 | 点击“全部暂停” |点击“全部暂停”,下载队列中正在下载的任务被暂停了|是|Pass| +|点击“全部开始”,然后在下载完成前点击“全部暂停”,然后再次点击“全部开始”查看被暂停的下载任务能否正常恢复下载| 1.进入案例页面 2.正常连接网络 | 点击“全部开始”,然后在下载完成前点击点击“全部暂停”,然后再次点击“全部开始” |下载任务重新恢复下载|是|Pass| +|下载文件保存是否正常| 1.进入案例页面 2.正常连接网络 | 点击“全部开始”,等待下载完成后,查看下载的文件是否保存在应用缓存路径下。(手工可用hdc shell cd /data/app/el2/100/base/com/north.cases/haps/entry/cache/) |下载完成后,在案例应用缓存路径下可以找到下载的文件:favor_list_tea.png favor_list_tree.png|是|Pass| +|在下载历史中已下载的文件是否能删除|1.进入案例页面 2.正常连接网络 3.点击下载历史tab|点击“删除”| 点击“删除”,文件被删除样式发生变化且在案例应用缓存路径下不能找到已删除的文件 |是|Pass| +|点击下载历史中的文件,是否能把已删除的文件重新下载|1.进入案例页面 2.正常连接网络 3.点击下载历史tab|点击已删除文件|弹出“xxx文件已被删除,是否重新下载”的弹窗提示用户是否重新下载,若用户点击“确认”文件重新下载|是|Pass| +|在下载历史中点击已下载的文件是否弹出“文件已存在”信息|1.进入案例页面 2.正常连接网络 3.点击下载历史tab|点击已下载文件|弹出“文件已存在”toast|是|Pass|