From 7284f5c2ff34baae01448f56a7878800872a4ab2 Mon Sep 17 00:00:00 2001 From: haixiangw Date: Tue, 24 Dec 2024 00:22:31 -0800 Subject: [PATCH] =?UTF-8?q?=E5=BA=94=E7=94=A8=E4=BB=A3=E7=A0=81=E8=BF=81?= =?UTF-8?q?=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: haixiangw --- AppScope/app.json5 | 25 + AppScope/resources/base/element/color.json | 8 + AppScope/resources/base/element/string.json | 8 + AppScope/resources/base/media/app_icon.png | Bin 0 -> 6790 bytes LICENSE | 176 ++ OAT.xml | 69 + README.en.md | 40 - README.md | 72 +- build-profile.json5 | 65 + bundle.json | 34 + doc/image/image-20220727141455437.png | Bin 0 -> 41862 bytes entry/.gitignore | 6 + entry/build-profile.json5 | 42 + entry/hvigorfile.ts | 21 + entry/obfuscation-rules.txt | 21 + entry/oh-package.json5 | 25 + .../main/ets/@ohos.security.certManager.d.ts | 203 ++ .../src/main/ets/Application/AbilityStage.ts | 22 + .../MainAbility/CertPickerUiExtAbility.ets | 90 + entry/src/main/ets/MainAbility/MainAbility.ts | 75 + .../ets/MainAbility/MainExtensionAbility.ts | 71 + entry/src/main/ets/common/GlobalContext.ts | 105 + entry/src/main/ets/common/NavEntryKey.ets | 29 + .../ets/common/component/ComponentConfig.ets | 37 + .../ets/common/component/headComponent.ets | 85 + .../common/component/subEntryComponent.ets | 131 ++ .../ets/common/constants/FileFilterParams.ets | 20 + entry/src/main/ets/common/util/ConfigData.ts | 55 + entry/src/main/ets/common/util/SheetParam.ets | 24 + entry/src/main/ets/model/BundleModel.ets | 54 + .../ets/model/CertManagerVo/AppAuthorVo.ets | 28 + .../ets/model/CertManagerVo/AppInfoVo.ets | 19 + .../model/CertManagerVo/CertAbstractVo.ets | 30 + .../ets/model/CertManagerVo/CertInfoVo.ets | 75 + .../CertManagerVo/CredentialAbstractVo.ets | 26 + .../ets/model/CertManagerVo/CredentialVo.ets | 41 + .../ets/model/CertManagerVo/RouterInfoVo.ets | 42 + entry/src/main/ets/model/CertMangerModel.ets | 755 ++++++++ .../src/main/ets/model/CheckUserAuthModel.ets | 85 + entry/src/main/ets/model/FileIoModel.ets | 63 + .../ets/model/PreventScreenshotsModel.ets | 58 + .../main/ets/pages/CertificateInstallPage.ets | 212 ++ entry/src/main/ets/pages/cerEvidenceFa.ets | 861 +++++++++ .../main/ets/pages/certInstallFromStorage.ets | 413 ++++ entry/src/main/ets/pages/certManagerFa.ets | 349 ++++ entry/src/main/ets/pages/certPwdInput.ets | 160 ++ .../detail/AuthorizedAppManagementPage.ets | 173 ++ .../ets/pages/detail/CaSystemDetailPage.ets | 375 ++++ .../ets/pages/detail/CaUserDetailPage.ets | 269 +++ .../ets/pages/detail/CredPwdInputPage.ets | 207 ++ .../ets/pages/detail/CredSystemDetailPage.ets | 205 ++ .../ets/pages/detail/CredUserDetailPage.ets | 249 +++ .../src/main/ets/pages/picker/CaCertPage.ets | 55 + .../ets/pages/picker/CertManagerSheetFa.ets | 377 ++++ .../main/ets/pages/picker/CredListPage.ets | 52 + .../src/main/ets/pages/picker/InstallPage.ets | 51 + entry/src/main/ets/pages/requestAuth.ets | 249 +++ entry/src/main/ets/pages/trustedCa.ets | 841 ++++++++ .../ets/presenter/CmAppCredAuthPresenter.ets | 120 ++ .../src/main/ets/presenter/CmFaPresenter.ets | 193 ++ .../main/ets/presenter/CmInstallPresenter.ets | 197 ++ .../ets/presenter/CmShowAppCredPresenter.ets | 147 ++ .../ets/presenter/CmShowSysCaPresenter.ets | 93 + .../ets/presenter/CmShowSysCredPresenter.ets | 107 ++ .../ets/presenter/CmShowUserCaPresenter.ets | 108 ++ entry/src/main/module.json5 | 131 ++ .../main/resources/base/element/color.json | 272 +++ .../main/resources/base/element/float.json | 1700 +++++++++++++++++ .../main/resources/base/element/string.json | 435 +++++ .../main/resources/base/media/background.png | Bin 0 -> 57364 bytes .../main/resources/base/media/foreground.png | Bin 0 -> 12430 bytes .../src/main/resources/base/media/ic_back.svg | 13 + .../main/resources/base/media/ic_close.svg | 3 + .../main/resources/base/media/ic_delete.svg | 13 + .../src/main/resources/base/media/ic_move.svg | 1 + .../resources/base/media/ic_public_cancel.svg | 13 + .../resources/base/media/ic_public_close.svg | 13 + .../base/media/ic_settings_arrow.svg | 28 + entry/src/main/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/media/layered_image.json | 7 + .../main/resources/base/media/startIcon.png | Bin 0 -> 20093 bytes .../resources/base/profile/backup_config.json | 3 + .../resources/base/profile/main_pages.json | 12 + .../main/resources/en_US/element/string.json | 436 +++++ .../main/resources/phone/element/float.json | 1012 ++++++++++ .../main/resources/phone/element/string.json | 48 + .../resources/rawfile/security_privacy.json | 7 + .../main/resources/zh_CN/element/string.json | 48 + entry/src/ohosTest/ets/test/Ability.test.ets | 50 + entry/src/ohosTest/ets/test/List.test.ets | 20 + entry/src/ohosTest/module.json5 | 27 + hvigor/hvigor-config.json5 | 26 + hvigorfile.ts | 21 + hvigorw | 64 + hvigorw.bat | 79 + oh-package.json5 | 28 + signature/OpenHarmony.p12 | Bin 0 -> 8252 bytes signature/OpenHarmonyApplication.cer | 44 + signature/OpenHarmonyProfileRelease.pem | 44 + .../ac/f9d9d313be7042d4933e55eb1e8402f6 | 1 + .../ce/f7cfa6201a1644c5a0ba82969741aea2 | Bin 0 -> 48 bytes .../fd/0/4667d093e8bc476ca6f801834e185457 | 1 + .../fd/1/f5ed619c706f4a4db277ca6beb69de56 | 1 + .../fd/2/3aa4a14d432a48efb922fca804629ff8 | 1 + signature/privacyCenter.p7b | Bin 0 -> 3946 bytes 105 files changed, 13431 insertions(+), 69 deletions(-) create mode 100755 AppScope/app.json5 create mode 100755 AppScope/resources/base/element/color.json create mode 100755 AppScope/resources/base/element/string.json create mode 100755 AppScope/resources/base/media/app_icon.png create mode 100755 LICENSE create mode 100755 OAT.xml delete mode 100644 README.en.md create mode 100755 build-profile.json5 create mode 100755 bundle.json create mode 100755 doc/image/image-20220727141455437.png create mode 100755 entry/.gitignore create mode 100755 entry/build-profile.json5 create mode 100755 entry/hvigorfile.ts create mode 100755 entry/obfuscation-rules.txt create mode 100755 entry/oh-package.json5 create mode 100755 entry/src/main/ets/@ohos.security.certManager.d.ts create mode 100755 entry/src/main/ets/Application/AbilityStage.ts create mode 100755 entry/src/main/ets/MainAbility/CertPickerUiExtAbility.ets create mode 100755 entry/src/main/ets/MainAbility/MainAbility.ts create mode 100755 entry/src/main/ets/MainAbility/MainExtensionAbility.ts create mode 100755 entry/src/main/ets/common/GlobalContext.ts create mode 100755 entry/src/main/ets/common/NavEntryKey.ets create mode 100755 entry/src/main/ets/common/component/ComponentConfig.ets create mode 100755 entry/src/main/ets/common/component/headComponent.ets create mode 100755 entry/src/main/ets/common/component/subEntryComponent.ets create mode 100755 entry/src/main/ets/common/constants/FileFilterParams.ets create mode 100755 entry/src/main/ets/common/util/ConfigData.ts create mode 100755 entry/src/main/ets/common/util/SheetParam.ets create mode 100755 entry/src/main/ets/model/BundleModel.ets create mode 100755 entry/src/main/ets/model/CertManagerVo/AppAuthorVo.ets create mode 100755 entry/src/main/ets/model/CertManagerVo/AppInfoVo.ets create mode 100755 entry/src/main/ets/model/CertManagerVo/CertAbstractVo.ets create mode 100755 entry/src/main/ets/model/CertManagerVo/CertInfoVo.ets create mode 100755 entry/src/main/ets/model/CertManagerVo/CredentialAbstractVo.ets create mode 100755 entry/src/main/ets/model/CertManagerVo/CredentialVo.ets create mode 100755 entry/src/main/ets/model/CertManagerVo/RouterInfoVo.ets create mode 100755 entry/src/main/ets/model/CertMangerModel.ets create mode 100755 entry/src/main/ets/model/CheckUserAuthModel.ets create mode 100755 entry/src/main/ets/model/FileIoModel.ets create mode 100755 entry/src/main/ets/model/PreventScreenshotsModel.ets create mode 100755 entry/src/main/ets/pages/CertificateInstallPage.ets create mode 100755 entry/src/main/ets/pages/cerEvidenceFa.ets create mode 100755 entry/src/main/ets/pages/certInstallFromStorage.ets create mode 100755 entry/src/main/ets/pages/certManagerFa.ets create mode 100755 entry/src/main/ets/pages/certPwdInput.ets create mode 100755 entry/src/main/ets/pages/detail/AuthorizedAppManagementPage.ets create mode 100755 entry/src/main/ets/pages/detail/CaSystemDetailPage.ets create mode 100755 entry/src/main/ets/pages/detail/CaUserDetailPage.ets create mode 100755 entry/src/main/ets/pages/detail/CredPwdInputPage.ets create mode 100755 entry/src/main/ets/pages/detail/CredSystemDetailPage.ets create mode 100755 entry/src/main/ets/pages/detail/CredUserDetailPage.ets create mode 100755 entry/src/main/ets/pages/picker/CaCertPage.ets create mode 100755 entry/src/main/ets/pages/picker/CertManagerSheetFa.ets create mode 100755 entry/src/main/ets/pages/picker/CredListPage.ets create mode 100755 entry/src/main/ets/pages/picker/InstallPage.ets create mode 100755 entry/src/main/ets/pages/requestAuth.ets create mode 100755 entry/src/main/ets/pages/trustedCa.ets create mode 100755 entry/src/main/ets/presenter/CmAppCredAuthPresenter.ets create mode 100755 entry/src/main/ets/presenter/CmFaPresenter.ets create mode 100755 entry/src/main/ets/presenter/CmInstallPresenter.ets create mode 100755 entry/src/main/ets/presenter/CmShowAppCredPresenter.ets create mode 100755 entry/src/main/ets/presenter/CmShowSysCaPresenter.ets create mode 100755 entry/src/main/ets/presenter/CmShowSysCredPresenter.ets create mode 100755 entry/src/main/ets/presenter/CmShowUserCaPresenter.ets create mode 100755 entry/src/main/module.json5 create mode 100755 entry/src/main/resources/base/element/color.json create mode 100755 entry/src/main/resources/base/element/float.json create mode 100755 entry/src/main/resources/base/element/string.json create mode 100755 entry/src/main/resources/base/media/background.png create mode 100755 entry/src/main/resources/base/media/foreground.png create mode 100755 entry/src/main/resources/base/media/ic_back.svg create mode 100755 entry/src/main/resources/base/media/ic_close.svg create mode 100755 entry/src/main/resources/base/media/ic_delete.svg create mode 100755 entry/src/main/resources/base/media/ic_move.svg create mode 100755 entry/src/main/resources/base/media/ic_public_cancel.svg create mode 100755 entry/src/main/resources/base/media/ic_public_close.svg create mode 100755 entry/src/main/resources/base/media/ic_settings_arrow.svg create mode 100755 entry/src/main/resources/base/media/icon.png create mode 100755 entry/src/main/resources/base/media/layered_image.json create mode 100755 entry/src/main/resources/base/media/startIcon.png create mode 100755 entry/src/main/resources/base/profile/backup_config.json create mode 100755 entry/src/main/resources/base/profile/main_pages.json create mode 100755 entry/src/main/resources/en_US/element/string.json create mode 100755 entry/src/main/resources/phone/element/float.json create mode 100755 entry/src/main/resources/phone/element/string.json create mode 100755 entry/src/main/resources/rawfile/security_privacy.json create mode 100755 entry/src/main/resources/zh_CN/element/string.json create mode 100755 entry/src/ohosTest/ets/test/Ability.test.ets create mode 100755 entry/src/ohosTest/ets/test/List.test.ets create mode 100755 entry/src/ohosTest/module.json5 create mode 100755 hvigor/hvigor-config.json5 create mode 100755 hvigorfile.ts create mode 100755 hvigorw create mode 100755 hvigorw.bat create mode 100755 oh-package.json5 create mode 100755 signature/OpenHarmony.p12 create mode 100755 signature/OpenHarmonyApplication.cer create mode 100755 signature/OpenHarmonyProfileRelease.pem create mode 100755 signature/material/ac/f9d9d313be7042d4933e55eb1e8402f6 create mode 100755 signature/material/ce/f7cfa6201a1644c5a0ba82969741aea2 create mode 100755 signature/material/fd/0/4667d093e8bc476ca6f801834e185457 create mode 100755 signature/material/fd/1/f5ed619c706f4a4db277ca6beb69de56 create mode 100755 signature/material/fd/2/3aa4a14d432a48efb922fca804629ff8 create mode 100755 signature/privacyCenter.p7b diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100755 index 0000000..d09ed65 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2024-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. + */ + +{ + "app": { + "bundleName": "com.ohos.certmanager", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/color.json b/AppScope/resources/base/element/color.json new file mode 100755 index 0000000..43e9ff2 --- /dev/null +++ b/AppScope/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "color_1", + "value": "#FFFFFF" + } + ] +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100755 index 0000000..bd57248 --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "security_privacy_center" + } + ] +} diff --git a/AppScope/resources/base/media/app_icon.png b/AppScope/resources/base/media/app_icon.png new file mode 100755 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 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 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.en.md b/README.en.md deleted file mode 100644 index a71df93..0000000 --- a/README.en.md +++ /dev/null @@ -1,40 +0,0 @@ -# applications_user_certificate_manager - -#### Description -用户证书管理应用是OpenHarmony中预置的系统应用,主要的功能包含: -1、系统预置CA证书的查看功能; -2、用户CA证书的安装、查看、启用禁用与卸载功能; -3、用户证书凭据的安装、查看、授权管理与卸载 -4、系统证书凭据(Wlan/vpn证书凭据)的安装、查看、卸载功能 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 7c065ef..dce6803 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,55 @@ -# applications_user_certificate_manager +# 用户证书管理 -#### 介绍 -用户证书管理应用是OpenHarmony中预置的系统应用,主要的功能包含: -1、系统预置CA证书的查看功能; -2、用户CA证书的安装、查看、启用禁用与卸载功能; -3、用户证书凭据的安装、查看、授权管理与卸载 -4、系统证书凭据(Wlan/vpn证书凭据)的安装、查看、卸载功能 +## 简介 -#### 软件架构 -软件架构说明 +用户证书管理是OpenHarmony中预置的系统应用,主要的功能包含系统根证书查看与启用禁用,用户根证书的安装、查看、启用禁用与卸载,公共业务证书的安装、查看、授权管理与卸载,私有业务证书的查看等功能。 +应用架构如图1所示。 +图1 用户证书管理架构图 -#### 安装教程 +![](doc/image/image-20220727141455437.png) -1. xxxx -2. xxxx -3. xxxx -#### 使用说明 +## 目录 -1. xxxx -2. xxxx -3. xxxx +``` +├── AppScope # 应用配置 +└── entry + └── src + └── main + ├── ets # 代码目录 + │   ├── Application # AbilityStage类实现 + │   ├── MainAbility # Ability类实现 + │   ├── model # model层功能类实现 + │   ├── pages # 页面展示实现 + │   ├── presenter # 页面presenter层功能类实现 + └── resources # 资源文件目录 +``` -#### 参与贡献 +## 说明 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +### 使用说明 +三方应用拉起用户证书管理应用并执行申请应用授权,使用方法如下: -#### 特技 +``` +//拉起应用申请应用授权 +globalThis.context.startAbilityForResult( +{ + bundleName: "com.ohos.certmanager", + abilityName: "MainAbility", + uri: "requestAuthorize", + parameters: { + appUid: appUid, //传入申请应用的appUid + } +}) +.then((data) => { + if (!data.resultCode) { + this.authUri = data.want.parameters.authUri; //授权成功后获取返回的authUri + } +}) +``` -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +## 相关仓 + +**security_certificate_manager** diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100755 index 0000000..6a67f3d --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2024-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. + */ + +{ + "app": { + "signingConfigs": [ + { + "name": "default", + "material": { + "storePassword": "000000161B64D26AFDA336B9D4E45469670FC05C21ACB86E817B3A0EBBEF557AEC2E73906C73", + "certpath": "signature/OpenHarmonyApplication.cer", + "keyAlias": "openharmony application release", + "keyPassword": "0000001669F0585B86DCE64A21563F2E7BA5320E86297F85E75F6EA4844BFB920AD864794581", + "profile": "signature/privacyCenter.p7b", + "signAlg": "SHA256withECDSA", + "storeFile": "signature/OpenHarmony.p12" + } + } + ], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compileSdkVersion": 12, + "compatibleSdkVersion": 12, + "targetSdkVersion": 12, + "runtimeOS": "OpenHarmony", + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "CertManager", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/bundle.json b/bundle.json new file mode 100755 index 0000000..cdc3802 --- /dev/null +++ b/bundle.json @@ -0,0 +1,34 @@ +{ + "name": "@ohos/user_certificate_manager", + "description": "user_certificate_manager", + "version": "5.1", + "license": "Apache License 2.0", + "publishAs": "code-segment", + "segment": { + "destPath": "applications/standard/user_certificate_manager" + }, + "dirs":{}, + "scripts": {}, + "author": {}, + "repository": "", + "component": { + "name": "user_certificate_manager", + "subsystem": "applications", + "syscap": [], + "features": [], + "adapted_system_type": [ + "standard" + ], + "hisysevent_config": [], + "rom": "1MB", + "ram": "1MB", + "deps": { + "components": [] + }, + "build": { + "sub_component": [], + "inner_kits": [], + "test": [] + } + } + } diff --git a/doc/image/image-20220727141455437.png b/doc/image/image-20220727141455437.png new file mode 100755 index 0000000000000000000000000000000000000000..b4102ad263aee8c2c955765341923f4629d2dea2 GIT binary patch literal 41862 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 { + (err: BusinessError, data: T): void; +} + +export interface BusinessError extends Error { + code: number; + data?: T; +} + +declare namespace CertManagerFunc { + function getSystemTrustedCertificateList(callback: AsyncCallback) : void; + function getSystemTrustedCertificateList() : Promise; + + function getSystemTrustedCertificate(certUri: string, callback: AsyncCallback) : void; + function getSystemTrustedCertificate(certUri: string) : Promise; + + function setCertificateStatus(certUri: string, store: number, status: boolean, callback: AsyncCallback) : void; + function setCertificateStatus(certUri: string, store: number, status: boolean) : Promise; + + function installUserTrustedCertificate(certificate: CertBlob, callback: AsyncCallback) : void; + function installUserTrustedCertificate(certificate: CertBlob,) : Promise; + + function uninstallAllUserTrustedCertificate(callback: AsyncCallback) : void; + function uninstallAllUserTrustedCertificate() : Promise; + + function uninstallUserTrustedCertificate(certUri: string, callback: AsyncCallback) : void; + function uninstallUserTrustedCertificate(certUri: string) : Promise; + + function getAllUserTrustedCertificates(callback: AsyncCallback) : void; + function getAllUserTrustedCertificates() : Promise; + + function getUserTrustedCertificate(certUri: string, callback: AsyncCallback) : void; + function getUserTrustedCertificate(certUri: string) : Promise; + + function installPublicCertificate(keystore: Uint8Array, keystorePwd: string, certAlias: string, callback: AsyncCallback) : void; + function installPublicCertificate(keystore: Uint8Array, keystorePwd: string, certAlias: string) : Promise; + + function installPrivateCertificate(keystore: Uint8Array, keystorePwd: string, certAlias: string, callback: AsyncCallback) : void; + function installPrivateCertificate(keystore: Uint8Array, keystorePwd: string, certAlias: string) : Promise; + + function generatePrivateCertificate(keyAlias: string, keyProperties: CMKeyProperties, callback: AsyncCallback) : void; + function generatePrivateCertificate(keyAlias: string, keyProperties: CMKeyProperties) : Promise; + + function updatePrivateCertificate(type: string, keyUri: string, certificate: CertBlob, callback: AsyncCallback) : void; + function updatePrivateCertificate(type: string, keyUri: string, certificate: CertBlob) : Promise; + + function uninstallAllAppCertificate(callback: AsyncCallback) : void; + function uninstallAllAppCertificate() : Promise; + + function uninstallPublicCertificate(keyUri: string, callback: AsyncCallback) : void; + function uninstallPublicCertificate(keyUri: string) : Promise; + + function uninstallPrivateCertificate(keyUri: string, callback: AsyncCallback) : void; + function uninstallPrivateCertificate(keyUri: string) : Promise; + + function getAllPublicCertificates(callback: AsyncCallback) : void; + function getAllPublicCertificates() : Promise; + + function getAllAppPrivateCertificates(callback: AsyncCallback) : void; + function getAllAppPrivateCertificates() : Promise; + + function getPublicCertificate(keyUri: string, callback: AsyncCallback) : void; + function getPublicCertificate(keyUri: string, ) : Promise; + + function getPrivateCertificate(keyUri: string, callback: AsyncCallback) : void; + function getPrivateCertificate(keyUri: string) : Promise; + + function grantPublicCertificate(keyUri: string, clientAppUid: string, callback: AsyncCallback) : void; + function grantPublicCertificate(keyUri: string, clientAppUid: string) : Promise; + + function isAuthorizedApp(keyUri: string, callback: AsyncCallback) : void; + function isAuthorizedApp(keyUri: string) : Promise; + + function getAuthorizedAppList(keyUri: string, callback: AsyncCallback) : void; + function getAuthorizedAppList(keyUri: string) : Promise; + + function removeGrantedPublicCertificate(keyUri: string, clientAppUid: string, callback: AsyncCallback) : void; + function removeGrantedPublicCertificate(keyUri: string, clientAppUid: string) : Promise; + + function init(authUri: string, spec: CMSignatureSpec, callback: AsyncCallback) : void; + function init(authUri: string, spec: CMSignatureSpec) : Promise; + + function update(handle: Uint8Array, data: Uint8Array, callback: AsyncCallback) : void; + function update(handle: Uint8Array, data: Uint8Array) : Promise; + + function finish(handle: Uint8Array, callback: AsyncCallback) : void; + function finish(handle: Uint8Array, signature: Uint8Array, callback: AsyncCallback) : void; + function finish(handle: Uint8Array, signature?: Uint8Array) : Promise; + + function abort(handle: Uint8Array, callback: AsyncCallback) : void; + function abort(handle: Uint8Array) : Promise; + + function installSystemAppCertificate(keystore: Uint8Array, keystorePwd: string, certAlias: string): Promise; + + function getAllSystemAppCertificates(): Promise; + + function getSystemAppCertificate(keyUri: string) : Promise; + + function uninstallSystemAppCertificate(keyUri: string) : Promise; + + export interface CertInfo { + uri: string; + certAlias: string; + status: boolean; + issuerName: string; + subjectName: string; + serial: string; + notBefore: string; + notAfter: string; + fingerprintSha256: string; + cert: Uint8Array; + } + + export interface CertAbstract { + uri: string; + certAlias: string; + status: boolean; + subjectName: string; + } + + export interface Credential { + type: string; + alias: string; + keyUri: string; + certNum: number; + keyNum: number; + credData:Uint8Array; + } + + export interface CredentialAbstract { + type: string; + alias: string; + keyUri: string; + } + + export interface CertBlob { + inData: Uint8Array; + alias: string; + } + + export interface CMResult { + certList?: Array; + certInfo?: CertInfo; + credentialList?: Array; + credential?: Credential; + appUidList?: Array; + uri?: string; + outData?: Uint8Array; + isAuth?: boolean; + } + + export interface CMKeyProperties { + type: string; + alg: string; + size: number; + padding: string; + purpose: string; + digest: string; + authType: string; + authTimeout: string; + } + + export enum CmKeyPurpose { + CM_KEY_PURPOSE_SIGN = 4, + CM_KEY_PURPOSE_VERIFY = 8, + } + + export interface CMSignatureSpec { + purpose: CmKeyPurpose; + } + + export interface CMHandle { + handle: Uint8Array; + } + + export enum CMErrorCode { + CM_SUCCESS = 0, + CM_ERROR_GENERIC = 17500001, + CM_ERROR_NO_FOUND = 17500002, + CM_ERROR_INCORRECT_FORMAT = 17500003, + CM_ERROR_MAX_CERT_COUNT_REACHED = 17500004, + CM_ERROR_NO_AUTHORIZATION = 17500005, + CM_ERROR_ALIAS_LENGTH_REACHED_LIMIT = 17500006, + CM_ERROR_PASSWORD_IS_ERR = 17500008 + } +} + +export default CertManagerFunc; diff --git a/entry/src/main/ets/Application/AbilityStage.ts b/entry/src/main/ets/Application/AbilityStage.ts new file mode 100755 index 0000000..2dedff4 --- /dev/null +++ b/entry/src/main/ets/Application/AbilityStage.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AbilityStage from '@ohos.app.ability.AbilityStage'; + +export default class MyAbilityStage extends AbilityStage { + onCreate(): void { + console.info('[CertManager] MyAbilityStage onCreate'); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/MainAbility/CertPickerUiExtAbility.ets b/entry/src/main/ets/MainAbility/CertPickerUiExtAbility.ets new file mode 100755 index 0000000..3200a81 --- /dev/null +++ b/entry/src/main/ets/MainAbility/CertPickerUiExtAbility.ets @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Want from '@ohos.app.ability.Want'; +import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; +import { GlobalContext, PwdStore } from '../common/GlobalContext'; +import UIExtensionAbility from '@ohos.app.ability.UIExtensionAbility'; +import { BusinessError } from '@ohos.base'; + +const PAGE_CA_INSTALL = 5; +const TAG = 'CertPickerUiExtAbility'; + +export default class CertPickerUiExtAbility extends UIExtensionAbility { + onCreate(): void { + console.info('[CertManager] CertPickerUiExtAbility onCreate'); + } + + onDestroy(): void { + console.info('[CertManager] CertPickerUiExtAbility onDestroy'); + } + + onSessionCreate(want: Want, session: UIExtensionContentSession): void { + console.info('[CertManager] CertPickerUiExtAbility onSessionCreate'); + + if (want === null || want === undefined) { + console.error('[CertManager] invalid want param'); + return; + } + let param: Record = { + 'session': session, + 'want': want + } + let storage: LocalStorage = new LocalStorage(param); + try { + if (this.isStartToInstall(want.parameters)) { + session.loadContent('pages/CertificateInstallPage', storage); + } else { + session.loadContent('pages/picker/CertManagerSheetFa', storage); + let pwdStore = new PwdStore(); + GlobalContext.getContext().setPwdStore(pwdStore); + GlobalContext.getContext().setAbilityWant(want); + } + } catch (err) { + let error = err as BusinessError; + console.error(TAG, `onSessionCreat load content failed: ${error?.code}, msg:${error?.message}`); + session.terminateSelf(); + return; + } + + try { + session.setWindowBackgroundColor('#00000000'); + } catch (err) { + console.error('[CertManager] CertPickerUiExtAbility setWindowBackgroundColor'); + } + } + + private isStartToInstall(parameters: Record | undefined): boolean { + if(parameters === undefined) { + return false; + } + return parameters['pageType'] === PAGE_CA_INSTALL; + } + onSessionDestroy(): void { + // Main window is destroyed, release UI related resources + GlobalContext.getContext().clearSession(); + console.info('[CertManager] CertPickerUiExtAbility onSessionDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + console.info('[CertManager] CertPickerUiExtAbility onForeground'); + } + + onBackground(): void { + // Ability has back to background + console.info('[CertManager] CertPickerUiExtAbility onBackground'); + } +} diff --git a/entry/src/main/ets/MainAbility/MainAbility.ts b/entry/src/main/ets/MainAbility/MainAbility.ts new file mode 100755 index 0000000..bc582e1 --- /dev/null +++ b/entry/src/main/ets/MainAbility/MainAbility.ts @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Ability from '@ohos.app.ability.UIAbility'; +import type Want from '@ohos.app.ability.Want'; +import type Window from '@ohos.window'; +import { GlobalContext, PwdStore } from '../common/GlobalContext'; + +export default class MainAbility extends Ability { + onCreate(want: Want, launchParam): void { + console.info('[CertManager] MainAbility onCreate'); + + if (want === null || want === undefined) { + console.error('[CertManager] invalid want param'); + return; + } + let pwdStore = new PwdStore(); + GlobalContext.getContext().setCmContext(this.context); + GlobalContext.getContext().setPwdStore(pwdStore); + GlobalContext.getContext().setAbilityWant(want); + GlobalContext.getContext().setFlag(false); + } + + onDestroy(): void { + console.info('[CertManager] MainAbility onDestroy'); + } + + onWindowStageCreate(windowStage: Window.WindowStage): void { + // Main window is created, set main page for this ability + console.info('[CertManager] MainAbility onWindowStageCreate'); + windowStage.loadContent('pages/certManagerFa', (err, data) => { + if (err.code) { + console.error('onWindowStageCreate failed, cause:' + JSON.stringify(err)); + return; + } + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + console.info('[CertManager] MainAbility onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + console.info('[CertManager] MainAbility onForeground'); + } + + onBackground(): void { + // Ability has back to background + console.info('[CertManager] MainAbility onBackground'); + } + + onNewWant(want: Want): void { + console.info('[CertManager] MainAbility onNewWant'); + + if (want === null || want === undefined) { + console.error('[CertManager] invalid want param'); + return; + } + GlobalContext.getContext().setAbilityWant(want); + } +}; diff --git a/entry/src/main/ets/MainAbility/MainExtensionAbility.ts b/entry/src/main/ets/MainAbility/MainExtensionAbility.ts new file mode 100755 index 0000000..81f58b6 --- /dev/null +++ b/entry/src/main/ets/MainAbility/MainExtensionAbility.ts @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Want from '@ohos.app.ability.Want'; +import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; +import { GlobalContext, PwdStore } from '../common/GlobalContext'; +import UIExtensionAbility from '@ohos.app.ability.UIExtensionAbility'; + +export default class MainExtensionAbility extends UIExtensionAbility { + onCreate(): void { + console.info('[CertManager] MainExtensionAbility onCreate'); + } + + onDestroy(): void { + console.info('[CertManager] MainExtensionAbility onDestroy'); + } + + onSessionCreate(want: Want, session: UIExtensionContentSession): void { + console.info('[CertManager] MainExtensionAbility onSessionCreate'); + + if (want === null || want === undefined) { + console.error('[CertManager] invalid want param'); + return; + } + let param: Record = { + 'session': session, + 'want': want + } + let storage: LocalStorage = new LocalStorage(param); + let pullType: string = want.parameters.pullType as string; + + if (pullType === 'systemCredInstall' || pullType === 'specifyInstall') { + session.loadContent('pages/certInstallFromStorage', storage); + } else { + session.loadContent('pages/certManagerFa', storage); + } + GlobalContext.getContext().setAbilityWant(want); + GlobalContext.getContext().setSession(session); + let pwdStore = new PwdStore(); + GlobalContext.getContext().setPwdStore(pwdStore); + GlobalContext.getContext().setFlag(true); + } + + onSessionDestroy(): void { + // Main window is destroyed, release UI related resources + GlobalContext.getContext().clearSession(); + console.info('[CertManager] MainExtensionAbility onSessionDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + console.info('[CertManager] MainExtensionAbility onForeground'); + } + + onBackground(): void { + // Ability has back to background + console.info('[CertManager] MainExtensionAbility onBackground'); + } +} diff --git a/entry/src/main/ets/common/GlobalContext.ts b/entry/src/main/ets/common/GlobalContext.ts new file mode 100755 index 0000000..77d257d --- /dev/null +++ b/entry/src/main/ets/common/GlobalContext.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type Want from '@ohos.app.ability.Want'; +import type UIAbilityContext from 'application/UIAbilityContext'; +import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; + +export class PwdStore { + private certPwd: string = ''; + + setCertPwd(pwd: string): void { + this.certPwd = pwd; + } + + getCertPwd(): string { + return this.certPwd; + } + + clearCertPwd(): void { + this.certPwd = ''; + } +} + +export class GlobalContext { + private constructor() { + }; + + private static instance: GlobalContext; + private context: UIAbilityContext; + private want: Want; + private pwdStore: PwdStore; + private session: UIExtensionContentSession; + private flag: Boolean; + + public static getContext(): GlobalContext { + if (!GlobalContext.instance) { + GlobalContext.instance = new GlobalContext(); + } + return GlobalContext.instance; + } + + getCmContext(): UIAbilityContext { + return this.context; + } + + getPwdStore(): PwdStore { + return this.pwdStore; + } + + getAbilityWant(): Want { + return this.want; + } + + getSession(): UIExtensionContentSession { + return this.session; + } + + getFlag(): Boolean { + return this.flag; + } + + setCmContext(context: UIAbilityContext): void { + this.context = context; + } + + setPwdStore(pwdStore: PwdStore): void { + this.pwdStore = pwdStore; + } + + setAbilityWant(want: Want): void { + this.want = want; + } + + setSession(session: UIExtensionContentSession): void { + this.session = session; + } + + setFlag(flag: Boolean): void { + this.flag = flag; + } + + clearAbilityWantUri(): void { + this.want.uri = ''; + } + + clearSession(): void { + this.session = undefined; + } + + clearAbilityWantParamsUri(): void { + this.want.parameters.uri = ''; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/common/NavEntryKey.ets b/entry/src/main/ets/common/NavEntryKey.ets new file mode 100755 index 0000000..5822df8 --- /dev/null +++ b/entry/src/main/ets/common/NavEntryKey.ets @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum NavEntryKey { + HOME_ENTRY = 'home_page', + CA_CERTIFICATE_ENTRY = 'ca_cert', + CREDENTIAL_LIST_ENTRY = 'cred_list', + INSTALL_ENTRY = 'install_from_storage', + CA_SYSTEM_DETAIL_ENTRY = 'ca_system_detail', + CA_USER_DETAIL_ENTRY = 'ca_user_detail', + CRED_SYSTEM_DETAIL_ENTRY = 'cred_system_detail', + CRED_USER_DETAIL_ENTRY = 'cred_user_detail', + AUTHORIZED_APP_ENTRY = 'authorized_app_management', + CRED_PWD_INPUT_ENTRY = 'cred_pwd_input', + + POP = 'pop' +} \ No newline at end of file diff --git a/entry/src/main/ets/common/component/ComponentConfig.ets b/entry/src/main/ets/common/component/ComponentConfig.ets new file mode 100755 index 0000000..090fac2 --- /dev/null +++ b/entry/src/main/ets/common/component/ComponentConfig.ets @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class ComponentConfig { + public WH_100_100 = '100%'; + public WH_30_100 = '30%'; + public WH_33_100 = '33%'; + public WH_35_100 = '35%'; + public WH_40_100 = '40%'; + public WH_45_100 = '45%'; + public WH_50_100 = '50%'; + public WH_55_100 = '55%'; + public WH_83_100 = '83%'; + public WH_90_100 = '90%'; + public value_20 = 20; + public font_20 = 20; + public MAX_LINES_1 = 1; + public MAX_LINES_2 = 2; + public MAX_LINES_3 = 3; + public DURATION_TIME = 200; +} + +let componentConfig = new ComponentConfig(); + +export default componentConfig as ComponentConfig; \ No newline at end of file diff --git a/entry/src/main/ets/common/component/headComponent.ets b/entry/src/main/ets/common/component/headComponent.ets new file mode 100755 index 0000000..87e1cec --- /dev/null +++ b/entry/src/main/ets/common/component/headComponent.ets @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import ComponentConfig from './ComponentConfig'; +import Router from '@ohos.router'; +import { GlobalContext } from '../GlobalContext'; + +const TAG = 'CertManager HeadComponent: '; + +/** + * head custom component + */ +@Component +export default struct HeadComponent { + private icBackIsVisibility: boolean = true; + private headName: string | Resource = ''; + @State isTouch: boolean = false; + + isStartBySheet: boolean = false; + onBackClicked?: () => void; + + build() { + Row() { + Stack({ alignContent: Alignment.Center }) { + Image($r('app.media.ic_back')) + .width($r('app.float.wh_value_24')) + .height($r('app.float.wh_value_24')) + .fillColor($r('sys.color.ohos_id_color_primary')) + } + .margin({ right: $r('app.float.wh_value_16') }) + .backgroundColor(this.isTouch ? $r('sys.color.ohos_id_color_click_effect') + : $r('sys.color.ohos_id_color_sub_background')) + .visibility(this.icBackIsVisibility ? Visibility.Visible : Visibility.None) + .onClick(() => { + if (this.isStartBySheet) { + this.onBackClicked?.(); + return; + } + let length = Router.getLength(); + console.info(TAG + 'router length: ' + Number(length)); + if (GlobalContext.getContext().getFlag()) { + Number(length) == 1 ? GlobalContext.getContext().getSession().sendData({'action': 'exit'}) : Router.back(); + } else { + Number(length) == 1 ? GlobalContext.getContext().getCmContext().terminateSelf() : Router.back(); + } + }) + .onTouch((event?: TouchEvent) => { + if (event?.type === TouchType.Down) { + this.isTouch = true; + } + if (event?.type === TouchType.Up) { + this.isTouch = false; + } + }); + + Text(this.headName) + .fontSize($r('app.float.head_font_20')) + .lineHeight($r('app.float.wh_value_33')) + .fontFamily('HarmonyHeiTi-Bold') + .fontWeight(FontWeight.Regular) + .maxFontScale(1) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .maxLines(ComponentConfig.MAX_LINES_1) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .textAlign(TextAlign.Start) + .margin({ top: $r('app.float.wh_value_13'), bottom: $r('app.float.wh_value_15') }); + } + .width(ComponentConfig.WH_100_100) + .padding({ left: $r('app.float.wh_value_12') }) + .height($r('app.float.wh_value_56')) + .alignItems(VerticalAlign.Center) + .align(Alignment.Start) + } +} diff --git a/entry/src/main/ets/common/component/subEntryComponent.ets b/entry/src/main/ets/common/component/subEntryComponent.ets new file mode 100755 index 0000000..57f09ab --- /dev/null +++ b/entry/src/main/ets/common/component/subEntryComponent.ets @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import ComponentConfig from './ComponentConfig'; + +/** + * Sub-Page Entry Component + */ +@Component +export struct SubEntryComponent { + private targetPage: string = ''; + private title: string | Resource = ''; + + onItemClicked?: (targetRouter: string) => void; + + @Styles normalStyle() { + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + }; + + @Styles pressedStyle() { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + }; + + build() { + Column() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Row() { + Text(this.title) + .fontSize($r('app.float.font_16')) + .lineHeight($r('app.float.wh_value_22')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ left: $r('app.float.wh_value_8') }) + .textAlign(TextAlign.Start); + } + + Image($r('app.media.ic_settings_arrow')) + .width($r('app.float.wh_value_12')) + .height($r('app.float.wh_value_24')) + .fillColor($r('sys.color.ohos_id_color_primary')) + .opacity($r('app.float.opacity_0_2')) + .margin({ right: $r('app.float.wh_value_8') }); + } + .borderRadius($r('app.float.radius_20')) + .height(ComponentConfig.WH_100_100) + .width(ComponentConfig.WH_100_100) + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }) + } + .onClick(event => { + this.onItemClicked?.(this.targetPage); + }) + .height($r('app.float.wh_value_48')) + .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary')); + } +} + + +/** + * Sub-Page Entry Component with EndText + */ +@Component +export struct SubEntryComponentWithEndText { + @Prop endText: string = ''; + private targetPage: string = ''; + private title: string | Resource = ''; + + @Styles normalStyle() { + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + }; + + @Styles pressedStyle() { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + }; + + build() { + Navigator({ target: this.targetPage }) { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Row() { + Text(this.title) + .fontSize($r('app.float.font_16')) + .lineHeight($r('app.float.wh_value_22')) + .fontWeight(FontWeight.Medium) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .margin({ left: $r('app.float.distance_8') }) + .textAlign(TextAlign.Start); + } + + Row() { + Text(this.endText) + .fontSize($r('app.float.font_14')) + .lineHeight($r('app.float.wh_value_19')) + .fontWeight(FontWeight.Regular) + .fontColor($r('sys.color.ohos_id_color_text_secondary')) + .margin({ right: $r('app.float.distance_4') }) + .textAlign(TextAlign.End); + Image('/res/image/ic_settings_arrow.svg') + .width($r('app.float.wh_value_12')) + .height($r('app.float.wh_value_24')) + .margin({ right: $r('app.float.distance_8') }) + .fillColor($r('sys.color.ohos_id_color_primary')) + .opacity($r('app.float.opacity_0_2')) + } + } + .height(ComponentConfig.WH_100_100) + .width(ComponentConfig.WH_100_100) + .borderRadius($r('app.float.radius_20')) + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }) + } + .padding($r('app.float.distance_4')) + .height($r('app.float.wh_value_56')) + .borderRadius($r('app.float.radius_24')) + .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary')); + } +} diff --git a/entry/src/main/ets/common/constants/FileFilterParams.ets b/entry/src/main/ets/common/constants/FileFilterParams.ets new file mode 100755 index 0000000..5e601ee --- /dev/null +++ b/entry/src/main/ets/common/constants/FileFilterParams.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default class FilterParams { + public static readonly MAX_SELECT_FILE_NUM = 1; + public static readonly CERT_FILE_SUFFIX = ['.pem,.cer,.crt,.der']; + public static readonly CREDENTIAL_FILE_SUFFIX = ['.pfx,.p12']; +} \ No newline at end of file diff --git a/entry/src/main/ets/common/util/ConfigData.ts b/entry/src/main/ets/common/util/ConfigData.ts new file mode 100755 index 0000000..cc31767 --- /dev/null +++ b/entry/src/main/ets/common/util/ConfigData.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum WidthPercent { + WH_AUTO = 'auto', + WH_100_100 = '100%', + WH_3_100 = '3%', + WH_4_100 = '4%', + WH_20_100 = '20%', + WH_25_100 = '25%', + WH_30_100 = '30%', + WH_33_100 = '33%', + WH_35_100 = '35%', + WH_40_100 = '40%', + WH_43_100 = '43%', + WH_45_100 = '45%', + WH_50_100 = '50%', + WH_53_100 = '53%', + WH_55_100 = '55%', + WH_60_100 = '60%', + WH_65_100 = '65%', + WH_70_100 = '70%', + WH_75_100 = '75%', + WH_80_100 = '80%', + WH_85_100 = '85%', + WH_88_100 = '88%', + WH_90_100 = '90%', + WH_92_100 = '92%', + WH_93_100 = '93%', + WH_94_100 = '94%' +}; + +export enum LocationChoice { + SWITCH_BUTTON_X_OFFSET = '-4vp', + DIALOG_DY_OFFSET = '-16vp' +}; + +export enum ConfigValue { + PWD_MAX_LENGTH = 64, + ALIAS_MAX_LENGTH = 16, + APP_AUTH_MAX_LENGTH = 4, + REQUEST_AUTH_MAX_LENGTH = 5 +}; \ No newline at end of file diff --git a/entry/src/main/ets/common/util/SheetParam.ets b/entry/src/main/ets/common/util/SheetParam.ets new file mode 100755 index 0000000..a442bfa --- /dev/null +++ b/entry/src/main/ets/common/util/SheetParam.ets @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2024-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. + */ + +@Observed +export class SheetParam { + public sheetType: number = -1; + public sheetMinHeight: number = 0; + public lastSheetPage: string = ''; + + constructor() { + } +} \ No newline at end of file diff --git a/entry/src/main/ets/model/BundleModel.ets b/entry/src/main/ets/model/BundleModel.ets new file mode 100755 index 0000000..0fa3db6 --- /dev/null +++ b/entry/src/main/ets/model/BundleModel.ets @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import bundleManager from '@ohos.bundle.bundleManager'; +import bundleResManager from '@ohos.bundle.bundleResourceManager'; +import { CMModelErrorCode } from '../model/CertMangerModel'; +import { AppInfoVo } from './CertManagerVo/AppInfoVo'; +import { BusinessError } from '@ohos.base'; + +const TAG = 'certManager BUNDLE:'; + +export class BundleNameModel { + async getAppInfoList(appUid: number, callback: Function): Promise { + console.info(TAG + 'getAppInfoList enter uid: ' + appUid); + try { + let appInfo: AppInfoVo = { + appImage: '', + appName: '', + }; + + let appCloneIdentity = await bundleManager.getAppCloneIdentity(appUid); + console.info(TAG + 'appBundleName: ' + appCloneIdentity.bundleName + ', appIndex: ' + appCloneIdentity.appIndex); + + let bundleFlags = bundleResManager.ResourceFlag.GET_RESOURCE_INFO_ALL; + let resourceInfo = bundleResManager.getBundleResourceInfo(appCloneIdentity.bundleName, bundleFlags, + appCloneIdentity.appIndex); + appInfo.appName = resourceInfo.label; + appInfo.appImage = resourceInfo.icon; + console.info(TAG + 'get bundle info success'); + + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, appInfo); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getAppInfoList failed, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION, undefined); + } + } +} + +let bundleNameModel = new BundleNameModel(); + +export default bundleNameModel as BundleNameModel; diff --git a/entry/src/main/ets/model/CertManagerVo/AppAuthorVo.ets b/entry/src/main/ets/model/CertManagerVo/AppAuthorVo.ets new file mode 100755 index 0000000..7ebd3b9 --- /dev/null +++ b/entry/src/main/ets/model/CertManagerVo/AppAuthorVo.ets @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class AppAuthorVo { + public appImage: string; + public uid: string; + public appName: string; + public status: boolean; + + constructor(appImage: string, uid: string, appName: string, status: boolean) { + this.appImage = appImage; + this.uid = uid; + this.appName = appName; + this.status = status; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/model/CertManagerVo/AppInfoVo.ets b/entry/src/main/ets/model/CertManagerVo/AppInfoVo.ets new file mode 100755 index 0000000..354cd0d --- /dev/null +++ b/entry/src/main/ets/model/CertManagerVo/AppInfoVo.ets @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2023-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class AppInfoVo { + public appImage: string = ''; + public appName: string = ''; +} \ No newline at end of file diff --git a/entry/src/main/ets/model/CertManagerVo/CertAbstractVo.ets b/entry/src/main/ets/model/CertManagerVo/CertAbstractVo.ets new file mode 100755 index 0000000..f20b1f7 --- /dev/null +++ b/entry/src/main/ets/model/CertManagerVo/CertAbstractVo.ets @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class CertAbstractVo { + public uri: string; + public certAlias: string; + public status: boolean; + public subjectName: string; + public subjectNameCN: string; + + constructor(uri: string, certAlias: string, status: boolean, subjectName: string, subjectNameCN: string) { + this.uri = uri; + this.certAlias = certAlias; + this.status = status; + this.subjectName = subjectName; + this.subjectNameCN = subjectNameCN; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/model/CertManagerVo/CertInfoVo.ets b/entry/src/main/ets/model/CertManagerVo/CertInfoVo.ets new file mode 100755 index 0000000..9bdd083 --- /dev/null +++ b/entry/src/main/ets/model/CertManagerVo/CertInfoVo.ets @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class CertInfoVo { + public uri: string; + public certAlias: string; + public status: boolean; + public issuerName: string; + public subjectName: string; + public serial: string; + public notBefore: string; + public notAfter: string; + public fingerprintSha256: string; + public cert: Uint8Array; + public subjectNameMap: Map; + public issuerNameMap: Map; + public dateMap: Map; + + constructor( + uri: string, + certAlias: string, + status: boolean, + issuerName: string, + subjectName: string, + serial: string, + notBefore: string, + notAfter: string, + fingerprintSha256: string, + cert: Uint8Array, + subjectNameMap: Map, + issuerNameMap: Map, + dateMap: Map) { + this.uri = uri; + this.certAlias = certAlias; + this.status = status; + this.issuerName = issuerName; + this.subjectName = subjectName; + this.serial = serial; + this.notBefore = notBefore; + this.notAfter = notAfter; + this.fingerprintSha256 = fingerprintSha256; + this.cert = cert; + this.subjectNameMap = subjectNameMap; + this.issuerNameMap = issuerNameMap; + this.dateMap = dateMap; + } + + clearCertInfoVo(): void { + this.uri = ''; + this.certAlias = ''; + this.status = false; + this.issuerName = ''; + this.subjectName = ''; + this.serial = ''; + this.notBefore = ''; + this.notAfter = ''; + this.fingerprintSha256 = ''; + this.cert = new Uint8Array(); + this.subjectNameMap.clear(); + this.issuerNameMap.clear(); + this.dateMap.clear(); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/model/CertManagerVo/CredentialAbstractVo.ets b/entry/src/main/ets/model/CertManagerVo/CredentialAbstractVo.ets new file mode 100755 index 0000000..0a9cc4d --- /dev/null +++ b/entry/src/main/ets/model/CertManagerVo/CredentialAbstractVo.ets @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class CredentialAbstractVo { + public credType: string; + public alias: string; + public keyUri: string; + + constructor(credType: string, alias: string, keyUri: string) { + this.credType = credType; + this.alias = alias; + this.keyUri = keyUri; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/model/CertManagerVo/CredentialVo.ets b/entry/src/main/ets/model/CertManagerVo/CredentialVo.ets new file mode 100755 index 0000000..c7d6e72 --- /dev/null +++ b/entry/src/main/ets/model/CertManagerVo/CredentialVo.ets @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class CredentialVo { + public credType: string; + public alias: string; + public keyUri: string; + public certNum: number; + public keyNum: number; + public credData: Uint8Array; + + constructor(credType: string, alias: string, keyUri: string, certNum: number, keyNum: number, credData: Uint8Array) { + this.credType = credType; + this.alias = alias; + this.keyUri = keyUri; + this.certNum = certNum; + this.keyNum = keyNum; + this.credData = credData; + } + + clearCredentialVo(): void { + this.credType = ''; + this.alias = ''; + this.keyUri = ''; + this.certNum = 0; + this.keyNum = 0; + this.credData = new Uint8Array(); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/model/CertManagerVo/RouterInfoVo.ets b/entry/src/main/ets/model/CertManagerVo/RouterInfoVo.ets new file mode 100755 index 0000000..d21accf --- /dev/null +++ b/entry/src/main/ets/model/CertManagerVo/RouterInfoVo.ets @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class RouterAppUidVo { + public appUid: string; + + constructor(appUid: string) { + this.appUid = appUid; + } +} + +export class RouterFileVo { + public uri: string; + public suffix: string; + + constructor(uri: string, suffix: string) { + this.uri = uri; + this.suffix = suffix; + } +} + +export class RouterParams { + public fileInfo: RouterFileVo; + public pwd: string + + constructor(fileInfo: RouterFileVo, pwd: string) { + this.fileInfo = fileInfo; + this.pwd = pwd + } +} \ No newline at end of file diff --git a/entry/src/main/ets/model/CertMangerModel.ets b/entry/src/main/ets/model/CertMangerModel.ets new file mode 100755 index 0000000..cf9e57b --- /dev/null +++ b/entry/src/main/ets/model/CertMangerModel.ets @@ -0,0 +1,755 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CertAbstractVo } from './CertManagerVo/CertAbstractVo'; +import { CertInfoVo } from './CertManagerVo/CertInfoVo'; +import { CredentialAbstractVo } from './CertManagerVo/CredentialAbstractVo'; +import { CredentialVo } from './CertManagerVo/CredentialVo'; +import { BusinessError } from '@ohos.base'; +import CertManager from '@ohos.security.certManager'; +import cert from '@ohos.security.cert'; + +const TAG = 'CertManager Model: '; + +export enum CMModelErrorCode { + CM_MODEL_ERROR_SUCCESS = 0, + CM_MODEL_ERROR_FAILED = -1, + CM_MODEL_ERROR_EXCEPTION = -2, + CM_MODEL_ERROR_UNKNOWN_OPT = -3, + CM_MODEL_ERROR_NOT_SUPPORT = -4, + CM_MODEL_ERROR_NOT_FOUND = -5, + CM_MODEL_ERROR_INCORRECT_FORMAT = -6, + CM_MODEL_ERROR_MAX_QUANTITY_REACHED = -7, + CM_MODEL_ERROR_ALIAS_LENGTH_REACHED_LIMIT = -8, + CM_MODEL_ERROR_PASSWORD_ERR = -9, + CM_MODEL_ERROR_ADVANCED_SECURITY = -10 +} + +export enum CMModelOptType { + CM_MODEL_OPT_UNKNOWN = 0, + CM_MODEL_OPT_SYSTEM_CA = 1, + CM_MODEL_OPT_USER_CA = 2, + CM_MODEL_OPT_APP_CRED = 3, + CM_MODEL_OPT_PRIVATE_CRED = 4, + CM_MODEL_OPT_SYSTEM_CRED = 5, +} + +export enum CertManagerStore { + /* credential certificate store for end entity certificates. */ + CERT_MANAGER_CREDENTIAL_STORE = 0, + /* read only, updated by system only. */ + CERT_MANAGER_SYSTEM_TRUSTED_STORE = 1, + /* modifiable by applications and user. */ + CERT_MANAGER_USER_TRUSTED_STORE = 2, + /* application specific trusted certificate store; modifiable by the application only. */ + CERT_MANAGER_APPLICATION_TRUSTED_STORE = 3, +} + +export class CertMangerModel { + getCertOrCredList(optType: CMModelOptType, callback: Function): void { + console.info(TAG + 'getCertOrCredList start'); + + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + this.getSystemTrustedCertificateList((errCode: CMModelErrorCode, certList: Array) => { + callback(errCode, certList); + }); + break; + case CMModelOptType.CM_MODEL_OPT_USER_CA: + this.getAllUserTrustedCertificates((errCode: CMModelErrorCode, certList: Array) => { + callback(errCode, certList); + }); + break; + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + this.getAppCredList((errCode: CMModelErrorCode, credList: Array) => { + callback(errCode, credList); + }); + break; + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + this.getSystemCredList((errCode: CMModelErrorCode, credList: Array) => { + callback(errCode, credList) + }) + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT, undefined); + break; + } + } + + getCertOrCred(optType: CMModelOptType, uri: string, callback: Function): void { + console.info(TAG + 'getCertOrCred start'); + + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + this.getSystemTrustedCertificate(uri, (errCode: CMModelErrorCode, certInfo: CertInfoVo) => { + callback(errCode, certInfo); + }); + break; + case CMModelOptType.CM_MODEL_OPT_USER_CA: + this.getUserTrustedCertificate(uri, (errCode: CMModelErrorCode, certInfo: CertInfoVo) => { + callback(errCode, certInfo); + }); + break; + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + this.getAppCredential(uri, (errCode: CMModelErrorCode, credInfo: CredentialVo) => { + callback(errCode, credInfo); + }); + break; + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + this.getSystemCredential(uri, (errCode: CMModelErrorCode, credInfo: CredentialVo) => { + callback(errCode, credInfo) + }) + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT, undefined); + break; + } + } + + deleteCertOrCred(optType: CMModelOptType, uri: string, callback: Function): void { + console.info(TAG + 'deleteCertOrCred start'); + + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_USER_CA: + this.deleteUserTrustedCertificate(uri, (errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + this.deleteAppCredential(uri, (errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + callback(CMModelErrorCode.CM_MODEL_ERROR_NOT_SUPPORT); + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + this.deleteSystemCredential(uri, (errCode: CMModelErrorCode) => { + callback(errCode) + }) + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT); + break; + } + } + + setCertStatus(optType: CMModelOptType, uri: string, status: boolean, callback: Function): void { + console.info(TAG + 'setCertStatus start'); + + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_USER_CA: + this.setCertificateStatus(uri, CertManagerStore.CERT_MANAGER_USER_TRUSTED_STORE, status, + (errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + this.setCertificateStatus(uri, CertManagerStore.CERT_MANAGER_SYSTEM_TRUSTED_STORE, status, + (errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + callback(CMModelErrorCode.CM_MODEL_ERROR_NOT_SUPPORT); + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT); + break; + } + } + + delAllCertOrCred(optType: CMModelOptType, callback: Function): void { + console.info(TAG + 'delAllCertOrCred start'); + + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_USER_CA: + this.delAllUserCertificate((errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + this.delAllAppCredential((errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + callback(CMModelErrorCode.CM_MODEL_ERROR_NOT_SUPPORT); + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT); + break; + } + } + + getAuthAppList(optType: CMModelOptType, uri: string, callback: Function): void { + console.info(TAG + 'getAuthAppList start'); + + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + this.getAuthorizedAppList(uri, (errCode: CMModelErrorCode, appUidList: Array) => { + callback(errCode, appUidList); + }); + break; + case CMModelOptType.CM_MODEL_OPT_USER_CA: + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + callback(CMModelErrorCode.CM_MODEL_ERROR_NOT_SUPPORT); + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT); + break; + } + } + + setAppAuthPromise(optType: CMModelOptType, uri: string, appUid: string, status: boolean): Promise { + console.info(TAG + 'setAppAuth start'); + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + return new Promise((resolve, reject) => { + this.setAuthorizedAppStatus(uri, appUid, status, (errCode: CMModelErrorCode, data: string) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + resolve(void(data)); + } else { + reject(errCode); + } + }); + }); + default: + return new Promise((resolve, reject) => { + reject(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT); + }); + } + } + + setAppAuth(optType: CMModelOptType, uri: string, appUid: string, status: boolean, callback: Function): void { + console.info(TAG + 'setAppAuth start'); + + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + this.setAuthorizedAppStatus(uri, appUid, status, (errCode: CMModelErrorCode, data: string) => { + callback(errCode, data); + }); + break; + case CMModelOptType.CM_MODEL_OPT_USER_CA: + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + callback(CMModelErrorCode.CM_MODEL_ERROR_NOT_SUPPORT); + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT); + break; + } + } + + installCertOrCred(optType: CMModelOptType, alias: string, data: Uint8Array, pwd: string, callback: Function): void { + console.info(TAG + 'installCertOrCred start'); + switch (optType) { + case CMModelOptType.CM_MODEL_OPT_USER_CA: + this.installUserCertificate(data, alias, (errCode: CMModelErrorCode, uri: string) => { + callback(errCode, uri); + }); + break; + case CMModelOptType.CM_MODEL_OPT_APP_CRED: + this.installPublicCertificate(data, alias, pwd, (errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED: + this.installSystemAppCertificate(data, alias, pwd, (errCode: CMModelErrorCode) => { + callback(errCode); + }); + break; + case CMModelOptType.CM_MODEL_OPT_SYSTEM_CA: + callback(CMModelErrorCode.CM_MODEL_ERROR_NOT_SUPPORT); + break; + default: + callback(CMModelErrorCode.CM_MODEL_ERROR_UNKNOWN_OPT); + break; + } + } + + private async getSystemTrustedCertificateList(callback: Function): Promise { + console.info(TAG + 'getSystemTrustedCertificateList start'); + try { + let subjectNameCN: string = ''; + let result = await CertManager.getSystemTrustedCertificateList(); + let certList: CertAbstractVo[] = []; + let regex: RegExp = new RegExp('(?<=CN=).*?(?=,)', 'g'); + if (result.certList !== undefined) { + for (let i = 0; i < result.certList.length; i++) { + if (result.certList[i].subjectName.length !== 0) { + let temp = result.certList[i].subjectName.match(regex); + subjectNameCN = (temp !== undefined) ? String(temp) : ''; + console.info('subjectNameCN is:' + subjectNameCN); + } + certList.push(new CertAbstractVo(String(result.certList[i].uri), String(result.certList[i].certAlias), + Boolean(result.certList[i].status), String(result.certList[i].subjectName), String(subjectNameCN))); + } + console.info(TAG + 'getSystemTrustedCertificateList end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, certList); + } else { + console.error(TAG + 'getSystemTrustedCertificateList failed, undefined'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getSystemTrustedCertificateList err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION, undefined); + } + } + + private async getSystemTrustedCertificate(certUri: string, callback: Function): Promise { + console.info(TAG + 'getSystemTrustedCertificate start'); + try { + let result = await CertManager.getSystemTrustedCertificate(certUri); + let subjectNameMap: Map = new Map(); + let issuerNameMap: Map = new Map(); + let dateMap: Map = new Map(); + let regex1: RegExp = new RegExp('(?<=CN=).*?(?=,)', 'g'); + let regex2: RegExp = new RegExp('(?<=OU=).*?(?=,)', 'g'); + let regex3: RegExp = new RegExp('(?<=O=).*', 'g'); + + if (result.certInfo !== undefined) { + if (result.certInfo.subjectName.length !== 0) { + let subjectNameCN = result.certInfo.subjectName.match(regex1); + console.info('subjectNameCN is:' + subjectNameCN); + let subjectNameOU = result.certInfo.subjectName.match(regex2); + console.info('subjectNameOU is:' + subjectNameOU); + let subjectNameO = result.certInfo.subjectName.match(regex3); + console.info('subjectNameO is:' + subjectNameO); + subjectNameMap.set('常用名称:', String(subjectNameCN)); + subjectNameMap.set('组织:', String(subjectNameO)); + subjectNameMap.set('组织单位:', String(subjectNameOU)); + subjectNameMap.set('序列号:', String(result.certInfo.serial)); + } + if (result.certInfo.issuerName.length !== 0) { + let issuerNameCN = result.certInfo.issuerName.match(regex1); + console.info('issuerNameCN is:' + issuerNameCN); + let issuerNameOU = result.certInfo.issuerName.match(regex2); + console.info('issuerNameOU is:' + issuerNameOU); + let issuerNameO = result.certInfo.issuerName.match(regex3); + console.info('issuerNameO is:' + issuerNameO); + issuerNameMap.set('常用名称:', String(issuerNameCN)); + issuerNameMap.set('组织:', String(issuerNameO)); + issuerNameMap.set('组织单位:', String(issuerNameOU)); + } + dateMap.set('颁发时间:', String(result.certInfo.notBefore)); + dateMap.set('有效期至:', String(result.certInfo.notAfter)); + + let certData: Uint8Array = result.certInfo.cert; + let certInfo = new CertInfoVo(String(result.certInfo.uri), String(result.certInfo.certAlias), + Boolean(result.certInfo.status), String(result.certInfo.issuerName), String(result.certInfo.subjectName), + String(result.certInfo.serial), String(result.certInfo.notBefore), + String(result.certInfo.notAfter), String(result.certInfo.fingerprintSha256), + certData, subjectNameMap, issuerNameMap, dateMap); + console.info(TAG + 'getSystemTrustedCertificate end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, certInfo); + } else { + console.error(TAG + 'getSystemTrustedCertificate failed, undefined'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getSystemTrustedCertificate err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION, undefined); + } + } + + private async getAllUserTrustedCertificates(callback: Function): Promise { + console.info(TAG + 'getAllUserTrustedCertificates start'); + try { + let subjectNameCN: string = ''; + let result = await CertManager.getAllUserTrustedCertificates(); + let certList: CertAbstractVo[] = []; + let regex: RegExp = new RegExp('(?<=CN=).*?(?=,)', 'g'); + if (result.certList !== undefined) { + for (let i = 0; i < result.certList.length; i++) { + if (result.certList[i].subjectName.length !== 0) { + let temp = result.certList[i].subjectName.match(regex); + subjectNameCN = (temp !== undefined) ? String(temp) : ''; + console.info('subjectNameCN is:' + subjectNameCN); + } + if (String(result.certList[i].uri).indexOf('u=0;') === -1) { + certList.push(new CertAbstractVo(String(result.certList[i].uri), String(result.certList[i].certAlias), + Boolean(result.certList[i].status), String(result.certList[i].subjectName), String(subjectNameCN))); + } + } + console.info(TAG + 'getAllUserTrustedCertificates end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, certList); + } else { + console.error(TAG + 'getAllUserTrustedCertificates failed, undefined'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getAllUserTrustedCertificates err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async getUserTrustedCertificate(certUri: string, callback: Function): Promise { + console.info(TAG + 'getUserTrustedCertificate start'); + try { + let result = await CertManager.getUserTrustedCertificate(certUri); + let subjectNameMap: Map = new Map(); + let issuerNameMap: Map = new Map(); + let dateMap: Map = new Map(); + let regex1: RegExp = new RegExp('(?<=CN=).*?(?=,)', 'g'); + let regex2: RegExp = new RegExp('(?<=OU=).*?(?=,)', 'g'); + let regex3: RegExp = new RegExp('(?<=O=).*', 'g'); + + if (result.certInfo !== undefined) { + if (result.certInfo.subjectName.length !== 0) { + let subjectNameCN = result.certInfo.subjectName.match(regex1); + console.info('subjectNameCN is:' + subjectNameCN); + let subjectNameOU = result.certInfo.subjectName.match(regex2); + console.info('subjectNameOU is:' + subjectNameOU); + let subjectNameO = result.certInfo.subjectName.match(regex3); + console.info('SubjectNameO is:' + subjectNameO); + subjectNameMap.set('常用名称:', String(subjectNameCN)); + subjectNameMap.set('组织:', String(subjectNameO)); + subjectNameMap.set('组织单位:', String(subjectNameOU)); + subjectNameMap.set('序列号:', String(result.certInfo.serial)); + } + if (result.certInfo.issuerName.length !== 0) { + let issuerNameCN = result.certInfo.issuerName.match(regex1); + console.info('issuerNameCN is:' + issuerNameCN); + let issuerNameOU = result.certInfo.issuerName.match(regex2); + console.info('issuerNameOU is:' + issuerNameOU); + let issuerNameO = result.certInfo.issuerName.match(regex3); + console.info('issuerNameO is:' + issuerNameO); + issuerNameMap.set('常用名称:', String(issuerNameCN)); + issuerNameMap.set('组织:', String(issuerNameO)); + issuerNameMap.set('组织单位:', String(issuerNameOU)); + } + dateMap.set('颁发时间:', String(result.certInfo.notBefore)); + dateMap.set('有效期至:', String(result.certInfo.notAfter)); + let certData: Uint8Array = result.certInfo.cert; + let certInfo = new CertInfoVo(String(result.certInfo.uri), String(result.certInfo.certAlias), + Boolean(result.certInfo.status), String(result.certInfo.issuerName), String(result.certInfo.subjectName), + String(result.certInfo.serial), String(result.certInfo.notBefore), + String(result.certInfo.notAfter), String(result.certInfo.fingerprintSha256), + certData, subjectNameMap, issuerNameMap, dateMap); + console.info(TAG + 'getUserTrustedCertificate end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, certInfo); + } else { + console.error(TAG + 'getUserTrustedCertificate failed, undefined'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getUserTrustedCertificate err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION, undefined); + } + } + + private async deleteUserTrustedCertificate(certUri: string, callback: Function): Promise { + console.info(TAG + 'deleteUserTrustedCertificate start'); + try { + await CertManager.uninstallUserTrustedCertificate(certUri); + console.info(TAG + 'deleteUserTrustedCertificate end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'deleteUserTrustedCertificate err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async setCertificateStatus(certUri: string, store: number, + status: boolean, callback: Function): Promise { + console.info(TAG + 'setCertificateStatus start'); + try { + await CertManager.setCertificateStatus(certUri, store, status); + console.info(TAG + 'setCertificateStatus end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'setCertificateStatus failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async getAppCredList(callback: Function): Promise { + console.info(TAG + 'getAppCredList start'); + try { + let result = await CertManager.getAllPublicCertificates(); + let credList: CredentialAbstractVo[] = []; + if (result.credentialList !== undefined) { + for (let i = 0; i < result.credentialList.length; i++) { + credList.push(new CredentialAbstractVo(String(result.credentialList[i].type), + String(result.credentialList[i].alias), String(result.credentialList[i].keyUri))); + } + console.info(TAG + 'getAppCredList end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, credList); + } else { + console.error(TAG + 'getAppCredList failed, undefined.'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getAppCredList failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async getSystemCredList(callback: Function): Promise { + console.info(TAG + 'getSystemList start'); + try { + let result = await CertManager.getAllSystemAppCertificates(); + let credList: CredentialAbstractVo[] = []; + if (result.credentialList !== undefined) { + for (let i = 0; i < result.credentialList.length; i++) { + credList.push(new CredentialAbstractVo(String(result.credentialList[i].type), + String(result.credentialList[i].alias), String(result.credentialList[i].keyUri))); + } + console.info(TAG + 'getSystemCredList end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, credList); + } else { + console.error(TAG + 'getSystemCredList failed, undefined.'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined) + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG, 'getSystemCredList failed with err, message: ' + e.message + ', code: ' + e.code) + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION) + } + } + + private async getAppCredential(certUri: string, callback: Function): Promise { + console.info(TAG + 'getAppCredential start'); + try { + let result = await CertManager.getPublicCertificate(certUri); + if (result.credential !== undefined) { + let certData: Uint8Array = result.credential.credData; + let credInfo = new CredentialVo(String(result.credential.type), String(result.credential.alias), + String(result.credential.keyUri), Number(result.credential.certNum), + Number(result.credential.keyNum), certData); + console.info(TAG + 'getAppCredential end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, credInfo); + } else { + console.error(TAG + 'getAppCredential failed, undefined'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getAppCredential failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION, undefined); + } + } + + private async getSystemCredential(certUri: string, callback: Function): Promise { + console.info(TAG + 'getSystemCredential start'); + try { + let result = await CertManager.getSystemAppCertificate(certUri); + if (result.credential !== undefined) { + let certData: Uint8Array = result.credential.credData; + let credInfo = new CredentialVo(String(result.credential.type), String(result.credential.alias), + String(result.credential.keyUri), Number(result.credential.certNum), + Number(result.credential.keyNum), certData); + console.info(TAG + 'getSystemCredential end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, credInfo); + } else { + console.error(TAG + 'getSystemCredential failed, undefined'); + callback(CMModelErrorCode.CM_MODEL_ERROR_FAILED, undefined); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getSystemCredential failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION, undefined); + } + } + + private async deleteAppCredential(certUri: string, callback: Function): Promise { + console.info(TAG + 'deleteAppCredential start'); + try { + await CertManager.uninstallPublicCertificate(certUri); + console.info(TAG + 'deleteAppCredential end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'deleteAppCredential failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async deleteSystemCredential(certUri: string, callback: Function): Promise { + console.info(TAG, 'deleteSystemCredential start'); + try { + await CertManager.uninstallSystemAppCertificate(certUri); + console.info(TAG, 'deleteSystemCredential end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'deleteSystemCredential failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async delAllUserCertificate(callback: Function): Promise { + console.info(TAG + 'delAllUserCertificate start'); + try { + await CertManager.uninstallAllUserTrustedCertificate(); + console.info(TAG + 'delAllUserCertificate end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'delAllUserCertificate failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async delAllAppCredential(callback: Function): Promise { + console.info(TAG + 'delAllAppCredential start'); + try { + await CertManager.uninstallAllAppCertificate(); + console.info(TAG + 'delAllAppCredential end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'delAllAppCredential failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async getAuthorizedAppList(uri: string, callback: Function): Promise { + console.info(TAG + 'getAuthorizedAppList start'); + try { + let result = await CertManager.getAuthorizedAppList(uri); + console.info(TAG + 'getAuthorizedAppList end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, result.appUidList); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'getAuthorizedAppList failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION, undefined); + } + } + + private async setAuthorizedAppStatus( + uri: string, + appUid: string, + status: boolean, + callback: Function + ): Promise { + console.info(TAG + 'setAuthorizedAppStatus start'); + try { + if (status) { + let result = await CertManager.grantPublicCertificate(uri, appUid); + console.info(TAG + 'setAuthorizedAppStatus true end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, result.uri); + } else { + console.info(TAG + 'appId:' + appUid + 'uri:' + uri); + await CertManager.removeGrantedPublicCertificate(uri, appUid); + console.info(TAG + 'setAuthorizedAppStatus false end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'setAuthorizedAppStatus failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + + private async installUserCertificate(data: Uint8Array, alias: string, callback: Function): Promise { + console.info(TAG + 'installUserCertificate start'); + if ((data === undefined) || (data.length === 0)) { + callback(CMModelErrorCode.CM_MODEL_ERROR_INCORRECT_FORMAT); + console.error(TAG + 'installUserCertificate data is empty.'); + return; + } + try { + let result = await CertManager.installUserTrustedCertificate({ + inData: data, + alias: alias + }); + console.info(TAG + 'installUserCertificate end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS, result.uri); + } catch (err) { + let e: BusinessError = err as BusinessError; + if (e.code === CertManager.CMErrorCode.CM_ERROR_INCORRECT_FORMAT) { + callback(CMModelErrorCode.CM_MODEL_ERROR_INCORRECT_FORMAT) + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_MAX_CERT_COUNT_REACHED) { + callback(CMModelErrorCode.CM_MODEL_ERROR_MAX_QUANTITY_REACHED) + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_ALIAS_LENGTH_REACHED_LIMIT) { + callback(CMModelErrorCode.CM_MODEL_ERROR_ALIAS_LENGTH_REACHED_LIMIT) + } else { + console.error(TAG + 'installUserCertificate failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + } + + private async installPublicCertificate( + data: Uint8Array, + alias: string, + pwd: string, + callback: Function + ): Promise { + console.info(TAG + 'installPublicCertificate start'); + try { + await CertManager.installPublicCertificate(data, pwd, alias); + console.info(TAG + 'installPublicCertificate end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + if (e.code === CertManager.CMErrorCode.CM_ERROR_INCORRECT_FORMAT) { + callback(CMModelErrorCode.CM_MODEL_ERROR_INCORRECT_FORMAT) + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_MAX_CERT_COUNT_REACHED) { + callback(CMModelErrorCode.CM_MODEL_ERROR_MAX_QUANTITY_REACHED) + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_ALIAS_LENGTH_REACHED_LIMIT) { + callback(CMModelErrorCode.CM_MODEL_ERROR_ALIAS_LENGTH_REACHED_LIMIT) + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_PASSWORD_IS_ERR) { + callback(CMModelErrorCode.CM_MODEL_ERROR_PASSWORD_ERR); + } else { + console.error(TAG + 'installPublicCertificate failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + } + + private async installSystemAppCertificate( + data: Uint8Array, + alias: string, + pwd: string, + callback: Function + ): Promise { + console.info(TAG, 'installSystemAppCertificate start'); + try { + await CertManager.installSystemAppCertificate(data, pwd, alias); + console.info(TAG + 'installSystemAppCertificate end'); + callback(CMModelErrorCode.CM_MODEL_ERROR_SUCCESS); + } catch (err) { + let e: BusinessError = err as BusinessError; + if (e.code === CertManager.CMErrorCode.CM_ERROR_INCORRECT_FORMAT) { + callback(CMModelErrorCode.CM_MODEL_ERROR_INCORRECT_FORMAT); + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_MAX_CERT_COUNT_REACHED) { + callback(CMModelErrorCode.CM_MODEL_ERROR_MAX_QUANTITY_REACHED); + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_ALIAS_LENGTH_REACHED_LIMIT) { + callback(CMModelErrorCode.CM_MODEL_ERROR_ALIAS_LENGTH_REACHED_LIMIT); + } else if (e.code === CertManager.CMErrorCode.CM_ERROR_PASSWORD_IS_ERR) { + callback(CMModelErrorCode.CM_MODEL_ERROR_PASSWORD_ERR); + } else { + console.error(TAG + 'installSystemAppCertificate failed with err, message: ' + e.message + ', code: ' + e.code); + callback(CMModelErrorCode.CM_MODEL_ERROR_EXCEPTION); + } + } + } +} + +let certMangerModel = new CertMangerModel(); + +export default certMangerModel as CertMangerModel; diff --git a/entry/src/main/ets/model/CheckUserAuthModel.ets b/entry/src/main/ets/model/CheckUserAuthModel.ets new file mode 100755 index 0000000..d81146c --- /dev/null +++ b/entry/src/main/ets/model/CheckUserAuthModel.ets @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import userAuth from '@ohos.userIAM.userAuth'; +import { BusinessError } from '@ohos.base'; + +export class CheckUserAuthModel { + public isAuthTypeSupported(authType: userAuth.UserAuthType): boolean { + try { + userAuth.getAvailableStatus(authType, userAuth.AuthTrustLevel.ATL1); + console.info('[CM&CheckUserAuthModel]: ' + 'userAuthType' + authType + 'is supported'); + return true; + } catch (error) { + let err: BusinessError = error as BusinessError; + console.error(`[CM&CheckUserAuthModel]: userAuthType ${authType} is not supported, message is ${err?.message}`); + return false; + } + } + + public auth(titleStr: string, callback: (authResult: boolean) => void): void { + let fingerPrint: boolean = this.isAuthTypeSupported(userAuth.UserAuthType.FINGERPRINT); + let pin: boolean = this.isAuthTypeSupported(userAuth.UserAuthType.PIN); + let authTypeArray = [userAuth.UserAuthType.FINGERPRINT]; + if (fingerPrint) { + authTypeArray = [userAuth.UserAuthType.FINGERPRINT]; + if (pin) { + authTypeArray = [userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.PIN]; + } + } else if (pin) { + authTypeArray = [userAuth.UserAuthType.PIN]; + } else { + /* The user does not set identity authentication. */ + callback(true); + return; + } + + const authParam: userAuth.AuthParam = { + challenge: new Uint8Array([49, 49, 49, 49, 49, 49]), + authType: authTypeArray, + authTrustLevel: userAuth.AuthTrustLevel.ATL1 + } + const widgetParam: userAuth.WidgetParam = { + title: titleStr + }; + + try { + let userAuthInstance = userAuth.getUserAuthInstance(authParam, widgetParam); + console.info('[CM&CheckUserAuthModel]: get userAuth instance success'); + userAuthInstance.start(); + + userAuthInstance.on('result', { + onResult(result) { + if (result.result === userAuth.UserAuthResultCode.SUCCESS) { + callback(true); + } else if (result.result === userAuth.UserAuthResultCode.CANCELED) { + /* User cancels authentication. */ + callback(false); + } else { + /* User authentication failed. */ + callback(false); + } + } + }) + } catch (error) { + let err: BusinessError = error as BusinessError; + console.error(`[CM&CheckUserAuthModel]: auth catch error. code is ${err?.code}, message is ${err?.message}`); + } + } +} + +let checkUserAuthModel = new CheckUserAuthModel(); + +export default checkUserAuthModel as CheckUserAuthModel; \ No newline at end of file diff --git a/entry/src/main/ets/model/FileIoModel.ets b/entry/src/main/ets/model/FileIoModel.ets new file mode 100755 index 0000000..23cc703 --- /dev/null +++ b/entry/src/main/ets/model/FileIoModel.ets @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fileUri from '@ohos.file.fileuri'; +import fs from '@ohos.file.fs'; +import { BusinessError } from '@ohos.base'; + +export class FileIoModel { + getMediaFileData(mediaUri: string, callback: Function): void { + console.info('CertManager FA getMediaFile start'); + let file: fs.File | undefined = undefined; + try { + file = fs.openSync(mediaUri, fs.OpenMode.READ_ONLY); + let stat = fs.statSync(file.fd); + let buf = new ArrayBuffer(Number(stat.size)); + let num = fs.readSync(file.fd, buf); + console.info('CertManager FA getMediaFile success'); + callback(new Uint8Array(buf)); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error('CertManager FA getMediaFileData failed with err, message: ' + e.message + ', code: ' + e.code); + callback(undefined); + } finally { + try { + if (file !== undefined && file !== null) { + fs.closeSync(file.fd); + } + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error('CertManager FA close io stream failed with err, message: ' + e.message + ', code: ' + e.code); + } + } + } + + getMediaFileSuffix(mediaUri: string, callback: Function): void { + try { + console.info('CertManager FA getMediaFileSuffix start'); + let uri = new fileUri.FileUri(mediaUri); + let suffix = uri.name.substring(uri.name.lastIndexOf('.') + 1); + callback(suffix); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error('CertManager FA getMediaFileSuffix failed with err, message: ' + e.message + ', code: ' + e.code); + callback(undefined); + } + } +} + +let fileIoModel = new FileIoModel(); + +export default fileIoModel as FileIoModel; diff --git a/entry/src/main/ets/model/PreventScreenshotsModel.ets b/entry/src/main/ets/model/PreventScreenshotsModel.ets new file mode 100755 index 0000000..114b611 --- /dev/null +++ b/entry/src/main/ets/model/PreventScreenshotsModel.ets @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import window from '@ohos.window'; +import { BusinessError } from '@ohos.base'; +import { GlobalContext } from '../common/GlobalContext'; +import type UIAbilityContext from 'application/UIAbilityContext'; +import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; + +const TAG: string = 'PreventScreenshotsModel'; + +export default class PreventScreenshotsModel { + private static sInstance: PreventScreenshotsModel; + + public static getInstance(): PreventScreenshotsModel { + if (PreventScreenshotsModel.sInstance == null) { + PreventScreenshotsModel.sInstance = new PreventScreenshotsModel(); + } + return PreventScreenshotsModel.sInstance; + } + + PreventScreenshots(flag: boolean, session: UIExtensionContentSession | undefined) { + let isPrivacyMode: boolean = flag; + if (session !== undefined) { + session.setWindowPrivacyMode(isPrivacyMode); + return; + } + + let windowClass: window.Window | undefined = undefined; + let context: UIAbilityContext = GlobalContext.getContext().getCmContext(); + if (context === undefined) { + console.error(TAG, 'context is undefined!'); + return; + } + + window.getLastWindow(context).then((window) => { + windowClass = window; + console.info(TAG,'Success in obtaining the top window data'); + windowClass.setWindowPrivacyMode(isPrivacyMode).catch((err: BusinessError) => { + console.error(TAG, 'setWindowPrivacyMode failed: ' + JSON.stringify(err)); + }) + }).catch((err: BusinessError) => { + console.info(TAG + 'getLastWindow failed: ' + JSON.stringify(err)); + }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/CertificateInstallPage.ets b/entry/src/main/ets/pages/CertificateInstallPage.ets new file mode 100755 index 0000000..807fc22 --- /dev/null +++ b/entry/src/main/ets/pages/CertificateInstallPage.ets @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CustomContentDialog, AlertDialog } from '@ohos.arkui.advanced.Dialog'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import checkUserAuthModel from '../model/CheckUserAuthModel'; +import { BusinessError } from '@ohos.base'; +import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; +import certManagerModel from '../model/CertMangerModel'; +import util from '@ohos.util'; + +/* instrument ignore file */ + +const TAG = 'CertificateInstallPage'; +let storage = LocalStorage.getShared(); + +@Entry(storage) +@Component +struct CertificateInstallPage { + @State installFailedDialogMessage: ResourceStr = ''; + @State callerName: string = ''; + @State resultCode: number = CMModelErrorCode.CM_MODEL_ERROR_FAILED; + @State successUri: string = ''; + ; + private session: UIExtensionContentSession = + storage?.get('session') as UIExtensionContentSession; + private want: Want = storage?.get('want') as Want; + + private context: Context = getContext(this); + + installFailedDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + showInSubWindow: true, + cancel: () => { + this.session?.terminateSelfWithResult({ + resultCode: this.resultCode + }); + }, + builder: AlertDialog({ + primaryTitle: $r('app.string.cert_install_failed'), + content: this.installFailedDialogMessage, + primaryButton: { + value: $r('app.string.OK'), + action: () => { + this.session?.terminateSelfWithResult({ + resultCode: this.resultCode + }); + } + } + }) + }); + + installSuccessDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + showInSubWindow: true, + cancel: () => { + this.session?.sendData({'uri': this.successUri}); + }, + builder: AlertDialog({ + primaryTitle: $r('app.string.cert_install_success'), + content: $r('app.string.cert_install_success_tip'), + primaryButton: { + value: $r('app.string.OK'), + action: () => { + this.session?.sendData({'uri': this.successUri}); + } + } + }) + }); + + rootCertificateDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + showInSubWindow: false, + cancel: () => { + this.session?.terminateSelf(); + }, + builder: AlertDialog({ + primaryTitle: $r('app.string.cert_install_tip', this.callerName), + content: $r('app.string.cert_install_warning'), + primaryButton: { + value: $r('app.string.root_certificate_cancel'), + action: () => { + console.info(TAG, 'USER_CA_STATUS_CONFIRM cancel'); + this.session?.terminateSelf(); + } + }, + secondaryButton: { + value: $r('app.string.root_certificate_continue'), + action: () => { + console.info(TAG, 'USER_CA_STATUS_CONFIRM confirm'); + this.checkUserAuth(); + } + } + }), + }) + + aboutToAppear(): void { + let isGranted = this.getCallerName(); + if (!isGranted) { + this.session?.terminateSelf(); + return; + } + this.rootCertificateDialog.open(); + } + + build() { + } + + private getCallerName(): boolean { + if (this.want === undefined || this.want === null) { + console.error(TAG, 'initData, want is undefined'); + return false; + } + let parameters = this.want.parameters; + if (parameters === undefined || parameters === null) { + console.error(TAG, 'initData, parameters is undefined'); + return false; + } + let callerName = parameters['bundleName']; + if (callerName === undefined || callerName === null) { + console.error(TAG, 'getCallerName, callerName is undefined'); + return false; + } + this.callerName = 'callerName as string'; + return true; + } + + private handleInstallResult(resultCode: CMModelErrorCode, uri: string) { + if (resultCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.successUri = uri; + this.installSuccessDialog.open(); + } else if (resultCode === CMModelErrorCode.CM_MODEL_ERROR_INCORRECT_FORMAT) { + this.resultCode = CMModelErrorCode.CM_MODEL_ERROR_INCORRECT_FORMAT; + this.installFailedDialogMessage = $r('app.string.Install_ERROR_INCORRECT_FORMAT'); + this.installFailedDialog.open(); + } else if (resultCode === CMModelErrorCode.CM_MODEL_ERROR_MAX_QUANTITY_REACHED) { + this.resultCode = CMModelErrorCode.CM_MODEL_ERROR_MAX_QUANTITY_REACHED; + this.installFailedDialogMessage = $r('app.string.Install_Error_MAX_QUANTITY_REACHED'); + this.installFailedDialog.open(); + } else { + console.debug(TAG, `result code ${resultCode}, need not show result`); + this.session?.terminateSelfWithResult({ + resultCode: resultCode + }); + } + } + + checkUserAuth() { + let titleStr = this.context?.resourceManager.getStringSync($r('app.string.Identity_Authentication')); + checkUserAuthModel.auth(titleStr, (authResult: boolean) => { + if (!authResult) { + console.warn(TAG, 'userAuth cancel!'); + this.session?.terminateSelf(); + return; + } + console.info(TAG, 'userAuth success!'); + this.installCaCertificate(); + }) + } + + private async getCertificateData(parameters: Record): Promise { + return new Promise(resolve => { + let certificateDataObj = parameters['cert']; + if (certificateDataObj === undefined || certificateDataObj === null) { + console.error(TAG, 'getCertificateData, certificate data is undefined'); + return resolve(undefined); + } + new util.Base64Helper().decode(certificateDataObj as string).then((value) => { + return resolve(value); + }).catch((error: BusinessError) => { + console.error(TAG, `decode certificate data err: ${error?.code}, msg: ${error?.message}`); + return resolve(undefined); + }); + }); + } + + private installCaCertificate(): void { + if (this.want === undefined || this.want === null) { + console.error(TAG, 'initData, want is undefined'); + return; + } + let parameters = this.want.parameters; + if (parameters === undefined || parameters === null) { + console.error(TAG, 'initData, parameters is undefined'); + return; + } + this.getCertificateData(parameters).then(data => { + if (data === undefined) { + console.error(TAG, 'installCaCertificate, certificate data is undefined'); + this.session?.terminateSelf(); + return; + } + certManagerModel.installCertOrCred(CMModelOptType.CM_MODEL_OPT_USER_CA, '', data, + '', (resultCode: CMModelErrorCode, uri: string) => { + console.info(TAG, `installCertOrCred result: ${resultCode}`); + this.handleInstallResult(resultCode, uri); + }); + }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/cerEvidenceFa.ets b/entry/src/main/ets/pages/cerEvidenceFa.ets new file mode 100755 index 0000000..f5b4058 --- /dev/null +++ b/entry/src/main/ets/pages/cerEvidenceFa.ets @@ -0,0 +1,861 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { WidthPercent, ConfigValue } from '../common/util/ConfigData'; +import HeadComponent from '../common/component/headComponent'; +import CmShowAppCredPresenter from '../presenter/CmShowAppCredPresenter'; +import CMFaPresenter from '../presenter/CmFaPresenter'; +import { GlobalContext } from '../common/GlobalContext'; +import { CredentialAbstractVo } from '../model/CertManagerVo/CredentialAbstractVo'; +import { AppAuthorVo } from '../model/CertManagerVo/AppAuthorVo'; +import router from '@ohos.router'; +import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; +import CmShowSysCredPresenter from '../presenter/CmShowSysCredPresenter'; +import { NavEntryKey } from '../common/NavEntryKey'; +import { CredSystemDetailParam } from './detail/CredSystemDetailPage'; +import { CredUserDetailParam } from './detail/CredUserDetailPage'; +import { SheetParam } from '../common/util/SheetParam'; +import { DialogComponent } from './detail/AuthorizedAppManagementPage'; + +const COPIES_NUM: number = 12; + +@Component +export struct componentPublic { + private alias: string = ''; + private keyUri: string = ''; + @Link mShowAppCaPresenter: CmShowAppCredPresenter; + @State authorInfo: CmShowAppCredPresenter = CmShowAppCredPresenter.getInstance(); + private authorScroller: Scroller = new Scroller(); + @State isHoverBackgroundColor: ResourceColor = ''; + @Styles pressedStyles(): void { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + } + @Styles normalStyles(): void { + .backgroundColor(this.isHoverBackgroundColor) + } + + onItemClicked?: () => void; + + getAuthorizedAppList(): void { + this.authorInfo.getAuthorizedAppList(this.mShowAppCaPresenter.credInfo.keyUri); + } + + deleteWarnDialog: CustomDialogController = new CustomDialogController({ + builder: CustomContentDialog({ + contentBuilder: () => { + this.deleteWarnContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.cancelAuthApp'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.appDialogController?.close(); + } + }, + { + value: $r('app.string.deleteAllCredDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.mShowAppCaPresenter.deleteAppCred(this.mShowAppCaPresenter.credInfo.keyUri); + this.deleteWarnDialog?.close(); + }, + role: ButtonRole.ERROR + } + ] + }), + }) + + appDialogController: CustomDialogController = new CustomDialogController({ + builder: CustomContentDialog({ + contentBuilder: () => { + this.appControllerContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.cancelAuthApp'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.appDialogController?.close(); + } + }, + { + value: $r('app.string.finishAuthApp'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.authorInfo.removeGrantedAppList(this.authorInfo.credInfo.keyUri).then(() => { + this.appDialogController?.close(); + }) + } + } + ] + }), + }) + + credDetailsDialog: CustomDialogController = new CustomDialogController({ + builder: CustomContentDialog({ + contentBuilder: () => { + this.credDetailContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.publicDetailsCancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.credDetailsDialog?.close(); + } + }, + { + value: $r('app.string.publicDetailsDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.onShowDeleteWarnDialog(); + }, + role: ButtonRole.ERROR + } + ] + }), + }) + + @Builder + appControllerContent(): void { + Column() { + Text($r('app.string.managerAuthApp')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + if (this.authorInfo.appInfoList.length > ConfigValue.APP_AUTH_MAX_LENGTH) { + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.authorScroller) { + List() { + ForEach(this.authorInfo.appInfoList, (item: AppAuthorVo, index) => { + ListItem() { + DialogComponent({ appImage: item.appImage, appName: item.appName, + indexNum: index, uidItem: $authorInfo }) + } + }) + } + .scrollBar(BarState.Off) + .divider({ + strokeWidth: $r('app.float.Evidence_strokeWidth'), + color: $r('sys.color.ohos_id_color_list_separator') + }) + .visibility(this.authorInfo.appInfoList.length > 0 ? Visibility.Visible : Visibility.None) + } + .scrollBar(BarState.Off) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + + ScrollBar({ scroller: this.authorScroller, direction: ScrollBarDirection.Vertical, + state: BarState.Auto }) { + Text() + .width($r('app.float.wh_value_3')) + .height($r('app.float.wh_value_50')) + .borderRadius($r('app.float.wh_value_10')) + .backgroundColor($r('sys.color.ohos_id_color_foreground')) + .opacity($r('app.float.text_opacity_0_4')) + } + .width($r('app.float.wh_value_3')) + .margin({ + right: $r('app.float.wh_value_3') + }) + } + .height(WidthPercent.WH_50_100) + } else { + List() { + ForEach(this.mShowAppCaPresenter.appInfoList, (item: AppAuthorVo, index) => { + ListItem() { + DialogComponent({ appImage: item.appImage, appName: item.appName, + indexNum: index, uidItem: $authorInfo }) + } + }) + } + .scrollBar(BarState.Off) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .divider({ + strokeWidth: $r('app.float.Evidence_strokeWidth'), + color: $r('sys.color.ohos_id_color_list_separator') + }) + .visibility(this.authorInfo.appInfoList.length > 0 ? Visibility.Visible : Visibility.None) + } + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + @Builder + credDetailContent(): void { + Column() { + Text($r('app.string.evidenceDetails')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text(this.mShowAppCaPresenter.credInfo.alias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.entryContains')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.wh_value_24'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.keyNum', String(this.mShowAppCaPresenter.credInfo.keyNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.userCerNum', String(this.mShowAppCaPresenter.credInfo.certNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text($r('app.string.managerAuthApp')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + } + + Row() { + Image($r('app.media.ic_settings_arrow')) + .width($r('app.float.managerAuthApp_image_wh')) + .height($r('app.float.managerAuthApp_image_hg')) + .fillColor($r('sys.color.ohos_id_color_primary')) + .opacity($r('app.float.managerAuthApp_image_opacity')) + } + } + .onClick(() => { + this.onShowAuthMngChange(); + }) + .margin({ + top: $r('app.float.wh_value_12'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .height('48vp') + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + @Builder + deleteWarnContent(): void { + Column() { + Text($r('app.string.warning_title')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.warning_message')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Regular) + .fontColor($r('sys.color.ohos_id_color_primary')) + .margin({ + top: $r('app.float.wh_value_16'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + onShowAuthMngChange() { + this.credDetailsDialog?.close(); + this.appDialogController.open(); + } + + onShowDeleteWarnDialog() { + this.credDetailsDialog?.close(); + this.deleteWarnDialog.open(); + } + + build() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(this.alias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + } + + Row() { + Image($r('app.media.ic_settings_arrow')) + .width($r('app.float.publicList_image_wh')) + .height($r('app.float.publicList_image_hg')) + } + } + .height($r('app.float.wh_value_48')) + .hoverEffect(HoverEffect.Highlight) + .onHover((isHover?: boolean) => { + this.isHoverBackgroundColor = isHover ? $r('sys.color.ohos_id_color_hover') : '' + }) + .backgroundColor(this.isHoverBackgroundColor) + .stateStyles({ + pressed: this.pressedStyles, + normal: this.normalStyles + }) + .padding({ + left: $r('app.float.wh_value_8'), + right: $r('app.float.wh_value_8') + }) + .borderRadius($r('app.float.wh_value_20')) + .onClick(() => { + this.mShowAppCaPresenter.getAppCred(this.keyUri, () => { + this.getAuthorizedAppList(); + if (this.onItemClicked !== undefined) { + this.onItemClicked(); + } else { + this.credDetailsDialog.open(); + } + }); + }) + } +} + +@Component +export struct componentSystem { + private alias: string = ''; + private keyUri: string = ''; + @State mSystemCredPresenter: CmShowSysCredPresenter = CmShowSysCredPresenter.getInstance(); + @State isHoverBackgroundColor: ResourceColor = ''; + @Link systemCredPresenter: CmShowSysCredPresenter; + @Styles pressedStyles(): void { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + } + @Styles normalStyles(): void { + .backgroundColor(this.isHoverBackgroundColor) + } + + onItemClicked?: () => void; + + sysCredDetailsDialog: CustomDialogController = new CustomDialogController({ + builder: CustomContentDialog({ + contentBuilder: () => { + this.sysCredDetailContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.publicDetailsCancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.sysCredDetailsDialog?.close(); + } + }, + { + value: $r('app.string.publicDetailsDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.sysCredDetailsDialog?.close(); + this.deleteWarnDialog.open(); + }, + role: ButtonRole.ERROR + } + ] + }), + }) + + deleteWarnDialog: CustomDialogController = new CustomDialogController({ + builder: CustomContentDialog({ + contentBuilder: () => { + this.deleteWarnContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.cancelAuthApp'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.sysCredDetailsDialog?.close(); + } + }, + { + value: $r('app.string.deleteAllCredDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.mSystemCredPresenter.deleteSystemCred(this.mSystemCredPresenter.credInfo.keyUri); + this.deleteWarnDialog?.close(); + this.systemCredPresenter.updateSystemCredList(); + }, + role: ButtonRole.ERROR + } + ] + }), + }) + + @Builder + sysCredDetailContent(): void { + Column() { + Text($r('app.string.evidenceDetails')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text(this.systemCredPresenter.credInfo.alias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.entryContains')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.wh_value_24'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.keyNum', String(this.systemCredPresenter.credInfo.keyNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.userCerNum', String(this.systemCredPresenter.credInfo.certNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + @Builder + deleteWarnContent(): void { + Column() { + Text($r('app.string.warning_title')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.warning_message')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Regular) + .fontColor($r('sys.color.ohos_id_color_primary')) + .margin({ + top: $r('app.float.wh_value_16'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + build() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(this.alias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + } + + Row() { + Image($r('app.media.ic_settings_arrow')) + .width($r('app.float.publicList_image_wh')) + .height($r('app.float.publicList_image_hg')) + } + } + .height($r('app.float.wh_value_48')) + .hoverEffect(HoverEffect.Highlight) + .onHover((isHover?: boolean) => { + this.isHoverBackgroundColor = isHover ? $r('sys.color.ohos_id_color_hover') : '' + }) + .backgroundColor(this.isHoverBackgroundColor) + .stateStyles({ + pressed: this.pressedStyles, + normal: this.normalStyles + }) + .padding({ + left: $r('app.float.wh_value_8'), + right: $r('app.float.wh_value_8') + }) + .borderRadius($r('app.float.wh_value_20')) + .onClick(() => { + this.mSystemCredPresenter.getSystemCred(this.keyUri, () => { + if (this.onItemClicked !== undefined) { + this.onItemClicked(); + } else { + this.sysCredDetailsDialog.open(); + } + }); + }) + } + +} + +@Entry +@Component +export struct evidenceList { + @State mShowAppCaPresenter: CmShowAppCredPresenter = CmShowAppCredPresenter.getInstance(); + @State mShowSysCredPresenter: CmShowSysCredPresenter = CmShowSysCredPresenter.getInstance(); + @State mFaPresenter: CMFaPresenter = CMFaPresenter.getInstance(); + private publicScroller: Scroller = new Scroller(); + private systemScroller: Scroller = new Scroller(); + @State currentIndex: number = 0; + @State fontColor: Resource = $r('app.color.evidenceList_TabBuilder_fontColor_182431'); + @State selectedFontColor: Resource = $r('app.color.font_color_007DFF'); + private controller: TabsController = new TabsController(); + @State animationDurationNum: number = 400; + + isStartBySheetFirst: boolean = false; + isStartBySheet: boolean = false; + selected?: (path: string, param?: Object) => void; + @Prop sheetParam: SheetParam; + @State headRectHeight: number = 64; + @State headRectHeightReal: number = 0; + @State systemScrollerHeight: number = 0; + @State publicScrollerHeight: number = 0; + + @Builder + TabBuilder(index: number) { + Column() { + Text(index == 0 ? $r('app.string.system') : $r('app.string.user')) + .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) + .fontSize($r('app.float.TrustedEvidence_TabBuilder_Text_fontSize_value')) + .fontWeight(this.currentIndex === index ? FontWeight.Medium : FontWeight.Regular) + .alignSelf(ItemAlign.Center) + .margin({ + top: $r('app.float.TrustedEvidence_TabBuilder_Text_padding_top_value') + }) + if (this.currentIndex === index) { + Divider() + .width($r('app.float.TrustedEvidence_TabBuilder_Divider_width_value')) + .margin({ top: $r('app.float.TrustedEvidence_TabBuilder_Divider_padding_top_value') }) + .color(this.selectedFontColor) + .alignSelf(ItemAlign.Center) + } + } + .width(WidthPercent.WH_100_100) + } + + aboutToAppear() { + this.mShowAppCaPresenter.updateAppCredListCallback(() => { + console.info('get AppCredList first'); + }) + this.mShowSysCredPresenter.updateSystemCredListCallback(() => { + console.info('get systemCredList start'); + }) + } + + onPageShow() { + let uri = GlobalContext.getContext().getAbilityWant().uri; + GlobalContext.getContext().clearAbilityWantUri(); + + if (uri === 'certInstall') { + router.pushUrl({ + url: 'pages/certInstallFromStorage' + }) + } else if (uri === 'requestAuthorize') { + this.mFaPresenter.startRequestAuth(GlobalContext.getContext().getAbilityWant().parameters?.appUid as string); + } else { + console.error('The want type is not supported.'); + } + } + + build() { + Column() { + GridRow({ + columns: COPIES_NUM, + gutter: vp2px(1) === 2 ? $r('app.float.wh_value_12') : $r('app.float.wh_value_0') + }) { + GridCol({ span: { xs: COPIES_NUM, sm: COPIES_NUM, md: COPIES_NUM, lg: COPIES_NUM }, + offset: { xs: 0, sm: 0, md: 0, lg: 0 } }) { + Row() { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.userEvidence'), isStartBySheet: this.isStartBySheet, + icBackIsVisibility: !this.isStartBySheetFirst, + onBackClicked: () => { + this.selected?.(NavEntryKey.POP); + }}) + .margin({ + left: $r('app.float.wh_value_16'), + top: this.isStartBySheet ? 8 : 0 + }) + }.zIndex(1) + .onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }) + + Column() { + Tabs({barPosition: BarPosition.Start, index: 0, controller: this.controller}) { + TabContent() { + Stack({alignContent: Alignment.TopEnd}) { + Scroll(this.systemScroller) { + List() { + ForEach(this.mShowSysCredPresenter.credList, (item: CredentialAbstractVo) => { + ListItem() { + componentSystem({ + alias: item.alias, + keyUri: item.keyUri, + systemCredPresenter: $mShowSysCredPresenter, + onItemClicked: this.isStartBySheet ? () => { + this.selected?.(NavEntryKey.CRED_SYSTEM_DETAIL_ENTRY, + new CredSystemDetailParam(this.mShowSysCredPresenter)); + } : undefined + }) + } + }, (item:CredentialAbstractVo) => JSON.stringify(item)) + } + .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) + .borderRadius($r('sys.float.padding_level10')) + .scrollBar(BarState.Off) + .padding({ + right: $r('app.float.wh_value_4'), + left: $r('app.float.wh_value_4'), + top: $r('app.float.wh_value_4'), + bottom: $r('app.float.wh_value_4') + }) + .divider({ + strokeWidth: $r('app.float.user_list_divider_strokeWidth_value'), + color: $r('sys.color.ohos_id_color_list_separator'), + endMargin: $r('app.float.wh_value_3') + }) + .visibility(this.mShowSysCredPresenter.credList.length > 0 + ? Visibility.Visible : Visibility.None) + } + .position({ y: $r('app.float.wh_value_0') }) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Off) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .align(Alignment.Top) + .edgeEffect(EdgeEffect.Spring) + .padding({ + left: $r('app.float.wh_value_16'), + right: $r('app.float.wh_value_16'), + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }).onAreaChange((oldArea, newArea) => { + this.systemScrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.systemScroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + }.height(this.systemScrollerHeight) + } + .width(WidthPercent.WH_100_100) + } + .tabBar(this.TabBuilder(0)) + + TabContent() { + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.publicScroller) { + List() { + ForEach(this.mShowAppCaPresenter.credList, (item: CredentialAbstractVo) => { + ListItem() { + componentPublic({ + alias: item.alias, + keyUri: item.keyUri, + mShowAppCaPresenter: $mShowAppCaPresenter, + onItemClicked: this.isStartBySheet ? () => { + this.selected?.(NavEntryKey.CRED_USER_DETAIL_ENTRY, + new CredUserDetailParam(this.mShowAppCaPresenter)); + } : undefined + }) + } + }, (item: CredentialAbstractVo) => JSON.stringify(item)) + } + .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) + .borderRadius($r('sys.float.padding_level10')) + .scrollBar(BarState.Off) + .padding({ + right: $r('app.float.wh_value_4'), + left: $r('app.float.wh_value_4'), + top: $r('app.float.wh_value_4'), + bottom: $r('app.float.wh_value_4') + }) + .divider({ + strokeWidth: $r('app.float.user_list_divider_strokeWidth_value'), + color: $r('sys.color.ohos_id_color_list_separator'), + endMargin: $r('app.float.wh_value_3') + }) + .visibility(this.mShowAppCaPresenter.credList.length > 0 ? Visibility.Visible : Visibility.None) + } + .position({ y: $r('app.float.wh_value_0') }) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .align(Alignment.Top) + .edgeEffect(EdgeEffect.Spring) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Off) + .padding({ + left: $r('app.float.wh_value_16'), + right: $r('app.float.wh_value_16'), + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }).onAreaChange((oldArea, newArea) => { + this.publicScrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.publicScroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + }.height(this.publicScrollerHeight) + } + .width(WidthPercent.WH_100_100) + } + .tabBar(this.TabBuilder(1)) + } + .vertical(false) + .scrollable(true) + .barMode(BarMode.Fixed) + .barWidth($r('app.float.tabs_barWidth_value')) + .barHeight($r('app.float.tabs_barHeight_value')) + .animationDuration(this.animationDurationNum) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .width(WidthPercent.WH_100_100) + .onChange((index: number) => { + this.currentIndex = index; + }) + } + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .width(WidthPercent.WH_100_100) + .padding({ + top: this.headRectHeight + }) + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < this.headRectHeightReal) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal - 56; + } +} diff --git a/entry/src/main/ets/pages/certInstallFromStorage.ets b/entry/src/main/ets/pages/certInstallFromStorage.ets new file mode 100755 index 0000000..0823a6a --- /dev/null +++ b/entry/src/main/ets/pages/certInstallFromStorage.ets @@ -0,0 +1,413 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { WidthPercent } from '../common/util/ConfigData'; +import HeadComponent from '../common/component/headComponent'; +import CmFaPresenter from '../presenter/CmFaPresenter'; +import { GlobalContext } from '../common/GlobalContext'; +import ComponentConfig from '../common/component/ComponentConfig'; +import router from '@ohos.router'; +import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; +import checkUserAuthModel from '../model/CheckUserAuthModel'; +import { NavEntryKey } from '../common/NavEntryKey'; +import picker from '@ohos.file.picker'; +import FileIoModel from '../model/FileIoModel'; +import CmInstallPresenter from '../presenter/CmInstallPresenter'; +import { BusinessError } from '@ohos.base'; +import { RouterFileVo } from '../model/CertManagerVo/RouterInfoVo'; +import { CredPwdInputParam } from './detail/CredPwdInputPage'; +import { SheetParam } from '../common/util/SheetParam'; +import FilterParams from '../common/constants/FileFilterParams'; + +const COPIES_NUM: number = 12; + +const TAG: string = 'CertInstallFromStorage: '; + +@Entry +@Component +export struct CertInstallFromStorage { + @State columnMargin: string = '12vp'; + @State mFaPresenter: CmFaPresenter = CmFaPresenter.getInstance(); + @State installCertFlag: boolean = false; + private context: Context = getContext(this); + + isStartBySheetFirst: boolean = false; + isStartBySheet: boolean = false; + selected?: (path: string, param?: Object) => void; + @Prop sheetParam: SheetParam; + @State private headRectHeight: number = 64; + @State private headRectHeightReal: number = 0; + private scroller: Scroller = new Scroller(); + @State private scrollerHeight: number = 0; + + @Styles normalStyle() { + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + }; + @Styles pressedStyle() { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + }; + + rootCertificateDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + builder: CustomContentDialog({ + contentBuilder: () => { + this.rootCertificateContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.root_certificate_cancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + } + }, + { + value: $r('app.string.root_certificate_continue'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.installCertFlag = true; + this.checkUserAuth(); + } + } + ] + }) + }) + + @Builder + rootCertificateContent(): void { + Column() { + Text($r('app.string.root_certificate')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.root_certificate_message')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Regular) + .fontColor($r('sys.color.ohos_id_color_primary')) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + checkUserAuth() { + let titleStr = getContext().resourceManager.getStringSync($r('app.string.Identity_Authentication')); + checkUserAuthModel.auth(titleStr, (authResult: boolean) => { + if (authResult) { + console.info('checkUserAuth success'); + if (this.installCertFlag) { + if (this.isStartBySheet) { + this.startInstallCertBySheet(); + } else { + this.mFaPresenter.startInstallCert(this.context); + } + } else { + if (this.isStartBySheet) { + this.startInstallEvidenceBySheet(); + } else { + this.mFaPresenter.startInstallEvidence(this.context); + } + } + } + }) + } + + startInstallCertBySheet(): void { + try { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.maxSelectNumber = FilterParams.MAX_SELECT_FILE_NUM; + documentSelectOptions.fileSuffixFilters = FilterParams.CERT_FILE_SUFFIX; + let documentPicker = new picker.DocumentViewPicker(this.context); + console.info(TAG + 'start documentPicker.select'); + documentPicker.select(documentSelectOptions).then((documentSelectResult) => { + if (documentSelectResult.length >= 1) { + this.routeToNextInstallCert(String(documentSelectResult[0])) + } else { + console.error(TAG + 'documentPicker.select length invalid:' + documentSelectResult.length); + } + }).catch((err: BusinessError) => { + console.error(TAG + 'documentPicker.select failed with err, message: ' + err.message + ', code: ' + err.code); + }); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'DocumentViewPicker failed with err, message: ' + e.message + ', code: ' + e.code); + } + } + + routeToNextInstallCert(fileUri: string): void { + FileIoModel.getMediaFileSuffix(fileUri, (suffix: string | undefined) => { + if (suffix !== undefined) { + console.debug(TAG, 'suffix = ', suffix); + if ((suffix === 'cer') || (suffix === 'pem') || (suffix === 'crt') || (suffix === 'der')) { + CmInstallPresenter.getInstance().installCert(fileUri, '', suffix, false); + } else { + this.mFaPresenter.unrecognizedFileTips(); + } + } + }) + } + + startInstallEvidenceBySheet(): void { + try { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.maxSelectNumber = FilterParams.MAX_SELECT_FILE_NUM; + documentSelectOptions.fileSuffixFilters = FilterParams.CREDENTIAL_FILE_SUFFIX; + let documentPicker = new picker.DocumentViewPicker(this.context); + console.info(TAG + 'start documentPicker.select'); + documentPicker.select(documentSelectOptions).then((documentSelectResult) => { + if (documentSelectResult.length >= 1) { + this.routeToNextInstallEvidence(String(documentSelectResult[0])) + } else { + console.error(TAG + 'documentPicker.select length invalid:' + documentSelectResult.length); + } + }).catch((err: BusinessError) => { + console.error(TAG + 'documentPicker.select failed with err, message: ' + err.message + ', code: ' + err.code); + }); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'DocumentViewPicker failed with err, message: ' + e.message + ', code: ' + e.code); + } + } + + routeToNextInstallEvidence(fileUri: string): void { + FileIoModel.getMediaFileSuffix(fileUri, (suffix: string | undefined) => { + if (suffix !== undefined) { + console.debug(TAG, 'suffix = ', suffix); + if ((suffix === 'p12') || (suffix === 'pfx')) { + this.selected?.(NavEntryKey.CRED_PWD_INPUT_ENTRY, new CredPwdInputParam(new RouterFileVo(fileUri, suffix))); + } else { + this.mFaPresenter.unrecognizedFileTips(); + } + } + }) + } + + build() { + Column() { + GridRow({ + columns: COPIES_NUM, + gutter: vp2px(1) === 2 ? $r('app.float.wh_value_12') : $r('app.float.wh_value_0') + }) { + GridCol({ span: COPIES_NUM }) { + Row({}) { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.installInStorageDevice'), isStartBySheet: this.isStartBySheet, + icBackIsVisibility: !this.isStartBySheetFirst, + onBackClicked: () => { + this.selected?.(NavEntryKey.POP); + }}) + .margin({ + top: this.isStartBySheet ? 8 : 0, + left: 12, + right: 12 + }) + }.zIndex(1) + .onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }) + + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.scroller) { + Column() { + Row() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Text($r('app.string.CA_cert')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ left: $r('app.float.wh_value_12') }) + .textAlign(TextAlign.Start) + } + } + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }) + .constraintSize({ + minHeight: $r('app.float.wh_value_48') + }) + .onClick(() => { + this.rootCertificateDialog.open(); + }) + + Divider() + .color($r('sys.color.ohos_id_color_list_separator')) + .margin({ + left: $r('app.float.wh_value_12'), + right: $r('app.float.wh_value_12') + }) + + Row() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Text($r('app.string.system_credentials')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ left: $r('app.float.wh_value_12') }) + .textAlign(TextAlign.Start) + } + .onClick(() => { + this.installCertFlag = false; + AppStorage.setOrCreate('installSystemCred', true); + AppStorage.setOrCreate('installUserCred',false); + if (this.isStartBySheet) { + this.startInstallEvidenceBySheet(); + } else { + this.mFaPresenter.startInstallEvidence(this.context); + } + }) + } + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }) + .constraintSize({ + minHeight: $r('app.float.wh_value_48') + }) + + Divider() + .color($r('sys.color.ohos_id_color_list_separator')) + .margin({ + left: $r('app.float.wh_value_12'), + right: $r('app.float.wh_value_12') + }) + + Row() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Text($r('app.string.user_certificate_credentials')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ left: $r('app.float.wh_value_12') }) + .textAlign(TextAlign.Start) + } + .onClick(() => { + this.installCertFlag = false; + AppStorage.setOrCreate('installUserCred', true); + AppStorage.setOrCreate('installSystemCred',false); + if (this.isStartBySheet) { + this.startInstallEvidenceBySheet(); + } else { + this.mFaPresenter.startInstallEvidence(this.context); + } + }) + } + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }) + .constraintSize({ + minHeight: $r('app.float.wh_value_48') + }) + } + .padding($r('app.float.wh_value_4')) + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + .borderRadius($r('app.float.radius_20')) + .width(ComponentConfig.WH_100_100) + } + .align(Alignment.Top) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Off) + .width(WidthPercent.WH_100_100) + .edgeEffect(EdgeEffect.Spring) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .padding({ + left: 16, + right: 16, + bottom: $r('app.float.wh_value_24') + }).constraintSize({ + minHeight: this.getScrollMinHeight() + }).onAreaChange((oldArea, newArea) => { + this.scrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.scroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: $r('app.float.wh_value_24') + }) + }.height(this.scrollerHeight) + }.padding({ + top: this.headRectHeight + }) + } + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100); + } + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100); + } + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100); + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < this.headRectHeightReal) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal; + } + + onPageShow() { + let uiExtensionFlag = GlobalContext.getContext().getFlag(); + let installType: string = GlobalContext.getContext().getAbilityWant().parameters?.installType as string; + let uri = GlobalContext.getContext().getAbilityWant().uri || + GlobalContext.getContext().getAbilityWant().parameters?.uri; + GlobalContext.getContext().clearAbilityWantUri(); + GlobalContext.getContext().clearAbilityWantParamsUri(); + if (uri === 'certInstall') { + router.pushUrl({ + url: 'pages/certInstallFromStorage' + }) + } else if (uri === 'requestAuthorize') { + this.mFaPresenter.startRequestAuth(GlobalContext.getContext().getAbilityWant().parameters?.appUid as string); + } else if (uiExtensionFlag && uri === 'systemCredInstall' && installType === 'systemCred') { + AppStorage.setOrCreate('installSystemCred', true); + AppStorage.setOrCreate('installUserCred', false); + this.mFaPresenter.startInstallEvidence(this.context); + } else if (uiExtensionFlag && uri === 'specifyInstall') { + let fileUri = GlobalContext.getContext().getAbilityWant().parameters?.fileUri as string; + this.mFaPresenter.startInstall(installType, fileUri); + } else { + console.error('The want type is not supported'); + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/certManagerFa.ets b/entry/src/main/ets/pages/certManagerFa.ets new file mode 100755 index 0000000..b58c158 --- /dev/null +++ b/entry/src/main/ets/pages/certManagerFa.ets @@ -0,0 +1,349 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { WidthPercent } from '../common/util/ConfigData'; +import HeadComponent from '../common/component/headComponent'; +import { SubEntryComponent } from '../common/component/subEntryComponent'; +import CmFaPresenter from '../presenter/CmFaPresenter'; +import { GlobalContext } from '../common/GlobalContext'; +import router from '@ohos.router'; +import promptAction from '@ohos.promptAction'; +import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; +import { NavEntryKey } from '../common/NavEntryKey'; +import { SheetParam } from '../common/util/SheetParam'; + +const DISPLAY_DURATION: number = 2000; +const COPIES_NUM: number = 12; + +class CertListItem { + public targetPage: string = ''; + public title: Resource = $r('app.string.CA_cert'); +}; + +@Entry +@Component +export struct CertificateComponent { + @State columnMargin: string = '12vp'; + @State mFaPresenter: CmFaPresenter = CmFaPresenter.getInstance(); + private listItems: Array = [ + { targetPage: 'pages/trustedCa', title: $r('app.string.CA_cert') }, + { targetPage: 'pages/cerEvidenceFa', title: $r('app.string.userEvidence') } + ]; + @Styles normalStyle() { + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + }; + @Styles pressedStyle() { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + }; + + isStartBySheet: boolean = false; + selected?: (path: string) => void; + @State private headRectHeight: number = 64; + @State private headRectHeightReal: number = 0; + @Prop sheetParam: SheetParam; + private scroller: Scroller = new Scroller(); + @State private scrollerHeight: number = 0; + + build() { + Column() { + GridRow({ + columns: COPIES_NUM, + gutter: vp2px(1) === 2 ? $r('app.float.wh_value_12') : $r('app.float.wh_value_0') + }) { + GridCol({ span: COPIES_NUM }) { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.certificateTab'), icBackIsVisibility: !this.isStartBySheet }) + .margin({ + top: this.isStartBySheet ? 8 : 0, + left: 12, + right: 12 + }) + }.onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }).zIndex(1) + + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.scroller) { + Column({ space: this.columnMargin }) { + List() { + ForEach(this.listItems, (item: CertListItem) => { + ListItem() { + SubEntryComponent({ targetPage: item.targetPage, title: item.title, + onItemClicked: targetRouter => { + if (this.isStartBySheet) { + if (targetRouter === 'pages/trustedCa') { + this.selected?.(NavEntryKey.CA_CERTIFICATE_ENTRY); + } else { + this.selected?.(NavEntryKey.CREDENTIAL_LIST_ENTRY); + } + } else { + router.pushUrl({ + url: targetRouter + }); + } + } }) + } + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }); + }, (item: CertListItem) => JSON.stringify(item)) + } + .padding($r('app.float.wh_value_4')) + .divider({ + strokeWidth: $r('app.float.sys_list_divider_strokeWidth_value'), + color: $r('sys.color.ohos_id_color_list_separator'), + startMargin: $r('app.float.wh_value_8'), + endMargin: $r('app.float.wh_value_8') + }) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) + + CertInstallComponent({ mFaPresenter: $mFaPresenter, onItemClicked: () => { + if (this.isStartBySheet) { + this.selected?.(NavEntryKey.INSTALL_ENTRY); + } else { + router.pushUrl({ + url: 'pages/certInstallFromStorage' + }, router.RouterMode.Standard) + } + } }) + + DeleteAll({ mFaPresenter: $mFaPresenter }) + } + .width(WidthPercent.WH_100_100) + } + .align(Alignment.Top) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Off) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .edgeEffect(EdgeEffect.Spring) + .padding({ + left: 16, + right: 16, + bottom: $r('app.float.wh_value_24') + }) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }).onAreaChange((oldArea, newArea) => { + this.scrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.scroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: $r('app.float.wh_value_24') + }) + }.height(this.scrollerHeight) + } + .padding({ + top: this.headRectHeight + }) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + } + .margin(vp2px(1) === 2 ? $r('app.float.item_common_horizontal_margin') : $r('app.float.wh_value_0')) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100); + } + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < this.headRectHeightReal) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal; + } + + onPageShow() { + let uri = GlobalContext.getContext().getAbilityWant().uri; + GlobalContext.getContext().clearAbilityWantUri(); + + if (uri === 'certInstall') { + router.pushUrl({ + url: 'pages/certInstallFromStorage' + }) + } else if (uri === 'requestAuthorize') { + this.mFaPresenter.startRequestAuth(GlobalContext.getContext().getAbilityWant().parameters?.appUid as string); + } else { + console.error('The want type is not supported'); + } + } +} + +@Component +struct DeleteAll { + @Link mFaPresenter: CmFaPresenter; + @Styles normalStyle() { + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + }; + @Styles pressedStyle() { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + }; + + deleteWarnDialog: CustomDialogController = new CustomDialogController({ + builder: CustomContentDialog({ + contentBuilder: () => { + this.deleteWarnContent(); + }, + contentAreaPadding: {right: $r('app.float.wh_value_0')}, + buttons: [ + { + value: $r('app.string.deleteAllCredCancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.deleteWarnDialog?.close(); + } + }, + { + value: $r('app.string.deleteAllCredDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.mFaPresenter.uninstallAllCert(); + this.deleteWarnDialog?.close(); + promptAction.showToast({ + message: $r('app.string.delete_success'), + duration: DISPLAY_DURATION, + }) + }, + role: ButtonRole.ERROR + } + ] + }), + }) + + @Builder + deleteWarnContent(): void { + Column() { + Text($r('app.string.deleteAllCredDialogTitle')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.deleteAllCredDialogMessage')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Regular) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .margin({ + top: $r('app.float.wh_value_16'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + } + + build() { + Row() { + Text($r('app.string.deleteAllCred')) + .fontSize($r('app.float.font_16')) + .lineHeight($r('app.float.wh_value_22')) + .fontWeight(FontWeight.Medium) + .fontColor($r('sys.color.ohos_id_color_text_hyperlink')) + .padding({ left: $r('app.float.distance_8') }) + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }) + .height(WidthPercent.WH_100_100) + .borderRadius($r('app.float.radius_20')) + .width(WidthPercent.WH_100_100) + .textAlign(TextAlign.Start); + } + .padding($r('app.float.distance_4')) + .height($r('app.float.wh_value_56')) + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.radius_24')) + .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary')) + .onClick(() => { + this.deleteWarnDialog.open(); + }) + } +} + +@Component +export struct CertInstallComponent { + @Link mFaPresenter: CmFaPresenter; + @Styles normalStyle() { + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + }; + @Styles pressedStyle() { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + }; + + onItemClicked?: () => void; + + build() { + Column() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Row() { + Text($r('app.string.installInStorageDevice')) + .fontSize($r('app.float.font_16')) + .lineHeight($r('app.float.wh_value_22')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ left: $r('app.float.wh_value_8') }); + } + + Image($r('app.media.ic_settings_arrow')) + .width($r('app.float.wh_value_12')) + .height($r('app.float.wh_value_24')) + .fillColor($r('sys.color.ohos_id_color_primary')) + .opacity($r('app.float.opacity_0_2')) + .margin({ right: $r('app.float.wh_value_8') }); + } + .borderRadius($r('app.float.radius_20')) + .height(WidthPercent.WH_100_100) + .width(WidthPercent.WH_100_100) + .stateStyles({ + normal: this.normalStyle, + pressed: this.pressedStyle + }) + } + .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary')) + .padding($r('app.float.distance_4')) + .height($r('app.float.wh_value_56')) + .borderRadius($r('app.float.radius_24')) + .onClick(() => { + this.onItemClicked?.(); + }); + } +} diff --git a/entry/src/main/ets/pages/certPwdInput.ets b/entry/src/main/ets/pages/certPwdInput.ets new file mode 100755 index 0000000..620ee65 --- /dev/null +++ b/entry/src/main/ets/pages/certPwdInput.ets @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import HeadComponent from '../common/component/headComponent'; +import { WidthPercent, ConfigValue } from '../common/util/ConfigData'; +import router from '@ohos.router'; +import CmFaPresenter from '../presenter/CmFaPresenter'; +import { GlobalContext } from '../common/GlobalContext'; +import { RouterFileVo } from '../model/CertManagerVo/RouterInfoVo'; +import CmInstallPresenter from '../presenter/CmInstallPresenter'; +import { CMModelErrorCode } from '../model/CertMangerModel'; +import ComponentConfig from '../common/component/ComponentConfig'; +import PreventScreenshotsPresenter from '../model/PreventScreenshotsModel'; + +@Entry +@Component +struct CertPwdInput { + @State certPwd: string = ''; + @State mFaPresenter: CmFaPresenter = CmFaPresenter.getInstance(); + @State isPasswordError: boolean = false; + @State passWordWarn: number = 0; + @State mAppCredAuthPresenter: CmInstallPresenter = CmInstallPresenter.getInstance(); + @State mPreventScreenshotsPresenter: PreventScreenshotsPresenter = PreventScreenshotsPresenter.getInstance(); + + onPageShow() { + this.mPreventScreenshotsPresenter.PreventScreenshots(true, GlobalContext.getContext().getSession()); + let uri = GlobalContext.getContext().getAbilityWant().uri; + GlobalContext.getContext().clearAbilityWantUri(); + + if (uri === 'certInstall') { + router.pushUrl({ + url: 'pages/certInstallFromStorage' + }) + } else if (uri === 'requestAuthorize') { + this.mFaPresenter.startRequestAuth(GlobalContext.getContext().getAbilityWant().parameters?.appUid as string); + } else { + console.error('The want type is not supported'); + } + } + + onPageHide() { + this.mPreventScreenshotsPresenter.PreventScreenshots(false, GlobalContext.getContext().getSession()); + } + + build() { + Column() { + Column() { + HeadComponent({ headName: $r('app.string.certificatePwdTab') }); + Text($r('app.string.certificatePwdInfo')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.distance_24'), + left: $r('app.float.distance_24') + }) + .alignSelf(ItemAlign.Start); + + TextInput({ text: this.certPwd }) + .type(InputType.Password) + .focusable(true) + .border(this.isPasswordError ? { + width: $r('app.float.wh_value_1'), + color: $r('sys.color.ohos_id_color_warning') + } : { width: $r('app.float.wh_value_0') }) + .maxLength(ConfigValue.PWD_MAX_LENGTH) + .margin({ + top: $r('app.float.distance_16'), + left: $r('app.float.distance_24'), + right: $r('app.float.distance_24') + }) + .height($r('app.float.wh_value_40')) + .onChange((value: string) => { + this.certPwd = value; + }) + + Row() { + Text($r('app.string.Password_Message')) + .fontFamily('HarmonyHeiTi') + .fontSize($r('app.float.distance_14')) + .fontWeight(FontWeight.Regular) + .lineHeight($r('app.float.distance_19')) + .width(ComponentConfig.WH_100_100) + .textAlign(TextAlign.Center) + .fontColor($r('sys.color.ohos_id_color_warning')) + .visibility(this.passWordWarn === CMModelErrorCode.CM_MODEL_ERROR_PASSWORD_ERR + ? Visibility.Visible : Visibility.None) + } + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_75_100); + + Column() { + Row() { + Button() { + Text($r('app.string.installPwdInputCancel')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Medium) + .fontColor($r('sys.color.ohos_id_color_text_hyperlink')) + } + .type(ButtonType.Capsule) + .backgroundColor($r('app.color.install_cancel_bt_bg_color')) + .width($r('app.float.component_button_width_phone')) + .height($r('app.float.application_button_height')) + .margin({ + left: $r('app.float.distance_24'), + }) + .onClick(() => { + router.back(); + }) + + Button() { + Text($r('app.string.installPwdInputConfirm')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Medium) + .fontColor($r('app.color.install_confirm_bt_font_color')) + } + .type(ButtonType.Capsule) + .backgroundColor($r('app.color.install_confirm_bt_bg_color')) + .width($r('app.float.component_button_width_phone')) + .height($r('app.float.application_button_height')) + .margin({ + left: $r('app.float.distance_16'), + right: $r('app.float.distance_24'), + }) + .enabled(this.certPwd !== undefined && this.certPwd.length > 0) + .onClick(() => { + let fileInfo: RouterFileVo = router.getParams() as RouterFileVo; + this.isPasswordError = false; + GlobalContext.getContext().getPwdStore().setCertPwd(this.certPwd); + CmInstallPresenter.getInstance().installCert(fileInfo.uri, '', fileInfo.suffix, true).then((resultCode) => { + this.passWordWarn = resultCode; + if (resultCode === CMModelErrorCode.CM_MODEL_ERROR_PASSWORD_ERR) { + this.isPasswordError = true; + } + }); + }) + } + .margin({ bottom: $r('app.float.wh_value_24') }) + } + .justifyContent(FlexAlign.End) + .height(WidthPercent.WH_25_100); + } + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_100_100); + } +} diff --git a/entry/src/main/ets/pages/detail/AuthorizedAppManagementPage.ets b/entry/src/main/ets/pages/detail/AuthorizedAppManagementPage.ets new file mode 100755 index 0000000..7284109 --- /dev/null +++ b/entry/src/main/ets/pages/detail/AuthorizedAppManagementPage.ets @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import HeadComponent from '../../common/component/headComponent'; +import { WidthPercent } from '../../common/util/ConfigData'; +import { AppAuthorVo } from '../../model/CertManagerVo/AppAuthorVo'; +import CmShowAppCredPresenter from '../../presenter/CmShowAppCredPresenter'; +import { BusinessError } from '@ohos.base'; +import { SheetParam } from '../../common/util/SheetParam'; +import { NavEntryKey } from '../../common/NavEntryKey'; + +const TAG: string = 'AuthorizedAppManagementPage: '; + +export class AuthorizedAppManagementParam { + public presenter: CmShowAppCredPresenter; + + constructor(presenter: CmShowAppCredPresenter) { + this.presenter = presenter; + } +} + +@Component +export struct DialogComponent { + @Link uidItem: CmShowAppCredPresenter; + private appName: string = ''; + private appImage: string = ''; + private indexNum: number = 0; + + onChanged?: () => void; + + build() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Row() { + Image(this.appImage) + .width($r('app.float.credentials_app_image_wh')) + .height($r('app.float.credentials_app_image_hg')) + + Text(this.appName) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ left: $r('app.float.credentials_app_name_margin') }) + } + .alignItems(VerticalAlign.Center) + + Toggle({ type: ToggleType.Switch, isOn: true }) + .width($r('app.float.credentials_app_Toggle_wh')) + .height($r('app.float.credentials_app_Toggle_hg')) + .selectedColor($r('app.color.credentials_app_Toggle_selectColor')) + .onChange((isOn: boolean) => { + this.uidItem.appInfoList[this.indexNum].status = isOn; + if (!isOn) { + this.onChanged?.(); + } + }) + .margin({ right: $r('app.float.wh_value_16') }) + + }.height($r('app.float.wh_value_72')) + } +} + +@Component +export struct AuthorizedAppManagementPage { + private stack?: NavPathStack; + + @State presenter: CmShowAppCredPresenter = CmShowAppCredPresenter.getInstance(); + @Prop sheetParam: SheetParam; + @State private headRectHeight: number = 64; + @State private headRectHeightReal: number = 0; + private scroller: Scroller = new Scroller(); + @State private scrollerHeight: number = 0; + + build() { + NavDestination() { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.managerAuthApp'), isStartBySheet: true, onBackClicked: () => { + this.stack?.pop(); + } }) + .margin({ + left: $r('app.float.wh_value_12'), + top: 8 + }) + }.zIndex(1) + .onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }) + + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.scroller) { + List() { + ForEach(this.presenter.appInfoList, (item: AppAuthorVo, index) => { + ListItem() { + DialogComponent({ appImage: item.appImage, appName: item.appName, + indexNum: index, uidItem: $presenter, + onChanged: async () => { + await this.presenter.removeGrantedAppList(this.presenter.credInfo.keyUri); + this.presenter.getAuthorizedAppList(this.presenter.credInfo.keyUri); + } }) + } + }) + } + .scrollBar(BarState.Off) + .divider({ + strokeWidth: $r('app.float.Evidence_strokeWidth'), + color: $r('sys.color.ohos_id_color_list_separator'), + startMargin: 64 + }) + } + .align(Alignment.Top) + .edgeEffect(EdgeEffect.Spring) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }) + .scrollBar(BarState.Off) + .padding({ + left: $r('app.float.wh_value_24'), + bottom: $r('app.float.wh_value_24') + }).onAreaChange((oldArea, newArea) => { + this.scrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.scroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: $r('app.float.wh_value_24') + }) + }.height(this.scrollerHeight) + }.padding({ + top: this.headRectHeight + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.AUTHORIZED_APP_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + try { + this.presenter = (ctx.pathInfo.param as AuthorizedAppManagementParam).presenter; + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'Get presenter failed: ' + error?.code + ', message: ' + error?.message); + } + }) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < this.headRectHeightReal) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/detail/CaSystemDetailPage.ets b/entry/src/main/ets/pages/detail/CaSystemDetailPage.ets new file mode 100755 index 0000000..dd4dac2 --- /dev/null +++ b/entry/src/main/ets/pages/detail/CaSystemDetailPage.ets @@ -0,0 +1,375 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import HeadComponent from '../../common/component/headComponent'; +import { NavEntryKey } from '../../common/NavEntryKey'; +import { WidthPercent } from '../../common/util/ConfigData'; +import CmShowSysCaPresenter from '../../presenter/CmShowSysCaPresenter'; +import { SheetParam } from '../../common/util/SheetParam'; + +@Component +export struct DialogSubjectComponent { + private map?: Map; + private subjectNameCN: string = ''; + private subjectNameO: string = ''; + private subjectNameOU: string = ''; + private serial: string = ''; + + aboutToAppear() { + if (this.map != null) { + let subjectNameCN = this.map.get('常用名称:'); + if (subjectNameCN !== undefined) { + this.subjectNameCN = subjectNameCN; + } + + let subjectNameO = this.map.get('组织:'); + if (subjectNameO !== undefined) { + this.subjectNameO = subjectNameO; + } + + let subjectNameOU = this.map.get('组织单位:'); + if (subjectNameOU !== undefined) { + this.subjectNameOU = subjectNameOU; + } + + let serial = this.map.get('序列号:'); + if (serial !== undefined) { + this.serial = serial; + } + } + } + + build() { + Column() { + Text($r('app.string.DialogSubjectComponent_firText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_l') + }) + Text(this.subjectNameCN) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + Text($r('app.string.DialogSubjectComponent_secText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_m') + }) + Text(this.subjectNameO) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + Text($r('app.string.DialogSubjectComponent_thdText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_m') + }) + Text(this.subjectNameOU) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + Text($r('app.string.DialogSubjectComponent_fouText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_m') + }) + Text(this.serial) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + }.alignItems(HorizontalAlign.Start); + } +} + +@Component +export struct DialogIssuerComponent { + private map?: Map; + private issuerNameCN: string = ''; + private issuerNameO: string = ''; + private issuerNameOU: string = ''; + + aboutToAppear() { + if (this.map != null) { + let issuerNameCN = this.map.get('常用名称:'); + if (issuerNameCN !== undefined) { + this.issuerNameCN = issuerNameCN; + } + + let issuerNameO = this.map.get('组织:'); + if (issuerNameO !== undefined) { + this.issuerNameO = issuerNameO; + } + + let issuerNameOU = this.map.get('组织单位:'); + if (issuerNameOU !== undefined) { + this.issuerNameOU = issuerNameOU; + } + } + } + + build() { + Column() { + Text($r('app.string.DialogSubjectComponent_firText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_l') + }) + Text(this.issuerNameCN) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + Text($r('app.string.DialogSubjectComponent_secText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_m') + }) + Text(this.issuerNameO) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + Text($r('app.string.DialogSubjectComponent_thdText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_m') + }) + Text(this.issuerNameOU) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + }.alignItems(HorizontalAlign.Start); + } +} + +@Component +export struct DialogDateComponent { + private map?: Map; + private notBefore: string = ''; + private notAfter: string = ''; + + aboutToAppear() { + if (this.map != null) { + let notBefore = this.map.get('颁发时间:'); + if (notBefore != undefined) { + this.notBefore = notBefore; + } + + let notAfter = this.map.get('有效期至:'); + if (notAfter != undefined) { + this.notAfter = notAfter; + } + } + } + + build() { + Column() { + Text($r('app.string.DialogSubjectComponent_fifText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_l') + }) + Text(this.notBefore) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + Text($r('app.string.DialogSubjectComponent_sixText')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_m') + }) + Text(this.notAfter) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + }.alignItems(HorizontalAlign.Start); + } +} + +@Component +export struct DialogFingerPrintComponent { + private fingerprintSha256: string = ''; + + build() { + Column() { + Text($r('app.string.CustomDialogExample_FingerPrint_text')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.sys_elements_margin_vertical_l') + }) + Text(this.fingerprintSha256) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + right: $r('app.float.wh_value_24') + }) + }.alignItems(HorizontalAlign.Start); + } +} + +@Component +export struct CaSystemDetailPage { + @State mShowSysCaPresenter: CmShowSysCaPresenter = CmShowSysCaPresenter.getInstance(); + + private stack?: NavPathStack; + + @State headRectHeight: number = 64; + @State headRectHeightReal: number = 0; + @Prop sheetParam: SheetParam; + private scroller: Scroller = new Scroller(); + @State scrollerHeight: number = 0; + + build() { + NavDestination() { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.CustomDialogExample_firText'), isStartBySheet: true, onBackClicked: () => { + this.stack?.pop(); + } }) + .margin({ + left: $r('app.float.wh_value_12'), + top: 8 + }) + }.zIndex(1) + .onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }) + + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.scroller) { + this.buildContent() + } + .padding({ + left: 16, right: 16, + bottom: $r('app.float.wh_value_24') + }) + .align(Alignment.Top) + .scrollable(ScrollDirection.Vertical) + .edgeEffect(EdgeEffect.Spring) + .scrollBar(BarState.Off) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }).onAreaChange((oldArea, newArea) => { + this.scrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.scroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: $r('app.float.wh_value_24') + }) + }.height(this.scrollerHeight) + }.padding({ + top: this.headRectHeight + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.CA_SYSTEM_DETAIL_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + }) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < this.headRectHeightReal) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal; + } + + @Builder + private buildContent() { + Column() { + Text(this.mShowSysCaPresenter.certInfo.certAlias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_8'), + bottom: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.CustomDialogExample_firListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + + DialogSubjectComponent({ map: this.mShowSysCaPresenter.certInfo.subjectNameMap }) + + Text($r('app.string.CustomDialogExample_secListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + + DialogIssuerComponent({ map: this.mShowSysCaPresenter.certInfo.issuerNameMap }) + + Text($r('app.string.CustomDialogExample_thdListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + + DialogDateComponent({ map: this.mShowSysCaPresenter.certInfo.dateMap }) + + Text($r('app.string.CustomDialogExample_fouListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + + DialogFingerPrintComponent({ fingerprintSha256: this.mShowSysCaPresenter.certInfo.fingerprintSha256 }) + } + .alignItems(HorizontalAlign.Start) + .width(WidthPercent.WH_100_100) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/detail/CaUserDetailPage.ets b/entry/src/main/ets/pages/detail/CaUserDetailPage.ets new file mode 100755 index 0000000..b61fc43 --- /dev/null +++ b/entry/src/main/ets/pages/detail/CaUserDetailPage.ets @@ -0,0 +1,269 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import HeadComponent from '../../common/component/headComponent'; +import { WidthPercent } from '../../common/util/ConfigData'; +import CmShowUserCaPresenter from '../../presenter/CmShowUserCaPresenter'; +import { + DialogDateComponent, + DialogFingerPrintComponent, + DialogIssuerComponent, + DialogSubjectComponent +} from './CaSystemDetailPage'; +import { AlertDialog } from '@ohos.arkui.advanced.Dialog'; +import { BusinessError } from '@ohos.base'; +import { SheetParam } from '../../common/util/SheetParam'; +import { NavEntryKey } from '../../common/NavEntryKey'; + +const TAG: string = 'CaUserDetailPage: '; + +export class CaUserDetailParam { + public showUserCaPresenter: CmShowUserCaPresenter; + + constructor(presenter: CmShowUserCaPresenter) { + this.showUserCaPresenter = presenter; + } +} + +@Component +export struct CaUserDetailPage { + private showUserCaPresenter: CmShowUserCaPresenter = CmShowUserCaPresenter.getInstance(); + + private stack?: NavPathStack; + + @State private bottomRectHeight: number = 80; + @State private headRectHeight: number = 64; + @State private headRectHeightReal: number = 0; + + @State toggleStatus: boolean = false; + @Prop sheetParam: SheetParam; + + rootCertificateDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + cancel: () => { + this.toggleStatus = false; + }, + builder: AlertDialog({ + primaryTitle: $r('app.string.root_certificate'), + content: $r('app.string.root_certificate_message'), + primaryButton: { + value: $r('app.string.root_certificate_cancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.toggleStatus = false; + this.showUserCaPresenter.updateUserTrustedCertificateList(); + } + }, + secondaryButton: { + value: $r('app.string.root_certificate_continue'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.showUserCaPresenter.setUserCertificateStatus(this.showUserCaPresenter.certInfo.uri, true) + .then(result => { + if (result) { + this.showUserCaPresenter.updateUserTrustedCertificateList(); + } else { + this.toggleStatus = false; + } + }); + } + } + }) + }) + + deleteCertWarnDialog: CustomDialogController = new CustomDialogController({ + builder: AlertDialog({ + primaryTitle: $r('app.string.CustomDialogExampleUser_warning_Button_title_text'), + content: $r('app.string.CustomDialogExampleUser_warning_Button_message_text'), + primaryButton: { + value: $r('app.string.deleteAllCredCancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + } + }, + secondaryButton: { + value: $r('app.string.deleteAllCredDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.showUserCaPresenter.deleteUserCertificate(this.showUserCaPresenter.certInfo.uri, () => { + this.showUserCaPresenter.updateUserTrustedCertificateList(); + }) + this.stack?.pop(); + }, + role: ButtonRole.ERROR + } + }) + }) + + build() { + NavDestination() { + Stack({ alignContent: Alignment.Bottom }) { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.CustomDialogExample_firText'), isStartBySheet: true, onBackClicked: () => { + this.stack?.pop(); + } }) + .margin({ + left: $r('app.float.wh_value_12'), + top: 8 + }) + }.zIndex(1) + .onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }) + + Stack({ alignContent: Alignment.TopEnd }) { + Scroll() { + this.buildContent() + } + .padding({ + left: 16, right: 16 + }) + .align(Alignment.Top) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Auto) + .edgeEffect(EdgeEffect.Spring) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }) + }.padding({ + top: this.headRectHeight + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .padding({ + bottom: this.bottomRectHeight + }) + + Column() { + Button($r('app.string.CustomDialogExampleUser_warning_Button_title_text')) + .onClick(() => { + this.deleteCertWarnDialog.open(); + }) + .role(ButtonRole.ERROR) + .buttonStyle(ButtonStyleMode.NORMAL) + .margin({ + top: $r('app.float.distance_16'), + bottom: $r('app.float.distance_24') + }) + .width(WidthPercent.WH_100_100) + }.onAreaChange((oldArea: Area, newArea: Area) => { + this.bottomRectHeight = newArea.height as number; + }) + .padding({ + left: 16, right: 16 + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.CA_USER_DETAIL_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + try { + this.showUserCaPresenter = (ctx.pathInfo.param as CaUserDetailParam).showUserCaPresenter; + this.toggleStatus = this.showUserCaPresenter.certInfo.status; + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'Get presenter failed: ' + error?.code + ', message: ' + error?.message); + } + }) + } + + @Builder + private buildContent() { + Column() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(this.showUserCaPresenter.certInfo.certAlias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + }.alignItems(HorizontalAlign.Start); + + Toggle({ type: ToggleType.Switch, isOn: $$this.toggleStatus }) + .margin({ + left: $r('app.float.wh_value_5') + }) + .flexShrink(0) + .onChange((isOn: boolean) => { + if (isOn) { + this.rootCertificateDialog.open(); + } else { + this.showUserCaPresenter.setUserCertificateStatus(this.showUserCaPresenter.certInfo.uri, false); + } + }) + }.margin({ top: 8 }) + + Text($r('app.string.CustomDialogExample_firListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + + DialogSubjectComponent({ map: this.showUserCaPresenter.certInfo.subjectNameMap }) + + Text($r('app.string.CustomDialogExample_secListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + + DialogIssuerComponent({ map: this.showUserCaPresenter.certInfo.issuerNameMap }) + + Text($r('app.string.CustomDialogExample_thdListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + + DialogDateComponent({ map: this.showUserCaPresenter.certInfo.dateMap }) + + Text($r('app.string.CustomDialogExample_fouListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + + DialogFingerPrintComponent({ + fingerprintSha256: this.showUserCaPresenter.certInfo.fingerprintSha256 + }) + } + .width(WidthPercent.WH_100_100) + .alignItems(HorizontalAlign.Start) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < (this.headRectHeightReal + this.bottomRectHeight)) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal - this.bottomRectHeight; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/detail/CredPwdInputPage.ets b/entry/src/main/ets/pages/detail/CredPwdInputPage.ets new file mode 100755 index 0000000..1769719 --- /dev/null +++ b/entry/src/main/ets/pages/detail/CredPwdInputPage.ets @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import HeadComponent from '../../common/component/headComponent'; +import { ConfigValue, WidthPercent } from '../../common/util/ConfigData'; +import { BusinessError } from '@ohos.base'; +import { CMModelErrorCode } from '../../model/CertMangerModel'; +import { RouterFileVo } from '../../model/CertManagerVo/RouterInfoVo'; +import { GlobalContext } from '../../common/GlobalContext'; +import CmInstallPresenter from '../../presenter/CmInstallPresenter'; +import { SheetParam } from '../../common/util/SheetParam'; +import { NavEntryKey } from '../../common/NavEntryKey'; + +const TAG: string = 'CredPwdInputPage: '; + +export class CredPwdInputParam { + public credFile: RouterFileVo; + + constructor(file: RouterFileVo) { + this.credFile = file; + } +} + +@Component +export struct CredPwdInputPage { + private stack?: NavPathStack; + + private credFile?: RouterFileVo; + + @State private bottomRectHeight: number = 80; + @State private headRectHeight: number = 64; + @State private headRectHeightReal: number = 0; + + @State certPwd: string = ''; + @State isPasswordError: boolean = false; + @State passWordWarn: number = 0; + @Prop sheetParam: SheetParam; + + build() { + NavDestination() { + Stack({ alignContent: Alignment.Bottom }) { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.certificatePwdTab'), isStartBySheet: true, onBackClicked: () => { + this.stack?.pop(); + } }) + .margin({ + left: $r('app.float.wh_value_12'), + top: 8 + }) + }.zIndex(1) + .onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }) + + Stack({ alignContent: Alignment.TopEnd }) { + Scroll() { + this.buildContent() + } + .align(Alignment.Top) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Off) + .edgeEffect(EdgeEffect.Spring) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }) + }.padding({ + top: this.headRectHeight + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .padding({ + bottom: this.bottomRectHeight + }) + + Column() { + Button() { + Text($r('app.string.installPwdInputConfirm')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Medium) + .fontColor($r('app.color.install_confirm_bt_font_color')) + } + .type(ButtonType.Capsule) + .backgroundColor($r('app.color.install_confirm_bt_bg_color')) + .width(WidthPercent.WH_100_100) + .height($r('app.float.wh_value_40')) + .margin({ + top: $r('app.float.distance_16'), + bottom: $r('app.float.distance_24') + }) + .enabled(this.certPwd !== undefined && this.certPwd.length > 0) + .onClick(() => { + if (this.credFile === undefined || this.credFile === null) { + console.error(TAG + 'Cred file is undefined'); + return; + } + this.isPasswordError = false; + GlobalContext.getContext().getPwdStore().setCertPwd(this.certPwd); + CmInstallPresenter.getInstance().installCert(this.credFile.uri, '', this.credFile.suffix, false).then((resultCode) => { + this.passWordWarn = resultCode; + console.log(TAG + 'install cred result code = ' + resultCode); + if (resultCode === CMModelErrorCode.CM_MODEL_ERROR_PASSWORD_ERR) { + this.isPasswordError = true; + } else { + this.stack?.pop(); + } + }); + }) + }.onAreaChange((oldArea: Area, newArea: Area) => { + this.bottomRectHeight = newArea.height as number; + }) + .padding({ + left: 16, right: 16 + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + } + .expandSafeArea([SafeAreaType.KEYBOARD]) + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.CRED_PWD_INPUT_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + try { + this.credFile = (ctx.pathInfo.param as CredPwdInputParam).credFile; + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'Get cred file failed: ' + error?.code + ', message: ' + error?.message); + } + }) + } + + @Builder + private buildContent() { + Column() { + Text($r('app.string.certificatePwdInfo')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.distance_24'), + left: $r('app.float.distance_24') + }) + .alignSelf(ItemAlign.Start); + + TextInput({ text: this.certPwd }) + .type(InputType.Password) + .focusable(true) + .border(this.isPasswordError ? { + width: $r('app.float.wh_value_1'), + color: $r('sys.color.ohos_id_color_warning') + } : { width: $r('app.float.wh_value_0') }) + .maxLength(ConfigValue.PWD_MAX_LENGTH) + .margin({ + top: $r('app.float.distance_16'), + left: $r('app.float.distance_24'), + right: $r('app.float.distance_24') + }) + .height($r('app.float.wh_value_40')) + .onChange((value: string) => { + this.certPwd = value; + }) + + Row() { + Text($r('app.string.Password_Message')) + .fontFamily('HarmonyHeiTi') + .fontSize($r('app.float.distance_14')) + .fontWeight(FontWeight.Regular) + .lineHeight($r('app.float.distance_19')) + .width(WidthPercent.WH_100_100) + .textAlign(TextAlign.Center) + .fontColor($r('sys.color.ohos_id_color_warning')) + .visibility(this.passWordWarn === CMModelErrorCode.CM_MODEL_ERROR_PASSWORD_ERR + ? Visibility.Visible : Visibility.None) + } + } + .width(WidthPercent.WH_100_100) + .alignItems(HorizontalAlign.Start) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < (this.headRectHeightReal + this.bottomRectHeight)) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal - this.bottomRectHeight; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/detail/CredSystemDetailPage.ets b/entry/src/main/ets/pages/detail/CredSystemDetailPage.ets new file mode 100755 index 0000000..4c0bb2d --- /dev/null +++ b/entry/src/main/ets/pages/detail/CredSystemDetailPage.ets @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import HeadComponent from '../../common/component/headComponent'; +import { WidthPercent } from '../../common/util/ConfigData'; +import { AlertDialog, CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; +import { BusinessError } from '@ohos.base'; +import CmShowSysCredPresenter from '../../presenter/CmShowSysCredPresenter'; +import { SheetParam } from '../../common/util/SheetParam'; +import { NavEntryKey } from '../../common/NavEntryKey'; + +const TAG: string = 'CredSystemDetailPage: '; + +export class CredSystemDetailParam { + public presenter: CmShowSysCredPresenter; + + constructor(presenter: CmShowSysCredPresenter) { + this.presenter = presenter; + } +} + +@Component +export struct CredSystemDetailPage { + private presenter: CmShowSysCredPresenter = CmShowSysCredPresenter.getInstance(); + + private stack?: NavPathStack; + + @State private bottomRectHeight: number = 80; + @State private headRectHeight: number = 64; + @State private headRectHeightReal: number = 0; + @Prop private sheetParam: SheetParam; + + deleteWarnDialog: CustomDialogController = new CustomDialogController({ + builder: AlertDialog({ + primaryTitle: $r('app.string.warning_title'), + content: $r('app.string.warning_message'), + primaryButton: { + value: $r('app.string.cancelAuthApp'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + } + }, + secondaryButton: { + value: $r('app.string.deleteAllCredDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.presenter.deleteSystemCred(this.presenter.credInfo.keyUri); + this.presenter.updateSystemCredList(); + this.stack?.pop(); + }, + role: ButtonRole.ERROR + } + }), + }) + + build() { + NavDestination() { + Stack({ alignContent: Alignment.Bottom }) { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.evidenceDetails'), isStartBySheet: true, onBackClicked: () => { + this.stack?.pop(); + } }) + .margin({ + left: $r('app.float.wh_value_12'), + top: 8 + }) + }.zIndex(1) + .onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }) + + Column() { + Scroll() { + this.buildContent() + } + .align(Alignment.Top) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Auto) + .edgeEffect(EdgeEffect.Spring) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }) + }.padding({ + top: this.headRectHeight + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .padding({ + bottom: this.bottomRectHeight + }) + + Column() { + Button($r('app.string.warning_title')) + .onClick(() => { + this.deleteWarnDialog.open(); + }) + .role(ButtonRole.ERROR) + .buttonStyle(ButtonStyleMode.NORMAL) + .margin({ + top: $r('app.float.distance_16'), + bottom: $r('app.float.distance_24') + }) + .width(WidthPercent.WH_100_100) + .constraintSize({ + minHeight: $r('app.float.wh_value_40') + }) + }.onAreaChange((oldArea: Area, newArea: Area) => { + this.bottomRectHeight = newArea.height as number; + }) + .padding({ + left: 16, right: 16 + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.CRED_SYSTEM_DETAIL_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + try { + this.presenter = (ctx.pathInfo.param as CredSystemDetailParam).presenter; + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'Get presenter failed: ' + error?.code + ', message: ' + error?.message); + } + }) + } + + @Builder + private buildContent() { + Column() { + Text(this.presenter.credInfo.alias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.entryContains')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.wh_value_24'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.keyNum', String(this.presenter.credInfo.keyNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.userCerNum', String(this.presenter.credInfo.certNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .alignItems(HorizontalAlign.Start) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < (this.headRectHeightReal + this.bottomRectHeight)) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal - this.bottomRectHeight; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/detail/CredUserDetailPage.ets b/entry/src/main/ets/pages/detail/CredUserDetailPage.ets new file mode 100755 index 0000000..18882b5 --- /dev/null +++ b/entry/src/main/ets/pages/detail/CredUserDetailPage.ets @@ -0,0 +1,249 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import HeadComponent from '../../common/component/headComponent'; +import { WidthPercent } from '../../common/util/ConfigData'; +import { AlertDialog } from '@ohos.arkui.advanced.Dialog'; +import { BusinessError } from '@ohos.base'; +import CmShowAppCredPresenter from '../../presenter/CmShowAppCredPresenter'; +import { NavEntryKey } from '../../common/NavEntryKey'; +import { AuthorizedAppManagementParam } from './AuthorizedAppManagementPage'; +import { SheetParam } from '../../common/util/SheetParam'; + +const TAG: string = 'CredUserDetailPage: '; + +export class CredUserDetailParam { + public presenter: CmShowAppCredPresenter; + + constructor(presenter: CmShowAppCredPresenter) { + this.presenter = presenter; + } +} + +@Component +export struct CredUserDetailPage { + private presenter: CmShowAppCredPresenter = CmShowAppCredPresenter.getInstance(); + + private stack?: NavPathStack; + + @State private bottomRectHeight: number = 80; + @State private headRectHeight: number = 64; + @State private headRectHeightReal: number = 0; + @Prop private sheetParam: SheetParam; + + @State private itemBackgroundColor: ResourceColor = ''; + + @Styles pressedStyles(): void { .backgroundColor($r('sys.color.ohos_id_color_click_effect')) } + + @Styles normalStyles(): void { .backgroundColor(this.itemBackgroundColor) } + + deleteWarnDialog: CustomDialogController = new CustomDialogController({ + builder: AlertDialog({ + primaryTitle: $r('app.string.warning_title'), + content: $r('app.string.warning_message'), + primaryButton: { + value: $r('app.string.cancelAuthApp'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + } + }, + secondaryButton: { + value: $r('app.string.deleteAllCredDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.presenter.deleteAppCred(this.presenter.credInfo.keyUri); + this.stack?.pop(); + }, + role: ButtonRole.ERROR + } + }), + }) + + build() { + NavDestination() { + Stack({ alignContent: Alignment.Bottom }) { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.evidenceDetails'), isStartBySheet: true, onBackClicked: () => { + this.stack?.pop(); + } }) + .margin({ + left: $r('app.float.wh_value_12'), + top: 8 + }) + }.onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }).zIndex(1) + + Column() { + Scroll() { + this.buildContent() + } + .align(Alignment.Top) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Auto) + .edgeEffect(EdgeEffect.Spring) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }) + }.padding({ + top: this.headRectHeight + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .padding({ + bottom: this.bottomRectHeight + }) + + Column() { + Button($r('app.string.warning_title')) + .onClick(() => { + this.deleteWarnDialog.open(); + }) + .role(ButtonRole.ERROR) + .buttonStyle(ButtonStyleMode.NORMAL) + .margin({ + top: $r('app.float.distance_16'), + bottom: $r('app.float.distance_24') + }) + .width(WidthPercent.WH_100_100) + .constraintSize({ + minHeight: $r('app.float.wh_value_40') + }) + }.onAreaChange((oldArea: Area, newArea: Area) => { + this.bottomRectHeight = newArea.height as number; + }) + .padding({ + left: 16, right: 16 + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.CRED_USER_DETAIL_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + try { + this.presenter = (ctx.pathInfo.param as CredUserDetailParam).presenter; + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'Get presenter failed: ' + error?.code + ', message: ' + error?.message); + } + }) + } + + @Builder + private buildContent() { + Column() { + Text(this.presenter.credInfo.alias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.entryContains')) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.wh_value_24'), + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.keyNum', String(this.presenter.credInfo.keyNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.userCerNum', String(this.presenter.credInfo.certNum))) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_10'), + right: $r('app.float.wh_value_10') + }) + .opacity($r('app.float.opacity_100_60')) + .alignSelf(ItemAlign.Start) + + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text($r('app.string.managerAuthApp')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + } + + Row() { + Image($r('app.media.ic_settings_arrow')) + .width($r('app.float.managerAuthApp_image_wh')) + .height($r('app.float.managerAuthApp_image_hg')) + .fillColor($r('sys.color.ohos_id_color_primary')) + .opacity($r('app.float.managerAuthApp_image_opacity')) + } + } + .onClick(() => { + this.stack?.pushPath(new NavPathInfo(NavEntryKey.AUTHORIZED_APP_ENTRY, + new AuthorizedAppManagementParam(this.presenter))); + }) + .margin({ + top: $r('app.float.wh_value_12'), + left: $r('app.float.wh_value_16'), + right: $r('app.float.wh_value_16') + }) + .borderRadius($r('sys.float.corner_radius_level8')) + .backgroundColor(this.itemBackgroundColor) + .onHover((isHover?: boolean) => { + this.itemBackgroundColor = isHover ? $r('sys.color.ohos_id_color_hover') : ''; + }) + .stateStyles({ + pressed: this.pressedStyles, + normal: this.normalStyles + }) + .padding($r('app.float.wh_value_8')) + .constraintSize({ + minHeight: $r('app.float.wh_value_48') + }) + } + .width(WidthPercent.WH_100_100) + .alignItems(HorizontalAlign.Start) + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < (this.headRectHeightReal + this.bottomRectHeight)) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal - this.bottomRectHeight; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/picker/CaCertPage.ets b/entry/src/main/ets/pages/picker/CaCertPage.ets new file mode 100755 index 0000000..e761696 --- /dev/null +++ b/entry/src/main/ets/pages/picker/CaCertPage.ets @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NavEntryKey } from '../../common/NavEntryKey'; +import { WidthPercent } from '../../common/util/ConfigData'; +import { TrustedEvidence } from '../trustedCa'; +import { SheetParam } from '../../common/util/SheetParam'; + +@Component +export struct CaCertPage { + private stack?: NavPathStack; + + @Prop sheetParam: SheetParam; + + build() { + NavDestination() { + Column() { + TrustedEvidence({ + isStartBySheet: true, + sheetParam: this.sheetParam, + selected: (path, param) => { + if (path === undefined || path === null || path.length === 0) { + return; + } + if (path === NavEntryKey.POP) { + this.stack?.pop(); + } else { + this.stack?.pushPath(new NavPathInfo(path, param)); + } + } + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.CA_CERTIFICATE_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/picker/CertManagerSheetFa.ets b/entry/src/main/ets/pages/picker/CertManagerSheetFa.ets new file mode 100755 index 0000000..1af4231 --- /dev/null +++ b/entry/src/main/ets/pages/picker/CertManagerSheetFa.ets @@ -0,0 +1,377 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { WidthPercent } from '../../common/util/ConfigData'; +import { CertificateComponent } from '../certManagerFa'; +import { NavEntryKey } from '../../common/NavEntryKey'; +import { CaCertPage } from './CaCertPage'; +import { CredListPage } from './CredListPage'; +import { InstallPage } from './InstallPage'; +import { CaSystemDetailPage } from '../detail/CaSystemDetailPage'; +import { CaUserDetailPage } from '../detail/CaUserDetailPage'; +import { CredSystemDetailPage } from '../detail/CredSystemDetailPage'; +import { CredUserDetailPage } from '../detail/CredUserDetailPage'; +import { AuthorizedAppManagementPage } from '../detail/AuthorizedAppManagementPage'; +import { CredPwdInputPage } from '../detail/CredPwdInputPage'; +import Want from '@ohos.app.ability.Want'; +import deviceInfo from '@ohos.deviceInfo'; +import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; +import { TrustedEvidence } from '../trustedCa'; +import { evidenceList } from '../cerEvidenceFa'; +import { CertInstallFromStorage } from '../certInstallFromStorage'; +import window from '@ohos.window'; +import { SheetParam } from '../../common/util/SheetParam'; +import uiExtensionHost from '@ohos.uiExtensionHost'; +import { BusinessError } from '@ohos.base'; +import PreventScreenshotsPresenter from '../../model/PreventScreenshotsModel'; + +const TAG: string = 'CertManagerSheetFa:'; + +let storage = LocalStorage.getShared(); + +const PAGE_MAIN: number = 1; +const PAGE_CA_CERTIFICATE: number = 2; +const PAGE_CREDENTIAL: number = 3; +const PAGE_INSTALL_CERTIFICATE: number = 4; +const PAGE_TYPE: string = 'pageType'; + +const BOTTOM_SHEET_MIN_PERCENT = 0.6; +const CENTER_SHEET_MAX_PERCENT = 0.9; +const CENTER_SHEET_MIN_HEIGHT = 560; + +interface AvoidAreaParam { + type: window.AvoidAreaType; + area: window.AvoidArea; +} + +@Entry(storage) +@Component +export struct CertManagerSheetFa { + private sheetSession: UIExtensionContentSession = + storage.get('session') as UIExtensionContentSession; + private want: Want = storage.get('want') as Want; + private preventScreenshotsPresenter: PreventScreenshotsPresenter = PreventScreenshotsPresenter.getInstance(); + + @State @Watch('onSheetPageChanged') private stack: NavPathStack = new NavPathStack(); + @State private sheetParam: SheetParam = new SheetParam(); + @State private pageType: number = PAGE_MAIN; + @State private maxSheetPageHeight: number = 0; + @State private rootWidth: number = 0; + @State private rootHeight: number = 0; + @State private statusBarHeight: number = 0; + @State private aiBarHeight: number = 0; + + private avoidAreaChangedCallback: Callback = (avoidArea) => { + if (avoidArea.type === window.AvoidAreaType.TYPE_SYSTEM) { + this.statusBarHeight = px2vp(avoidArea.area.topRect.height); + this.updateSheetHeightLimit(); + } else if (avoidArea.type === window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) { + this.aiBarHeight = px2vp(avoidArea.area.bottomRect.height); + this.updateSheetHeightLimit(); + } else { + console.warn(TAG + 'avoidAreaChangedCallback, sheet type is invalid:' + this.sheetParam.sheetType); + } + } + + onSheetPageChanged(): void { + let allPageArray = this.stack.getAllPathName(); + if (allPageArray === undefined || allPageArray.length === 0) { + this.preventScreenshotsPresenter.PreventScreenshots(false, this.sheetSession); + this.sheetParam.lastSheetPage = ''; + return; + } + let lastPage = allPageArray[allPageArray.length - 1]; + this.sheetParam.lastSheetPage = lastPage; + if (lastPage !== NavEntryKey.CRED_PWD_INPUT_ENTRY) { + this.preventScreenshotsPresenter.PreventScreenshots(false, this.sheetSession); + return; + } + this.preventScreenshotsPresenter.PreventScreenshots(true, this.sheetSession); + } + + aboutToAppear(): void { + this.registerAvoidChangedListener(); + const parameters = this.want?.parameters; + if (parameters === undefined || parameters[PAGE_TYPE] === undefined) { + console.warn(TAG + 'page type param is undefined'); + return; + } + this.pageType = parameters[PAGE_TYPE] as number; + console.log(TAG + 'page type = ' + this.pageType); + } + + aboutToDisappear(): void { + this.unregisterAvoidChangedListener(); + } + + initStatusBarHeight(extWindow: uiExtensionHost.UIExtensionHostWindowProxy): void { + try { + let avoidArea: window.AvoidArea = extWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + if (avoidArea === undefined) { + console.warn(TAG + 'initStatusBarHeight, avoid area is undefined'); + } + this.statusBarHeight = px2vp(avoidArea.topRect.height); + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'initStatusBarHeight, occur error:' + error?.code + ', msg:' + error?.message); + } + } + + initAiBarHeight(extWindow: uiExtensionHost.UIExtensionHostWindowProxy): void { + try { + let avoidArea: window.AvoidArea = extWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR); + if (avoidArea === undefined) { + console.warn(TAG + 'initAiBarHeight, avoid area is undefined'); + } + this.aiBarHeight = px2vp(avoidArea.bottomRect.height); + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'initAiBarHeight, occur error:' + error?.code + ', msg:' + error?.message); + } + } + + registerAvoidChangedListener(): void { + try { + let extWindow: uiExtensionHost.UIExtensionHostWindowProxy = this.sheetSession?.getUIExtensionHostWindowProxy(); + if (extWindow === undefined) { + console.error(TAG + 'registerAvoidChangedListener, get extra window is undefined'); + return; + } + this.initStatusBarHeight(extWindow); + this.initAiBarHeight(extWindow); + extWindow.on('avoidAreaChange', this.avoidAreaChangedCallback); + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'registerAvoidChangedListener, occur error:' + error?.code + ', msg:' + error?.message); + } + } + + unregisterAvoidChangedListener(): void { + try { + let extWindow: uiExtensionHost.UIExtensionHostWindowProxy = this.sheetSession?.getUIExtensionHostWindowProxy(); + if (extWindow === undefined) { + console.error(TAG + 'unregisterAvoidChangedListener, get extra window is undefined'); + return; + } + extWindow.off('avoidAreaChange', this.avoidAreaChangedCallback); + } catch (err) { + let error = err as BusinessError; + console.error(TAG + 'unregisterAvoidChangedListener, occur error:' + error?.code + ', msg:' + error?.message); + } + } + + private updateSheetHeightLimit(): void { + if (this.rootHeight === 0) { + return; + } + if (this.sheetParam.sheetType === SheetType.BOTTOM) { + this.maxSheetPageHeight = this.rootHeight - this.statusBarHeight - 8; + this.sheetParam.sheetMinHeight = this.rootHeight * BOTTOM_SHEET_MIN_PERCENT; + } else if (this.sheetParam.sheetType === SheetType.CENTER) { + let shortSide = Math.min(this.rootWidth, (this.rootHeight - this.statusBarHeight - this.aiBarHeight)); + this.maxSheetPageHeight = shortSide * CENTER_SHEET_MAX_PERCENT; + this.sheetParam.sheetMinHeight = CENTER_SHEET_MIN_HEIGHT; + } else { + console.warn(TAG + 'updateSheetHeightLimit, sheet type is invalid:' + this.sheetParam.sheetType); + } + } + + @Builder + private routerMap(name: string, param?: Object) { + if (name === NavEntryKey.CA_CERTIFICATE_ENTRY) { + CaCertPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.CREDENTIAL_LIST_ENTRY) { + CredListPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.INSTALL_ENTRY) { + InstallPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.CA_SYSTEM_DETAIL_ENTRY) { + CaSystemDetailPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.CA_USER_DETAIL_ENTRY) { + CaUserDetailPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.CRED_SYSTEM_DETAIL_ENTRY) { + CredSystemDetailPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.CRED_USER_DETAIL_ENTRY) { + CredUserDetailPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.AUTHORIZED_APP_ENTRY) { + AuthorizedAppManagementPage({ + sheetParam: this.sheetParam + }) + } else if (name === NavEntryKey.CRED_PWD_INPUT_ENTRY) { + CredPwdInputPage({ + sheetParam: this.sheetParam + }) + } + } + + build() { + Column() { + Column() { + }.bindSheet(true, this.buildContent(), { + height: SheetSize.FIT_CONTENT, + preferType: ['2in1', 'tablet'].includes(deviceInfo.deviceType) ? SheetType.CENTER : null, + showClose: true, + onTypeDidChange: (type) => { + this.sheetParam.sheetType = type; + console.log(TAG + 'sheet type:' + this.sheetParam.sheetType); + if (this.rootHeight === 0) { + return; + } + this.updateSheetHeightLimit(); + }, + onDisappear: () => { + console.debug(TAG + 'Sheet page disappear, session is undefined?' + (this.sheetSession === undefined)); + this.sheetSession?.sendData({'action': 'exit'}) + } + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_100_100) + .onAreaChange((oldArea, newArea) => { + this.rootWidth = newArea.width as number; + this.rootHeight = newArea.height as number; + this.updateSheetHeightLimit(); + }) + } + + @Builder + private buildContent() { + Navigation(this.stack) { + if (this.pageType === PAGE_CA_CERTIFICATE) { + this.buildCaCertPage() + } else if (this.pageType === PAGE_CREDENTIAL) { + this.buildCredListPage() + } else if (this.pageType === PAGE_INSTALL_CERTIFICATE) { + this.buildInstallPage() + } else { + this.buildHomePage() + } + } + .hideTitleBar(true) + .titleMode(NavigationTitleMode.Mini) + .mode(NavigationMode.Stack) + .navDestination(this.routerMap) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .constraintSize({ + maxHeight: this.maxSheetPageHeight + }) + .backgroundColor($r('sys.color.background_secondary')) + } + + @Builder + private buildHomePage() { + Column() { + CertificateComponent({ + isStartBySheet: true, + sheetParam: this.sheetParam, + selected: (path) => { + if (path === undefined || path === null || path.length === 0) { + console.warn(TAG + 'buildHomePage, empty path'); + return; + } + this.stack.pushPath(new NavPathInfo(path, '')); + } + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + } + + @Builder + private buildCaCertPage() { + Column() { + TrustedEvidence({ + isStartBySheetFirst: true, + isStartBySheet: true, + sheetParam: this.sheetParam, + selected: (path, param) => { + if (path === undefined || path === null || path.length === 0) { + console.warn(TAG + 'buildCaCertPage, empty path'); + return; + } + if (path === NavEntryKey.POP) { + this.stack?.pop(); + } else { + this.stack?.pushPath(new NavPathInfo(path, param)); + } + } + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + } + + @Builder + private buildCredListPage() { + Column() { + evidenceList({ + isStartBySheetFirst: true, + isStartBySheet: true, + sheetParam: this.sheetParam, + selected: (path, param) => { + if (path === undefined || path === null || path.length === 0) { + console.warn(TAG + 'buildCredListPage, empty path'); + return; + } + if (path === NavEntryKey.POP) { + this.stack?.pop(); + } else { + this.stack?.pushPath(new NavPathInfo(path, param)); + } + } + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + } + + @Builder + private buildInstallPage() { + Column() { + CertInstallFromStorage({ + isStartBySheetFirst: true, + isStartBySheet: true, + sheetParam: this.sheetParam, + selected: (path, param) => { + if (path === undefined || path === null || path.length === 0) { + console.warn(TAG + 'buildInstallPage, empty path'); + return; + } + if (path === NavEntryKey.POP) { + this.stack?.pop(); + } else { + this.stack?.pushPath(new NavPathInfo(path, param)); + } + } + }) + } + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/picker/CredListPage.ets b/entry/src/main/ets/pages/picker/CredListPage.ets new file mode 100755 index 0000000..3fe7598 --- /dev/null +++ b/entry/src/main/ets/pages/picker/CredListPage.ets @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NavEntryKey } from '../../common/NavEntryKey'; +import { WidthPercent } from '../../common/util/ConfigData'; +import { evidenceList } from '../cerEvidenceFa'; +import { SheetParam } from '../../common/util/SheetParam'; + +@Component +export struct CredListPage { + private stack?: NavPathStack; + + @Prop sheetParam: SheetParam; + + build() { + NavDestination() { + Column() { + evidenceList({ + isStartBySheet: true, + sheetParam: this.sheetParam, + selected: (path, param) => { + if (path === NavEntryKey.POP) { + this.stack?.pop(); + } else { + this.stack?.pushPath(new NavPathInfo(path, param)); + } + } + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(this.sheetParam?.lastSheetPage === NavEntryKey.CREDENTIAL_LIST_ENTRY ? + WidthPercent.WH_AUTO : this.sheetParam?.sheetMinHeight) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/picker/InstallPage.ets b/entry/src/main/ets/pages/picker/InstallPage.ets new file mode 100755 index 0000000..382da77 --- /dev/null +++ b/entry/src/main/ets/pages/picker/InstallPage.ets @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NavEntryKey } from '../../common/NavEntryKey'; +import { WidthPercent } from '../../common/util/ConfigData'; +import { CertInstallFromStorage } from '../certInstallFromStorage'; +import { SheetParam } from '../../common/util/SheetParam'; + +@Component +export struct InstallPage { + private stack?: NavPathStack; + + @Prop sheetParam: SheetParam; + + build() { + NavDestination() { + Column() { + CertInstallFromStorage({ + isStartBySheet: true, + sheetParam: this.sheetParam, + selected: (path, param) => { + if (path === NavEntryKey.POP) { + this.stack?.pop(); + } else { + this.stack?.pushPath(new NavPathInfo(path, param)); + } + } + }) + } + } + .hideTitleBar(true) + .width(WidthPercent.WH_100_100) + .height(WidthPercent.WH_AUTO) + .backgroundColor($r('sys.color.background_secondary')) + .onReady((ctx: NavDestinationContext) => { + this.stack = ctx.pathStack; + }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/requestAuth.ets b/entry/src/main/ets/pages/requestAuth.ets new file mode 100755 index 0000000..681cb45 --- /dev/null +++ b/entry/src/main/ets/pages/requestAuth.ets @@ -0,0 +1,249 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import CmAppCredAuthPresenter from '../presenter/CmAppCredAuthPresenter'; +import router from '@ohos.router'; +import { WidthPercent, ConfigValue } from '../common/util/ConfigData'; +import { CredentialAbstractVo } from '../model/CertManagerVo/CredentialAbstractVo'; +import { RouterAppUidVo } from '../model/CertManagerVo/RouterInfoVo'; +import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; + +@Entry +@Component +struct RadioAuth { + @State mAppCredAuthPresenter: CmAppCredAuthPresenter = CmAppCredAuthPresenter.getInstance(); + selectUri: string = ''; + private authScroller: Scroller = new Scroller(); + + dialogController: CustomDialogController = new CustomDialogController({ + cancel: () => { + this.mAppCredAuthPresenter.cancelProcess(); + }, + builder: CustomContentDialog({ + contentBuilder: () => { + this.dialogControllerContent(); + }, + contentAreaPadding: {right: $r('app.float.wh_value_0')}, + buttons: [ + { + value: $r('app.string.requestAuthCancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.mAppCredAuthPresenter.cancelProcess(); + this.dialogController?.close(); + } + }, + { + value: $r('app.string.requestAuthFinish'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + console.debug('requestAuthorize uri: ' + this.selectUri); + let appUidInfo: RouterAppUidVo = router.getParams() as RouterAppUidVo; + if (appUidInfo !== undefined && appUidInfo.appUid != undefined) { + console.debug('requestAuthorize appUid: ' + appUidInfo.appUid); + this.mAppCredAuthPresenter.requestAuthorize(this.selectUri, appUidInfo.appUid); + this.dialogController?.close(); + } else { + console.info('requestAuthorize fail'); + } + } + } + ] + }), + }) + + @Builder + dialogControllerContent(): void { + Column() { + Text($r('app.string.pickCredToAuth')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .height($r('app.float.wh_value_56')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.requestAuthMsg', this.mAppCredAuthPresenter.appName)) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + if (this.mAppCredAuthPresenter.credList.length > ConfigValue.REQUEST_AUTH_MAX_LENGTH) { + Stack({ alignContent: Alignment.End }) { + Scroll(this.authScroller) { + List() { + ForEach(this.mAppCredAuthPresenter.credList, (item: CredentialAbstractVo, index: number) => { + ListItem() { + Column() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(item.alias) + .fontSize($r('app.float.font_18')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .textAlign(TextAlign.Start); + } + .alignItems(HorizontalAlign.Start) + + Column() { + Radio({ value: item.alias, group: 'appCredGroup' }) + .checked(false) + .onChange((value: boolean) => { + console.info(item.alias + ' status is ' + value); + if (value) { + this.selectUri = item.keyUri; + } + }) + } + .alignItems(HorizontalAlign.End) + } + .width(WidthPercent.WH_100_100) + + Divider() + .color($r('sys.color.ohos_id_color_list_separator')) + .margin({ + top: $r('app.float.distance_12') + }) + .visibility(index === (this.mAppCredAuthPresenter.credList.length - 1) + ? Visibility.None : Visibility.Visible) + } + } + .height($r('app.float.wh_value_64')) + }, (item: CredentialAbstractVo) => JSON.stringify(item)) + } + .scrollBar(BarState.Off) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + .visibility(this.mAppCredAuthPresenter.credList.length > 0 ? Visibility.Visible : Visibility.None) + } + .scrollBar(BarState.Off) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + + ScrollBar({ scroller: this.authScroller, direction: ScrollBarDirection.Vertical, + state: BarState.Auto }) { + Text() + .width($r('app.float.wh_value_3')) + .height($r('app.float.wh_value_50')) + .borderRadius($r('app.float.wh_value_10')) + .backgroundColor($r('sys.color.ohos_id_color_foreground')) + .opacity($r('app.float.text_opacity_0_4')) + } + .width($r('app.float.wh_value_3')) + .margin({ + right: $r('app.float.wh_value_3') + }) + } + .height(WidthPercent.WH_50_100) + } else { + List() { + ForEach(this.mAppCredAuthPresenter.credList, (item: CredentialAbstractVo, index: number) => { + ListItem() { + Column() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(item.alias) + .fontSize($r('app.float.font_18')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .textAlign(TextAlign.Start); + } + .alignItems(HorizontalAlign.Start) + + Column() { + Radio({ value: item.alias, group: 'appCredGroup' }) + .checked(false) + .onChange((value: boolean) => { + console.info(item.alias + ' status is ' + value); + if (value) { + this.selectUri = item.keyUri; + } + }) + } + .alignItems(HorizontalAlign.End) + } + .width(WidthPercent.WH_100_100) + + Divider() + .color($r('sys.color.ohos_id_color_list_separator')) + .margin({ + top: $r('app.float.distance_12') + }) + .visibility(index === (this.mAppCredAuthPresenter.credList.length - 1) + ? Visibility.None : Visibility.Visible) + } + } + .height($r('app.float.wh_value_64')) + }, (item: CredentialAbstractVo) => JSON.stringify(item)) + ListItem() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text() + } + .alignItems(HorizontalAlign.Start) + + Column() { + Radio({ value: 'null', group: 'appCredGroup' }) + .checked(false) + .onChange((value: boolean) => { + if (value) { + this.selectUri = 'null'; + } + }) + } + } + .visibility(Visibility.None) + } + } + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .scrollBar(BarState.Off) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + .visibility(this.mAppCredAuthPresenter.credList.length > 0 ? Visibility.Visible : Visibility.None) + } + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + aboutToAppear() { + console.debug('enter requestAuth page'); + let appUidInfo: RouterAppUidVo = router.getParams() as RouterAppUidVo; + if (appUidInfo !== undefined && appUidInfo.appUid != undefined) { + this.mAppCredAuthPresenter.updateAppNameFromUid(appUidInfo.appUid as string); + this.mAppCredAuthPresenter.updateAppCredList(); + this.dialogController.open(); + } else { + console.error('requestAuth appear failed'); + router.pushUrl({ url: 'pages/certManagerFa' }); + } + } + + build() { + + } +} diff --git a/entry/src/main/ets/pages/trustedCa.ets b/entry/src/main/ets/pages/trustedCa.ets new file mode 100755 index 0000000..de474d8 --- /dev/null +++ b/entry/src/main/ets/pages/trustedCa.ets @@ -0,0 +1,841 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { WidthPercent } from '../common/util/ConfigData'; +import HeadComponent from '../common/component/headComponent'; +import CmShowSysCaPresenter from '../presenter/CmShowSysCaPresenter'; +import CMShowUserCaPresenter from '../presenter/CmShowUserCaPresenter'; +import CmFaPresenter from '../presenter/CmFaPresenter'; +import { GlobalContext } from '../common/GlobalContext'; +import { CertAbstractVo } from '../model/CertManagerVo/CertAbstractVo'; +import ComponentConfig from '../common/component/ComponentConfig'; +import router from '@ohos.router'; +import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; +import { NavEntryKey } from '../common/NavEntryKey'; +import { CaUserDetailParam } from './detail/CaUserDetailPage'; +import { SheetParam } from '../common/util/SheetParam'; +import { + DialogDateComponent, + DialogFingerPrintComponent, + DialogIssuerComponent, + DialogSubjectComponent +} from './detail/CaSystemDetailPage'; + +const COPIES_NUM: number = 12; + +@Component +struct ComponentSystem { + private certAlias: string = ''; + private subjectName: string = ''; + private uri: string = ''; + @Link setStatus: CmShowSysCaPresenter; + private sysCaScroller: Scroller = new Scroller(); + + onItemClicked?: () => void; + + @State isHoverBackgroundColor: ResourceColor = ''; + @Styles pressedStyles(): void { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + } + @Styles normalStyles(): void { + .backgroundColor(this.isHoverBackgroundColor) + } + + systemDetailsDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + builder: CustomContentDialog({ + contentBuilder: () => { + this.systemDetailContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [{ + value: $r('app.string.CustomDialogExample_Button_text'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.systemDetailsDialog?.close(); + } + }] + }) + }) + + @Builder + systemDetailContent(): void { + Column() { + Text($r('app.string.CustomDialogExample_firText')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .height($r('app.float.wh_value_56')) + .margin({ + bottom: $r('app.float.wh_value_8'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text(this.setStatus.certInfo.certAlias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + bottom: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Scroll(this.sysCaScroller) { + List() { + ListItem() { + Text($r('app.string.CustomDialogExample_firListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + DialogSubjectComponent({ map: this.setStatus.certInfo.subjectNameMap }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + Text($r('app.string.CustomDialogExample_secListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + .margin({ + top: $r('app.float.wh_value_24') + }) + + ListItem() { + DialogIssuerComponent({ map: this.setStatus.certInfo.issuerNameMap }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + Text($r('app.string.CustomDialogExample_thdListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + .margin({ + top: $r('app.float.wh_value_24') + }) + + ListItem() { + DialogDateComponent({ map: this.setStatus.certInfo.dateMap }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + Text($r('app.string.CustomDialogExample_fouListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + .margin({ + top: $r('app.float.wh_value_24') + }) + + ListItem() { + DialogFingerPrintComponent({ fingerprintSha256: this.setStatus.certInfo.fingerprintSha256 }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + } + .margin({ right: $r('app.float.wh_value_24') }) + .scrollBar(BarState.Off) + } + .scrollable(ScrollDirection.Vertical) + .scrollBarWidth('3vp') + .height($r('app.float.CustomDialogExample_list_height_value')) + } + .padding({ + left: $r('app.float.wh_value_24'), + }) + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + build() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(this.certAlias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .textAlign(TextAlign.Start) + .margin({ top: $r('app.float.wh_value_10') }); + Text(this.subjectName) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_secondary')) + .fontWeight(FontWeight.Regular) + .textAlign(TextAlign.Start) + .margin({ top: $r('app.float.wh_value_2'), bottom: $r('app.float.wh_value_11') }); + }.alignItems(HorizontalAlign.Start); + } + .onHover((isHover?: boolean) => { + this.isHoverBackgroundColor = isHover ? $r('sys.color.ohos_id_color_hover') : '' + }) + .backgroundColor(this.isHoverBackgroundColor) + .stateStyles({ + pressed: this.pressedStyles, + normal: this.normalStyles + }) + .padding({ + left: $r('app.float.wh_value_8'), + right: $r('app.float.wh_value_8') + }) + .borderRadius($r('app.float.wh_value_20')) + .onClick(() => { + this.setStatus.getSystemTrustedCertificate(this.uri, () => { + if (this.onItemClicked !== undefined) { + this.onItemClicked(); + } else { + this.systemDetailsDialog.open(); + } + }); + }) + } +} + +@Component +struct ComponentUser { + private certAlias: string = ''; + private subjectName: string = ''; + private indexNum: number = -1; + private uri: string = ''; + @State statusText: Resource = $r('app.string.CustomDialogExampleUser_Status_false'); + @Link setStatus: CMShowUserCaPresenter; + @State mUserCaPresenterDetail: CMShowUserCaPresenter = CMShowUserCaPresenter.getInstance(); + private userCaScroller: Scroller = new Scroller(); + + onItemClicked?: () => void; + + @State isHoverBackgroundColor: ResourceColor = ''; + @Styles pressedStyles(): void { + .backgroundColor($r('sys.color.ohos_id_color_click_effect')) + } + @Styles normalStyles(): void { + .backgroundColor(this.isHoverBackgroundColor) + } + + rootCertificateDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + cancel: () => { + this.setStatus.updateUserTrustedCertificateList(); + }, + builder: CustomContentDialog({ + contentBuilder: () => { + this.rootCertificateContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.root_certificate_cancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.setStatus.updateUserTrustedCertificateList(); + } + }, + { + value: $r('app.string.root_certificate_continue'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.setStatus.setUserCertificateStatus(this.mUserCaPresenterDetail.certInfo.uri, true).then(result => { + if (result) { + this.setStatus.updateUserTrustedCertificateList(); + } + }); + } + } + ] + }) + }) + + userDetailsDialog: CustomDialogController = new CustomDialogController({ + alignment: DialogAlignment.Center, + cancel: () => { + this.setStatus.updateUserTrustedCertificateList(); + }, + builder: CustomContentDialog({ + contentBuilder: () => { + this.userDetailContent(this.setStatus.certList[this.indexNum].status); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.CustomDialogExampleUser_Flex_firButton_text'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.setStatus.updateUserTrustedCertificateList(); + this.userDetailsDialog?.close(); + } + }, + { + value: $r('app.string.CustomDialogExampleUser_Flex_secButton_text'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.onShowDeleteWarnDialog(); + }, + role: ButtonRole.ERROR + } + ] + }) + }) + + deleteCertWarnDialog: CustomDialogController = new CustomDialogController({ + cancel: () => { + this.setStatus.updateUserTrustedCertificateList(); + }, + builder: CustomContentDialog({ + contentBuilder: () => { + this.deleteWarnContent(); + }, + contentAreaPadding: { right: $r('app.float.wh_value_0') }, + buttons: [ + { + value: $r('app.string.deleteAllCredCancel'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.deleteCertWarnDialog?.close(); + this.setStatus.updateUserTrustedCertificateList(); + } + }, + { + value: $r('app.string.deleteAllCredDelete'), + buttonStyle: ButtonStyleMode.TEXTUAL, + action: () => { + this.setStatus.deleteUserCertificate(this.mUserCaPresenterDetail.certInfo.uri, () => { + this.setStatus.updateUserTrustedCertificateList(); + this.deleteCertWarnDialog?.close(); + }) + }, + role: ButtonRole.ERROR + } + ] + }) + }) + + @Builder + rootCertificateContent(): void { + Column() { + Text($r('app.string.root_certificate')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.root_certificate_message')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Regular) + .fontColor($r('sys.color.ohos_id_color_primary')) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + @Builder + deleteWarnContent(): void { + Column() { + Text($r('app.string.CustomDialogExampleUser_warning_Button_title_text')) + .height($r('app.float.wh_value_56')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Text($r('app.string.CustomDialogExampleUser_warning_Button_message_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontWeight(FontWeight.Regular) + .fontColor($r('sys.color.ohos_id_color_primary')) + .margin({ + left: $r('app.float.wh_value_24'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + } + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + @Builder + userDetailContent($$: boolean): void { + Column() { + Text($r('app.string.CustomDialogExample_firText')) + .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .height($r('app.float.wh_value_56')) + .margin({ + bottom: $r('app.float.wh_value_8'), + right: $r('app.float.wh_value_24') + }) + .alignSelf(ItemAlign.Start) + + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(this.mUserCaPresenterDetail.certInfo.certAlias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + }.alignItems(HorizontalAlign.Start); + + Toggle({ type: ToggleType.Switch, isOn: $$ }) + .margin({ + left: $r('app.float.wh_value_16'), + right: $r('app.float.wh_value_24') + }) + .flexShrink(0) + .width($r('app.float.CustomDialogExampleUser_Flex_Toggle_width_value')) + .height($r('app.float.CustomDialogExampleUser_Flex_Toggle_height_value')) + .selectedColor($r('app.color.CustomDialogExampleUser_Flex_Toggle_selectedColor_007DFF')) + .onChange((isOn: boolean) => { + if (isOn) { + this.onShowRootCertificateDialog(); + } else { + this.setStatus.setUserCertificateStatus(this.mUserCaPresenterDetail.certInfo.uri, false); + } + }); + } + + Scroll(this.userCaScroller) { + List() { + ListItem() { + Text($r('app.string.CustomDialogExample_firListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + DialogSubjectComponent({ map: this.mUserCaPresenterDetail.certInfo.subjectNameMap }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + Text($r('app.string.CustomDialogExample_secListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + DialogIssuerComponent({ map: this.mUserCaPresenterDetail.certInfo.issuerNameMap }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + Text($r('app.string.CustomDialogExample_thdListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + DialogDateComponent({ map: this.mUserCaPresenterDetail.certInfo.dateMap }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + Text($r('app.string.CustomDialogExample_fouListItem_text')) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.wh_value_24') + }) + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + + ListItem() { + DialogFingerPrintComponent({ + fingerprintSha256: this.mUserCaPresenterDetail.certInfo.fingerprintSha256 + }); + }.width(ComponentConfig.WH_100_100).align(Alignment.Start) + } + .margin({ + right: $r('app.float.wh_value_24') + }) + .scrollBar(BarState.Off) + } + .height($r('app.float.CustomDialogExample_list_height_value')) + .scrollable(ScrollDirection.Vertical) + .scrollBarWidth('3vp') + } + .padding({ + left: $r('app.float.wh_value_24') + }) + .width(WidthPercent.WH_100_100) + .borderRadius($r('app.float.user_list_divider_borderRadius_value')) + .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) + } + + onShowRootCertificateDialog() { + this.userDetailsDialog?.close(); + this.rootCertificateDialog.open(); + } + + onShowDeleteWarnDialog() { + this.deleteCertWarnDialog.open(); + this.userDetailsDialog?.close(); + } + + aboutToAppear() { + console.info('in ComponentUser aboutToAppear'); + if (this.setStatus.certList[this.indexNum].status == true) { + this.statusText = $r('app.string.CustomDialogExampleUser_Status_true'); + } else { + this.statusText = $r('app.string.CustomDialogExampleUser_Status_false'); + } + } + + build() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Column() { + Text(this.certAlias) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Medium) + .textAlign(TextAlign.Start) + .margin({ top: $r('app.float.wh_value_10') }) + Text(this.subjectName) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontWeight(FontWeight.Regular) + .textAlign(TextAlign.Start) + .margin({ top: $r('app.float.wh_value_2'), bottom: $r('app.float.wh_value_11') }); + } + .alignItems(HorizontalAlign.Start); + Row() { + Text(this.statusText) + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_secondary')) + .textAlign(TextAlign.End) + .margin({ + right: $r('app.float.componentUser_thdText_margin_value') + }) + Image($r('app.media.ic_settings_arrow')) + .width($r('app.float.componentUser_image_width_value')) + .height($r('app.float.componentUser_image_height_value')) + .fillColor($r('sys.color.ohos_id_color_primary')) + .opacity($r('app.float.componentUser_image_opacity_value')); + } + .flexShrink(0) + .margin($r('app.float.wh_value_12')) + } + .onHover((isHover?: boolean) => { + this.isHoverBackgroundColor = isHover ? $r('sys.color.ohos_id_color_hover') : '' + }) + .backgroundColor(this.isHoverBackgroundColor) + .stateStyles({ + pressed: this.pressedStyles, + normal: this.normalStyles + }) + .padding({ + left: $r('app.float.wh_value_8'), + right: $r('app.float.wh_value_8') + }) + .borderRadius($r('app.float.wh_value_20')) + .onClick(() => { + this.mUserCaPresenterDetail.getUserTrustedCertificate(this.uri, () => { + if (this.onItemClicked !== undefined) { + this.onItemClicked(); + } else { + this.userDetailsDialog.open(); + } + }); + }); + } +} + +@Entry +@Component +export struct TrustedEvidence { + @State mShowSysCaPresenter: CmShowSysCaPresenter = CmShowSysCaPresenter.getInstance(); + @State mShowUserCaPresenter: CMShowUserCaPresenter = CMShowUserCaPresenter.getInstance(); + @State mFaPresenter: CmFaPresenter = CmFaPresenter.getInstance(); + @State currentIndex: number = 0; + @State fontColor: Resource = $r('app.color.TrustedEvidence_TabBuilder_fontColor_182431'); + @State selectedFontColor: Resource = $r('app.color.font_color_007DFF'); + private controller: TabsController = new TabsController(); + private sysCaScroller: Scroller = new Scroller(); + private userCaScroller: Scroller = new Scroller(); + @State animationDurationNum: number = 400; + + isStartBySheetFirst: boolean = false; + isStartBySheet: boolean = false; + selected?: (path: string, param?: Object) => void; + @Prop sheetParam: SheetParam; + @State headRectHeight: number = 64; + @State headRectHeightReal: number = 0; + @State private sysScrollerHeight: number = 0; + @State private userScrollerHeight: number = 0; + + @Builder + TabBuilder(index: number) { + Column() { + Text(index == 0 ? $r('app.string.system') : $r('app.string.user')) + .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) + .fontSize($r('app.float.TrustedEvidence_TabBuilder_Text_fontSize_value')) + .fontWeight(this.currentIndex === index ? FontWeight.Medium : FontWeight.Regular) + .alignSelf(ItemAlign.Center) + .margin({ + top: $r('app.float.TrustedEvidence_TabBuilder_Text_padding_top_value') + }) + if (this.currentIndex === index) { + Divider() + .width($r('app.float.TrustedEvidence_TabBuilder_Divider_width_value')) + .margin({ top: $r('app.float.TrustedEvidence_TabBuilder_Divider_padding_top_value') }) + .color(this.selectedFontColor) + .alignSelf(ItemAlign.Center) + } + } + .width(WidthPercent.WH_100_100) + } + + aboutToAppear() { + this.mShowSysCaPresenter.updateSystemTrustedCertificateList(); + this.mShowUserCaPresenter.updateUserTrustedCertificateList(); + } + + onPageShow() { + let uri = GlobalContext.getContext().getAbilityWant().uri; + GlobalContext.getContext().clearAbilityWantUri(); + + if (uri === 'certInstall') { + router.pushUrl({ + url: 'pages/certInstallFromStorage' + }) + } else if (uri === 'requestAuthorize') { + this.mFaPresenter.startRequestAuth(GlobalContext.getContext().getAbilityWant().parameters?.appUid as string); + } else { + console.error('The want type is not supported'); + } + } + + build() { + Column() { + GridRow({ columns: COPIES_NUM, gutter: vp2px(1) === 2 ? $r('app.float.wh_value_12') : $r('app.float.wh_value_0') }) { + GridCol({ span: COPIES_NUM }) { + Row() { + Stack({ alignContent: Alignment.Top }) { + Column() { + HeadComponent({ headName: $r('app.string.CA_cert'), isStartBySheet: this.isStartBySheet, + icBackIsVisibility: !this.isStartBySheetFirst, + onBackClicked: () => { + this.selected?.(NavEntryKey.POP); + }}) + .margin({ + left: $r('app.float.wh_value_12'), + top: this.isStartBySheet ? 8 : 0 + }) + }.onAreaChange((oldArea, newArea) => { + this.headRectHeight = newArea.height as number; + this.headRectHeightReal = newArea.height as number; + }).zIndex(1) + + Column() { + Tabs({ barPosition: BarPosition.Start, index: 0, controller: this.controller }) { + TabContent() { + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.sysCaScroller) { + List() { + ForEach(this.mShowSysCaPresenter.certList, (item: CertAbstractVo) => { + ListItem() { + ComponentSystem({ + certAlias: item.certAlias, + subjectName: item.subjectNameCN, + uri: item.uri, + setStatus: $mShowSysCaPresenter, + onItemClicked: this.isStartBySheet ? () => { + this.selected?.(NavEntryKey.CA_SYSTEM_DETAIL_ENTRY); + } : undefined + }) + } + }, (item: CertAbstractVo) => JSON.stringify(item)) + } + .borderRadius($r('sys.float.padding_level10')) + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + .scrollBar(BarState.Off) + .padding({ + right: $r('app.float.wh_value_4'), + left: $r('app.float.wh_value_4'), + top: $r('app.float.wh_value_4'), + bottom: $r('app.float.wh_value_4') + }) + .divider({ + strokeWidth: $r('app.float.sys_list_divider_strokeWidth_value'), + color: $r('sys.color.ohos_id_color_list_separator'), + startMargin: $r('app.float.wh_value_8'), + endMargin: $r('app.float.wh_value_8') + }) + .visibility(this.mShowSysCaPresenter.certList.length > 0 + ? Visibility.Visible : Visibility.None) + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .align(Alignment.Top) + .edgeEffect(EdgeEffect.Spring) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Off) + .padding({ + left: $r('app.float.wh_value_16'), + right: $r('app.float.wh_value_16'), + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }).onAreaChange((oldArea, newArea) => { + this.sysScrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.sysCaScroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + }.height(this.sysScrollerHeight) + } + } + .tabBar(this.TabBuilder(0)) + + TabContent() { + Stack({ alignContent: Alignment.TopEnd }) { + Scroll(this.userCaScroller) { + List() { + ForEach(this.mShowUserCaPresenter.certList, (item: CertAbstractVo, index) => { + ListItem() { + ComponentUser({ + certAlias: item.certAlias, + subjectName: item.subjectNameCN, + uri: item.uri, + setStatus: $mShowUserCaPresenter, + indexNum: index, + onItemClicked: this.isStartBySheet ? () => { + this.selected?.(NavEntryKey.CA_USER_DETAIL_ENTRY, + new CaUserDetailParam(this.mShowUserCaPresenter)); + } : undefined + }) + } + }, (item: CertAbstractVo) => JSON.stringify(item)) + } + .borderRadius($r('sys.float.padding_level10')) + .backgroundColor($r('sys.color.ohos_id_color_card_bg')) + .divider({ + strokeWidth: $r('app.float.sys_list_divider_strokeWidth_value'), + color: $r('sys.color.ohos_id_color_list_separator'), + startMargin: $r('app.float.wh_value_8'), + endMargin: $r('app.float.wh_value_8') + }) + .scrollBar(BarState.Off) + .padding({ + left: $r('app.float.wh_value_4'), + right: $r('app.float.wh_value_4'), + top: $r('app.float.wh_value_4'), + bottom: $r('app.float.wh_value_4') + }) + .visibility(this.mShowUserCaPresenter.certList.length > 0 + ? Visibility.Visible : Visibility.None) + } + .position({ y: $r('app.float.wh_value_0') }) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .align(Alignment.Top) + .edgeEffect(EdgeEffect.Spring) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Off) + .padding({ + left: $r('app.float.wh_value_16'), + right: $r('app.float.wh_value_16'), + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + .constraintSize({ + minHeight: this.getScrollMinHeight() + }).onAreaChange((oldArea, newArea) => { + this.userScrollerHeight = newArea.height as number; + }) + + Column() { + ScrollBar({ + scroller: this.userCaScroller, + direction: ScrollBarDirection.Vertical, + state: BarState.Auto + }).margin({ + bottom: this.isStartBySheet ? $r('app.float.wh_value_80') : $r('app.float.wh_value_24') + }) + }.height(this.userScrollerHeight) + } + } + .tabBar(this.TabBuilder(1)) + } + .vertical(false) + .scrollable(true) + .barMode(BarMode.Fixed) + .barWidth($r('app.float.tabs_barWidth_value')) + .barHeight($r('app.float.tabs_barHeight_value')) + .animationDuration(this.animationDurationNum) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .onChange((index: number) => { + this.currentIndex = index; + }) + } + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + .width(WidthPercent.WH_100_100) + .padding({ + top: this.headRectHeight + }) + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100) + } + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100); + } + } + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .width(WidthPercent.WH_100_100) + .height(this.isStartBySheet ? WidthPercent.WH_AUTO : WidthPercent.WH_100_100); + } + } + + getScrollMinHeight() { + if (this.sheetParam === undefined || this.headRectHeightReal === 0 || + this.sheetParam.sheetMinHeight < this.headRectHeightReal) { + return 0; + } + return this.sheetParam.sheetMinHeight - this.headRectHeightReal - 56; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/presenter/CmAppCredAuthPresenter.ets b/entry/src/main/ets/presenter/CmAppCredAuthPresenter.ets new file mode 100755 index 0000000..28e4e01 --- /dev/null +++ b/entry/src/main/ets/presenter/CmAppCredAuthPresenter.ets @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import certManagerModel from '../model/CertMangerModel'; +import BundleModel from '../model/BundleModel'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import { CredentialAbstractVo } from '../model/CertManagerVo/CredentialAbstractVo'; +import router from '@ohos.router'; +import { GlobalContext } from '../common/GlobalContext'; +import { AppInfoVo } from '../model/CertManagerVo/AppInfoVo'; +import { BusinessError } from '@ohos.base'; +import Common from '@ohos.app.ability.common' + +const TAG = 'CMAppCredAuthPresenter Presenter: '; +const SUCCESS = 0; +const FAIL = -1; + +export default class CmAppCredAuthPresenter { + private static sInstance: CmAppCredAuthPresenter; + public credList: CredentialAbstractVo[] = []; + public appName: string = ''; + + public static getInstance(): CmAppCredAuthPresenter { + if (CmAppCredAuthPresenter.sInstance == null) { + CmAppCredAuthPresenter.sInstance = new CmAppCredAuthPresenter(); + } + return CmAppCredAuthPresenter.sInstance; + } + + onAboutToAppear(): void { + + } + + aboutToDisappear(): void { + this.credList = []; + } + + updateAppNameFromUid(uid: string): void { + try { + BundleModel.getAppInfoList(Number(uid), (errCode: CMModelErrorCode, appInfo: AppInfoVo) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.appName = appInfo.appName; + console.info('getAppNameFromUid success, appName = ' + this.appName); + } else { + console.error('getAppNameFromUid fail, uid = ' + uid); + } + }); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error('updateAppNameFromUid failed with err, message: ' + e.message + ', code: ' + e.code); + } + } + + updateAppCredList(): void { + certManagerModel.getCertOrCredList(CMModelOptType.CM_MODEL_OPT_APP_CRED, + (errCode: CMModelErrorCode, credList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.credList = credList; + console.info('updateAppCredList success.'); + } else { + console.error('updateAppCredList failed'); + } + }); + } + + requestAuthorize(uri: string, appUid: string): void { + let want = GlobalContext.getContext().getAbilityWant(); + certManagerModel.setAppAuth(CMModelOptType.CM_MODEL_OPT_APP_CRED, uri, appUid, + true, (errCode: CMModelErrorCode, result: string) => { + router.clear(); + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + console.info('requestAuthorize success result: ' + result); + if (want.parameters != undefined) { + want.parameters.authUri = result; + let ret1: Common.AbilityResult = { + resultCode: SUCCESS, + want: want + }; + GlobalContext.getContext().getCmContext().terminateSelfWithResult(ret1); + } else { + let ret2: Common.AbilityResult = { + resultCode: FAIL, + want: want + }; + console.error(TAG + 'requestAuthorize failed, undefined'); + GlobalContext.getContext().getCmContext().terminateSelfWithResult(ret2); + } + } else { + console.error('requestAuthorize fail'); + let ret3: Common.AbilityResult = { + resultCode: FAIL, + want: want + }; + GlobalContext.getContext().getCmContext().terminateSelfWithResult(ret3); + } + }); + } + + cancelProcess(): void { + console.info('cancelProcess start'); + router.clear(); + let ret: Common.AbilityResult = { + resultCode: FAIL, + want: GlobalContext.getContext().getAbilityWant() + }; + GlobalContext.getContext().getCmContext().terminateSelfWithResult(ret); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/presenter/CmFaPresenter.ets b/entry/src/main/ets/presenter/CmFaPresenter.ets new file mode 100755 index 0000000..f0e4060 --- /dev/null +++ b/entry/src/main/ets/presenter/CmFaPresenter.ets @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import picker from '@ohos.file.picker'; +import router from '@ohos.router'; +import certManagerModel from '../model/CertMangerModel'; +import FileIoModel from '../model/FileIoModel'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import { BusinessError } from '@ohos.base'; +import { RouterFileVo, RouterAppUidVo } from '../model/CertManagerVo/RouterInfoVo'; +import checkUserAuthModel from '../model/CheckUserAuthModel'; +import CmInstallPresenter from './CmInstallPresenter'; +import FilterParams from '../common/constants/FileFilterParams'; + +const TAG = 'CMFaPresenter: '; +const gridCountNum: number = 4; + +export default class CmFaPresenter { + private static sInstance: CmFaPresenter; + + public static getInstance(): CmFaPresenter { + if (CmFaPresenter.sInstance == null) { + CmFaPresenter.sInstance = new CmFaPresenter(); + } + return CmFaPresenter.sInstance; + } + + onAboutToAppear(): void { + + } + + aboutToDisappear(): void { + } + + unrecognizedFileTips(): void { + AlertDialog.show({ + message: $r('app.string.Install_Error_NOT_FOUND'), + autoCancel: true, + alignment: DialogAlignment.Bottom, + offset: { + dx: $r('app.float.wh_value_0'), dy: $r('app.float.wh_value_0') + }, + gridCount: gridCountNum, + primaryButton: { + value: $r('app.string.OK'), + action: () => { + } + }, + }) + } + + routeToNextInstallCert(fileUri: string): void { + FileIoModel.getMediaFileSuffix(fileUri, (suffix: string | undefined) => { + if (suffix !== undefined) { + console.debug(TAG, 'suffix = ', suffix); + if ((suffix === 'cer') || (suffix === 'pem') || (suffix === 'crt') || (suffix === 'der')) { + CmInstallPresenter.getInstance().installCert(fileUri, '', suffix, true); + } else { + this.unrecognizedFileTips(); + } + } + }) + } + + routeToNextInstallEvidence(fileUri: string): void { + FileIoModel.getMediaFileSuffix(fileUri, (suffix: string | undefined) => { + if (suffix !== undefined) { + console.debug(TAG, 'suffix = ', suffix); + if ((suffix === 'p12') || (suffix === 'pfx')) { + let fileInfo = new RouterFileVo(fileUri, suffix); + router.pushUrl({ + url: 'pages/certPwdInput', + params: fileInfo + }) + } else { + this.unrecognizedFileTips(); + } + } + }) + } + + startInstallCert(context: Context): void { + if (context === undefined || context === null) { + console.error(TAG + 'startInstallCert, context is undefined'); + return; + } + try { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.maxSelectNumber = FilterParams.MAX_SELECT_FILE_NUM; + documentSelectOptions.fileSuffixFilters = FilterParams.CERT_FILE_SUFFIX; + let documentPicker = new picker.DocumentViewPicker(context); + console.info(TAG + 'start documentPicker.select'); + documentPicker.select(documentSelectOptions).then((documentSelectResult) => { + if (documentSelectResult.length >= 1) { + this.routeToNextInstallCert(String(documentSelectResult[0])) + } else { + console.error(TAG + 'documentPicker.select length invalid:' + documentSelectResult.length); + } + }).catch((err: BusinessError) => { + console.error(TAG + 'documentPicker.select failed with err, message: ' + err.message + ', code: ' + err.code); + }); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'DocumentViewPicker failed with err, message: ' + e.message + ', code: ' + e.code); + } + } + + startInstallEvidence(context: Context): void { + if (context === undefined || context === null) { + console.error(TAG + 'startInstallEvidence, context is undefined'); + return; + } + try { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.maxSelectNumber = FilterParams.MAX_SELECT_FILE_NUM; + documentSelectOptions.fileSuffixFilters = FilterParams.CREDENTIAL_FILE_SUFFIX; + let documentPicker = new picker.DocumentViewPicker(context); + console.info(TAG + 'start documentPicker.select'); + documentPicker.select(documentSelectOptions).then((documentSelectResult) => { + if (documentSelectResult.length >= 1) { + this.routeToNextInstallEvidence(String(documentSelectResult[0])) + } else { + console.error(TAG + 'documentPicker.select length invalid:' + documentSelectResult.length); + } + }).catch((err: BusinessError) => { + console.error(TAG + 'documentPicker.select failed with err, message: ' + err.message + ', code: ' + err.code); + }); + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG + 'DocumentViewPicker failed with err, message: ' + e.message + ', code: ' + e.code); + } + } + + startRequestAuth(uri: string): void { + let appUidInfo = new RouterAppUidVo(uri); + router.pushUrl({ + url: 'pages/requestAuth', + params: appUidInfo + }); + } + + uninstallAllCert(): void { + certManagerModel.delAllCertOrCred(CMModelOptType.CM_MODEL_OPT_USER_CA, (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + console.info(TAG + 'uninstallAllCert CM_MODEL_OPT_USER_CA success'); + } else { + console.error(TAG + 'uninstallAllCert CM_MODEL_OPT_USER_CA failed'); + } + }); + + certManagerModel.delAllCertOrCred(CMModelOptType.CM_MODEL_OPT_APP_CRED, (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + console.info(TAG + 'uninstallAllCert CM_MODEL_OPT_APP_CRED success'); + } else { + console.error(TAG + 'uninstallAllCert CM_MODEL_OPT_APP_CRED failed'); + } + }); + } + + startInstall(installType: string, fileUri: string) { + if (installType === 'CACert') { + let titleStr = getContext().resourceManager.getStringSync($r('app.string.Identity_Authentication')); + checkUserAuthModel.auth(titleStr, (authResult: boolean) => { + if (authResult) { + console.info('checkUserAuth success'); + this.routeToNextInstallCert(fileUri); + } + }) + } else if (installType === 'userCred') { + AppStorage.setOrCreate('installSystemCred', false); + AppStorage.setOrCreate('installUserCred', true); + this.routeToNextInstallEvidence(fileUri); + } else if (installType === 'systemCred') { + AppStorage.setOrCreate('installSystemCred', true); + AppStorage.setOrCreate('installUserCred', false); + this.routeToNextInstallEvidence(fileUri); + } else { + console.error(TAG, 'The installType is not supported'); + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/presenter/CmInstallPresenter.ets b/entry/src/main/ets/presenter/CmInstallPresenter.ets new file mode 100755 index 0000000..d45a7a5 --- /dev/null +++ b/entry/src/main/ets/presenter/CmInstallPresenter.ets @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import certManagerModel from '../model/CertMangerModel'; +import FileIoModel from '../model/FileIoModel'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import router from '@ohos.router'; +import { GlobalContext } from '../common/GlobalContext'; +import promptAction from '@ohos.promptAction'; +import { BusinessError } from '@ohos.base'; + +const TAG = 'CMInstallPresenter: '; +const DURATION = 2000; +const gridCountNum: number = 4; +const bottomNum: number = 100; + +export default class CmInstallPresenter { + private static sInstance: CmInstallPresenter; + private optType: CMModelOptType = CMModelOptType.CM_MODEL_OPT_UNKNOWN; + + public static getInstance(): CmInstallPresenter { + if (CmInstallPresenter.sInstance == null) { + CmInstallPresenter.sInstance = new CmInstallPresenter(); + } + return CmInstallPresenter.sInstance; + } + + onAboutToAppear(): void { + + } + + aboutToDisappear(): void { + this.optType = CMModelOptType.CM_MODEL_OPT_UNKNOWN; + AppStorage.setOrCreate('installUserCred',false); + AppStorage.setOrCreate('installSystemCred',false); + } + + updateCertFileType(suffix: string): void { + console.debug(TAG + 'updateCertFileType suffix: ' + suffix); + if ((suffix === 'cer') || (suffix === 'pem') || (suffix === 'crt') || (suffix === 'der')) { + this.optType = CMModelOptType.CM_MODEL_OPT_USER_CA; + } else if (((suffix === 'p12') || (suffix === 'pfx')) && + AppStorage.get('installUserCred') === true) { + this.optType = CMModelOptType.CM_MODEL_OPT_APP_CRED; + } else if (((suffix === 'p12') || (suffix === 'pfx')) && + AppStorage.get('installSystemCred') === true) { + this.optType = CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED; + } else { + console.debug(TAG, 'The file type is not supported. suffix: ' + suffix); + } + } + + getFileDataFromUri(uri: string, callback: Function): void { + FileIoModel.getMediaFileData(uri, (data: Uint8Array) => { + callback(data); + }); + } + + checkCertNameLength(uri: string, alias: string, suffix: string, pwd: string): Promise { + return new Promise((resolve) => { + this.updateCertFileType(suffix); + this.getFileDataFromUri(uri, (data: Uint8Array) => { + certManagerModel.installCertOrCred(this.optType, alias, data, pwd, (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_ALIAS_LENGTH_REACHED_LIMIT) { + resolve(errCode) + } else { + resolve(0) + } + }) + }) + }) + } + + installSuccessTips(): void { + try { + promptAction.showToast({ + message: this.optType === CMModelOptType.CM_MODEL_OPT_USER_CA ? + $r('app.string.Install_Cert_Success') : $r('app.string.Install_Cred_Success'), + duration: DURATION, + bottom: bottomNum + }) + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG, 'show result failed, message: ' + e.message + ', code: ' + e.code) + } + } + + errorFormatTips(): void { + AlertDialog.show({ + message: $r('app.string.Install_ERROR_INCORRECT_FORMAT'), + autoCancel: true, + alignment: DialogAlignment.Bottom, + offset: { + dx: $r('app.float.wh_value_0'), dy: $r('app.float.wh_value_0') + }, + gridCount: gridCountNum, + primaryButton: { + value: $r('app.string.OK'), + action: () => { + } + }, + }) + } + + maxQuantityReachedTips(): void { + AlertDialog.show({ + message: $r('app.string.Install_Error_MAX_QUANTITY_REACHED'), + autoCancel: true, + alignment: DialogAlignment.Bottom, + offset: { + dx: $r('app.float.wh_value_0'), dy: $r('app.float.wh_value_0') + }, + gridCount: gridCountNum, + primaryButton: { + value: $r('app.string.OK'), + action: () => { + router.back({ + url: 'pages/certManagerFa' + }) + } + }, + }) + } + + installFailedTips(): void { + try { + promptAction.showToast({ + message: this.optType === CMModelOptType.CM_MODEL_OPT_USER_CA ? + $r('app.string.Install_Cert_Failed') : $r('app.string.Install_Cred_Failed'), + duration: DURATION, + bottom: bottomNum + }) + } catch (err) { + let e: BusinessError = err as BusinessError; + console.error(TAG, 'show result failed, message: ' + e.message + ', code: ' + e.code) + } + } + + installCert(uri: string, alias: string, suffix: string, isNeedJumpBack: boolean): Promise { + return new Promise((resolve => { + this.updateCertFileType(suffix); + this.getFileDataFromUri(uri, (data: Uint8Array) => { + certManagerModel.installCertOrCred(this.optType, alias, data, + GlobalContext.getContext().getPwdStore().getCertPwd(), (errCode: CMModelErrorCode) => { + GlobalContext.getContext().getPwdStore().clearCertPwd(); + this.handleInstallResult(errCode, isNeedJumpBack); + resolve(errCode); + }); + }); + })); + } + + private handleInstallResult(errCode: CMModelErrorCode, isNeedJumpBack: boolean) { + console.info(TAG + 'installCertOrCred result: ' + JSON.stringify(errCode)); + let isNeedJumpHomePage = true; + switch (errCode) { + case CMModelErrorCode.CM_MODEL_ERROR_SUCCESS: + this.installSuccessTips(); + break; + + case CMModelErrorCode.CM_MODEL_ERROR_INCORRECT_FORMAT: + this.errorFormatTips(); + break; + + case CMModelErrorCode.CM_MODEL_ERROR_MAX_QUANTITY_REACHED: + this.maxQuantityReachedTips(); + break; + + case CMModelErrorCode.CM_MODEL_ERROR_PASSWORD_ERR: + isNeedJumpHomePage = false; + break; + + default: + this.installFailedTips(); + break; + } + if (!isNeedJumpHomePage || !isNeedJumpBack) { + return; + } + router.clear(); + router.replaceUrl({ + url: 'pages/certManagerFa' + }); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/presenter/CmShowAppCredPresenter.ets b/entry/src/main/ets/presenter/CmShowAppCredPresenter.ets new file mode 100755 index 0000000..00da422 --- /dev/null +++ b/entry/src/main/ets/presenter/CmShowAppCredPresenter.ets @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import certManagerModel from '../model/CertMangerModel'; +import bundleModel from '../model/BundleModel'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import { CredentialAbstractVo } from '../model/CertManagerVo/CredentialAbstractVo'; +import { CredentialVo } from '../model/CertManagerVo/CredentialVo'; +import { AppAuthorVo } from '../model/CertManagerVo/AppAuthorVo'; +import { AppInfoVo } from '../model/CertManagerVo/AppInfoVo'; +import { BusinessError } from '@ohos.base'; + +export default class CmShowAppCredPresenter { + private static sInstance: CmShowAppCredPresenter; + public credList: CredentialAbstractVo[] = []; + public appInfoList: AppAuthorVo[] = []; + public credInfo: CredentialVo = new CredentialVo('', '', '', 0, 0, new Uint8Array()); + + public static getInstance(): CmShowAppCredPresenter { + if (CmShowAppCredPresenter.sInstance == null) { + CmShowAppCredPresenter.sInstance = new CmShowAppCredPresenter(); + } + return CmShowAppCredPresenter.sInstance; + } + + aboutToDisappear(): void { + this.credList = []; + this.credInfo = new CredentialVo('', '', '', 0, 0, new Uint8Array()); + this.appInfoList = []; + } + + updateAppCredListCallback(callback: Function): void { + certManagerModel.getCertOrCredList(CMModelOptType.CM_MODEL_OPT_APP_CRED, + (errCode: CMModelErrorCode, credList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + credList.sort((certAbs, certAbsOther): number => { + let certAlias = certAbs.alias; + let certAliasOther = certAbsOther.alias; + if (certAlias <= certAliasOther) { + return -1; + } else { + return 1; + } + }); + this.credList = credList; + callback(); + } else { + console.error('updateAppCredList error :' + JSON.stringify(errCode)); + this.credList = []; + callback(); + } + }); + } + + updateAppCredList(): void { + certManagerModel.getCertOrCredList(CMModelOptType.CM_MODEL_OPT_APP_CRED, + (errCode: CMModelErrorCode, credList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + credList.sort((certAbs, certAbsOther): number => { + let certAlias = certAbs.alias; + let certAliasOther = certAbsOther.alias; + if (certAlias <= certAliasOther) { + return -1; + } else { + return 1; + } + }); + this.credList = credList; + } else { + console.error('updateAppCredList error :' + JSON.stringify(errCode)); + this.credList = []; + } + }); + } + + getAppCred(uri: string, callback: Function): void { + certManagerModel.getCertOrCred(CMModelOptType.CM_MODEL_OPT_APP_CRED, uri, + (errCode: CMModelErrorCode, credInfo: CredentialVo) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.credInfo = credInfo; + } else { + console.error('getAppCred error :' + JSON.stringify(errCode)); + this.credInfo.clearCredentialVo(); + } + callback(); + }); + } + + deleteAppCred(uri: string): void { + certManagerModel.deleteCertOrCred(CMModelOptType.CM_MODEL_OPT_APP_CRED, uri, (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.updateAppCredList(); + } else { + console.error('deleteAppCred error :' + JSON.stringify(errCode)); + } + }); + } + + getAuthorizedAppList(uri: string): void { + this.appInfoList = []; + certManagerModel.getAuthAppList(CMModelOptType.CM_MODEL_OPT_APP_CRED, uri, + (errCode: CMModelErrorCode, appUidList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + for (let i = 0; i < appUidList.length; i++) { + bundleModel.getAppInfoList(Number(appUidList[i]), (errCode: CMModelErrorCode, appInfo: AppInfoVo) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.appInfoList.push( + new AppAuthorVo(String(appInfo.appImage), String(appUidList[i]), String(appInfo.appName), true)); + } + }); + } + } else { + console.error('getAuthorizedAppList error :' + JSON.stringify(errCode)); + this.appInfoList = []; + } + }); + } + + async removeGrantedAppList(uri: string): Promise { + console.info('enter removeGrantedAppList'); + for (let i = 0; i < this.appInfoList.length; i++) { + if (!this.appInfoList[i].status) { + try { + let res = await certManagerModel.setAppAuthPromise(CMModelOptType.CM_MODEL_OPT_APP_CRED, uri, + this.appInfoList[i].uid, false); + console.info('removeGrantedAppList succeed'); + } catch (error) { + let e: BusinessError = error as BusinessError; + console.error('removeGrantedAppList error, message: ' + e.message + ', code: ' + e.code); + } + } + } + console.info('leave removeGrantedAppList'); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/presenter/CmShowSysCaPresenter.ets b/entry/src/main/ets/presenter/CmShowSysCaPresenter.ets new file mode 100755 index 0000000..b162a15 --- /dev/null +++ b/entry/src/main/ets/presenter/CmShowSysCaPresenter.ets @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import certManagerModel from '../model/CertMangerModel'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import { CertAbstractVo } from '../model/CertManagerVo/CertAbstractVo'; +import { CertInfoVo } from '../model/CertManagerVo/CertInfoVo'; + +const TAG = 'CMShowSysCa Presenter: '; + +export default class CmShowSysCaPresenter { + private static sInstance: CmShowSysCaPresenter; + public certList: CertAbstractVo[] = []; + public certInfo: CertInfoVo = new CertInfoVo('', '', false, '', '', '', '', '', '', + new Uint8Array(), new Map(), new Map(), new Map()); + + public static getInstance(): CmShowSysCaPresenter { + if (CmShowSysCaPresenter.sInstance == null) { + CmShowSysCaPresenter.sInstance = new CmShowSysCaPresenter(); + } + return CmShowSysCaPresenter.sInstance; + } + + onAboutToAppear(): void { + this.updateSystemTrustedCertificateList(); + } + + aboutToDisappear(): void { + this.certList = []; + this.certInfo = new CertInfoVo('', '', false, '', '', '', '', '', '', + new Uint8Array(), new Map(), new Map(), new Map()); + } + + updateSystemTrustedCertificateList(): void { + certManagerModel.getCertOrCredList(CMModelOptType.CM_MODEL_OPT_SYSTEM_CA, + (errCode: CMModelErrorCode, certList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + certList.sort((certAbs, certAbsOther): number => { + let certAlias = certAbs.certAlias; + let certAliasOther = certAbsOther.certAlias; + if (certAlias <= certAliasOther) { + return -1; + } else { + return 1; + } + }); + this.certList = certList; + } else { + console.error(TAG + 'updateSystemTrustedCertificateList fail,errCode is' + errCode); + this.certList = []; + } + }); + } + + getSystemTrustedCertificate(uri: string, callback: Function): void { + certManagerModel.getCertOrCred(CMModelOptType.CM_MODEL_OPT_SYSTEM_CA, uri, + (errCode: CMModelErrorCode, certInfo: CertInfoVo) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + console.info(TAG + 'getSystemTrustedCertificate success,errCode is ' + errCode); + this.certInfo = certInfo; + callback(); + } else { + console.error(TAG + 'getSystemTrustedCertificate fail,errCode is' + errCode); + this.certInfo.clearCertInfoVo(); + callback(); + } + }); + } + + setSystemCertificateStatus(uri: string, status: boolean): void { + certManagerModel.setCertStatus(CMModelOptType.CM_MODEL_OPT_SYSTEM_CA, uri, status, + (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + console.info(TAG + 'setSystemCertificateStatus success,errCode is' + errCode); + this.updateSystemTrustedCertificateList(); + } else { + console.error(TAG + 'setSystemCertificateStatus fail,errCode is' + errCode); + } + }); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/presenter/CmShowSysCredPresenter.ets b/entry/src/main/ets/presenter/CmShowSysCredPresenter.ets new file mode 100755 index 0000000..666f2a0 --- /dev/null +++ b/entry/src/main/ets/presenter/CmShowSysCredPresenter.ets @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import certManagerModel from '../model/CertMangerModel'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import { CredentialAbstractVo } from '../model/CertManagerVo/CredentialAbstractVo'; +import { CredentialVo } from '../model/CertManagerVo/CredentialVo'; +import { AppAuthorVo } from '../model/CertManagerVo/AppAuthorVo'; +@Observed +export default class CmShowSysCredPresenter { + private static sInstance: CmShowSysCredPresenter; + public credList: CredentialAbstractVo[] = []; + public appInfoList: AppAuthorVo[] = []; + public credInfo: CredentialVo = new CredentialVo('', '', '', 0, 0, new Uint8Array()); + + public static getInstance(): CmShowSysCredPresenter { + if (CmShowSysCredPresenter.sInstance == null) { + CmShowSysCredPresenter.sInstance = new CmShowSysCredPresenter(); + } + return CmShowSysCredPresenter.sInstance; + } + + aboutToDisappear(): void { + this.credList = []; + this.credInfo = new CredentialVo('', '', '', 0, 0, new Uint8Array()); + this.appInfoList = []; + } + + updateSystemCredListCallback(callback: Function): void { + certManagerModel.getCertOrCredList(CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED, + (errCode: CMModelErrorCode, credList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + credList.sort((certAbs, certAbsOther): number => { + let certAlias = certAbs.alias; + let certAliasOther = certAbsOther.alias; + if (certAlias <= certAliasOther) { + return -1; + } else { + return 1; + } + }); + this.credList = credList; + callback(); + } else { + console.error('updateAppCredList error :' + JSON.stringify(errCode)); + this.credList = []; + callback(); + } + }); + } + + updateSystemCredList(): void { + certManagerModel.getCertOrCredList(CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED, + (errCode: CMModelErrorCode, credList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + credList.sort((certAbs, certAbsOther): number => { + let certAlias = certAbs.alias; + let certAliasOther = certAbsOther.alias; + if (certAlias <= certAliasOther) { + return -1; + } else { + return 1; + } + }); + this.credList = credList; + } else { + console.error('updateSystemCredList error :' + JSON.stringify(errCode)); + this.credList = []; + } + }); + } + + getSystemCred(uri: string, callback: Function): void { + certManagerModel.getCertOrCred(CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED, uri, + (errCode: CMModelErrorCode, credInfo: CredentialVo) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.credInfo = credInfo; + } else { + console.error('getAppCred error :' + JSON.stringify(errCode)); + this.credInfo.clearCredentialVo(); + } + callback(); + }); + } + + deleteSystemCred(uri: string): void { + certManagerModel.deleteCertOrCred(CMModelOptType.CM_MODEL_OPT_SYSTEM_CRED, uri, (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.updateSystemCredList(); + } else { + console.error('deleteAppCred error :' + JSON.stringify(errCode)); + } + }); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/presenter/CmShowUserCaPresenter.ets b/entry/src/main/ets/presenter/CmShowUserCaPresenter.ets new file mode 100755 index 0000000..8524bcb --- /dev/null +++ b/entry/src/main/ets/presenter/CmShowUserCaPresenter.ets @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import certManagerModel from '../model/CertMangerModel'; +import { CMModelErrorCode, CMModelOptType } from '../model/CertMangerModel'; +import { CertAbstractVo } from '../model/CertManagerVo/CertAbstractVo'; +import { CertInfoVo } from '../model/CertManagerVo/CertInfoVo'; + +const TAG = 'CMFaShowUserCa Presenter: '; + +export default class CmShowUserCaPresenter { + private static sInstance: CmShowUserCaPresenter; + public certList: CertAbstractVo[] = []; + public certInfo: CertInfoVo = new CertInfoVo('', '', false, '', '', '', '', '', '', + new Uint8Array(), new Map(), new Map(), new Map()); + + public static getInstance(): CmShowUserCaPresenter { + if (CmShowUserCaPresenter.sInstance == null) { + CmShowUserCaPresenter.sInstance = new CmShowUserCaPresenter(); + } + return CmShowUserCaPresenter.sInstance; + } + + onAboutToAppear(): void { + + } + + aboutToDisappear(): void { + this.certList = []; + this.certInfo = new CertInfoVo('', '', false, '', '', '', '', '', '', + new Uint8Array(), new Map(), new Map(), new Map()); + } + + updateUserTrustedCertificateList(): void { + certManagerModel.getCertOrCredList(CMModelOptType.CM_MODEL_OPT_USER_CA, + (errCode: CMModelErrorCode, certList: Array) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + certList.sort((certAbs, certAbsOther): number => { + let certAlias = certAbs.certAlias; + let certAliasOther = certAbsOther.certAlias; + if (certAlias <= certAliasOther) { + return -1; + } else { + return 1; + } + }); + this.certList = certList; + } else { + console.error(TAG + 'updateUserTrustedCertificateList fail,errCode is ' + errCode); + this.certList = []; + } + }); + } + + getUserTrustedCertificate(uri: string, callback: Function): void { + certManagerModel.getCertOrCred(CMModelOptType.CM_MODEL_OPT_USER_CA, uri, + (errCode: CMModelErrorCode, certInfo: CertInfoVo) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + console.info(TAG + 'getUserTrustedCertificate success, errCode is ' + errCode); + this.certInfo = certInfo; + callback(); + } else { + console.error(TAG + 'getUserTrustedCertificate fail, errCode is ' + errCode); + this.certInfo.clearCertInfoVo(); + callback(); + } + }); + } + + setUserCertificateStatus(uri: string, status: boolean): Promise { + return new Promise(resolve => { + certManagerModel.setCertStatus(CMModelOptType.CM_MODEL_OPT_USER_CA, uri, status, (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.getUserTrustedCertificate(uri, () => { + console.info(TAG + 'setCerStatus then getUserTrustedCertificate,errCode is' + errCode); + }); + return resolve(true); + } else { + console.error(TAG + 'setUserCertificateStatus fail,errCode is ' + errCode); + return resolve(false); + } + }); + }) + } + + deleteUserCertificate(uri: string, callback: Function): void { + certManagerModel.deleteCertOrCred(CMModelOptType.CM_MODEL_OPT_USER_CA, uri, (errCode: CMModelErrorCode) => { + if (errCode === CMModelErrorCode.CM_MODEL_ERROR_SUCCESS) { + this.updateUserTrustedCertificateList(); + } else { + console.error(TAG + 'deleteUserCertificate fail,errCode is ' + errCode); + } + callback(); + }); + } +} \ No newline at end of file diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 new file mode 100755 index 0000000..eed087b --- /dev/null +++ b/entry/src/main/module.json5 @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2022-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. + */ +{ + "module": { + "name": "CertManager", + "type": "feature", + "description": "$string:mainability_description", + "mainElement": "MainAbility", + "deviceTypes": [ + "default" + ], + "metadata": [ + { + "name": "ArkTSPartialUpdate", + "value": "true" + }, + { + "name": "partialUpdateStrictCheck", + "value": "all" + } + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "MainAbility", + "srcEntry": "./ets/MainAbility/MainAbility.ts", + "description": "$string:mainability_description", + "icon": "$media:icon", + "label": "$string:entry_MainAbility", + "exported": true, + "launchType": "singleton", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:color_1", + "orientation": "auto_rotation_restricted", + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "MainExtensionAbility", + "srcEntry": "./ets/MainAbility/MainExtensionAbility.ts", + "description": "$string:mainability_description", + "icon": "$media:icon", + "label": "$string:entry_MainAbility", + "exported": true, + "permissions": ['ohos.permission.ACCESS_CERT_MANAGER'], + "type": "sys/commonUI", + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home", + "action.access.privacy.center", + "ohos.want.action.viewData" + ] + } + ], + "metadata": [ + { + "name": 'metadata.access.privacy.center', + "value": 'security_privacy.json' + } + ] + }, + { + "name": "CertPickerUIExtAbility", + "srcEntry": "./ets/MainAbility/CertPickerUiExtAbility.ets", + "exported": true, + "permissions": ['ohos.permission.ACCESS_CERT_MANAGER'], + "type": "sys/commonUI" + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.GET_BUNDLE_INFO" + }, + { + "name": "ohos.permission.ACCESS_CERT_MANAGER_INTERNAL" + }, + { + "name": "ohos.permission.ACCESS_CERT_MANAGER" + }, + { + "name": "ohos.permission.GET_BUNDLE_RESOURCES" + }, + { + "name": "ohos.permission.ACCESS_SECURITY_PRIVACY_CENTER" + }, + { + "name": "ohos.permission.ACCESS_BIOMETRIC" + }, + { + "name": "ohos.permission.PRIVACY_WINDOW" + }, + { + "name": "ohos.permission.ACCESS_SYSTEM_APP_CERT" + }, + { + "name": "ohos.permission.ACCESS_USER_TRUSTED_CERT" + }, + { + "name": "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED" + } + ] + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json new file mode 100755 index 0000000..2cb0964 --- /dev/null +++ b/entry/src/main/resources/base/element/color.json @@ -0,0 +1,272 @@ +{ + "color": [ + { + "name": "color_333333_grey", + "value": "#333333" + }, + { + "name": "color_666666_grey", + "value": "#666666" + }, + { + "name": "color_999999_grey", + "value": "#999999" + }, + { + "name": "color_E3E3E3_grey", + "value": "#E3E3E3" + }, + { + "name": "color_D8D8D8_grey", + "value": "#D8D8D8" + }, + { + "name": "color_button_grey", + "value": "#1824310D" + }, + { + "name": "color_00000000_transparent", + "value": "#00000000" + }, + { + "name": "volume_bg_color", + "value": "#CCFFFFFF" + }, + { + "name": "white_bg_color", + "value": "#FFFFFF" + }, + { + "name": "font_color_182431", + "value": "#182431" + }, + { + "name": "font_color_007DFF", + "value": "#007DFF" + }, + { + "name": "search_no_result_text_color", + "value": "$color:font_color_182431" + }, + { + "name": "search_result_text_color", + "value": "$color:font_color_182431" + }, + { + "name": "search_result_text_color_highlight", + "value": "$color:font_color_007DFF" + }, + { + "name": "bluetooth_text_color_highlight", + "value": "$color:font_color_007DFF" + }, + { + "name": "FAFAFA", + "value": "#FAFAFA" + }, + { + "name": "DCEAF9", + "value": "#DCEAF9" + }, + { + "name": "4C89F0", + "value": "#4C89F0" + }, + { + "name": "D1D0DB", + "value": "#D1D0DB" + }, + { + "name": "cm_del_all_font_color", + "value": "#007DFF" + }, + { + "name": "cm_del_all_cancel_button_color", + "value": "#007DFF" + }, + { + "name": "cm_del_all_delete_button_color", + "value": "#FA2A2D" + }, + { + "name": "request_auth_font_color", + "value": "#182431" + }, + { + "name": "request_auth_background_color", + "value": "#FFFFFF" + }, + { + "name": "request_auth_bt_font_color", + "value": "#4C89F0" + }, + { + "name": "request_auth_color", + "value": "#E5E5E5" + }, + { + "name": "install_cancel_bt_font_color", + "value": "#007DFF" + }, + { + "name": "install_cancel_bt_bg_color", + "value": "#0d182431" + }, + { + "name": "install_confirm_bt_font_color", + "value": "#ffffffff" + }, + { + "name": "install_confirm_bt_bg_color", + "value": "#007DFF" + }, + { + "name": "install_confirm_bt_bg_disable_color", + "value": "#66007DFF" + }, + { + "name": "install_info_font_color", + "value": "#E6000000" + }, + { + "name": "credentials_app_name_color", + "value": "#182431" + }, + { + "name": "credentials_app_Toggle_selectColor", + "value": "#007DFF" + }, + { + "name": "credentials_app_title_color", + "value": "#182431" + }, + { + "name": "credentials_app_list_backgroundColor", + "value": "#FFFFFF" + }, + { + "name": "credentials_app_finishColor", + "value": "#4C89F0" + }, + { + "name": "credentials_app_finish_backgroundColor", + "value": "#FFFFFF" + }, + { + "name": "credentials_detail_keyNum_fontColor", + "value": "#000000" + }, + { + "name": "credentials_detail_close_fontColor", + "value": "#4C89F0" + }, + { + "name": "warning_delete_fontColor", + "value": "#FF0000" + }, + { + "name": "public_delete_fontColor", + "value": "#CD5C5C" + }, + { + "name": "evidenceList_TabBuilder_fontColor_182431", + "value": "#182431" + }, + { + "name": "Evidence_color", + "value": "#0D000000" + }, + { + "name": "Evidence_backgroundColor", + "value": "#F1F3F5" + }, + { + "name": "sys_list_divider_color_000000", + "value": "#0D000000" + }, + { + "name": "TrustedEvidence_TabBuilder_fontColor_182431", + "value": "#182431" + }, + { + "name": "sys_list_backgroundColor_FFFFFF", + "value": "#FFFFFF" + }, + { + "name": "user_list_divider_color_000000", + "value": "#0D000000" + }, + { + "name": "user_list_backgroundColor_FFFFFF", + "value": "#FFFFFF" + }, + { + "name": "componentUser_text_fontColor_182431", + "value": "#182431" + }, + { + "name": "ComponentSystem_text_fontColor_182431", + "value": "#182431" + }, + { + "name": "ComponentSystem_Toggle_selectedColor_007DFF", + "value": "#007DFF" + }, + { + "name": "CustomDialogExample_text_fontColor_182431", + "value": "#182431" + }, + { + "name": "CustomDialogExample_list_backgroundColor_FFFFFF", + "value": "#FFFFFF" + }, + { + "name": "CustomDialogExample_Button_backgroundColor_FFFFFF", + "value": "#FFFFFF" + }, + { + "name": "CustomDialogExample_Button_fontColor_4C89F0", + "value": "#4C89F0" + }, + { + "name": "CustomDialogExampleUser_text_fontColor_182431", + "value": "#182431" + }, + { + "name": "CustomDialogExampleUser_Flex_Toggle_selectedColor_007DFF", + "value": "#007DFF" + }, + { + "name": "CustomDialogExampleUser_Flex_firButton_backgroundColor_FFFFFF", + "value": "#FFFFFF" + }, + { + "name": "CustomDialogExampleUser_Flex_firButton_fontColor_4C89F0", + "value": "#4C89F0" + }, + { + "name": "CustomDialogExampleUser_warning_secButton_fontColor_FF0000", + "value": "#FF0000" + }, + { + "name": "CustomDialogExampleUser_Flex_secButton_backgroundColor_FFFFFF", + "value": "#FFFFFF" + }, + { + "name": "CustomDialogExampleUser_Flex_secButton_fontColor_FF0000", + "value": "#FF0000" + }, + { + "name": "DialogSubjectComponent_Text_fontColor_182431", + "value": "#182431" + }, + { + "name": "CertManager_Divider_Color_182431", + "value": "#182431" + }, + { + "name": "TrustedEvidence_tabs_backgroundColor_F1F3F5", + "value": "#F1F3F5" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json new file mode 100755 index 0000000..9a0b7c6 --- /dev/null +++ b/entry/src/main/resources/base/element/float.json @@ -0,0 +1,1700 @@ +{ + "float": [ + { + "name": "wh_value_0", + "value": "0vp" + }, + { + "name": "wh_value_1", + "value": "1vp" + }, + { + "name": "wh_value_1_5", + "value": "1.5vp" + }, + { + "name": "wh_value_2", + "value": "2vp" + }, + { + "name": "wh_value_3", + "value": "3vp" + }, + { + "name": "wh_value_4", + "value": "4vp" + }, + { + "name": "wh_value_5", + "value": "5vp" + }, + { + "name": "wh_value_6", + "value": "6vp" + }, + { + "name": "wh_value_8", + "value": "8vp" + }, + { + "name": "wh_value_9", + "value": "9vp" + }, + { + "name": "wh_value_10", + "value": "10vp" + }, + { + "name": "wh_value_11", + "value": "11vp" + }, + { + "name": "wh_value_12", + "value": "12vp" + }, + { + "name": "wh_value_13", + "value": "13vp" + }, + { + "name": "wh_value_15", + "value": "15vp" + }, + { + "name": "wh_value_16", + "value": "16vp" + }, + { + "name": "wh_value_18", + "value": "18vp" + }, + { + "name": "wh_value_19", + "value": "19vp" + }, + { + "name": "wh_value_20", + "value": "20vp" + }, + { + "name": "wh_value_21", + "value": "21vp" + }, + { + "name": "wh_value_22", + "value": "22vp" + }, + { + "name": "wh_value_24", + "value": "24vp" + }, + { + "name": "wh_value_26", + "value": "26vp" + }, + { + "name": "wh_value_28", + "value": "28vp" + }, + { + "name": "wh_value_30", + "value": "30vp" + }, + { + "name": "wh_value_32", + "value": "32vp" + }, + { + "name": "wh_value_33", + "value": "33vp" + }, + { + "name": "wh_value_40", + "value": "40vp" + }, + { + "name": "wh_value_44", + "value": "44vp" + }, + { + "name": "wh_value_48", + "value": "48vp" + }, + { + "name": "wh_value_50", + "value": "50vp" + }, + { + "name": "wh_value_52", + "value": "52vp" + }, + { + "name": "wh_value_56", + "value": "56vp" + }, + { + "name": "wh_value_60", + "value": "60vp" + }, + { + "name": "wh_value_64", + "value": "64vp" + }, + { + "name": "wh_value_65", + "value": "65vp" + }, + { + "name": "wh_value_67", + "value": "67vp" + }, + { + "name": "wh_value_70", + "value": "70vp" + }, + { + "name": "wh_value_80", + "value": "80vp" + }, + { + "name": "wh_value_96", + "value": "96vp" + }, + { + "name": "wh_value_100", + "value": "100vp" + }, + { + "name": "wh_value_104", + "value": "104vp" + }, + { + "name": "wh_value_109", + "value": "109vp" + }, + { + "name": "wh_value_118", + "value": "118vp" + }, + { + "name": "wh_value_119", + "value": "119vp" + }, + { + "name": "wh_value_120", + "value": "120vp" + }, + { + "name": "wh_value_130", + "value": "1300vp" + }, + { + "name": "wh_value_152", + "value": "152vp" + }, + { + "name": "wh_value_160", + "value": "160vp" + }, + { + "name": "wh_value_200", + "value": "200vp" + }, + { + "name": "wh_value_208", + "value": "208vp" + }, + { + "name": "wh_value_210", + "value": "210vp" + }, + { + "name": "wh_value_212", + "value": "212vp" + }, + { + "name": "wh_value_216", + "value": "216vp" + }, + { + "name": "wh_value_230", + "value": "230vp" + }, + { + "name": "wh_value_248", + "value": "248vp" + }, + { + "name": "wh_value_250", + "value": "250vp" + }, + { + "name": "wh_value_263", + "value": "263vp" + }, + { + "name": "wh_value_280", + "value": "280vp" + }, + { + "name": "wh_value_298", + "value": "298vp" + }, + { + "name": "wh_value_336", + "value": "336vp" + }, + { + "name": "wh_value_362", + "value": "362vp" + }, + { + "name": "wh_value_390", + "value": "390vp" + }, + { + "name": "wh_value_400", + "value": "400vp" + }, + { + "name": "wh_value_410", + "value": "410vp" + }, + { + "name": "wh_value_620", + "value": "620vp" + }, + { + "name": "wh_value_720", + "value": "720vp" + }, + { + "name": "font_35", + "value": "35fp" + }, + { + "name": "font_10", + "value": "10fp" + }, + { + "name": "font_12", + "value": "12fp" + }, + { + "name": "font_14", + "value": "14fp" + }, + { + "name": "font_16", + "value": "16fp" + }, + { + "name": "font_17", + "value": "17fp" + }, + { + "name": "font_18", + "value": "18fp" + }, + { + "name": "font_20", + "value": "20fp" + }, + { + "name": "font_21", + "value": "21fp" + }, + { + "name": "font_22", + "value": "22fp" + }, + { + "name": "font_24", + "value": "24fp" + }, + { + "name": "font_28", + "value": "28fp" + }, + { + "name": "font_30", + "value": "30fp" + }, + { + "name": "font_50", + "value": "50fp" + }, + { + "name": "lineHeight_19", + "value": "19vp" + }, + { + "name": "lineHeight_21", + "value": "21vp" + }, + { + "name": "lineHeight_22", + "value": "22vp" + }, + { + "name": "lineHeight_33", + "value": "33vp" + }, + { + "name": "lineHeight_41", + "value": "41vp" + }, + { + "name": "distance_1", + "value": "1vp" + }, + { + "name": "distance_2", + "value": "2vp" + }, + { + "name": "distance_4", + "value": "4vp" + }, + { + "name": "distance_6", + "value": "6vp" + }, + { + "name": "distance_8", + "value": "8vp" + }, + { + "name": "distance_9", + "value": "9vp" + }, + { + "name": "distance_9_5", + "value": "9.5vp" + }, + { + "name": "distance_10", + "value": "10vp" + }, + { + "name": "distance_11", + "value": "11vp" + }, + { + "name": "distance_11_5", + "value": "11.5vp" + }, + { + "name": "distance_12", + "value": "12vp" + }, + { + "name": "distance_13", + "value": "13vp" + }, + { + "name": "distance_14", + "value": "14vp" + }, + { + "name": "distance_15", + "value": "15vp" + }, + { + "name": "distance_16", + "value": "16vp" + }, + { + "name": "distance_17", + "value": "17vp" + }, + { + "name": "distance_18", + "value": "18vp" + }, + { + "name": "distance_19", + "value": "19vp" + }, + { + "name": "distance_19_5", + "value": "19.5vp" + }, + { + "name": "distance_20", + "value": "20vp" + }, + { + "name": "distance_21", + "value": "21vp" + }, + { + "name": "distance_22_5", + "value": "22.5vp" + }, + { + "name": "distance_24", + "value": "24vp" + }, + { + "name": "distance_26", + "value": "26vp" + }, + { + "name": "distance_27_5", + "value": "27.5vp" + }, + { + "name": "distance_30", + "value": "30vp" + }, + { + "name": "distance_32", + "value": "32vp" + }, + { + "name": "distance_36", + "value": "36vp" + }, + { + "name": "distance_48", + "value": "48vp" + }, + { + "name": "distance_64", + "value": "64vp" + }, + { + "name": "distance_66", + "value": "66vp" + }, + { + "name": "distance_80", + "value": "80vp" + }, + { + "name": "distance_96", + "value": "96vp" + }, + { + "name": "sys_corner_radius_clicked", + "value": "8vp" + }, + { + "name": "sys_elements_margin_vertical_l", + "value": "16vp" + }, + { + "name": "sys_elements_margin_vertical_m", + "value": "8vp" + }, + { + "name": "sys_elements_margin_horizontal_l", + "value": "16vp" + }, + { + "name": "sys_elements_margin_horizontal_m", + "value": "16vp" + }, + { + "name": "page_margin_horizontal", + "value": "24vp" + }, + { + "name": "square_click_image_size", + "value": "48vp" + }, + { + "name": "control_common_font_size", + "value": "20fp" + }, + { + "name": "item_common_vertical_margin", + "value": "20vp" + }, + { + "name": "item_common_horizontal_margin", + "value": "24vp" + }, + { + "name": "item_icon_size", + "value": "24vp" + }, + { + "name": "item_arrow_width", + "value": "12vp" + }, + { + "name": "slider_text_padding_left", + "value": "5vp" + }, + { + "name": "page_header_height", + "value": "56vp" + }, + { + "name": "volume_border_radius", + "value": "15vp" + }, + { + "name": "radius_12", + "value": "12vp" + }, + { + "name": "radius_20", + "value": "20vp" + }, + { + "name": "radius_24", + "value": "24vp" + }, + { + "name": "radius_32", + "value": "32vp" + }, + { + "name": "radius_40", + "value": "40vp" + }, + { + "name": "search_placeholder_font", + "value": "22fp" + }, + { + "name": "search_input_height", + "value": "40vp" + }, + { + "name": "search_input_margin_start", + "value": "64vp" + }, + { + "name": "search_input_horizontal_margin", + "value": "8vp" + }, + { + "name": "search_input_border_radius", + "value": "20vp" + }, + { + "name": "search_no_result_image_size", + "value": "160vp" + }, + { + "name": "search_no_result_text_font_size", + "value": "14fp" + }, + { + "name": "search_no_result_margin_top", + "value": "174vp" + }, + { + "name": "search_item_height", + "value": "72vp" + }, + { + "name": "search_result_item_title_font_size", + "value": "16vp" + }, + { + "name": "search_result_item_summary_font_size", + "value": "14fp" + }, + { + "name": "slider_image_width", + "value": "40vp" + }, + { + "name": "slider_image_height", + "value": "40vp" + }, + { + "name": "slider_image_margin", + "value": "15vp" + }, + { + "name": "audio_subtitle_font", + "value": "18fp" + }, + { + "name": "audio_subtitle_margin_left", + "value": "10vp" + }, + { + "name": "audio_subtitle_margin_bottom", + "value": "5vp" + }, + { + "name": "audio_title_summary_margin_left", + "value": "15vp" + }, + { + "name": "audio_summary_subtitle_margin_bottom", + "value": "10vp" + }, + { + "name": "audio_subtitle_margin_top", + "value": "10vp" + }, + { + "name": "audio_sound_mode_outer_height", + "value": "280vp" + }, + { + "name": "audio_sound_mode_inner_padding_top", + "value": "15vp" + }, + { + "name": "audio_sound_mode_inner_padding_bottom", + "value": "15vp" + }, + { + "name": "audio_sound_mode_outer_padding", + "value": "16vp" + }, + { + "name": "audio_sound_mode_item_inner_padding_top", + "value": "5vp" + }, + { + "name": "audio_sound_mode_border_width", + "value": "1vp" + }, + { + "name": "audio_sound_mode_border_radius", + "value": "15vp" + }, + { + "name": "audio_sound_mode_image_size", + "value": "40vp" + }, + { + "name": "audio_sound_mode_text_margin_top", + "value": "16vp" + }, + { + "name": "audio_sound_mode_font_size", + "value": "20fp" + }, + { + "name": "audio_sound_mode_radio_size", + "value": "36vp" + }, + { + "name": "audio_image_common_size", + "value": "50vp" + }, + { + "name": "audio_image_margin_right", + "value": "10vp" + }, + { + "name": "audio_subtitle_font_size", + "value": "18fp" + }, + { + "name": "audio_margin_left", + "value": "15vp" + }, + { + "name": "audio_start_end_margin_left", + "value": "15vp" + }, + { + "name": "audio_icon_height", + "value": "60vp" + }, + { + "name": "audio_no_icon_height", + "value": "70vp" + }, + { + "name": "audio_slider_height", + "value": "250vp" + }, + { + "name": "audio_volume_height", + "value": "280vp" + }, + { + "name": "audio_border_width", + "value": "1vp" + }, + { + "name": "audio_border_radius", + "value": "15vp" + }, + { + "name": "audio_volume_component_padding", + "value": "16vp" + }, + { + "name": "restore_factory_font_size", + "value": "20fp" + }, + { + "name": "restore_factory_button_width", + "value": "186vp" + }, + { + "name": "restore_factory_button_height", + "value": "40vp" + }, + { + "name": "switch_title_subtitle_size", + "value": "20fp" + }, + { + "name": "switch_component_margin", + "value": "12vp" + }, + { + "name": "switch_toggle_width", + "value": "36vp" + }, + { + "name": "switch_toggle_height", + "value": "20vp" + }, + { + "name": "switch_component_height", + "value": "70vp" + }, + { + "name": "application_common_size", + "value": "45vp" + }, + { + "name": "application_button_subtitle_size", + "value": "16fp" + }, + { + "name": "application_button_height", + "value": "40vp" + }, + { + "name": "storage_common_margin", + "value": "15vp" + }, + { + "name": "location_font_size", + "value": "16fp" + }, + { + "name": "location_common_margin", + "value": "15vp" + }, + { + "name": "storage_data_panel_height", + "value": "230vp" + }, + { + "name": "password_list_item_height", + "value": "48vp" + }, + { + "name": "password_list_item_title_font_size", + "value": "16vp" + }, + { + "name": "password_input_message_vertical_margin", + "value": "20vp" + }, + { + "name": "password_count_down_view_vertical_margin", + "value": "20vp" + }, + { + "name": "password_input_button_space", + "value": "10vp" + }, + { + "name": "password_input_button_margin_top", + "value": "40vp" + }, + { + "name": "radio_component_height", + "value": "30vp" + }, + { + "name": "radio_component_margin_bottom_right", + "value": "15vp" + }, + { + "name": "dataPanel_distance", + "value": "320vp" + }, + { + "name": "dataPanel_proportion_font_size", + "value": "66.67fp" + }, + { + "name": "dataPanel_percent_font_size", + "value": "17.78fp" + }, + { + "name": "dataPanel_used_font_size", + "value": "17.78fp" + }, + { + "name": "location_toggle_width", + "value": "36vp" + }, + { + "name": "location_toggle_height", + "value": "20vp" + }, + { + "name": "location_toggle_margin", + "value": "12vp" + }, + { + "name": "wh_value_72", + "value": "72vp" + }, + { + "name": "switch_summary_margin", + "value": "2vp" + }, + { + "name": "single_item_height", + "value": "48vp" + }, + { + "name": "wh_value_240", + "value": "240vp" + }, + { + "name": "wh_value_245", + "value": "245vp" + }, + { + "name": "wh_value_260", + "value": "260vp" + }, + { + "name": "wh_value_288", + "value": "288vp" + }, + { + "name": "wh_value_324", + "value": "324vp" + }, + { + "name": "opacity_0_2", + "value": "0.2" + }, + { + "name": "paired_device_button_width", + "value": "186vp" + }, + { + "name": "component_button_width", + "value": "263vp" + }, + { + "name": "component_button_width_phone", + "value": "148vp" + }, + { + "name": "pinCode_font_size", + "value": "32fp" + }, + { + "name": "deviceName_button_width", + "value": "263vp" + }, + { + "name": "applicationInfo_button_width", + "value": "324vp" + }, + { + "name": "passwordSetting_button_width", + "value": "261vp" + }, + { + "name": "dialog_16", + "value": "0vp" + }, + { + "name": "dialog_118", + "value": "118vp" + }, + { + "name": "wh_4", + "value": "4vp" + }, + { + "name": "wh_10", + "value": "10vp" + }, + { + "name": "wh_20", + "value": "20vp" + }, + { + "name": "wh_23", + "value": "23vp" + }, + { + "name": "wh_24", + "value": "24vp" + }, + { + "name": "wh_35", + "value": "16vp" + }, + { + "name": "wh_37", + "value": "0vp" + }, + { + "name": "wh_42", + "value": "42vp" + }, + { + "name": "wh_48", + "value": "52vp" + }, + { + "name": "wh_192", + "value": "192vp" + }, + { + "name": "wh_205", + "value": "205vp" + }, + { + "name": "wh_263", + "value": "263vp" + }, + { + "name": "wh_600", + "value": "600vp" + }, + { + "name": "wh_hed_14", + "value": "0vp" + }, + { + "name": "padding_24", + "value": "24vp" + }, + { + "name": "padding_26", + "value": "26vp" + }, + { + "name": "wh_padding_32", + "value": "32vp" + }, + { + "name": "wh_padding_33", + "value": "33vp" + }, + { + "name": "wh_padding_35", + "value": "35vp" + }, + { + "name": "wh_padding_48", + "value": "48vp" + }, + { + "name": "wh_padding_112", + "value": "112vp" + }, + { + "name": "wh_padding_128", + "value": "128vp" + }, + { + "name": "wh_padding_212", + "value": "212vp" + }, + { + "name": "dataPanel_proportion_font_size_1", + "value": "60fp" + }, + { + "name": "dataPanel_percent_font_size_1", + "value": "16vp" + }, + { + "name": "dataPanel_used_font_size_1", + "value": "14vp" + }, + { + "name": "divider_wh", + "value": "0.5vp" + }, + { + "name": "progress_bottom", + "value": "24vp" + }, + { + "name": "head_font_20", + "value": "20vp" + }, + { + "name": "opacity_100_60", + "value": "0.6" + }, + { + "name": "request_auth_radio_width", + "value": "36vp" + }, + { + "name": "request_auth_radio_height", + "value": "20vp" + }, + { + "name": "request_auth_dialog_height", + "value": "500vp" + }, + { + "name": "request_auth_divider_endMargin", + "value": "15vp" + }, + { + "name": "request_auth_borderRadius", + "value": "24vp" + }, + { + "name": "request_auth_strokeWidth", + "value": "1px" + }, + { + "name": "credentials_app_image_wh", + "value": "40vp" + }, + { + "name": "credentials_app_image_hg", + "value": "40vp" + }, + { + "name": "credentials_app_image_margin", + "value": "50vp" + }, + { + "name": "credentials_app_name_margin", + "value": "16vp" + }, + { + "name": "credentials_app_Toggle_margin", + "value": "35vp" + }, + { + "name": "credentials_app_Toggle_wh", + "value": "36vp" + }, + { + "name": "credentials_app_Toggle_hg", + "value": "20vp" + }, + { + "name": "credentials_app_title_margin_bottom", + "value": "10vp" + }, + { + "name": "credentials_app_title_margin_left", + "value": "16vp" + }, + { + "name": "credentials_app_title_margin_top", + "value": "24vp" + }, + { + "name": "credentials_app_ListItem_margin_top", + "value": "30vp" + }, + { + "name": "credentials_app_List_margin_bottom", + "value": "20vp" + }, + { + "name": "credentials_app_Flex_margin_bottom", + "value": "20vp" + }, + { + "name": "credentials_app_dialog_margin_top", + "value": "31vp" + }, + { + "name": "credentials_app_list_hg", + "value": "360vp" + }, + { + "name": "credentials_app_list_margin_top", + "value": "20vp" + }, + { + "name": "credentials_app_list_padding_top", + "value": "4vp" + }, + { + "name": "credentials_app_list_padding_bottom", + "value": "14vp" + }, + { + "name": "credentials_app_list_borderRadius", + "value": "24vp" + }, + { + "name": "credentials_app_finish_margin_top", + "value": "10vp" + }, + { + "name": "credentials_app_finish_margin_bottom", + "value": "20vp" + }, + { + "name": "private_detail_title_margin_left", + "value": "16vp" + }, + { + "name": "private_detail_title_margin_top", + "value": "24vp" + }, + { + "name": "private_detail_title_margin_bottom", + "value": "20vp" + }, + { + "name": "private_detail_Text_margin_bottom", + "value": "10vp" + }, + { + "name": "private_detail_opacity", + "value": "0.7" + }, + { + "name": "private_detail_keyNum_margin_left", + "value": "30vp" + }, + { + "name": "private_detail_close_margin", + "value": "24vp" + }, + { + "name": "private_detail_margin", + "value": "10vp" + }, + { + "name": "managerAuthApp_hg", + "value": "33vp" + }, + { + "name": "managerAuthApp_image_wh", + "value": "12vp" + }, + { + "name": "managerAuthApp_image_hg", + "value": "24vp" + }, + { + "name": "managerAuthApp_image_margin", + "value": "24vp" + }, + { + "name": "managerAuthApp_image_opacity", + "value": "0.2" + }, + { + "name": "managerAuthApp_margin", + "value": "6vp" + }, + { + "name": "publicDetailsCancel_marginTop", + "value": "20vp" + }, + { + "name": "publicDetailsCancel_marginBottom", + "value": "24vp" + }, + { + "name": "publicList_hg", + "value": "33vp" + }, + { + "name": "publicList_margin", + "value": "12vp" + }, + { + "name": "publicList_image_wh", + "value": "12vp" + }, + { + "name": "publicList_image_hg", + "value": "24vp" + }, + { + "name": "publicList_image_margin", + "value": "12vp" + }, + { + "name": "publicList_image_opacity", + "value": "0.2" + }, + { + "name": "publicList_height", + "value": "64vp" + }, + { + "name": "publicList_borderRadius", + "value": "24vp" + }, + { + "name": "publicList_item_hg", + "value": "96vp" + }, + { + "name": "Evidence_wh", + "value": "672vp" + }, + { + "name": "Evidence_margin", + "value": "24vp" + }, + { + "name": "Evidence_strokeWidth", + "value": "1px" + }, + { + "name": "Evidence_startMargin", + "value": "15vp" + }, + { + "name": "Evidence_endMargin", + "value": "15vp" + }, + { + "name": "Evidence_borderRadius", + "value": "24vp" + }, + { + "name": "Evidence_barWidth", + "value": "672vp" + }, + { + "name": "Evidence_barHeight", + "value": "56vp" + }, + { + "name": "evidenceList_TabBuilder_Text_fontSize_value", + "value": "20vp" + }, + { + "name": "evidenceList_TabBuilder_Text_padding_left_value", + "value": "57vp" + }, + { + "name": "evidenceList_TabBuilder_Text_padding_top_value", + "value": "22vp" + }, + { + "name": "evidenceList_TabBuilder_Text_padding_opacity_value", + "value": "0.6" + }, + { + "name": "evidenceList_TabBuilder_Divider_width_value", + "value": "25vp" + }, + { + "name": "evidenceList_TabBuilder_Divider_padding_left_value", + "value": "57vp" + }, + { + "name": "evidenceList_TabBuilder_Divider_padding_top_value", + "value": "5vp" + }, + { + "name": "TrustedEvidence_TabBuilder_Text_fontSize_value", + "value": "16vp" + }, + { + "name": "TrustedEvidence_TabBuilder_Text_padding_left_value", + "value": "57vp" + }, + { + "name": "TrustedEvidence_TabBuilder_Text_padding_top_value", + "value": "18vp" + }, + { + "name": "TrustedEvidence_TabBuilder_Text_padding_opacity_value", + "value": "0.6" + }, + { + "name": "TrustedEvidence_TabBuilder_Divider_width_value", + "value": "48vp" + }, + { + "name": "TrustedEvidence_TabBuilder_Divider_padding_left_value", + "value": "57vp" + }, + { + "name": "TrustedEvidence_TabBuilder_Divider_padding_top_value", + "value": "7vp" + }, + { + "name": "sys_list_divider_strokeWidth_value", + "value": "1px" + }, + { + "name": "sys_list_divider_startMargin_value", + "value": "24vp" + }, + { + "name": "sys_list_divider_endMargin_value", + "value": "24vp" + }, + { + "name": "sys_list_divider_borderRadius_value", + "value": "24vp" + }, + { + "name": "user_list_divider_strokeWidth_value", + "value": "1px" + }, + { + "name": "user_list_divider_startMargin_value", + "value": "24vp" + }, + { + "name": "user_list_divider_endMargin_value", + "value": "24vp" + }, + { + "name": "user_list_divider_borderRadius_value", + "value": "24vp" + }, + { + "name": "tabs_barWidth_value", + "value": "192vp" + }, + { + "name": "tabs_barHeight_value", + "value": "56vp" + }, + { + "name": "componentUser_firText_height_value", + "value": "22vp" + }, + { + "name": "componentUser_firText_fontSize_value", + "value": "22vp" + }, + { + "name": "componentUser_firText_margin_left_value", + "value": "24vp" + }, + { + "name": "componentUser_secText_height_value", + "value": "19vp" + }, + { + "name": "componentUser_secText_fontSize_value", + "value": "18vp" + }, + { + "name": "componentUser_secText_margin_left_value", + "value": "24vp" + }, + { + "name": "componentUser_secText_opacity_value", + "value": "0.6" + }, + { + "name": "componentUser_thdText_fontSize_value", + "value": "18vp" + }, + { + "name": "componentUser_thdText_margin_value", + "value": "4vp" + }, + { + "name": "componentUser_thdText_opacity_value", + "value": "0.6" + }, + { + "name": "componentUser_image_width_value", + "value": "12vp" + }, + { + "name": "componentUser_image_height_value", + "value": "24vp" + }, + { + "name": "componentUser_image_margin_right_value", + "value": "24vp" + }, + { + "name": "componentUser_image_opacity_value", + "value": "0.2" + }, + { + "name": "componentUser_flex_height_value", + "value": "64vp" + }, + { + "name": "componentUser_flex_borderRadius_value", + "value": "24vp" + }, + { + "name": "ComponentSystem_firText_height_value", + "value": "22vp" + }, + { + "name": "ComponentSystem_firText_fontSize_value", + "value": "22vp" + }, + { + "name": "ComponentSystem_firText_margin_left_value", + "value": "24vp" + }, + { + "name": "ComponentSystem_secText_height_value", + "value": "19vp" + }, + { + "name": "ComponentSystem_secText_fontSize_value", + "value": "18vp" + }, + { + "name": "ComponentSystem_secText_margin_left_value", + "value": "24vp" + }, + { + "name": "ComponentSystem_secText_opacity_value", + "value": "0.6" + }, + { + "name": "ComponentSystem_Toggle_margin_right_value", + "value": "24vp" + }, + { + "name": "ComponentSystem_Toggle_width_value", + "value": "36vp" + }, + { + "name": "ComponentSystem_Toggle_height_value", + "value": "20vp" + }, + { + "name": "ComponentSystem_Flex_height_value", + "value": "64vp" + }, + { + "name": "ComponentSystem_Flex_borderRadius_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_firText_fontSize_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_firText_margin_top_value", + "value": "18vp" + }, + { + "name": "CustomDialogExample_firText_margin_left_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_secText_fontSize_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_secText_margin_top_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_secText_margin_left_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_firListItem_Text_fontSize_value", + "value": "17vp" + }, + { + "name": "CustomDialogExample_firListItem_Text_margin_left_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_firListItem_Text_margin_bottom_value", + "value": "6vp" + }, + { + "name": "CustomDialogExample_secListItem_Text_fontSize_value", + "value": "17vp" + }, + { + "name": "CustomDialogExample_secListItem_Text_margin_left_value", + "value": "20vp" + }, + { + "name": "CustomDialogExample_secListItem_Text_margin_top_value", + "value": "13vp" + }, + { + "name": "CustomDialogExample_secListItem_Text_margin_bottom_value", + "value": "8vp" + }, + { + "name": "CustomDialogExample_thdListItem_Text_fontSize_value", + "value": "17vp" + }, + { + "name": "CustomDialogExample_thdListItem_Text_margin_left_value", + "value": "20vp" + }, + { + "name": "CustomDialogExample_thdListItem_Text_margin_top_value", + "value": "13vp" + }, + { + "name": "CustomDialogExample_thdListItem_Text_margin_bottom_value", + "value": "8vp" + }, + { + "name": "CustomDialogExample_list_height_value", + "value": "342vp" + }, + { + "name": "CustomDialogExample_list_margin_top_value", + "value": "21vp" + }, + { + "name": "CustomDialogExample_list_padding_top_value", + "value": "4vp" + }, + { + "name": "CustomDialogExample_list_padding_bottom_value", + "value": "4vp" + }, + { + "name": "CustomDialogExample_list_borderRadius_value", + "value": "24vp" + }, + { + "name": "CustomDialogExample_Column_margin_top_value", + "value": "6vp" + }, + { + "name": "CustomDialogExample_Button_margin_top_value", + "value": "2vp" + }, + { + "name": "CustomDialogExample_Column_height_value", + "value": "576vp" + }, + { + "name": "CustomDialogExampleUser_Flex_firText_fontSize_value", + "value": "22vp" + }, + { + "name": "CustomDialogExampleUser_Flex_firText_margin_left_value", + "value": "16vp" + }, + { + "name": "CustomDialogExampleUser_Flex_secText_fontSize_value", + "value": "18vp" + }, + { + "name": "CustomDialogExampleUser_Flex_secText_margin_left_value", + "value": "16vp" + }, + { + "name": "CustomDialogExampleUser_Flex_secText_opacity_value", + "value": "0.6" + }, + { + "name": "CustomDialogExampleUser_Flex_Toggle_margin_right_value", + "value": "37vp" + }, + { + "name": "CustomDialogExampleUser_Flex_Toggle_width_value", + "value": "36vp" + }, + { + "name": "CustomDialogExampleUser_Flex_Toggle_height_value", + "value": "20vp" + }, + { + "name": "CustomDialogExampleUser_Flex_height_value", + "value": "64vp" + }, + { + "name": "CustomDialogExampleUser_Flex_margin_top_value", + "value": "24vp" + }, + { + "name": "CustomDialogExampleUser_list_height_value", + "value": "374vp" + }, + { + "name": "CustomDialogExampleUser_list_padding_bottom_value", + "value": "14vp" + }, + { + "name": "CustomDialogExampleUser_Column_margin_top_value", + "value": "6vp" + }, + { + "name": "CustomDialogExampleUser_Flex_Button_margin_top_value", + "value": "10vp" + }, + { + "name": "CustomDialogExampleUser_Column_felx_height_value", + "value": "80vp" + }, + { + "name": "CustomDialogExampleUser_Column_height_value", + "value": "614vp" + }, + { + "name": "DialogSubjectComponent_Text_fontSize_value", + "value": "17vp" + }, + { + "name": "DialogSubjectComponent_Text_margin_left_value", + "value": "20vp" + }, + { + "name": "DialogSubjectComponent_Text_margin_top_value", + "value": "10vp" + }, + { + "name": "authorDialog_size", + "value": "500vp" + }, + { + "name": "appDialog_size", + "value": "340vp" + }, + { + "name": "privateDialog_size", + "value": "260vp" + }, + { + "name": "deleteAllDialog_size", + "value": "160vp" + }, + { + "name": "text_opacity_0_4", + "value": "0.4" + }, + { + "name": "offset_dy", + "value": "-20vp" + }, + { + "name": "offset_dy_30", + "value": "-30vp" + }, + { + "name": "scroll_bar_width", + "value": "3vp" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json new file mode 100755 index 0000000..c867dfb --- /dev/null +++ b/entry/src/main/resources/base/element/string.json @@ -0,0 +1,435 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "证书管理" + }, + { + "name": "mainability_description", + "value": "ETS_Empty Feature Ability" + }, + { + "name": "version", + "value": "版本 " + }, + { + "name": "certificateTab", + "value": "证书与凭据" + }, + { + "name": "certificateManage", + "value": "证书管理" + }, + { + "name": "trustedEvidence", + "value": "受信任的证书" + }, + { + "name": "userEvidence", + "value": "凭据列表" + }, + { + "name": "installInStorageDevice", + "value": "从存储设备安装" + }, + { + "name": "installCertificateFailed", + "value": "从存储设备安装失败" + }, + { + "name": "certificatePwdTab", + "value": "提取证书" + }, + { + "name": "certificateAliasTab", + "value": "设置证书" + }, + { + "name": "certificatePwdInfo", + "value": "输入证书密码以提取" + }, + { + "name": "certificateAliasInfo", + "value": "输入证书名称" + }, + { + "name": "deleteAllCred", + "value": "删除所有证书与凭据" + }, + { + "name": "deleteAllCredDialogTitle", + "value": "删除所有证书与凭据" + }, + { + "name": "deleteAllCredDialogMessage", + "value": "所有用户证书与凭据将被永久删除。是否删除?" + }, + { + "name": "deleteAllCredCancel", + "value": "取消" + }, + { + "name": "deleteAllCredDelete", + "value": "删除" + }, + { + "name": "pickCredToAuth", + "value": "选择证书授权" + }, + { + "name": "requestAuthMsg", + "value": "应用\"%s\"请求使用证书。选择一个证书可让该应用对服务器使用此身份凭证。该应用已将请求服务标识为\"localhost\",您只应向信任的应用授予使用证书的权限。" + }, + { + "name": "requestAuthCancel", + "value": "拒绝" + }, + { + "name": "requestAuthFinish", + "value": "授权" + }, + { + "name": "requestFailPromptMsg", + "value": "授权失败,无效AppUid" + }, + { + "name": "installPwdInputCancel", + "value": "取消" + }, + { + "name": "installPwdInputConfirm", + "value": "提取" + }, + { + "name": "installAliasInputCancel", + "value": "取消" + }, + { + "name": "installAliasInputConfirm", + "value": "确定" + }, + { + "name": "installAliasInputInfo", + "value": "注:证书名称仅支持英文和数字" + }, + { + "name": "managerAuthApp", + "value": "管理授权应用" + }, + { + "name": "cancelAuthApp", + "value": "取消" + }, + { + "name": "finishAuthApp", + "value": "完成" + }, + { + "name": "evidenceDetails", + "value": "凭据详情" + }, + { + "name": "entryContains", + "value": "此条目包含:" + }, + { + "name": "keyNum", + "value": " %s个用户密钥" + }, + { + "name": "userCerNum", + "value": " %s个用户证书" + }, + { + "name": "privateDetailsClose", + "value": "关闭" + }, + { + "name": "publicDetailsCancel", + "value": "取消" + }, + { + "name": "publicDetailsDelete", + "value": "删除" + }, + { + "name": "warning_title", + "value": "删除凭据" + }, + { + "name": "warning_message", + "value": "删除后,被授权此凭据的应用将无法正常使用。是否删除?" + }, + { + "name": "warning_cancel", + "value": "取消" + }, + { + "name": "warning_delete", + "value": "删除" + }, + { + "name": "tabName_public", + "value": "公共" + }, + { + "name": "tabName_private", + "value": "私有" + }, + { + "name": "system", + "value": "系统" + }, + { + "name": "user", + "value": "用户" + }, + { + "name": "CustomDialogExample_firText", + "value": "证书详情" + }, + { + "name": "CustomDialogExample_firListItem_text", + "value": "颁发给:" + }, + { + "name": "CustomDialogExample_secListItem_text", + "value": "颁发者:" + }, + { + "name": "CustomDialogExample_thdListItem_text", + "value": "有效期:" + }, + { + "name": "CustomDialogExample_Button_text", + "value": "取消" + }, + { + "name": "CustomDialogExample_Button_on", + "value": "启用" + }, + { + "name": "CustomDialogExample_Button_off", + "value": "禁用" + }, + { + "name": "CustomDialogExampleUser_Status_true", + "value": "已启用" + }, + { + "name": "CustomDialogExampleUser_Status_false", + "value": "已禁用" + }, + { + "name": "CustomDialogExampleUser_Flex_firButton_text", + "value": "取消" + }, + { + "name": "CustomDialogExampleUser_Flex_secButton_text", + "value": "删除" + }, + { + "name": "CustomDialogExampleUser_warning_Button_text", + "value": "取消" + }, + { + "name": "CustomDialogExampleUser_warning_Button_title_text", + "value": "删除证书" + }, + { + "name": "CustomDialogExampleUser_warning_Button_message_text", + "value": "删除后,被授权此证书的应用将无法正常使用。是否删除?" + }, + { + "name": "CustomDialogExampleUser_warning_firButton_text", + "value": "取消" + }, + { + "name": "CustomDialogExampleUser_warning_secButton_text", + "value": "删除" + }, + { + "name": "DialogSubjectComponent_firText", + "value": "常用名称:" + }, + { + "name": "DialogSubjectComponent_secText", + "value": "组织:" + }, + { + "name": "DialogSubjectComponent_thdText", + "value": "组织单位:" + }, + { + "name": "DialogSubjectComponent_fouText", + "value": "序列号:" + }, + { + "name": "DialogSubjectComponent_fifText", + "value": "颁发时间:" + }, + { + "name": "DialogSubjectComponent_sixText", + "value": "有效期至:" + }, + { + "name": "CustomDialogExample_fouListItem_text", + "value": "指纹:" + }, + { + "name": "CustomDialogExample_FingerPrint_text", + "value": "SHA-256 指纹:" + }, + { + "name": "root_certificate", + "value": "根证书" + }, + { + "name": "root_certificate_message", + "value": "警告:为网站启用此证书将允许第三方查看发送给网站的任何私人数据。" + }, + { + "name": "root_certificate_cancel", + "value": "取消" + }, + { + "name": "root_certificate_continue", + "value": "继续" + }, + { + "name": "CA_cert", + "value": "CA证书" + }, + { + "name": "user_certificate_credentials", + "value": "用户凭据" + }, + { + "name": "delete_success", + "value": "删除成功" + }, + { + "name": "importCertDialogTitle", + "value": "安装用户受信任的证书" + }, + { + "name": "importCertDialogMessage", + "value": "即将安装用户受信任的证书。是否安装?" + }, + { + "name": "importCredDialogTitle", + "value": "安装公共凭据" + }, + { + "name": "importCredDialogMessage", + "value": "即将安装公共凭据。是否安装?" + }, + { + "name": "invalidCertAndCredDialogTitle", + "value": "无法使用此文件" + }, + { + "name": "invalidCertAndCredDialogMessage", + "value": "无法将此文件用作证书和凭据" + }, + { + "name": "importCancel", + "value": "取消" + }, + { + "name": "importConfirm", + "value": "确认" + }, + { + "name": "invalidCertAndCredConfirm", + "value": "关闭" + }, + { + "name": "authenticationTitle", + "value": "请进行身份认证" + }, + { + "name": "certInputPassword", + "value": "输入证书密码" + }, + { + "name": "inputConfirm", + "value": "确认" + }, + { + "name": "setCertName", + "value": "设置证书名称" + }, + { + "name": "setCertMessage", + "value": "此证书颁布者可能会检查进出设备的所有流量,该数据包含:CA证书" + }, + { + "name": "Install_Cert_Success", + "value": "安装证书成功" + }, + { + "name": "Install_Cred_Success", + "value": "安装凭据成功" + }, + { + "name": "Install_Cert_Failed", + "value": "安装证书失败" + }, + { + "name": "Install_Cred_Failed", + "value": "安装凭据失败" + }, + { + "name": "Install_Error_NOT_FOUND", + "value": "无法识别此文件。" + }, + { + "name": "Install_ERROR_INCORRECT_FORMAT", + "value": "证书已损坏。" + }, + { + "name": "Install_Error_MAX_QUANTITY_REACHED", + "value": "证书数量已达上限,请清理证书。" + }, + { + "name": "OK", + "value": "知道了" + }, + { + "name": "Password_Message", + "value": "密码错误" + }, + { + "name": "Identity_Authentication", + "value": "请进行身份验证" + }, + { + "name": "inputAliasWarn", + "value": "已达到长度上限" + }, + { + "name": "system_credentials", + "value": "系统凭据" + }, + { + "name": "cert_install_success", + "value": "安装成功" + }, + { + "name": "cert_install_failed", + "value": "安装失败" + }, + { "name": "cert_install_tip", + "value": "“%s”请求安装CA 证书" + }, + { + "name": "cert_install_success_tip", + "value": "CA 证书已安装。" + }, + { + "name":"cert_install_warning", + "value":"为网站启用此证书将允许第三方查看发送给网站的任何私人数据。" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/media/background.png b/entry/src/main/resources/base/media/background.png new file mode 100755 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d GIT binary patch literal 57364 zcmYIuc|6qL_rIk#Su&MMQlYU)cz{|$Qc0x~A^BEf( z`{n=HaSk>%wsfNM*uUkN^8dI{qxxW z*@b_`#>VlLWSG9 z0>QdPQ-&i_RCVdp2s$-u%S362^SHV0`EO6;@n(xK));G>#qwhPWrDXGk@OBMV}H!J za!48&`xhWJKj{_+f3ir<>Jg6Ax<&Xgn;)U7UJyAw{(u?zlf{oLsJTS-_o1?+lSg-j z8fcZj1*Ad(!X>WuuxM!H5t@V3*8vLL6`QnC!q!BwQjI{yk*;~@|3;B)`p$WYcDmnZ zt`R zr=oS6o-D$WZsYKh1PiOdhhX&YWGOzpc<6ITKzr^zi-#>z){t;yz3tu_a!>)(tTU9d zd}COuy~Tb}UIRNX@aVGJqEKUa)1#E-u}pl!sY)z4cu+Hu9==`6=0Ob#x-%q}t@jBp zmoiZDcfF1WL{PB0ZO**8yZ+%;LF6K*JDUoHrJkl0Wzak+Y%E( znUmuA^p@Jv6{%Y;MsiZ4O?#ID2b2ssEq6_KGL z8T%zdA3YhMnkBu19bNsa_$$_1^16jadx`0ZzPx`M%T>qZpYyNYOeDdmqLTNWpR5T% zOlRrW_xNCD+*3_WSxvt4P-@qQ9g_$aedDk-hcV~t>Oxw;UaAk1V?9m5<2k4%VrM$- z?{KH{)m_U~yJcBbX+vqVfq&4)Vf+FvAHd|s{V34=f#uJM!Tp?b32THmfzNn1unwY& zPNtaE{ZZ=OkZFh*xW2FT&fDF?64Q%l>dwdZ#Bg;^v;dAbU*QLEQG@_|ucNXFyx~H( z#h?kJKeI3jD^U~`e`*^zcm?PlIWj|tL_a8NC?HVl*gX%;5PW5Y%ZZ*G=jPn5#o+Sh zhnE>D@Wb!f*O>cZ0}ZT=HlEdoWVWk}5H1S;$vxe#Rv~;l5rJ=w--wPl621jCW}B|gxECKzT9 z3FKlD{=OfN5$J3?Ag0g4F5t8_D(RvO8W!*~?#q{Dhx(Sj=)^9ZlE|LyI?p1NXMWr| zGGbzFN^3)5?P^vfnD7XZo*8yf&O&>7XULUUvhJT@rHcF>PmjodH~u4RSmX4TH?v`IKg2cy7 z(T@e4&pPRHRczikEvwvO?jbblSVp z2qpyT+LHUFhHwcunP(^h)G#uA95vF`Gd&1k%F@wuCk3DnjNjw;b}*;dY{F5{7tNsg zLf4y|)RTV`PjQ^!NoWB3YA@S@Cw zUAr?mUcn7g)n!3J`D7*H`y{%TuT$wNY;))rP(y@kdFdPH#h|rjcW2#oRybxTchXlQ zwMW{bVcqRRc_2r^tI)Zav_+qLwdd|Bw=*pM!|pflbT%K&Eof^{6+|k{2_;HcrWd3? z#z;>@Y3dp#B^R5c9RhH8lT5MRr*;>xd<%C3sV2Y}>{On@a*oump`g#H<6V&DKeZ-?Zic$S$>ulEiZvJG8kHMeSzVE(R|E-<}cEG^n2E*Cp z-25-DQv_Mf+&WhT3r?23Phid$q`Z3HE($RgC{EJA0Yc1SP6(a(oZ4RU2L1~H6k0Q< zHY1Mj{)b(ll3Wr=HakbiEk13zYKN&f#9*}tMZiQ7h@Us+N(Jk`aWQHf)r!ObZAT>_STJuzjuO{qHMlTjN9^hPZ8sZBMl zl&MX}xk{d5VUEInRK9r^Tnx#HE2;hFoa7?NDufAxZV6Mj9B^NaAt4;oStAtWfVg8< zjQAfLPj#u>Xp*sALAi;M(f1>la|_-k(E*-1Sa_Vdt$KsCNAwAbm8CmvpDbwL$`Cx8 zkBC0&3#@q@7E3LVtGQcrGS=s-uh6FHuC)WTtU_@t5c_T~`Wv+F0Jd$a9s(?ucd&l{ zWThjQ*u4YqU6Wq{+^0sC%S;vXx~qO|+s%Am1m-N}zkd84>GT;5u}a1*p9&!g%3wk2 zl=rj+H9g>!z4_zdU1iItL}Zox?lwK^ykQ+_#Ym~p>s8CgcLQYV4wezL^K-_HzM$r! z1m$U&G13HqDckgHschNcoe73o=)$P$j46Y)SnaZK(U|F7d#{AGb%>@b+jX#5*Rf5x zq}@ejPTyyn&&@n|dDGl-o-=XF%6dndW+}@7JDd?6b}Mt-SX_GV^3{!3Yz5a~X@$Fw zyDIkaWq*rtn{8knumG6=yF(6lzQnq)&M@%8RzdC%{%-0Ey{v&0pp-aIPP$bTrF|=~!MvLftx2pd=0-86i#@A;;(b^r-TzBJn~W4d42|-V*)} zt}h95!TwDQ%xWD9TFS{BwGO@d9P>kia=+LQ@r>0>5VvEV8(&tEuw%+YP*Qm6KzZs9 z#qL6SPwl9DtPZ{0`)Vph`^ryNV|=I7r2Vf@LrX3<=$f6zv1^z*!<6j{f$|6Jw=%s2 zb)|d{?()1m_Xoab$B5r9#&taTI^E@0yTQ$UB1_f0nc<oQhFOi;b@!o=y6w&Tsrw|K5XXEJEA>@Eb?8hi( zlT-*bXZd6g*C+W9V6B5iF$2f(_-ek(ko^JW%$@}`#GJVV0S8A~FwzM(JdY)c1B&ls(qJ=bvy&S10cqD8@1Clbooq|3kmbD_she z@O#tu^ibgYfM#HD%WIF%%uf7+)sc&Dejs@WRQE+Q1jXlN2z>9dB;X9e>Y3a-&-A;T z>||D+o$j^$E>F`4y02DTELRMYH*biv(5+ed(cQq&82Gu z2~UNnOcNc&MwT3lD@S}nPJMsvOT%0L{`dN}DU&^Z#6?2^aE!5ulUV_Zct}2~K6R!_ z4ReuaX$@AP?M!XMpi&ZJwsY2up5F-xe0{ym`9#@pr%63v->d&@UoFthcC1`k$L=ze zYX1{xl49Q=z953h>NzyMc3UuH96t7)-k|lRw-P=T%Q`;dC7@r`uCOq8Eqi7gKC)~7 zb(*Q>H|T2(e>5DVf9nswM~C%V2G2 z#B|VOitZm{FlV>EydvsFF|Ue~ium0%0KOaFiMOLk(X}jHq@dI@*AM2G6XzCU zSpFR?#U4MPz~VZR>RA@a!CZu45#f<)^f#kJ+ULtRLJKzSj=cf+NxQ}Kw)Yme6wJz; zu3W=Jz<}rEm$g7sNy>yr-Z|OiI>qQ4m37~);`_~Xgr~N4wOAssk(HTh5er1XtFm+! zb`5FT&FoKA{ADaUP!Y#o^sGPb?mT2wBY9ZfQ}ujLk`C_dyTvT&)34sj!RXJcZ%lCzF?kE~i-xCSGh{ zy%iUR0+S_JP(#%W9!Npk=RL(8tFB7(up1ms-Q#8 z$-{dva97!EQB<5#@0KgW&2S|ddKN*<(?}37-=O@1bF668sG)3(D61=Ech&sJ;j|An zqu1a;`}bcMj;#tF3l~&Ue9ES7GRw~kIPKK&q&^No_3M#yjp?ygI;To&wcXbe%ju*z zpMI!gbi8@{AJVkgXR+py{dMSfko}H`^q^elZQ-5<2bG-K8tYq8Q@*4t)`Blvz!#v> zE;3kk_e^|Kew4?}eU;3n)q48yWgAm)d+F(;W(>jPB_glQLiH|IE=EDVFI*j_FBebS0vXyh5@x9LS?RNi7vXf?RckfXjvy^Pifki$9G zzwp&k7S+aNOI8%DUON~#xxv+a5rJDE+^6;@RcjnwKZ|%#%Ukq~@&vL#Pts;`f?jwYL)Y zDOROB^T8hlFfA@(=$bFYKWy{F^5$#{h*A1FG5GZZ1?>Y+!}UULap(oEekfHZCJkpC zppRS@+Uvrs>_Df!YT#HWpuaEwRq)V49)TgZ7Jf{A6@tpv&>tG)c9F&eZWo)(tDPDB z4Fkl6@ov*S4!gboeokhZ>My7@q%!Z93-zy>Y(_9axnH2W2Ie&#X2Z->o1A6ZoV(OgY z@PpdL`E%U}QN-vzdLCdkVX)Vp-z|CGg)^e06LvMfbj%1)ZdXNB>r>{Jk&ApwTkkLr z-2C5e31{3c{*xsm?)EItQ%pSW(%723B}AHgke#M{7KJW6TT*>9^+`FIe4;VHRwSF$ z9rBta7_>vwCuV;vFY=|NZ2KlX$A`EUk*phH=Pd~I8Kkr|v!j3sBAD^fPD!FoPpnHf zqP&jc&^s{jm0M&oBNXjUol2${7|G^u7UtOd2kxA0b?japS#xlwo_TaY+jh-`+$sfO zFLgfqb~kaemX{ErUn7}?_tb>g?G@UyT99HoY^;BG(5|gh>F3J!9J* zvrz6TP+;XdE$<41%Vony^Y}i*aCz@+4v^38p)5?Nhw`m%Cbg5Lpz%VOxaBnlA9P;N z9D=#{(>`$N_!?&CKf9eJGzIc>dhWes8XtpX`{gOhP;HMklZ8~@Yu~YT1bZZ{VwrAffDNiZ6Mh5vEzpq z=5A;0ff@>1MG@vbwRU!?7ZFD-SYng>JN(=>uwrkrl@4u6M^n6jl1shsk;DM`t#|F? z(H9W(@&~b(mmUR)30H=vAZdIrX%9iR7rMruZ_I4$Eq7YnBI4Z8T zj5;RTUu8?(ZsW>30%Hk#$^zfAtgZ&y!|p@5%e_4oe7)3{Y6c^x>zv=o_XPiF*wI1y zNe5L3p=L;8_D7-+5I+LfNgDYrOIUD_Iu_VJQD^=4v=Gd z_u%h$8{Lobyu6%VkeZI%T_vssgc#J4yD+&6pVkdLYl@3@NdcQbwl!J%4{RC80oF1z z`ksIXyrZT=Apq3kOR#m795+y}-8NizKBNESZCmBS#jqG`n4kCydp-4DZ^BF-zWD2# z1@F?p*^9m)EPrkd^E&cimk<1mN+iwSCVTHpqz^#`_Dj;-5xURqxK*!kp5asE##*=< zc{bFC-`m;q4VL3=| zKN6@)%XIu=yS*-K-9Bw`jN+-lWBttd77x>|g)~$UgPB_qH0h&bm}j3#sdLfV&xcR^ zQFk=d3;U8~YLqm@^61C zmaLbHw=dJ0oLP?>eyJ&=wgtZm!2mS9V!i~62x+n`%jyesf0bKruxRDH-)c2uF;&qT z4Z0drBbHg-G#ueH1vVaEJFTw$U))8mlUjFz?!PDqNpcIqZ%B6$Ju$CzrK@_na@?na5LpJODS}`)`8j7i#>C z0RNEb>nnQ8v$qXrgh)-(=VVRFwj4 zZKH}5T4rlZ$PiI2z3_*{`av5A0jPJY!Y*RQ?XbKPZmNdwp6ufAH4m~1%r{gYeOJBR zai+gl7I{Z35P0Q7EoGmkkLGHe5rR^{bdxWyMiC1~&kI@I-bYJrdGv{=O7!p&kKxN3 ztOoyzWj_asX!zA>`fa~&>#$n@3{c@VVcl3(1m5=dCI-~1uR+4s;@87ozKCU|Z(EhE z7~Csbr}e|&-zPK~*W}WcKqB+rv-rNRzvqfY299AvP zA5u^Rs->xN6b@MzP_f(M+}|~RxUHs#zO%D772V@B$F;5<%Jx|0#Oh_?#%yrHfV>}I z!Lfe59_VCjJ!pEQOWyUr;CdyL z-RzERMQjU_j%}N!Av?++44uVMc#r_KCTZxxSZL>4`xbm)#)*?4I#nFDOZLv10s^{6 zAyo6zfA)w8n^jk|KBb4J;|Gbx9)grFflY-Nyl_v8_@}gizDNn(Y2l6TqM&aN(+9Qg zTBo#J4N$h%f!;K&2NqBlT~J6aqHGy6HI`Xn*)UV$w2>iLk~P=l)VTdah9Ab`z%}dg zxIvG$xPG=H0NRw|6_-~Bzh+BPv9&C;z)58?`7t~$HupdHcF!F5dirrGrn3d}wAHr! z^@&!aoW@3sENjl#i@LzRYOZ4b#v|Jk_Mo$-VYlgbE3LQVKniS1mH)uO`90X{bc~{1 z*%Wm4$E_2-W__`4`mDu;Ld(wv8e147=mMu!AKSC=mw*4n^8S>~fm9mJgf4~8t(bb> z^_3WSK>aAZ6lK3OZ#_7g@)?z1#pZ zoR2>rm%_enbG!+Y34#Jmal)V9@-s8li+_Le^~z8cxHeF5vR%p~{93TJv%YmeTB|@^ zc=}q4Gofbju_Z#%Iv9|44|pawNvh^mFGBA_KZ5C^rx-l~Ytqf4;%SxezE8%O)aJh& z>2it7b`epB=P&=s^y`mJMjMq&9Jvpdhn}6sFHk)q%d zE_RV6%-}?H)w7yAW9TA)&7XbMyu=N}tRA-JTl2iG6u8;@?;!BW;ykyof{i+alo zJu1v~ITow6y^)5crWdi)&;yNs0d)3*vN+aSszJ%`1`(%9X-Hi}3gH#iRg@{Svm?cP zM}T*)U{A8FTQ7b@oc$7vr_EeTIj6N%Cr}VI5VcfZk+@1UFc>zpJkm3S%cb<~=~`BV ztbyjzOPJuDkTJJ!hL^nLk}*=2EXd?->%+3NWrq&5a$%1G{r2~cLQT2W>8!pd$9G;K ziQIDUErsVk$XQPRm)pDFYVuLFlx&eiFlnoixT|jvAoB)ryM_}euaYFXrdKLqi|4AL zG`rnvWi4Qa>Wvo=;Y+t@ecMjl{#37K;?VkYdoSbT(2m}8!k~RT{yv0l8cPp{jtiXr z$7KAJAvM_g4ak}0Yo*q!sO%PN_CK)Pv>lC7xoB~vG1hs?Wv>^kpOBU0WV@$|oL!cE z1FV3%^4Pjr5Fqc)|Sv+upxx8BCM z9*cYQYi3jY(^pUL8`I|3rHf+5>sq98e!hkPsfNMQ1@y7Tnf4{F2p zx9AO&@zYO;WpCQUf4G@!d<{t43@&RHh2Ukg^D-8_;De`dc{hz?yPS_7BzU!x^P-tj zBWt_uk{g94M1uo_&0l?m1qh!Q>=dKy5cx zRa7mv(}`xYKJOm)h3s8goQ*XK1OT<#&Ozf35uTB^VD8m)Z6Bnlal5r-bkso}J^TcM zo)ZSc#2@`h0Si}lrnCFt67JFa*e&}2avKCL|IIk<$R2*5sILkv4P( zesTX_tP#NqXN#>Q{4oe!N=G{SZ_I#~%^kq5ilGc=Q63_5uRt!D^j$k=&$`Ha&bGlAjZ2&hWa=M};Cw|5onME2e;8le z)-hK+mgNbGw-4puLN6g_q5p6T?0XM^dMo810rSBSw7Rrl(jt2JNVBwhB0o3``lZ1y zBr`Dy8LdVilxv`X5b0N8#{#(y<2vQrLj;qv`XA#RZ+@Q~*aYa^UY~;#F>6BL>75+E zeH2(L#HhLeI=Mz1#%^96zY$Se;@N)biYOvM6H1p6-4LcvA=&GP()#?u=_WXgAoZl* z+bR{6BA52?12Rex)v?(LMRsKvf9{KzP<^4&NISV{2!a;wEhr&E)EloHqSR9%ezb)? zl9X;qQSTg@es%UevGs9-KQk6RqJ;Ui(v@S0=JpkXQVYgXlRKQcfFLT2A%*#c?7(b} zjki==Q^Y#Qf}ZVpFtF6<4SbGKkkU>I6wY*Ps*EAzemS5Z0r!-oD>~r!<<+c~fHK+{ z`u4nWcW&4!()0%2>r>@zr$F6$;5*IAuq5bc>cn-IEZ+B|hkO&NPeBi&47YiU-<$w0 zq-j9aGH~K;Y%0{D&e90RZ(J_@o*`(e0TgqWM zz>V1_2|7MMg_6zbeK`A2oW6>`dUuDIll*?4hKaK{^>2t!B*N9o7_!iC51?A=hss#S zTOD48mGM}}JkMLeB>f0zNw|zPj8Efyx1Qh?QyT7Bp*PsC1%+$kgboSqDR=rTEs%8X z-t2|68n3XC`A-sBYO9tXuQqE7{}pE3mRASQTvScN7(%JH0{M|k4t%rE7xh`qUf4A- zgEE3f#zcuMyMYyiu;w=#PFC-_W0rb;u#{l@E}K0uMy~Ec1MBz-KglT}I_AG%m9nb!XAkpoW-`_85Umy)5g0j(3(>`;o1;w;CKp zLKdGc@@LrE*Y6B#H>jMeTcD6nZx;FZw zZ?8nd;T;sv#~t>9Stu`V2=$pLBHrDq3VNw9{KZU-50LlNLK@?o*hLF?1Kjl3op`;u z=nFLXc(CuUKp%gcxwwBm08`iDki>51cyobB9Eypc5@0Uv%$x+m$P}vtzJ@yXv2Y(6 z%G|Dfw#*GyPhoZ)9Obc;u$h*k0~W zv)EW8ChYvHNP~Ws5(MQk4JSGnG!l*4I-odrw$8E;u9uTN)1sDTSK-9%H|jqRi1XpO z_RLbdR5?V7FZiM9a@_RLzrIa?o8u(&ct}&dJFEmRO#py=1J(LW)$S@B$xLi6T)SOw|;fa7Myzv z?MOZ*b$o!rCg?J9&v6SsP#m&goHWvlC%0`IUKT~X&=s1cU$O`0Ea`_f|aU@(<=bXW{`6+7W#cu@H9t zagx-Usc&&vez&!Mjqpdk+Ol(}Uo_B;A&JhUaOe-iG9|*Z<)SYRZ;!m{$5X=V;9Cl+ zs(#H}WR`823f+9`wmRKF;(;wyt*?b3@Y`H^;&@1GipUF_{Gb_RzIV!3$qMq++{iyr8Th+msVi*eA69cY1K|TmaXNA-rCXT%k z%$21aDiQY_-+BI`52BI$rv}FI)tg7-CaaD7_O`l9ngVYH9#Xu44ly2flHy-xuzEyCWC^6c-^K*QrZW zNG1PL`B#xfh_CD57q**Q+=Ty9EEolHUwT`)Z`SWJPvsxa-f8_iHO;AQOj^^?v$Pd6 zy~3pjahT&?UwB@2zW1)s8+UfK$SFAL~tHHx3whuvPyW4mh3w z`_Q5~nHOsoDT0sx@+N~J<-Y&TvqV4MCkgXgo^ntecjdoSopR%@?wkEfAuHDOIVHQe z|K0}d$IAWT3jC{=QJCD$*L3=%k#f)T)tT7R=nTHqn)i5$Q)sm)53ZV1w&{swK_X#n zpD3;2Eb$r)$CDg__L8tv=0-5U5hB))B~SI2(6`QM95phAkktAVs0hU305vOGT{|^t zH`?>)3!24y5TBnjRfAJG|J9jjj_JYwB?gujfD3QwPf@~K(A2Z4KynC|m! zMt!}`yx4=~u?@-#ab5-T?In;dGAUlGajcN(yFF%ypy(av6(B6-=d(A}}k7wcgUJ%c_TA&p~<@ZA~EU-mvx5S_ykM?O8{R|mH|RE75BR5QQ#CTpy{;f{(N zFpFjUOJ}EEwov(%eX6wm&~H5dD|PO&*VQvG&6Br6eo1I>i7L)sk`T?{8}`lQfCB2R z@nDF(51Rl?^;uv9K%Wz-qpmyIoZjoO+tGhY)P>lU7U1Rpv;b{^)mu_I7=1e%POI7M zneWYe`!E(sG!D4Pm@9XD2!jhItDw15w=Vl)ioN}tjFK(3~fxy=!h!`6@!cQuCP6#aH;{{dyV2@O1#ZX{Zl4pLmD z7*{Ip)`V*gV-QVaE+>|4R`><5Z1*;n%pfkb3AiZ1s39)5f5khONJ{XZ5dEj{AwE^i zj6G1{WVlyMNlC3!_Nyk^Z0DjKo$ha)xbx}7UO*rnNj8he_fyO?v!so#$d4^uhxAXf zZNG(a)^5wM7^{-xB|`JITdre*!q^0$>^GMLKm@oauH?5G^;l>0Hp)xxzomAmYTE02 z+c%CPd*0$Be%v~(u%mMywt>EgIlKPOZH{Q%Y5c6=;F0usNLUPph9Xez1H1>s1YOPG zz|s4D9}W5qUuupaM_2#&;|@Jl=mK~Bc0i~OYb643=Gzqz>i%czm6IJ}e-nj~`8ZFe zGWf#c?5=VP0hlqMCIlRJj0p>6ob8O5e(*AYuP~QI>C$d^Yi`)_a|r1LwH(~NZ9P?Y ze?ts^N2upq=Br??YX8%HZ%xopU$9Z$(sjX zPFNIynnhW{IRi^L#G9#+Ew!gHJ%T1dibisJk2~6dM4r$&WR1@Yh3+PZbrp7G519Z>UKXw(mZMT+M-ozzkggshV_x`b zthj%~?f*E&m2-P{17aTUsk&fyuduoa3w}G`Ii-fByRE*XlORaY&Ax;2q^Y}9DeUiq zyMK?>G}eX;GkTjbS%GZr z5T&~;Y#yW|>Ep#W|B^P_r=X1$4uFNPGyw?zjr2lT?F6>ZQaaY;=%~?w4R^35Z=yWu z?(pW}`Hbg{7^L5u3abb48R>Wz-8&e~ld& zG34mkg*Nsz8LkANRe$e1~y0OAYcFkLVXfFw#0X3 z=EB)RkCjS-zhk?;_Eww$ZWCeYf2AIt@_v0+O&5H%+nUcKQQZ*-D#Mj9~nh zx&c!=`cApy)!}O~mTV6{@dbum`*7{`e8wKXQ$qf(L_&%pEl%&9Hz4Ua`%w=5(|{Fe zG=KtAxRHvVR%isJiC+qS)RMDX`xiqORyFg!x&NkABWs5}rYfi3W6r|&5P*I>{#$0n zSspPdl-FAPCWDVqU+`hp5SJ)}U4;QxQ*A|gM$`7-D_HnBBw1Px+%y8Fr*ZBkK&P(5 zLO)g}sM)3#vqJr|zOLiUYMzC)Ip0^+BMHE(YMU_d9|WolPeKCgmx*JYTE6;S>Wa~2 z4x7~9yMFQiL85QHvJtCUi;sWX->6#j?bP;4-B$$B=t*-7v~dwa7d_l5=?cxUgm6Cd zaZr_|B^X5;{k6{%BEZY5G{tgIXaw~PMvhi$_PDnHbyno3v;_gj5-=Qm12)lz+O@kglm5{q;M_RZxMCq-* znMrLfk)rYkS^lo@-6`Sd+^FUeRw9NYH^+}naYE(H+Zh38KI`SA9vUIYM`w7n(({Fc z<0<5oW06nE*}@UB$5AV7a^dI2srSJRcWrClmn7EQdBmJ6?_NrBl@wo_%pe-;K3ph= zN1j@y%^Bw-|7I#-OsQL<1zRV2i1N8h%Jz zJwR0GxN$z5cL7T2`h@=Nn-d!(GsG9!?+6zh=pQ$E{l5S3TiBHQ1&Bvy(*8{} z3j>EOJw+p*2|#VfcRh@u)N+@NMx-@QrQhRg>Tr5cY}aHl3CA*moGLkK0}rdRVR=E^ z{#;gyR7l*RccCrEo*H}%3X|@5YPQ+FM>u|=k#sp-M{J+EGRGl7LH4Z8UIUZqJ%O1S$-a-TXZC__K^ zV}HQ($I)a#fHDGwtEVN4+}*Rszq5|ewZGZEuA5Iw2OpA6%g^thr!`g2lSe?v{V!Zs zZR|Oezz_e)(WIs7nejBn3Q;m~{el(T15QaA3slu+pDiHa->pWfN1C6rVtf%}cuYmO zgKLKj2iNqdxC5nzUkN5bWkY7QyW{~Jm`(yqq=456x~COUo&to>DhmwrE0T1u8eLBX zmGKaO;crc6pm6&VjM@?bZCAXTbba*pRUvkbglVZYwEkF8YfO`T(Y8Hj5McaI z|C{H>yx3qKlRMuy-lc?Sc1!2)CVr8jr{HCfqbxH-_?m>w0h)fl`U3oh{a{=<4u=GG zzB1dSG{rJNtgG}nPU<2q1UPrW{mUkc8)_`L7OAnol7dZB_a(SX@-|yK8Wwm(0F1NEm_aN1wVsURw>% zPcJ-K`1h9E5@B%#7Tn`q0}2)m8v1N;72R}2#~JeoV=z!u6nMx5Hh%7WcQf@>B}s}j zpX2a$CtQcsC3W?=6QyG8m#bS^7MwKolNJR0blaxwZnvS?S;Zd`$Td4sdlY4B=DpVj z;GB--4WcwwL>bZgwia+-FoH)nTd?9m$)`kWfURntsPevI9OkDUq}At_Fhr2*m>J<7 z|K^#22*1UDq{{(|XIx*ulqtAAdQ3OrRygED^IBKe*@i}bZ9_@AZve0qu;T?J2LZ}j zw%cP}y=TD%H^Z>GUW2*063o&E!US9==;FnvZpXFNHRbelmmD_~T)}7{w z&e;xBEsak%$=pypJ3t9=dtnbS!6w40@X`hEdjEiR%*$gfB`8X5t54B?{Y@k+{O-C( zyWn|kD&H^1e6{Z}+mjH!-{_d1n-62-&sj0eAIe`j`?O4m+Khn*F7;(ko`grc}wJs-Gcu{X=-q9>JtlE}duQ+wL-kpryH@ zy?9QcUQwlU%a{$3@vO{6uEg-;vQ6$i3UQK;nO(8qR*T1<;wvvr-5aev6Kzq@WY?yI z8CkJ-_v2o5#Cy<>1tkp7W+umyd18ce*OX=Fs(i}ooB^lb_(Z+B(#0c+peWSQ7vamb z`z_V8WZ6ITb0VsHVCX3uI!$aMYq+2H_VJv|H+xOae}8%g0Ho5T!|3N(fPIQlqqpY} zehINqo%!U~bwZHmWWWQHbG6yOu;gWGMqLHRHz7_bwPG8clq4AvuJY+yO|fZb!!O?8 zu}-gsTJ7>_YGOwb9ZP{7Y~E_-54t0uZ3t;;kkys%#n||9@a5P2V=teS?-R*n9l4LU zX`b4bjK#bVZd&U8y01tpmu%od$DMxAMMv9l&MoL=#mqz@UrVGR_l0_DR1(?*60e1Gde-2*c+IsqkdsUBQplCu zbAh}kVEU~E+wWc#ljwacB1;-}=6;qO#+T9U6+R*7gTqwax52TW8BT?9baXZbe&3!{KI_6)y4?e%W{LkWI2jCl?{Trz8L**uH#O^Q>E0F; zvZVDQPmj+y3P_#pP5&8F;btP7L{R3-N@^b&z}P6C*IselB-bHG;@9&O))tmx7<0R@ zq~8V%kqZ)eZcoE~O~sQ8B8+i&1Ue*r4H|9dY8S&zqWooS;5LT2)V0emG9SEr9t7AM z08Kh_ER&MkZz||l>!~yU@mi`?QQ4AitwkZp6F1DCU$U*G8x922-bf6%3pYrD#i2*< zwpz(G$kV;(&?c|bI?kVkWtK(xu`&B#;UTMoJn+{-FXYMJH&~sfC%3D^A2%%pYB~Fx zYFb@KR!L)a;xpqnrzd^@O_;-5c!|es9)R%NkQ;Y{;h&+Ck8^jTn&jZ}P=M)n>!7A9 zbI=`ms%#Cn4 zcD|SP<@REH*!8~greM*drUAx|97aK~i?nk84xe+fW zZ{VZUt^WcR{^_IyCA?BgZ6gdxVu5?G1|-aEz1&EUsaWP+cJ~=7?fk17Km5W&X3{&= zr6*juZl+Xa>izM!qk7^<2X1*30KepqIdjyV2i+e+zNXSxbK7Tpa}Fm~tK0+5Cmz|g zd=qVePKdNVx^>DVw^plZ?2M6Lxb`!8Ti#RkyDG;^w5l=4mTJ7GuF?>G>j?|lQi82< zNSi&Ar21!4wJGm%haIm3(&qHRaalgKQ+Zo*VUmdvO3d*r$tQiZdevGg?sUI{@hBMB z#c4dG%$ziRt^bWNf~3wy9fsIN_Xz#^hwnqZ)3n%{%nU9mIShVGJbF@_aV%R@{2`Bd zRRV1z;iLf8vnhQhV!*)}h_XFiU+=HG5zruPk-I(^EEW2+SP43iUg88Ktt+fn{a3`C zxH5^rzt^)}NibifBptLnWW>O$q<;o81Ytp^|JHO2c^)R9nQizz@=pua-L?WcDwzsk zqLYg1NS}l0EoS1SEwfU_n>3wtIkq4r(>>1vzP9Z)u* z7!cFZk(y94Ta9;@KGI}VuVTz%OclFRP84+NBUYBAN9)j18h-Dk(N_YxRc+#$@;E!G zk3>;{dx`$+A4-y+OCDz=U?O~&oq10pF2=@SEP`h*hn*uC*BdqRBV;NUWL%7GQwvf+ zy^@Jg8oV=aF&&>FIZfBUhPx!`mVdKBuW_kcOjuX6o{4h~GUS(Oc#=*IhjnUUK6V>q z3|r^NJ1i%lyLPs-RMaW{5i$=F!>FC4M0Pj0<<@G%muXC?eGi&&ai*KS|^#9Ba>V z1r&49PJmi&clkkAhrws5!q)&@Ng2>63rG~VPQPfM6P3_7JQhw!k2;x7`97!rb;o&f zj*N+5e^fk>D^vzYxcBT!!vc`_!+5f!_>XV3z@oz}r2l;7v?ybOOoLg1yQEm1p==et z8!M{V&DaVz@Xg1^2sOzN<|B~4p!Qqom;IvMJuhY^iq(pcg1vcJBD)9j$F|MVwyRM%Pf=l_jD+NyPHL%YE6 z$(-O5y>IX=Oj2(?JA*YBgFzC#Ok z9`8k0Tqim&9(eUu$uOl3X@wSOFmmcm0q`1mIA64Ve_<%3$nNID@10j(FXICMN0-)z_1h!Y(wFt@%rzn&KWkzAN|(aV{DA=J;-G z#?ZdfVo{uhmv0)tmnXPt7NlYVPN%)+Ps(HATs zB#a;EeCAVi=f9W$o`(OvXpJzf;CLh}-04ibR;6BeF3%HSpb7P|@BS;Ns&;?bSOo4F z4DlH!B~h1(AX80$!u6fC-}OET`Dlw`(|?}OMDd~ z>qFr8tnPYIjcmoZtVUn^-ei%&OQA5Tc=Z`Iz9m6b8v)SNDYgGI z&ufpuaQSeQ_2BtH5K)eKXd4pr>O-P(?zf3-LUZVNwLsusL-~7SqM_*WS%%V#M4_TG z{P&M5x)q1sQS4zgx}C=u@Q?t@YU*P&n!}ih@#Hx{2kRN*I*QhP*keYtJ=k?c?y9!B$5bcgrQql3d(MDOE& z$&4)a62X+@f)63w)4wmU=x5`h3F6ai?c0HhJ~iZLYXK!aa#)hyA>2 z|mZaulq=2%a+*w}~-#`f<0;rmBC$8kUReVyk83I8Vz z9h*!SORnHE+X=(t1767g6#NDfz8iGC>whkQKj)G}l@4r;Kv22N_b&h+TX2u|j7#Oj z(K3uiNL1XY*yk@SMq0V^nF^C4tY7F%Xkl1!XVbIhi9k&fR@zT?lM-aSH@RdqE*fzT z0x=nU5YhN`oe2_Me7X&Slwrh-emZTam}o^KV=~utowP0%qBQVdeF^BBD(JrsnqT=g z0Kw~8J^_6p*PaLgV@w0$mjgf4%j*&bCxW;?u04g`wLQC{3<iiFFlUUNQ@-0`3U0PTr^* zMu`6+{ji*^jscj}HzT-Ix^mFBSE+}Zet434IpXr-z;GbHM|<6Z$ud>QLOHm$q>Yj? zi=X^?XVKh5dmh63E6q?c-(MkM>f(9y>kJ)X*W=($$*zh%V_IowxHcM_Px=q^tBS~D z^CNokYN*qIzqTFLw@*J|W1E6Y93dEjFM7bVH;omm!&C=Z%kF zDZ!5^rmEV)HlD6O6Tr*st_e4;^fb1cMxb2+e*K7{dMXd+lY~LT*&%qoG(^LQ;xu2U zlX&3i8OG86!Vntf_USh9iF4*U|J`}Z=mVM)PeAs{D4WZ*4$7P zB%t)P&$2Kr04o8Xy;J`g@KPzWe`1T}m6IZ9MOy`GPfato?=$ik(>JsouPv<{^B1k$GpotiH# zAFc}^jX-(p!24l8(M&7@pUe|Pfm=;J8d^`&7M`y}lC2ikiklLO3&7s(v`TZM_wLvp z)BGvu*V#(5myOg0-#f?hZM~gOm)pbI4r6l2`c;x+BoKN zlf8pTUa5LIE_z>y*IP(5Wwu|3hR`D}LJe2Z{OO%LwF75itx_bm2;*V*L_d!<^U`113iZ?AUR2fo{~@G!O7S z8ry*a+L@ya1s~1tUwKIw=9Y$~W4(^vWXYd@p8Pzd41rg5Et!ZFn)0i|BZzsFQS{Ma z45FpX$A2OpdxJDya+vhWuRX!EMr)~=G60EB#(9=Cm{yUH#1~9tH^>Jf<0R6m#c}G< zi(K*ezx7%l*|KrLE}7Nbi?ghND_o~9`pZ1q-*}Q*Q`{_{6rWZ;i3So3-$FI8e&&NC zWaY{pZS>)b>-mE2`c_1^jB#|!C|63e+q*hQFKyk1RQ#UTkJI!M6}>*G=VmpY(8bq{tn;^1f`?7^Zc-BLmxn4n zI7ms3JW&2@wCq%Iun#b{=0FF4fUU|6)~D`fAdrMDf-%qb7}(_}O-Q%nk`;V~i0&E` znTDr*@a5IOZ9_&vz`~lLmNpX8``JG1kxEJD;}0!4K|3<0TVqBa%r23*zlrBZWH4U0 z5PQ(DoTHN$fb7YEFYgjdU<)3`W~2TCFZR=#A)q&Z+nJ$iP35--s`>pS@B(Z1_+$t{8(iqnGXFSA(Eez$U z(rAcMIv(%#M&j7W?q4q*k#Rn$E zuip+NtT*wwH#{;4u5GD8u}hZ<6@&20Q`j4GxWAW}!MyTY;KIYKaj~9lLj|ADb-{w> zXQV5^!qH%Z=(nxMKm85L9tLs3cFQNel6fR6KmK|2x@yy>gzqqyx%l2?3(eDsLCocG zdslQ2dcLqbO%Nc`$|v^)KCTKql8YQ&?l90WQGtlNjj$*dWc`kau){M=;cMhq|fFjQ_6$TE)+((=L zN}9jU#9gO~MwryIRsj`Atd^e}?`()lD^;B%s>2xr9u$3Ux0maqBQ-M>|74?_%Xg7K z!Rj9hvpde``3walaYgh+!5Q07qw5!{qQ@py4<7ToKiaHbesEVf#mwc)!Ha{sUwaYR zYil{4w$X?jszTm52%aZddax+>6ZVji-I*L2fukc8YS$2F;Fp7qW|#QMx9#UKh&WC@ z@b|j|WKkGzxI%6W_|)$N(vBy^<2S&=M}T&+nZ~}8nxXRO<)lH7nb=UnCA)@o7GYXG zo3mta!~WY5Dh@By(QrLSG!7x6di% zS9=>}2G(da?F-j0X5}QM<)9<2P^&l*D$0iYCMgnRBFhgP;FHiQ{{xc#7njIn&F46G z?iOCDCSZ+j2-Bt2p^J`aBdnQ2?1U{L4m?WeF)8Z<2czjUtR`T$m;{Z_29g z>0R-hEnP?RcHD}C;UCvlJW`!Q#=eH%5m;&(#~y)~Xxx)!XmTP*e;VXL8x+aO(;`p| z^Y7W=lRA)%A&Qg4Ci82P=5l54I9(e#7KD~f&prgcc-_0=Y$*(6kGR#%a+Hj=nMsHH z{nStbI?Mq~mcO0m3g4GMOW%!sg=~(F zHo*;$bSAPDVg*dJd-V~f&<4;QrUGPQ6G10(WzW(3hbT`A_0#Y>R2$q%MZMcYywII% z>aI2%Lsu?S5d6~Z&+thwjJ}cHCua1T#4KIVsE)J)J~nf3t4Di|CU2=n)FGexBvJ*U zcqjy-l@EC24Xf1KX1_uW^(#D5hrp2oIs)xY*_=Xl}7sic0DaxuVQ;Vj(H8jl6{ ztl@;=7&sO8d1Gy79NJS|g5yuZoY}H4{hxfL0oDiPGb?VB&s?rXwe~sbb+Sdvx96Mi zf7XvCdY<~>#8qEs6=adRIh)T#cly&iVqloGZYgq2DE$sBY(0R;w#HyO5m{Xi|j`ryzeJhFvObXi}zQ$^dkUa z8-=*j7t{_XJ~$Hv+WXY=obm2O&HfejylNDi~KEqaO>WLW#z~4D&S_4?L?|I7O zd9bOA>y97h8sWz}k$zJxC8agx00PU z=&q>}m9ckFl0H+8hHU7@QXQTDL?Q5QW~dH6U!?M-P2yvDhHyR=*S$jlFb&0tEg}In&YcQjdt18>ST2pa1*s+G_eQ z$i_(cvP~<#>q^Bp?-6%4Xz=QHw?E&1dQfBsGqE1{N7)PW@SLg91&af=IdJ<2o23%I z=B3MHDwg?zEY+b7?2pWuog5RCD;Ts$p6L=wk|sWaAE$aA+6Z*uB?%5v$opCbw9)s| zLe|cu36WL79#gea+kAOY86xuP@wbA8`P>mQkI<_463)vU;mhz}ev%wYe9GJV8DG zsI*WsdD7gNyjS4W75N&vocg7{z5xOXo$IkwyV2@+8uJ0z_5FJ|yr3t0HolQ8DNX*! z@UtBrYSwpRoJm))>Ui-&I|GfHtg}9}+AglmSHBzP+5p0(>?gKNG`pAQ!o9wA#@CUV?kk=n|xk;NAC7^On%cCA6GUg(8h74Mx zmW0D{fTc@BUs1k3M=8z#svN%Ei)~)D$!SRh)g|_VkdkQiW;lkt?N}oDiND=P-Idjx zkXC>GUNXXJwB{;*6!`ng08u+T37|1I=G#2R0wvra0A!Sc!<9r=?}l{$d_EW{5PB5< zwUrHoXWjP(om^Xc&*V*LNj~HwO;dHpPQq`eu13BY+nHVMI=pjOlsk;VH~8AK#p3E# z1Ayw~&8+%!P<)FVQz)NqdGfTyNTcPU!_)~5lQhDRYkp zC_%1KG3Srg*YlBCiN@6Rz58(IAeQR&A_FooBDOZM83P*b{nB%0neKaT#g$Y7rGmbH zHMCz_Yq+w?u72_rRDz6F4}2GfvaFfx80_zu;fIdvk1$FYLSXCbPQ#V%gzb)_Nq(}y zU3ZOC)Aq>!)bT44i|W`IwFgrG;@_%k*I%D4G6?l|eYRk%UGdM|8h^+cnFz~LymyV5 z5h^5j|4ieG`CvT0^v)hdx>x$4e6v^czfVQlAfgj#Fy_(pxneG?yXsOU8$@^>PX-We zw`wab$am3g+C&Uz4)|>7a*fvwKsEZ&?Ybqt9)qDXf}-cC5E22Loax}F)rj@7O7$(2 z?!By3nfztcBnGSUa1VZ)041(8iYs;m!`C^1Tiyg?|0l^IwgFc*BSY;i+Ru*Uh}%B( zpGlO&;XTgsH^=xdf>7^jmsz*4(_pfM?Wj~cXnBx z$yXh{O^XBq{@qVmy!3{Fe;!W@={=aK2j2UzP5%pMBJj0CeFX*AMz0*|e5> z0wrQ0n97T;j_W9N+s3LX;fTC8`{qy)IZ0K9riL!D!5uE5b9WPVf&!-Q=RVOjTSwBi z;k8~2s=sRnuy~C3mJ|d`StNjPSpD|gN1T; zzn|xTg~NK#smNy7NR@gBtcTMt3~%0kdbzV9%NPq6P)tbZzz0`C{C#mdv%>;Ao>|XF z9T!uW%f{;V^q70#wi`Y&^GyCG4UkW@$`FG>2r$|+R>cng%Ay@aip@1NWmZ1+gcN$V zGh=iq+^Iy7a|>y}@#KfqSDsgM>yr($WF&@~n1*KGhMF{vmm|Fakd5mo!~zM$Gew zn{T}s^aD5dq_;fJQ%))f`$5s3r1`G7tNu9Cv_YzL=G)n86=SkQN(esj_>Q{^f$Q0l zj$sILcM@Rv$kp*t$s4ktEp{iiV&b;eWR+O7^3?$9y^dc_N(V^%wbpl*ZmZW}s~61t zC)3`KlBcpmunVa)|J8NwWr3e`izfB^AQkzeKpWXQY){k@)2p5_!R@8GcPFT#3p_sS zU2P7<-pWbsgYLk%M&LUO#ycYKV59bKe8nkHyyH-9+I^Gtsekp|x9$Vh6x$K2JW4MH z?B97keW}HJL>CBgaJvcIuqZwH&v0t{zp6rmOjcJdt=5#U0gz%O;r5BPbli`~bn-B~x)jPcuX;Qa4p=fVKCY!AcXB)_9R@svcMQ3a+3Qf#anpAW6c zy`hp8b*Np5O#tA*6rhnIK0?8wYULw21)NewAS@DQyw=aryfmQb0zC~6F(8jHAmH%yD&YeYF3g2R$mBpYO8RPkdMs{f+{XJILUCPEi(lE9^uM}al?6z}`_pj_)mbUDDEc^i26 z^#|94ClCxrF#PNB6U=hBSP%DQzhg!rc^sg`bNY4$x@IgCJ_Sk>1Ce0sp47kZzXIY9 z|7!cT`@e6#M>bl%n(^E0X@sPdj`Wk)&2m9A|eG&Uv*S&;NUT2*W&tD|}H=7Wpy5$Op4C z;lrxxFPj050yU58a@~5snJrO;gF|XTcxBFwrycmk?zoNvu6Cu}Gr@DrqBwXLlharC zl1vBO)RIe=mBUAV+QtI_*stF9v3zwjExdyrp!b|Em z^Qi{xZ+SxKi*%CxJR`=belBN2@N*NRaj@ydsNK{UIK2gkP!gwG=z;sfD^oQzTA#La zO5vBp_e3}q=cE4-Kbqa{n-PV-zF=n@csZ2&dJ< zfPr0T)65}Y8PR7?#2yb`jv;P)6TsvSoOqenNdzgKy#1i7h!>dojt|V;PIc}Z;55sXdP=l9(^p|759HpLCBthH#}Aa`oZ`9GAO=*n{lX#bRAm^gh`ld{8~~gycM6iYEUB7zn&$9I}i%`)4W;V0V(Jht>^f zV!k8yO{{Cv1jw`yBk8d85UqHM5mK#FpJ3fnn2WQtrDy9`CEQO68Kxw??(_}4`m&iQ zn>(Hh5S=F6y#FT24V9j|Trq(4`!-UVkr>`Hu!LD=3vz0ks3PQsHSoStgeYXiK=vGzZpKaR8a6rQN!4etGo|kBLTOdJzt8YADqF*68=L zY+4i#i9+9$xs`EF*s$V5G6!#;J-EZDvfDh2F4xfkUa^ny{IpzpCqRC?vPY5~C+HEo zw2A<6CfR4qiAr<&J`>#S`=sNLi@g%rg=i@z|;p+JN}{J+d~3!bwR|1_p_WZ*zFg8JdY2H&$(=>qm|h~`0d88 zWfyZh%%J_j4Dq6hl=rxTCAnU4frH$_ytGsCU*D1mn`Z+sw9>F*#!002LkOF@J|RgG z&VYXmonzYG{uD{CvS4 z2zvgHZG^kGrEZme_YMX^>Jp5Ekly?SG)UqM2$JF;2kQZuO3HlZJBAWt5XB?QAtk6p z;PZBUYmLv}O4#vA`t8Ta9W!j|LYfuO*R{kX~Gkj&k=x{OR zgyuxc7eyW4QKwM~Y;XaJ4k9|Rj;;=@E%@FF)P+@9Wx#6|HcbPs9Er>v%et4vJrx)Y z3O+mlAgaHtAg>Nf|0Z2za?+B6+hfpony5lDAE$d(o?L1}N0%V|tJR#e1J<;%&1W}W z4sdoDCj#!=VGrjHHMfK~!Aastb2s_g)o|qjTPwpxh%bS!912Ze_R1@tsT?0hUX>l= z0g~f3qq>IyyT|fEsc3UU%%e9f@6tYuSbu!PUgly3^o}%#>ptxjwWfP1pM1AwR0`_Q z%ul*q5UsD$nLPe0@(4Nfp56?GD!KCH8Cq7Ut-*bUr}KB^_liJCg=aP&2w@$IA|4wz z09gyWU?8N!5TMlMU;(rK)zk;6jObF@{cH>4aH;$*7AvDf@#!;Um?R*(8&!b z5TAj!VC4&7_>dCm<;$(+T{TeoPk0>2{Bi?uVfbTXN!yb(S#~8f2){1p713Ty*{jc_ zRf2HseOZT8+!fPXa&@%N3i994vCh!EtP(;}!4)kKE%-$Ir&(6wqjxugE|6~v?;rNi z^h=ZRn^;Nzm0U~}M7eO*=BYA-tWFv8ZnP1qe?Ete!mwVw)ZOGc|2qNyR1{vBFqdt9 zt8xG7xKiWPD||`~g42zB1A?)^}Kb zHZN&k&5<=QopZ~J#!ma`OZ1?J|EfUB-SQyjl4>N4fd(x7L!Tv?k{Xl|Zi zj!2NPdK#Lr$aN7wpAeRyx5Er=tJ$^W!M|(Z|tTlIzdC>lf3BIlUt5Nq<^Tm~-|%FF_W;5qeHfl!yrS z9V6$z>|&Do^kuvZw?FH)k}b0zXk(QJeS<=)fX#LP&{-( zR1mXZ<8?!2fYl{@0Ezi8RS2-g=bTa3d*Q&5p}B_RA`OEM>K{D%u@0Na==gQGyV{eE z-kFU(OR^Kv7pt2ORs?Lq@qv7IXi2vKqKf33 zR~4e`{tcY0mG_o&UQI&*yPiUi5dRcXr0|&)XZQi&;?5gVlgjsGONiCF!slVgk!>pJ ztZJM|yhmK~(d5AOK36q1cB9m~^hW}b?T;y(@{Wy2Pli96zt0DS-1xLeo%g87+w+(p z>nEs|=n}0MPb;Eh_?gkGvf)rv3^I(x!*_Q~yK^$LoJi7p0jnH_?F3AMe?u6qKfACz zxBXJe>2EQe*q$tu`?_BD9)1(HV@WigmKpH)8qa8vN?apP0c^wh78>C_RjVEiq^C_M ziLc~F=qyRnDrNWFk00VNCHidqC;&lO-YJo^ilZH&&-2-nnG7s%+mw0h_s~!K*O8R3 zdXceMp|+2$u<*a4dybOy{rsWgc1HcLhxIs2qQ3&MoFc#~p7=ka}> zSXC^xPkO?8?qUqhJM_C!S!&(m8G3Jwc`Rc0Lv(=16$e0NUMq zg&0AcMq)4ca){?MH15c7r++038WzbRm^di@BInT7Q-|RVTyl#F$ zN#cH-@iNC$)^ouQ!q6}$)J3U?09q+e;jv%7R-)S-Tg~Fv-s)g$Za{wkkBTK+0U;hs zJXGJte6PM&iTX!8$oZr`sB{db{2cefDoJ1AZ*D#m-oYZdmG{q?_rL4IK4v0^_kBK= z-j#xDpZt3e8`$7C&CK}3T!m8lU>~eN6kQ*41SgS%V5hKZw=j)Y0#FP)dY2(Th|uUH z*sKv>v8vZVEx?Sto1+TzzFaFnv5g#17WrL9fQ9+6OXt`vpdPYF5qWs`#godJitEns zqdqueW_c6LUNyQ!6e)bV(zIh${I@c-qB98Qqq!2VR${EvJCyR!=6RF<@y{hl_Qyl2 zRdh>gWyr&rj-TmBVa~l0g-EWuk#WqPgx0ure2V|klh;4=KQV%yBZ<&=`Hd`3vbOwb zM`EK7C~{MW#PqMwf&TJ@9#J1^mA=^L?)=LLp?z4} zz^fRs$dnB19)LxSBwkz09b)2&L~W|Jf5_!{@4+(syl>;jtxMRO)@!;>_C* zf|Li*srkh>E${4jGP6<;xw<_rokHRO<7G2pVd?P#keF5p9sPK4xZ#+U7-rMwnLkG= zQp}}lGrZ!*cZq-z186@_t{%;RgXMksAD(?aQ)6-CqZ=`L_M!Oh1Io|y@hP=8=Z;nE6WMYM!8hA-?f{1$b8cd%+$!rUIY(C?#tyd?@}8%cbPu%fuV zHmJ?qK(RGCn^1^sz0*lppm$UUzNT_2bypgib!{*TbgoE-8kMliGrE|*OR;L`nD~#8B-YU(wWNs_(+5Un**Ep zff5*To$NlVS%x59R8Luue(S12jXGt_L*fDL?dgaseG8>+IdO-~L@F|zkWY>U^Dh1x z0rk7Qi)kd!8?2c~1Fy)kWslqI^)fQSdt)j@1z`Z2M)M41OCzTRx}ZKg!ot(XDZH5;arI>LD3nB^1q++cv|OT~`i z8ZoAX%GydeBvt!>ee56IT-VRx%(otrPQUJ(00XuH?IE}$Y?tClldCSub+=SuqEB+D zkt!~vrgb*u#_nbS1i$a3D{OkQhQ9C*_ovEATl&}ISmP<2KAlQ_-Grxw;okhm`w5qK z$_!LEkAFQ2I`dNsF(z*}iya2}T2Gyy!JHg6a?(VNYQ-;G6|4Wf_7F}vyw!Qmqj_bZ z4>QdG;vN z=^|&NU-I7b*sajdJc@(!q=!6FXSTadlX49Q)nc-2%~l9^p=1bvHRosomH4qXkdb@k zwK%z;z?zgB&4?-P8#|sLzsT z%{Y;tU%0KwHCb3~$ktLakPPO$8i3d~dkjW@-}c&{roA_Xy008E#BLYgH~|6E5d|T5 z1-=~Mav%F2rjId+NmKW#&3}4tNTnvK&2WU!&Nh^Zcj&P(k)yJceJO~@ zoS%KO6uItbmOcCzhD!{lYhWV4@#fZO*oy7o-8*q#kz1lxvw;y#OF@^7UpH9N5Gr9D zYX;BMkr2>|+2vZuzwSUhgC&IIbE^sZG9UEj@$y~S&z<4_c`&!!@pbI=$YmMMAVTzP z!hhUsnCf~c_FROUC;_J{ehp==1oXfm^pPqb?6%TBxJWN{YB}-$xNgnc47!yy?)4~9 zW6^M%8DbP(-}y*_8Fcpo(^}Ga9~-mB)pA8)~?JOV4olI{h0(@B+Q$xC5d~le-8b& zY#`>{j%RNi=Y+3Q8JeK8lqc~AWDpn6ABE0bo)xBW^l5+iByDp*_AG z{a+ch7yxnh2-*Dy0ou!wH}(i)Tdy_C+LlrjNC}H6oR&W~t|{>)!iqZ@y6F z{Z9uEMXfon-58Px??G!D5oo{xn_qE58U8r<{UL@3iFJ7md=6aaM45`lyZE<6eG8P0 zM+Mung>esC$yKLmsfO4+x7~jV3cjMTb@*iwBQd_KiT~bVMD7G_Fp-i#3Ag3VvwvgJ zeDa^SDwA}O33bLZdDOqk{PT2>}^ZuiwC z;D=h{g{AxG60UoTEx_=y8X}RY`67bD=rAHwZ~`vs`Cl9+)W^D#c=^|MK^l0IzPS41 z>RH|V-K#!>g^OjYfWDh6G?-KFP~=n8*#jfad4nU}&x-_VP)ifu|NZ2NXLv%`xe)Rm zaN2*^Is&#*_a^vh`05^UOnY*g&NH5O**!7oW}4H9xfyUZnHgZ~0K+~v_b!(td%2#s zA|rICEg_#ru(Op_*H7m-p+vt=$fN zl0Qxne}1|j#4)x@(su-^ZXsUZ&0`U>#&wsB4sdxCkP>pfg9q8I)PzY^z-%`J?NJ5B#wAUF*E2Sh8%o4VuZNg zhn+rNdZLtMTj=$|uiVd*tJpT=#8*~vliD`09q3=`vI~SPiE2whwhMl##D7H+MK?>c z9qx91xPZQD#cTSpLwZk5pbp&Wau1%yZ&}IM+_TuhJ}t1BDZ>aUr;y5D*_dLM_>Nhu zW{83uG!i$muzqsesr7=fVVV|SlyYf&jCFxqiSH+5-I=A@KglOh93TnIQ06WWwkHLi z`0(;_E#OI;>y-BS` zRm|I);;aH=hTh%rn;-wey*2XFe+YF-UJX&cX5d(H!3o{=vw*t1xcbYe_}x`48RXm( z2qznisI9=Rd#nlMm0S%6sVZoNE5d{J7WmoU2tT+%aICh?!;F{08 zghazF>D0pG24#JQ)Ma6K)cNP>Qr8}e3zM4XO&dkAwC6^+Tqz0GK((Yks9PR52Y)ee zaK?{9Fh z1OzF{6Z6zi=_B4F_4tM&(p6ufcX59*0K|pS-EFRos`0#BxB7L5LxZ5_UPTdAX^u+4 zk$9hZ+`{9j{Wzi@62z>L9lE~Nu3YmmKinE@mFXWlux76q1Ml#$2J zy~IT%@vm!(DmvUe<1z?0uks9UEt46=ExfsnMMi5nUL=8;h@pbhLh_fZRqa!_-VAAd zZ4kcH@p+K$r|y5suWeCLiF|VN$gz@cGdn9NDaOHVBs;=*wIW}drsdk;6KY3lo`2{AI5+U$BDWJUFm)aqj6;(x(Lbi7|Yf6yphgBoS@~ z@&3jP+jYo3-s7Jh6Ll86nw__T=~6!L{6`!G;#on#%J<>gaa>pc!8nirBEEOvD83b2DkFGe}n&vL_Vt7~BYWb7J?oTY5-bIK) zp$Wj)JV^Tv$30cGG-B}zio@Xc`g9iODv@tv5F<*T9f*EXNsILj(&5p#`)vj&LmKE@ zJYK=(vAM@6xoIfSeNoq*%i(xKmjsrk_OgAueO~k`*L~Z7e zG3nQs*XWS(`E4m7!$u$_u$@tYTjlC(IjL@S==w_alVmiyuJ(^(Bk{5D*_u!pd?>(} z^uz1f=n5YEtRF!919q7GvVTZ946bY&zn`pou#&sWCoFn+UqEnf?{`r&uIVIm^~=t0jOnZog6W`^$>?)m1L z2WWq_QHkKRuh>q}4<3bzfY;F?HpDLG%OYwa7>9-nN+Ul$mb z)}d>ObXR{(Il?cG)(n0iFAyZ)9h^xvS4GnJ9BiMuw#9}|PnZ4``H#`sEItn+NY_H$ zMv-g$J)?uqt%56~B=5pwGp^d|uO2)V^?gePPWIHo$*p{ z6+>TaHo3+CrpMqvE_U%n%+Vyhm-mR_ATK2a?1MwQ%*mg=@YteVRT%l&W=yGK4z;hMYLiI-d7jH45`uo~Q7q7}y zfK7gF5dWbfX3pw)gOG;zXTO37mt-de`NkO^)!O{6<{4L)>i%1|53+~T9A(i`akJ^c zVFDALp43U8v>D_o9SpxwQi_`DP?%B&Ku-1){GRrlX=HAikQD)Me2ovR&?D%ca(EBy zc=&6#_LtuIsY!%%sA6fY@p~ziWhoQ=OCt;>AmG}gWuKyRHw+T%Zbbhx{2bgE2x;5! zB)Z951iOh|T-)vNQ3|j7e*I<$-p-u(XT(}{B8#*cX%1cNXeg+HS=?>T`tI0~hTw>N zhzHIt z-wJuuWFu!DV+jd3l5|wjKaQ|98RQ;JOz;H4ncj#z+^U` zrh{^b3RJ;17r6k%*gQr2UScJ8CD{Z1z(^5DtkdW}FR`S0=iBIWdp-)hfq8OYqaLfU z1j)d>Q8r|9uSww}e2xa&1zfFBm|-k`-&=jWhFe5At#mxI%{ zxjnzZQw#Kz8CyxCor{W>(GN?%*p)0Xv_PMTs$O2ZtL9|Ug4sOdsva*IZz%yyz6G$* z;-;YwJo=@9yjDSv?qfC`PdR~rF{7Wd);QPDwHYZ!7!Y7Gm~U! zPTv^s34I*{I?#&xv?sFNk?XNy@n%dg#LZ~za)Xn18G{%qTRd_Op)?D{3rivId@I6w zWO>o~SO{H*=eR5;{Z(3$xo3UK!SZcP9P99=JicQ3&^^Dw^?L%;Fj+G>Xe>|_dx)<~~ZxS{*H1P97@Za9mlfgC*wjU)~yV?`)M#>TrI1Q(tWCw*OwNV6^i5qdA5vX?j-LrqYfo7yX$8s?i zB&WcgzHzMi`pM*atDU{M*6tg4=^GUi0(f9>GJ;sxPN-fqYe^WAM3x@MzT=A*ViVp~YzR!-_9svJmMlBU;YuI& zB7T*I{Ix8mee5wL*+JO8dUtdMBbwX!t(~x2fO~qFx(8f*9Neeg4#bHB=YUKSmdzEziS6~iVSC^u(*farDs5R(tY^Xw6_y%; z^E>>!^z6x7;=2R?S(xHg#>*bjZ>y12AMNW>=vUWb> z{bfD^cEU>vj`kl$t;6MidWc4%E?U$wc+7wgbwC7g>^gFH1o2o@d(9PE>al6T6J;pAt)TKLm zG5w}$NZ@v)%JyIY?_6iiObOg2t$}0#g|R3~p0~x^h4LjU-918XT5Vz;XmRa@&Ycu3 z)(0M;zK)$F*|@oUcs1eSgQp#Fq&9Ykc^C_x)1XTA82F*U+S-Oo?Gl)RDsMpc70trd zg3{VgqdG=0Xlem!%O1q5_Fj|y<8stHbqkYdB(dUj%{tB8qLLJj^v^mPDp^~H?Yw_~ zkM}I-*RTA&g+nbnt+uww4yo;%)&wz0L)F6@1q$e>4xDKg-+Bjx9RRI7H`SOGIGhxG zD$V_3JanT!yi%WTyM-NfD8m|uru{+MME}-aT@wny`_(~~bd+yN1DR4@833DS?Yqm-|<5+gF7u)C>4f?f}&Xc{@vbRpcB?YG2!*^m1M)UieMh zw~N)&APr53HF6MxBukt?E$KQC zB6A}^=jseIY#R|bC#fB9q)U-tfj;U+X^&&GiiY3hT${ym`!k$>pSFA(8+*`kFHK2q zAzFTtdV4^C+7<0JROnyM>u0C_Dqx*`=y-KKDM-PGzwiTFX!XdJu=tEBfkT!=(Tl@2 zz!_e0q8m8?nYo!t_k9D{N*svv7bn9Y-9Y^K|9x=S6m#G$rc(wM0aXw+(%A(J6C`6S z+jY@&Q3v8v$9>(}aL&d)Mz+jc8?^qi8FJ|+3TS_^d-=vx zKFR8FKAp!#ex_PL&W?_3Fw~_S;9jSiqaVR=65uVF2ImC3+dre!&uGe7NGn>-_jI%g zj1)1_#*OVA*!_CK(Ido zaR)cL>XJ5VK%w3MpW!cuVY9{^!l)JzJDwr6Wt#I@(nF-1rw-P0a_b2_`=<8rYuS%R zn@fUwb*pJhgylPNKPBuoI=lT3=wNYD@S8PXU>Ng(7z5dny=~6v-k$-tPIftYNyJ>U z?xgCCsQddaz=^zurlg+=_-(qqp4(*B$J19*IALzYuZaQ`@11i_r(kQ$$XLPN?V5ul ztIh)9K-#Qb2YiJJQQ=e?GR;ixB86K%-GlKjt=0`kRqn(XMeM=VLhc}^&#Nrh!uS!Z z%=x8p;9w~NqLaz$`v-5wrJWwMoZfd%!M#ExN&m;a5sYxy|6BkR&5lBpR{mTh@@O&V_ar;XKeAZ*~?F4PEGzjal z(F_R1QT?90Le7%LUCR^%S*B;lk?&Xf}{r(5{mwO-Y zdtT=}pA~+SSKH!J@e;dPI{T-7&!;Mo) zhWCtZ*wr{k8#RuE|LSgxnf`TL;vhKSL}Fe|-fQT_#Hv^@r}wor1OAm;t{17?V|QkK!+JqCehFni7@_sOh_S3HiwgNHRV6>J%EwIQdXB>rIBo^_yCT zUx(?^>NTtUQtkCi*6#=vlTx4KDH0{p%lDMb9ehT3K$6PS-39q>{<>NR zm;Q?W6vAX|ck2|BQDgYMp<*klK(QoAYGrbq4=m$~a^5f-DqP;d0LZwv)>vdBEqUwF z?B35U0^_!80O1I<#q$a!MkU*&>y`J=Xe70qdF45 zLGzB#Blk3N57~M-L{F*;N60obdO(5`~06DL?qHL$^kx= zZ&>@B(*8Qimsl>B)(;P+#*q84%;u=Ek}`aI!aucI3mFLhzspI#YoT0@i0}~-nO3_E zDiu&ZT^j5Nw_7~R0Uc8X{;+!2{NSTvIC|ETwaxem?A9u;`||VXmc*7E#)F&*ATbHv zj?(kR-LL>|!!}D=?QFPEMFY&xYl<>o-kl9bfhoN-f55_9j3*M>KMa%&U+A6Q==?T8*J;%dbIRf-;pYA&M@X;-D*1i z7wouNogBnKFJa&IvY1vA|Np5K0%Y}@FW<8GM&%{p(haA776W?f?_Mv${1}+&Q zwqiY{_>6{XZd(sSnX*69BnIb?zu+cD?|-WnbeUiUiP=Cb7RpQ7%e7+5?s6eMIPGjU zMc(O&B1N##BW-b~)1~Ec+1X2sfFAAk)10mHJw|})SYZD6SK$eyt{$9OJ5RosaMzLJ z@qN0pgrW5!b4zH;U{o#0Oxkph2JD)ao%=C$+BD)s}q-aJI zRv_?_7i8^a!G8}&9D*%hrhKzbbt~5$gZ}tty!?XPp?@Ohg+sdgud6Z$evIBSgEkXT zFr1qTb2_M+kCX*=cE4qSxQO0Am%3QRI=FZmSq1WSmxnWwXg9UZ0pewPh_EQq!vT$B zr>S6+p;SF961n^rFJk%>Kj-21{K4c)iIG$o^~lR*fyyIkfmj4G*VJ3y?UlA;T)-*a zp=(PXBLDCBos+S9)o-U49|Q;`3cK>Etz7xJ!nSU!y1itzR) zcpaG+%B%9lU;Vz;WQ^FyHr(GW*FsyJg463D9G~_TC+so+tAqkWkS-!KHj40C#{`l* z@5g&wi85gFTWcxhtDn3UdjRJ}c5X`dE&Yc1j-vS8=yex>-1SUo&?YGzuD55o#H zqu;vsdRpMw`G`-_89A+FfdAZcJ#8dhXy?z`q?WOEW2f^zGR>T^p?i$2tA|TIzp;O|ZwINSoEoHpO z^E$(+rz@ycjUiyXPQaOd?C_wNPj;M@oP$EzWCn~|6`|sxu74>Hp}A~W7KefshCT8b zZY3YJ-}z8ieFhH&N5sk1=sqV?ZB@rFo&V9j>vNdAyGs^Q74Y-L^v3&7USa)(Vqo1c z*5zUw$Za=yStsg^)izn$fK4x%YT71W=E>mxKY;sf4vwrkY(SY|Fjp_e{IVOMcoOc4 zBYBhHpj_^?LjFoa*>utBiIsMyQ@V}ACt~Wz&p*Z=u2;$4=%K9uhU=K}T6fqD3qnt6 z_Ex4S8z@F5T&vv?+}y$Pn2+97bMc2P!)8rU9w8Cxm-=O^ca2HiO^SPZ^kHQ^N3RZ3 zn+W1i7W+E(TVr>>r?uQoQ+&+)4>A`&%0+8##oi0TZ_aEC^L|Y{j6LF*@&GQ_?5jab zrX%chQIWK&3O!ckoBz6*12;xW2*!MMe)utN14?lyz_flV^mn2PeyuvTZ{Pz~mkkIT zr1h;iH3P;wql4n|Ul-NJdh5LF(CquRW$szN&1zH7&!q73bRHo4>4p z_O*+feaIKIZv$l?2Gf&nBNkyB^&~l@1^Q3dG@yj|SgBE~sQi*olYapT+1;qP(E>bwc?=sSAhQrrN8%ey; zNyxa1bNH2;zzrQCM0=>y?ZDv?KUsMKm%@$IezQbo_@!-LrzN8t3G=a3T@0a zB$-^g`m+gnEBCoI_3mL7Ge;chmf}$BJqKzRDc}&e3`-1tvp#zpbex7`E>-kQ&?V5D zkWlr)w}l|sG0r8O`?1v#OT6>NiuRwlNoE}v9m?EtsD539S1<-JyAHOvGW(MOqtivR zUB4Q;sFYMLIFAKT=UC1#c(OsEMdN4}N(^Zq&Z8jZFUuikG9>Ico@N`*let@10Tl(Y zbC$~O7v0(M5vm4Z+oCkt{#_J(M)qFM`u(zL!U213*Zz$$hVRCbb0cVg#W#mI6)wKqz$W>3pn>%45liDw^ETFqD7 z546xl)PqV8>K3nyXIzRANr|LDRv#!*t^i_!J?iea6g7O!@%edv&-;)sX=PAuebbj` zqEpWYQty;ciJrz*|Kr#seFjl)C~TS#4Ih^8k$!_A#CeVY@@!>jZ)W&*(%Tsr zj}x5JkSy%X3G|Zv3HdEXj6+p>{_qyd{MmjZ&}@cJp*ncyy`D~b>q7W5c~WvGCw9fM zNaFDRu#5~pGjbzF*2{1>A|n}^zn6s)%u+y$fIS8t{yUziuPEmB=+Wsbg3aB z7EG(0D^^&jBrb;}6|ftWg^pzVYVDc%nzm8BlQE}zQ|mCG>KU!47Otu}X*KH-1R`I= z)4z;tRejDuKHRN1*B1fL1VwgZ1>nmmpSO?Uj~`49|M#bIj)$#W9C*c>`Gehk?07k3 z(78ie-MDA#y(o2*M|;+BX}7$By<(i*_Xa##+seuG+HG=eH~@&fcYSN5-FIlu17Y*E z2_$t8*(BR_X4rhuvp+MTs9+YP{dyvo@iNGa-Mj0JtCoB-U%~-nIqt-xB?*}=> z!Q#P-xyS<}D9beLe4L>Zi=$P4<WAFo; z1Ik5R)Fjxf^$CpT&ueiU_YIUm`pf}vDZx(8A?rVxK4=Z%cKEL`0Jb!>PqtJYjIaDU zKhpWjZNCpjXWg}=86)5t8vLDqA>N$7%Sv93V{7^s47ba;MVFoI!dtYzOY4lLLHraP z{Y=_C2O5OG>}6~fQ);n(y!*!8gOq}HM&!ixtpb$Ui+17W2$zX+P@)YbqD7#Z7Uli@ zrBaXv_3QPT8-_iLxvgY&SSEYQfAa%5S=n{6$~%?4+)tzrzwZw zT9oli5B}_tx8nw}EAYME$%7l6^~*guhP7_*+|&J@9zd?Oovw*1$7qxG=RtGV6y%}b6qBb!V$-MA|P^@|a`8a$7bdCBCyi!vY_bmgYLMRl- zC%-38_HuR~B;;GTrED8rcYHy6*lTVa5=s}rBqW=k4$G%54}G`g`D$(!UGVeLts>`b zX&YhX&u!-8X@r_$1o}hKG^WKrW+{s6UTu_zk{_)}+9&ZZBNJcpnF>HJ+NF+zPVTLe zC`gtFHJvxE2sR`!ej2t$xyiSg@JRH|BE{jX_t8Q(xkFmFyo|;i9QMH#1m1AM)~i*d zTIk_OMO#hM`sjLjqTltyON}R#ZZvArA>`cua+RDPrn%e+5=P(<;Ah-3Vz4Lp4N&LH zxFthC3Pd#R>3@5}O64(uVZdIEBcGWk?Am*;&Z*F>usHRkvBd0*jQpX1?*)E^vjYY= zYkft|Zv{4_FmNj5&HkCEYsu$5J_r{A>k~PO_(1dJ=7$%DC%FOgM1$sU>8Zo<+Fu~p z*Q=UeemyYo&W}*W8z@1xM?C8KxauaW<-h`Pe60YT8g1atirF9wY4CVa97`{%{wv=; z+1u@n&6OWdOYmOgoto`9nd0RuKd&>1RD4LX^hNVT`OKcfM`ZyXMh-4fLu=X}QIxi>8fhws)z>zwT2V&}Dp=ov zjwy#+!j2DK(OvKeb9YW=MOyD` zHn>&8`!8^(u#|n@{FCd6DQuAQf@-&t->L#BaUzQUxV@5`cr*+w1yMhf)*=x zoV}dHfw3C!V@7Bp$F7vZWsJ)HjZfH!C*S(Kb*aS}>Lp!YXOK!kJ0i_y`faDq(0{xD z2nKPgCy!f>tS;~fHvM>m#5OGT3{UYbx{Fk>IQ7+)$Du0qsu}JQUG(tfXy{piOu5-Z zkz?7d-zLm-Kx4tYk?-DXIZ15C5PGD`+vJw90ZrWZxLXgDeIEVWy`@oi_L45W?ta$< zBh=UUHB$jU0?W}v{okg+(3ZlKg*x%X zHC`?fE9u5v?B)a`JCmh5_IysX;t>_gig{wKP81wYO9{SBx$nUv9T}2xaDa9k!ka?4 z&DbUi4gv@;bRiJWVL>8jdxUYU;8Pfn1~cVN`R_?Xi*sJGfqsoCbiK(uHypUK1>z!A zzcac|az+3kG3G|YIh~iHUwuMQs#il7Q@XDR(`(c~9Ou#QwU7A)c>#D{mj$BI^UsQB z7xL;e-g|u2fw^<$3=5!k}S?Xg7AhdpF^JUM^F zOR=@eQ?P3G^fD@hAATp$c>}y|;(kFo=|N_TZQM!K*wUvt|5;ABU))UOa{#8T8=p!D_~U8%ME>V2Irm^m$HnxvYMmNC$e1*MOmbXBYvJt*bW`1 zZl%R~Z_QFf%3Y7re)wrsQgiulGeY6N<00;VjPvB;e+PpC|KLiUb1}b z`5L?bC0VV^IW?ALoblV0#V?F57jW(KJ=;y%-;bb&k6> z!0N^Gqu>83e#7WZ`$k6l-^*%8ft&a@uz!c;G_D;OsdUPuZW_44LXBQ__Q(5^QL|z` zWp=nMwRRArI5a*G1PRzqnKU?jGy=MOA_knp2fEImd2qC8-M1(B+qU9O?5FO@g~`q@ ziUEPRl!rvLu5hd`=J|ojU?xJ=48cAEcC|Hf09TKV^Gf?R((Vw{{i)&#Swe1@dF_ z8bF7y|FPH!Ep$bKrghtD#m02`dBkvBzdsx(W*XooPL!RJ!_^jDZTs&a*I7Gb9M)hs z+C!(PgGdydXSb=V;dd#1YTSeYb~XavtesuF`G()j_UAli_Q-qbh5glUxc|&{6hQ3r ziu39m5)Z6t@7`?stYxs<7WY~pqtLi#@IPZcv(q0}=kfO9b4hyKeyJRERpi3jWuj3Nkcbl$TzOQTl|+a_wH&*%phVtk^V1ad--#iLN77V8e-0e?YT^! zf-HP+q75i=@h@uR7aS)VE_}KBaxahk+X!O%uYwB^P94otejug)@7Z3Smk0BMn*B6v zpMV354hSh?c~e8_r?@Ejo{6}9f-5|!J>mlv-R*u)`J4n;0UmEd++l+HQ;B>mZ~mNFY%`>JuCWKvbnPFLrOAxRE)+Xt}yt4YA&DG`lK z`7y57u`AO?yx_);#vn&)v1!MO&1;9o=l0aOqYy5ZZ z1?$>YqV;%#ds``o!_hVxyXpE4JEWHC@kz#hhZ=;tt3%0+z@_d?|A=NJD&79wGWo%P z(%wYTgS3r(0p#bZS{*x`8XR_0`thirMoGNqs4H`L`5)xT!q;>7s9dL4xF;iAC0TT1 zfP|s#-gv}OAEIj?N;S^BZe_oQ_h$_6gddG{ndaFJ z{3p4o5Z?DIu-fPK8|mU4dE{&pq&$9x}{~okfwzMlJ+Tjnua5nC<(Ge85&_ z`64SI==z}c8cueu@#f|oSyG^N3$Z*1>-~;V3o7|LKNe0MKe6>STsPbFOuZRb!R}zz zcFz@_i*lB(^B|J6rrT@Ya8V-vq)2Z8opKVK%SxV@4qOB$aU7e~1|>Mrq)Wa2dn^4Y zm8tFab)!=tG_x3jYhEmbe+(G`QT}dF#Ib_W=%M`wM5y2}$XWzOR+r=3xSscSDy1VS zDMimsiD~n%qigf;X+yE6@gt_V4=(f55_A4Rmnnmf8;gu<3acYF1ky+6-Zngk4|cA2 zgyChD{@&=f@4)6atG(O8+w0Nk_yQW>Y0+t2cJu`UT%6RxzSLN`UK+No{D8}$MLe%5Z7xd$z7+H zq_va|EGiLjYcUH9xi5511H5|1&kfa(>s0t#1^eMm5GKyaD+bCw4xax^0m9a%1R|Dx zEd1+sv_CkVrIy+^Txtd5L(1wNn=$)c>tu4w8r|#J3dQK0&F{aK#t1+sat2(mH(;1Q z=zOg*e?=Bf-e6@4YPMFKD-$^Q3b89UL9_R&L9YmcuLzdv53gQJm9)qglViHSw&l#z+UO)(6kwwhneyUv$=c z4&H zwY{VMxu?@_;7*V#@Hh=vZCQaooPCl(v||t{?w>40S2k&S{SArw1YqczbymV#lKXp8 zO;TC^Am-wvjQs0`V5sUl1pWa6(N9_h5cXaCl0X|bH7VOGLpBu|aOXcb^mQZ7+-+O+ zWwZi4gZ&cX_w_olH|F?d*Hb|E#Gy?T0);5%b}ajZwBJS>ncnpO_Q~0L=a0qLSy%}6 zKkc>Y?byWMqTL(ATr`x@r>T2un1M1cX%EEnEFjYmBdkmmS(^Cx>j7!31XiitqVsOB znK0ILnxm(VD?VS(^6KJ7L{&UuPOlF8B2Xc6>l@8>FfMw~Uvb2lCe{AqC!Ooh5t5rw z?6#CBZdJhUx)B7p}ImJCvuH2<%YgQ3N zo3;Os4HJxYYtnS|nqq`9$%vK@+m|f!u`nE@_!nRDk6{iE<4Lln_nH_&dUJLNe^ zL;DS3P(xnN@w+W))Rb{=^V2_Wgn*P`Oc{ynf1NPseSdg(lk&Cq$u16Z{C6B}4U>3=a)uaH0tg_D4~#r!ql5;4_VtN_)sb_o6B0(t)Ip)X7Ov6~Dq6e|Fw zpYm&PP(C)k9UHm7pwz`QsMse}gOYyTPDS!=-)-zNft-h!2S@euiZm86!15SCeRqgi zAkLdX*>8Wb!fFq$uU!IE!FYLRwmBJy)UGoQI=ueX`R!K!#1H?To*UY^Ik_oELCR`bWUXv9zn_v)e@D^=;u0Ms9Y|P7MD&>*TsBrGq4f5OL)4i# za<~Qos`b*53M0X?HI$NQ_)#qByNegESw(?*Z%Redvh~ZU7g0#cDI!|kO^U&R=LX*= zTG+}T_B%aW@NOrL+x2`Bh@`rX5OjKM>X*evOD7%q`z6eZQ`95xMZO+mvc%^?7s2=+ z!->Ust<%q(IyNmoj7YCjk~I&ry+cA|ZVL@7r9>(`^UeL`qbxT7^y2LSD}RQfMNO`c z#C=y1FC}eK%I}%m?JBhm3KObP#m0}uF*F}I1WFWN=XPH!e-FF!W+ep-7Dv!#0PjVC zT><#uJsSup`*_0S$2BCogeM{au9gl!9Zx)o1ml%hpa0lQN{4Ix+Vz0K0`Mz6?3avC z>ly^H6DRA1-NqUA$~IB@9Y~D1zN!^nS|QBkxz*K$P5IuM>yqotF(dxh8LY3k$P~GC zJNQa~_+Jv;ALsBCMv{41_o~bJr1kzKu<+UsY#7$3PuDaIX$ljg1TP?&c8dun`b6f+fPmOfc3*voorAuD8!)ALz z9zmE=$M(#ucTl0&f)2S$r7i%;8K-AK7e{pAhX6C}_7JKR!Q>=*E zI>zmtr1{dOf&z64lKZJ(FOABJ;)6a+3FP~I1>%;DVV~|x*b@YHBXHT8xY8#0=_2|4#`FMq=gy>8??~k+8Sri<=(^<)lp~ z(x7CwP&6=LW~EkW(uA;#Ip)W4GFVCdNL+Q3??o6xP~>Ize#cgUbMRg&d~VEgZ>@8D zV(L#8Bhc`&8jhMSpM1rQNcvVm<^fNn(c$ZFC-Z^v6>d@A48ne63-!K&@ezQI0NjcM zIm4fR4GVL52{XdHDj*+Mi0hq&PoJWMUGxj7HFZVAh2mzd*24onvm)(=CwVs;vtHb! z8(Nivy(f5J`3QNSY_l+kQvB7(G}iQ}XWJw{Rh!dbV;UeCP(eyS67`9(AOJmjvm&>$ zlAFXdqog{#Zg&OlxK}*-bZC9|lgrsqFXM(dbfl$&EaITOcg2A1wRA9|>s;nH7B-A;3h7$0;GOCM$ke znTned0rm$g0EK;N zDLIeIf4j~~dU|lsmuP;r(3G|gn)sT}*`Ie{1`H*kkBYZo{Da0SjiJl}@#nQ4HCTB1 z*ev>vS@?e*4;J6$pUL4-F`U>sXSMh%;F!^83$qK*nu*H!Spn#m2K?M`f4VidAc z964PLdw}u+G{J)IihQ#->zC5Cz&0Sm4}6}{*YPi3uh?S!^rTi>QJdLk4=~-7{QmA} z4usypjbj8c)}WgdJTLz({aR44rW)!b=(}?l55%NpA?+XY-4xE%MgFjYyi~y_UIw_H z5f;U*%QgQZ#-w8p;=|WtO{BNd)`}++rUNwaSKbG&Uq?iAq6rm37QfK3Hf8u1>9F_H zlYwaAtw6VV1n%)D_54O9xasz%W13G#^IPnDh4W)$^XK&(Ev6=yoqx86hIr{(YcPjqnS0dIglTK*jWdpr!eLkr;J&p5gns&Hb zc`F#s{4_L?{o>36d(v#65)*xDXY-LoHT7<3=vBza)TTL!wa1d^=By(Cz%w;b;g1@kCc95U9Rn zzI~K%GFGB(eMqj~a2Qcv3U@wx$6heU2BCF-EJyNxnruGA;cvtJbL!tlfVM=#lN{#) z4NK}~@~oVa?IvH+2w=%!tB7+bc0Ee*R-HnwFCL5!!f)jKj##!_aB*J>ygA}LGXF%f zm=XTk={<~2?$JeLLi3HD@^Wr|%hso?!~gVcGA7=`l1|sItgZ>L3yXP8Nc+#4J6iXJ zsWA!cj3s*FHLRd{5VSdvK@CW8t@5YDi$txkKc5|{c6a>2`X01E~3MgRA3_ws31vt+DENJiEr8BW+} zv%`C)s0`sD&%b}}b6{5l48Ko^Zh%fS(lKeqLBrgy2^mt-T+2y*@(<3}+>2{?xG5DM zl;?E3zf_IlZYqD41VTr(;C)6-CQ6#s=#KRpn;D{z{zg3BuOx4NyF|>LU?^S$VXN>- zdX?KJMwNO6QJuj&m!|{tYVcod>XJWAmk%Qd<1UH3e z3yX0ru`B%}3b)_}wFbrGL}5hZ($ThKeV%>Ausf!PTlF-bto&kBN>u&Fn+@jK8Q`Bi zh>v(+Z<>M%m*Z3Mea=a?vKn_$s@RqKUf<~$?;eKRnQ9HnZ0sFa!>-JBuk4G?m90Ps zmS#h0s9c7=;?ab+m&LOS*PfgHK)>ZZrKfM|tgJ*70C&1t$SWOFxaPeaQZiW4^Ka8M zTEJtc2DL{C(F|^j5%Iss5ZM?>WSS1XfMRl7_RwT)BF8rWuaxl8t_;SO<7o*N-Q3X} zfEytr(d6EQpers`Lna?0+fgJ!GyPDmUu?q7{{@3EzvX(I)H{W9kwO+fW++hAtP7$`Y@-OyKm|JCJij8#Te4JE&w3oa+S1`XXN4^!2|7Wsq?~-;?vr=a7N|`_E-FE zEPE&={pK8g?mQ4v2GXJ{W&?+FOUA$Vj_rBh=H_%mg{v8p6!%D*2z3>!G*rJqni7A8z;wiCOhVZt;3!|9xfM-^RWFyi{)#7W_zr{q67dT1+DxI{BvNk%ok zo@Dd!DU`@dQZ}=Lr0kY3d;f{0EX&*+^g&uWFP%PCZJ1PlQ@G**JQmp`#Wh3Tu>ZwN zsXigqr9eOo7g?vBcP8B|Z22-m{hIlvsc-6xW4$@6{Fs z=eX>H3uwH*eUQjtLAm1cgY83?^BG#+@(*~RibD}UXfAp4(F4PvNukrBruIW22l-~v zd>6Bg56qE?YpbrcT%KPP%7Xz%WWjA;2O_ zzy0!a)Wkby1BaVnMdzVNz(TRWN9GO2E%WjB_8W|TxL|G(fjY<^1qm;4#Ci9(1a7}F z$qz(1QUUpOICJ_7R52-pMh6<93VAyj89U9(pc}4&nT?H~c#cy@ECDB_5||$G_#1L` z`{>zqRgXjx2+a!sQehS<8!*+oyt-=ESJU)=Xv_l{H-662Zj_NQfAV`Kmg?J*xPjXB z6ga{9RaE#UMt=Upy$J%3zq4<&r))&V=vd268jsvXDONCeRcq6{4k%0v>&7}vVvY8G zrvWEdqe^V9rEqzoiG%Z|1Rx}OsCtJL^u5-b8f}V4!P8EjDSpd-3-D_i`C4;P4pR7p zt4KrKxV^f#xB5dO!e>_%~x1xshps8f^f6`A1 zTP$J76FV&k@?A=>+lptg7~$S$;Mrzq?RJ+=nzCZ3rZwAtv>S7GQWA2m?tIcvk>WT_{TrDw+JD;PtZ$m!g7EYLiyx-oe z=3)h5oijW@*_^?OEaK!N=h~;WDdL9rviT=0aeU0oy-&fDO_Ol-!vOWFDpK-4KFHR6 z#Z;%K5Gn9ablk@?hF=p6Y7>TYFT~+}PG80Xu(hE6>)zt_H-B~&Q+&dPbeu=0McUr} z$ukJY2TB!Y+&+Ngh*a8R=j(J!rBt=cGIHTVi}xyHn9Iy#=yQj4-)8NxnMl?pP*%%| zCnc?1o9QvN`z4`zQ^r)`jb>JMRUX5=4y=zpl*Uq|TGZ17gu7oSa4_ql=LyWZB&{%i zV0|rDaygdKrEc*zDj6o8^W_nDyQ$uDBgKFd0SXY#{ZTDJ6M9loK!q~=z7T=Hx?dzh zm_#@H2s=}R>?8pu?3l+Ru5X&tVo<_0$cK>>7y$n|x=*F`Dr3SzeP0ZZ z(@N7Pw6(s}73u7Bz4l9;AC5kvUueD~vDG4!vZ5c9r^O)KN zAn0{r2(q$0=p2>DdGg_mOv-IT13Ev9cFsJx*$*fFb%#aw)XnVQbO#S=zy~*MhwY)jvcFvf|jPcZ%$FHf|o0N5lk7(0qZrGNHD?@@na2O-F zV>$x}+&H0tgn%LGbn4O&Iek@S^><|WIsoyx?#{11JnqKlIOm{_w_bl+G$A9IrUsiWgU3vh@d+TIWa}S(L+8$>>$^$Frv*N4q^1ZC^ zTY}4;1P?jawj$Z$KYzu&lub|2mcQ*gAz%sf5FWbJik5d^cI>>!ocPMp->1T>6PXZWh<7+ z%lLTajSwXwY5XvA+tCL28YY&^W7y~kWI-vjbHMYf(i zQ{4-7L=Wk$pbzGoefNMPmn2F+7QS6!lAID!LXO=$+YD6Z#G#1{Aid<-D_a9`xXMx4QI$7Q$r6eMcVaGxt!(Uv8QJcVl(dBX#_m%**6G=*M4z9ptE3%c=4X~fj?BfrFRI7fQ zXC2rX^LVjAySbJh!Ogh|z`L{ky^lH73F*n(7a4ot@Gq$z?+T_d!*d!u0<6YO$dawkN;1(go^0Fo2ffdmob*hx#)5N$(+N_T9 zKm`A&y^7Y+Mr|QqKG?I>KlaGw^6!7jCLx>aKWTfTMZ36kpq6p9jgGvsELP!AB#BF!)?Z6 ziHwYt!-vz0%dgb$6zDmHY>2`K`Y2sLjrfoDlSGkoVWq18JP^@X@DqX4?%`N@)bL*)5)V`W5u-@Ws6>w8h~w@iDAk~=Y&Dj+al}|F=3<~6 zf5izR$#$rhj`sE5YMGAnZt0Qg$#72BOt&JVl(LXYk@G&`kEZussaRJS3pms3_^lua zk}O7D5EdQN=0z1Vsu`En&P$sVZ&Z~ zuik`VN|eO&Db7)6YtB{?Ouh_2NaXCku*)j)jev!p7~a3(Z>g5I~{f4I?|d7 zWt>u6pM}H+J{Mc+8R=B~J%i?J(msew+X@XuD>f-qNv@B;`t{?upw5a#2Q_3xRbIo3 zL&y+sPi#q++PvA&MX2dwTX%6o>s$A%O-J@s&I+TIKDcwY-Si#JpyMnyE+d;ImUVjf z7oV~-0eXpPrfEzl}FPi=k8FEdXH|ARpw5J_+V_9vTtP#b35y z-F`r>nXm_b8S!_)(Z4xgP0`q3MV8oLJ%FFZNS#<$E#k3D%SIzeG&J5gk%ZZ4tbBcc z{S3a+vP(i!LVda6u=R2hX;_g`RLg5w6VX;eBB2!JyhFMNhj+7P^L>PcTAzebQG`=E zIGl~XzW5!1sf_+_>yi_%0bITNZ4#FlEbvKZsM~aq;m+o@z*@iM(bJdOdH0yZ>(|HW z{O{iqMm~`4u4hZ^5zxr>g<)URP_!;*&2~`4QPBNIG!5y~4Y@KHkOxO0^{TyqSZ&ri zh+m`#w!eUO*k2Nl6L4vpAP&X!U^Wf}(}Kz%>@{ge!}^~(-@!m_;;lID43G(S zmMc7-3+4RkO_d4+Gx5f#R-6^Sgg?BWo+#}z_!hmUY6y}~Bb|gE?`~)Ncj*lF zxm~F{8QZkI#ynizt0&GOr3J(}{8!NjeJFxG+nTDl{j&V%&?{!Y}a4 z-k=?%dL%~3X|3!Ujizd0W49PgiW@dx&<&#sMhU;gwznSSmAL~oaagI^4iJ_vZf^ZZ zsR0fNiWz>Db3GTbD&9y4I5pbR11{945~N_e8*j5t?oZva8-QS^LzL=H(f5#6=K}I2 ztzfJQ5;F7qR&6kT+_XISl_s1wWe`W!56|(zm_*%I@9z`)h5E=Nkn#DVYOdSj>~#@xg1do>VbZ3I&YPiX=G zsF3stE0q~1#!aADQwS@(`{X?%sFXa~U?8wU)0t)5N)?%+FT3YI9uz<^C?oak4+>pK zta-`Z!I7VJ6sgs_`A%m877UL*aw2|-BgADd8Ie@6qVTI&um?2X=y#4@YlUDj zNdUPKY@qT<86Qy2H?f){XVWtPDqj4Mk2STiQn>SRX5NzXpVV`uOR2Mv(A9vXiL9gKK&|P}GAM=|0^Aas_|a1xvpUdfwD!d|-FEB;lV|Fpu7>qR}qU$cKyILbUUp>{m5#j-_t zX!@`9!3)7e?1)FmT>xHZZ1KO560#`|moyt<&P5o}n_P8n=y)8xj+z&~H6iw$M+fzA zd(4!_%^U~?;a1v`KQX)tRl2PipwR<5lp}Rh*S7BtkZ4Hwp`uPKg^p9sdqtj zL(-LK9GOj7v+8(m3c*Kv`eXHq{Pw%}K6nY2SLxk3=<2rn;toGa&HB?Xqy0yveNuMd z`0^}zC`rQ*sAA`mNlEUT`BV8wF?3=$Ofh2<1@J--CF9(bjP4w8-39tdO=lK6;Zhtr zc+$o-)Nbzq&C^Or!x( z8A*)EpHX`0UDyRat$#0i{`QqD`Zv;4ix4$&O_J3OxABRpnF~06X=-K{Wc;)(bbR^K zzl}s1h+jIw9~_r}u_}l4+IBC)hNh;9V~$%S)6F;~iUV=&{M4g>9+@bf!G?uf*(^w0 zhGN=>#};(&jw>mE;1q$5z-7^^DCpeZ+tMPPDy!4&pMTmERlA_#U~|M#0S#tZPD$qz z6BrvLt@%(Y1&05;su^M?G7)l&p|KS?6w&Etwkz7{N^7Ti>3scv6`hGc6aF8^UBx#_ zCCa&!tCF))WGh1CsN99g8Oa>EXH#TuIYx+8lB-C`S(|(A$z6`wm}_E(W7Ce`exJYL z^LTtd@AvC?uC}?z!xkmbYed%L7^70p18+^m_q(UM#nKW%-OT>n+Bb+l zSqH8|`QAur+(M-);uX>tGc|kis&JCVLCiFTcIM*wLY%(W#b3b1A(PkVD65)K756nZ zU!1QDD_T(#ojel4xaZ=|lnA2wdcIZqO_-UrL~QZFOjIuJ=a4CWL+<4QMr#Lb=G>r} za}UK&8?CNGz1K^f!ekRokg5?WhAa*EQLe@kU$}BRBle zl~PIZkT17oV7f;I@M%24qOn&T#%ZhjPw0jl$xH3&1x5sALWow&=#7V%$|iVNEQO5p z4LqBiwQ&839J^6njLC@)M&JB)*hQr1dF<4ckKyN~1foa7T)D+A&o$9&94Y+h*=~x@ z%Hks#N{-F*wd0&ON;QE|2u(KiE8yby>4YE5&N$D|BXF_KlYo55o*(+2bx2|I4LB~^ z?5FKhc*p7S1e)v6Uy3V~x&nX&>BuW0ARwK5fJL9vPRPjbRbE|Ra*&*Ts-Ylh8sI^X zr9a8Sjk^6c^+DjZt=6CSeiMAPb}$oR6K{YWK2Q-qOU-;B4YhktnZHXPgXvpBeN^)^5%}xrU_rdc%d33*q;Y20HZM&X0bm zJO(=|)FlC&4kyHGrYO&qQ%GkcSR^c`9UIE@a&8g&rXT?Mm70nBFOpIC4Ila78t!Lrq{E!Q#_v*6R__?`ZP-ZeUz8`VfE{dGtsw#QMg;-0?0H%LxEK6Nt`L@w4?%v%Y=A~fpKd# zF@^&oS2_Jc#&&4l{aSvq-Yq({;}!Vx^8NV;pkgF#kiD8YREuKq*yTFv_#>$uRW=pU zjs6ku^j~5Z2{|^MN+M$%cg{<&9V`Gw60eyyf>9JT0q{M?J44f}8|zzX2BOWQU#jjZ zB|5_0pjSU-kG*~F#e#VC+6^e^FkE`V45_yi3TkvcnDI|#e4*6e*=pr$npT26OV;; zGS?{NSCyn1Zh!e;`expBc6$a~E;o63zh|YEaX{ixwL5FU_#t}BhAE>7bSv29=Dj6t z#O$Y|?9BgL2aqJR{Z~TWnY*W5sv;Rr4=TSMHuwnM;ST5jsN-2%ddJWIu+8{Bk$6S^ z5_Y#~rQQcf)|MCnZ{8HVUtRBU*uDLrdr@Skvl<@YL9;w=DwlVJ#;CqnPrzc2NtsoP zH=GQacFI{CS`dc6i8?w`Z2B3h_r=R=Z7eD8Umwa?I^W0M(72{;AX9NroIOx$J-avr z3D}0M39HmE%>&R&Mc|d$V{B3QMxV$WQPtcb`ZMSJ7MmfF18xNsRAHPfp3b*p7&*Ro zMN}7QMXfURQxwV$TNL>GLRc?+i3~Smjo99t80Ffn=MMKZ?9VnWTd&dYhy66ayIFY) z+=%5P4WG-Q<=}k^1N;BAtI|${GL#rSkb4uTFedDTJp78JN;b}Xy?!$ z_8rsf9Kt?ghHm#EMGY=|eHL8EIYn*925V#!w_+K(KezLZrq>}Svl%M|e_ z+2yZ3ak4Z&d?KjQzauYB0|ef0?|ty<4moc5Tf|7N(zpN9SdDl8@N!qF90VGQ8|yzK zd5hPFE@AOHJZ|{*q-aV$)O3-j2}|31_uf75-w$4bQpzvzCbi4iMtC^7Cn=>Gy!^#G z4^aK8RPL=auT;#@St{gdl%cUWXl^4!VG*@5_VMXn?=@RJ$zl=xNH4wcovlDccc#*8 zb=#*nMKzMh(w=y?!DqN7uR^Wp8S7;63ZEIv+S6(ZO{IQ8DV^D}jwueTTtE$N;LufxV^OO+#+psO~ocX-5I93%G6mctSgcFPGgxBzwLYI5NM1w_~nX{A%- zQ~=hgA4ezp@&>B)N8%dXPMo`!EA+VX8YxrY?LyLm5k|R7Q;J&c%a8+He}}Y*d+7ot z3jm=ZNO5QRf+MK_3&U9h!ZqQu;(&A7wl}{Fe^n91bm|caHnK^A4akvWjmIw- zR>sehuo(GwESIH_SFPuRA`b^K7W5VJZ6cUi4e!X-WiK9hBCHFF|Gk=*bQOK?{Dr{p#W(XqZOk*8qrS>u z=a;5ZQ9DH_5r&de032c*a?-p7T6f`b9elxdonok5a6mu#RJd4)vgSlZ`Td=nHyxP6 z*_#KuQqrJ9kiH}ES)RHw@yeYEJ7g!A+;4LN%5mv9^=Z?Qv+d7V7Q-ABzB_zFrRR$XL;n*&xnB?%ty0QwqX8=6`=H97Add5 zgEhoA+cZXOo_Rr4E#}}EZGF>C2PRo{4Zu~+J1M_6 z+B|+8Jhpp248{tsGq3Y>pI)@V>; zn&kyfS7nZdJPeDd1v%9~SaTIr=2<`o!O@uM!(F0RBCM#=>0R=5Nm;rzvuj5^YidNF zR``BOU+00>{Eb!e!mcB5>#Gp68Od{|L5Z^aqVUT<8SabV_M>tJuJE)WP7dbDL1ONc zVrhMivCHag8PMlW$Tz(z4(CqBszunvuvkSD?%TVrM2XFYhbQI!`?&Yd(^WH7>d)!< z{nN-d#(qJd$V1mT9cFja#ZgNe&LIl$?+Nu#BM8v!;>SfU5iv=uhBI!-aZ>>^(A&U$ zHh&XKymV0>zYo?0R)&CSuY~j#cxv) zI9T@!Jw=tz?c=Szwvt53?o_uPjImq+t2~L48}ewuEXCV%0ZgRBE|^l}vZI2)d7pXt z9%rO;7gnwd%f3oGaOd1+fcc5Zrpv-tC#><20gn{Or+$3Vv9rF|j1_?Aeg#6WO!RUd z>+nUWHMda35L=2@S%G)_nl!mh|FWTrHisA%6RK}J9SMXYVkR`s?l1D*oumUChlgSr z87&u&&8+F6UA5d9`kmOKK4Fxd^77`nwmOcJN2~vKy6J}4bbl4Q!#8;XVdJMp1;!H= zlbbX&P^%=tQ4^8*7-?N+G<}NRJyp>=+Yxm8r}NQ1cdRf-kaajIMtE*W9u%mj1bZCV58=2k zE_ORNGYs`vC#>wgbSV_ZlOPO&UMj~%5e<1LsXu|*=|qfOymXIPRHu7kQn?H?J*Fo6 zmF2{h2I}8NlEo4;4THSQ}dFv3UkI?<)NqdlxK@_#9ti2PrKLi%2 zaO*zEQiWN>(O=fO{uF#=(YIAyJrwNVslH3hQFi<*pKE7?MU1TBV%)U$E=R=V#n_m; z$i7*Vo}QqVOJ&#Mqk0TY7cUxfzg6OyLa*}UQc+A{e2C*w$h}KiFY)>QB#VSZ0wrgG z;>i+3J!SO(9#C%Qsi1E0A@JdR1W^P17T2A|*;3Fq=H1s52*~M|OZ(}ydlZ}ZUZn!` z5F5&xsid-4*m*Dz*lieL8WJg{6>kIlYlr4|@DMluPQzK2;5~`H8=nWtH&5}3OYWSj zXc4BFp+z&`D-p&{s;a*Z=rnB`IFBnk*MjD0FDg4@aQrdWGAYjj9$1Xu#pNiawx%+) z72r+Tv>&Yk$i)z9x(hlQ#QY&iLNk$Yy8Sn(l3m!Q(sqC6`s=g>beQXeXvB+Hbrdoc zyhm8{^D5Oj=PN^d=DrcE*LJDq&uc=fKJI(oYW`r{fJ=>s2MR9uZlp^l4#0C(w0qF<3R$nCK;ldd{ zlP=_V)gQ@d$EF&IRls|+6<}&70V>5YYmGBL32tu#`!&IjD+D-&05g~7bGQ$KOJfDc zz8}HR6%D6Wr-G<6Uwokb@(9NkYE%+;wik0!TSQdQ#MhSg8)WcVvb-kZgMR+EvtTx1 z=rU{5g=y$Us(m=sX>%UkT1^6TY(_HB6u~&HRp5ma;R4gfg9}kWj_h{A;>E+bznO;% z#LOz0{rRc%?ug%?91W~E6kU59#om^aM_;y)&mEXhS=KEZn{TaP?0=ZA`9y2flXk#B zWqmjV&|1>$Z?#XbEEF{V#h&B~BzQm0J!{M5PC!fX(0X_6UZ^IDa#t}F;4Zx5N;GQ` z-sXCBVR*&*N}_rZ$^}e|GWszC51zdRwJF`z9yDVT=^BEni%HT(76@%nv`2lO>kn=a z$tBk=3=Xx|XfnSCEK?Q*b+x^=j#{i?E|>c6NQhvHwRZ`)%&WcK{l0~<6CZL_ zBDeE#$JH3kt2Tpk;HpLYj%ui78J$s@f|>wxB; zV!n?%v@;e4kNmEKwod3BDn)&KN^wls}WE98?}`ogG~W7%*AbR-Xt7jhfh z#SZhfOyVPYs*AqSg?BQvajV2uHQmw_{XMbau*^&<$fJ#GM&Gowk*KWJdT3@}`F$qY zcOShO9^A252-M?~mBO|gXFI1FPtUyP5C={U zr9)lL_vbJvs)8-94qU%-fy3#QN2&nm3n$?cc0y&!gBLDfXy(T+|FG1R`FXi%WAxnH z-aknn@`?cS^&nt4KM}uRBU7;Fgr;uyJwXAIKY9HzOt^lVi;7`_E{&aB;uZgUdwm>}*NAV4eKUxa}N8$*BzCE}DS3MX>>eMm>eeYEy}#QXlt zX#Y-;I-odap3l4-13llvCJ6FP44l!i>s?B~Xxth_72%pV(}+y!p$8nGsyIz>sXE`2 zsbL=P%ssO1GLXRL!nVO7BZ;|V{eENNehua4>#T#1Y}!^B29^U%9z1yvkl#LhMGTZa z&rz0ARdx~F6zstom)bLkc4{6DbXh85}FxVEdkLi z$&Z_E!$W6Nxa})i>;>^%qF}fFbfT6#5720~gTxR{yR|%7m?!hX+T4Sf1Kb1Lvzc>& zfKX6;q)Bgq!#E9#{s2!dhkM7NyedKEh~fb~Y;y2Jx5a?)h*+zb_a6hV*c)x`;Q1#w z3xJ56(Thc9qEygNA%C!{`z+OlzSo;v0G3r3-5A8zt)@26_A}r>sl1)8n1%x_X+x?CwjqDxeM_(>kwQ?t zckV}7=1c^~J^588R}Yp}4M4jApk6l1qYv;FWwW93p6V})%ixtad8WyhYqet~1Gze~ z-tyxnHlIp#r#^oN1g}D_%%=DS%RY)@-3r~NPw+$kWIO+!f&R0I?>bH;3d468s({1B zXr@3jzvZZlCd}va-txmQ#mS?*+%=J;8yQy+ODkHXNTM4f38%IZ)hKKzkGPv^6r~^`$$~7=Cv38mE@XnbOb-2psK<3!<4&L|O{_KdwXGc%4-3eqSPFI>e zbKSrNYy76<*wnj%8JhrK%_RWj$LnccB>%+M*IQ(rY37Dw&lvoZNQ}~|Fkps(^Ouy- zc0*+%G#^z<8yYAdf?f6s@t#^S=KAKrhoZQ5GEN}DC%iOuZX*XDXp}u@u0xsYxW_ouBxwM}`0H_=wyA| zE8)_i>OKbmw$;eho9to8`su9p#>P@i{m>v!HYrMx`by5{s2fgqV%IN2u``G2{;S#} z7(C_JHL#g4!TVKzH-;cqyTWYUbYJYD51;o&OW{neeF^8u{&=>3MOrA~?FdpJV zSYd`@e7yIF=r>t}q62JMgr{OifCEZ+OqL@U0qnPCM~vzAVAWSinbTGsoAj%8aAv*o zuWD3^SdZJGJp`)nD#ZmjSqj)I^?gr($f>AJ$#J))lJ(;mu}!}FFX04CDff;uyZT$@ z44yzaWcc(;REg2B-keS7+|){0hao1Ky6u~P!(lZL$EGcIp3i^I>#mUn%_C6l5a^P! z>!#Rsp#cEt6KG$x)xQV)s9bQ9Udl5Q!j2ysPa78L&HdLqdHuyUL@dr}NJnn_or0#u z)ho3h3FLS-gf8mRizhfvtzM0;@IyPk-^a6h9oP}I+0o=6~N{Rb6BX3y4 z5iV4cW^ZW|en}IQMT+TnetP+OC=>YD9ENf2e>0Cg{8J!oHPOl6dW}=^aM*Unss)1+rbRF+Sba7% zS^dsY{r8^f?G9m8-(u)oUlX_hU>wvBfuHDZcJ$scFzxx_sGe>&>$_MnNuJCsS&yi* z?S#{Ys<=ZKzX4zFL(&!$TFy;eGq<}lHtC1pKHZ{AsJ|Suh|q}G&Hj5`YQ6kg>-TLH z@Kyi8(;^duC=6+%3mPF4l)6`@ir!|39??Zz7I ztV%vhgYW=#7VO2Wemv>Gq}*g@;q;+w3>`V;kYxK;6FPKtq`3YYe^ONz(}&E_>Aq4d zi=*$Z4@FD3K~IDg#yC21E&p50#uK=4t=!6S^zF}6jtF|OY2C#@@z}oC8anXk#M0LC zd+<`)JID$k59QE^GI&PGf^LN=Mk)-?G zAp#plve>m9P|9#iZEcyjfDFB2Y_A!F^9a*j3Pm!I-(LKYNI0 A4*&oF literal 0 HcmV?d00001 diff --git a/entry/src/main/resources/base/media/foreground.png b/entry/src/main/resources/base/media/foreground.png new file mode 100755 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 GIT binary patch literal 12430 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 cSLciz6 + + Public/ic_public_back + + + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_close.svg b/entry/src/main/resources/base/media/ic_close.svg new file mode 100755 index 0000000..e895e81 --- /dev/null +++ b/entry/src/main/resources/base/media/ic_close.svg @@ -0,0 +1,3 @@ + + +_ic \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_delete.svg b/entry/src/main/resources/base/media/ic_delete.svg new file mode 100755 index 0000000..1717615 --- /dev/null +++ b/entry/src/main/resources/base/media/ic_delete.svg @@ -0,0 +1,13 @@ + + + xxxhdpi/ic_delete + + + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_move.svg b/entry/src/main/resources/base/media/ic_move.svg new file mode 100755 index 0000000..578b6e5 --- /dev/null +++ b/entry/src/main/resources/base/media/ic_move.svg @@ -0,0 +1 @@ +ic \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_public_cancel.svg b/entry/src/main/resources/base/media/ic_public_cancel.svg new file mode 100755 index 0000000..7885cdc --- /dev/null +++ b/entry/src/main/resources/base/media/ic_public_cancel.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_cancel + + + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_public_close.svg b/entry/src/main/resources/base/media/ic_public_close.svg new file mode 100755 index 0000000..cc18043 --- /dev/null +++ b/entry/src/main/resources/base/media/ic_public_close.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_close + + + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_settings_arrow.svg b/entry/src/main/resources/base/media/ic_settings_arrow.svg new file mode 100755 index 0000000..ff6293c --- /dev/null +++ b/entry/src/main/resources/base/media/ic_settings_arrow.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + diff --git a/entry/src/main/resources/base/media/icon.png b/entry/src/main/resources/base/media/icon.png new file mode 100755 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 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

c)ijgxDm}8EsRXFt3h@+Ufr7@DN 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 literal 0 HcmV?d00001 diff --git a/entry/src/main/resources/base/profile/backup_config.json b/entry/src/main/resources/base/profile/backup_config.json new file mode 100755 index 0000000..d742c2f --- /dev/null +++ b/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json new file mode 100755 index 0000000..cd3ba33 --- /dev/null +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,12 @@ +{ + "src": [ + "pages/certManagerFa", + "pages/certPwdInput", + "pages/trustedCa", + "pages/cerEvidenceFa", + "pages/requestAuth", + "pages/certInstallFromStorage", + "pages/CertificateInstallPage", + "pages/picker/CertManagerSheetFa" + ] +} diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json new file mode 100755 index 0000000..9e04393 --- /dev/null +++ b/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,436 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "Certificate Manager" + }, + { + "name": "mainability_description", + "value": "ETS_Empty Feature Ability" + }, + { + "name": "version", + "value": "Version " + }, + { + "name": "certificateTab", + "value": "Certificate and Credential" + }, + { + "name": "certificateManage", + "value": "Certificate Manager" + }, + { + "name": "trustedEvidence", + "value": "Trusted Certificate" + }, + { + "name": "userEvidence", + "value": "Credential List" + }, + { + "name": "installInStorageDevice", + "value": "Install From Storage" + }, + { + "name": "installCertificateFailed", + "value": "Install From Storage Failed" + }, + { + "name": "certificatePwdTab", + "value": "Get Certificated" + }, + { + "name": "certificateAliasTab", + "value": "Set Certificated" + }, + { + "name": "certificatePwdInfo", + "value": "Input Password" + }, + { + "name": "certificateAliasInfo", + "value": "Input Certificate Alias" + }, + { + "name": "deleteAllCred", + "value": "Delete All Credentials and Certificates" + }, + { + "name": "deleteAllCredDialogTitle", + "value": "Delete All Credentials and Certificates" + }, + { + "name": "deleteAllCredDialogMessage", + "value": "All credentials and certificates will be deleted. Whether or not to delete?" + }, + { + "name": "deleteAllCredCancel", + "value": "Cancel" + }, + { + "name": "deleteAllCredDelete", + "value": "Delete" + }, + { + "name": "pickCredToAuth", + "value": "Select credential" + }, + { + "name": "requestAuthMsg", + "value": "Application \"%s\" Request a certificate. Selecting a certificate allows the app to use this identity credential for the server. The app has identified the requested service as \"localhost\", You should only grant permission to use certificates to trusted apps." + }, + { + "name": "requestAuthCancel", + "value": "Reject" + }, + { + "name": "requestAuthFinish", + "value": "Authorize" + }, + { + "name": "requestFailPromptMsg", + "value": "Authorize failed , invalid AppUid" + }, + { + "name": "installPwdInputCancel", + "value": "Cancel" + }, + { + "name": "installPwdInputConfirm", + "value": "Extract" + }, + { + "name": "installAliasInputCancel", + "value": "Cancel" + }, + { + "name": "installAliasInputConfirm", + "value": "Confirm" + }, + { + "name": "installAliasInputInfo", + "value": "Attention: certificate names only support English and numbers" + }, + { + "name": "managerAuthApp", + "value": "Manage Authorized Applications" + }, + { + "name": "cancelAuthApp", + "value": "Cancel" + }, + { + "name": "finishAuthApp", + "value": "Finish" + }, + { + "name": "evidenceDetails", + "value": "Credential details" + }, + { + "name": "entryContains", + "value": "This item contains:" + }, + { + "name": "keyNum", + "value": " %s user keys" + }, + { + "name": "userCerNum", + "value": " %s user certificates" + }, + { + "name": "privateDetailsClose", + "value": "Close" + }, + { + "name": "publicDetailsCancel", + "value": "Cancel" + }, + { + "name": "publicDetailsDelete", + "value": "Delete" + }, + { + "name": "warning_title", + "value": "Delete certificate" + }, + { + "name": "warning_message", + "value": "After deletion, the application authorized with this certificate will not work normally. Whether or not to delete?" + }, + { + "name": "warning_cancel", + "value": "Cancel" + }, + { + "name": "warning_delete", + "value": "Delete" + }, + { + "name": "tabName_public", + "value": "Public" + }, + { + "name": "tabName_private", + "value": "Private" + }, + { + "name": "system", + "value": "System" + }, + { + "name": "user", + "value": "User" + }, + { + "name": "CustomDialogExample_firText", + "value": "Certificate Detail" + }, + { + "name": "CustomDialogExample_firListItem_text", + "value": "Issued to:" + }, + { + "name": "CustomDialogExample_secListItem_text", + "value": "Issued by:" + }, + { + "name": "CustomDialogExample_thdListItem_text", + "value": "Validity:" + }, + { + "name": "CustomDialogExample_Button_text", + "value": "Cancel" + }, + { + "name": "CustomDialogExample_Button_on", + "value": "Enable" + }, + { + "name": "CustomDialogExample_Button_off", + "value": "Disable" + }, + { + "name": "CustomDialogExampleUser_Status_true", + "value": "Enabled" + }, + { + "name": "CustomDialogExampleUser_Status_false", + "value": "Disabled" + }, + { + "name": "CustomDialogExampleUser_Flex_firButton_text", + "value": "Cancel" + }, + { + "name": "CustomDialogExampleUser_Flex_secButton_text", + "value": "Delete" + }, + { + "name": "CustomDialogExampleUser_warning_Button_text", + "value": "Cancel" + }, + { + "name": "CustomDialogExampleUser_warning_Button_title_text", + "value": "Delete certificate" + }, + { + "name": "CustomDialogExampleUser_warning_Button_message_text", + "value": "After deletion, the application authorized with this certificate will not work normally. Whether or not to delete?" + }, + { + "name": "CustomDialogExampleUser_warning_firButton_text", + "value": "Cancel" + }, + { + "name": "CustomDialogExampleUser_warning_secButton_text", + "value": "Delete" + }, + { + "name": "DialogSubjectComponent_firText", + "value": "Common name:" + }, + { + "name": "DialogSubjectComponent_secText", + "value": "Organization:" + }, + { + "name": "DialogSubjectComponent_thdText", + "value": "Organizational unit:" + }, + { + "name": "DialogSubjectComponent_fouText", + "value": "Serial number:" + }, + { + "name": "DialogSubjectComponent_fifText", + "value": "Issued on:" + }, + { + "name": "DialogSubjectComponent_sixText", + "value": "Expires on:" + }, + { + "name": "CustomDialogExample_fouListItem_text", + "value": "Fingerprints:" + }, + { + "name": "CustomDialogExample_FingerPrint_text", + "value": "SHA-256 fingerprints:" + }, + { + "name": "root_certificate", + "value": "root certificate" + }, + { + "name": "root_certificate_message", + "value": "Warning: Enabling this certificate for a website will allow third parties to view any private data sent to the website." + }, + { + "name": "root_certificate_cancel", + "value": "Cancel" + }, + { + "name": "root_certificate_continue", + "value": "Continue" + }, + { + "name": "CA_cert", + "value": "CA certificate" + }, + { + "name": "user_certificate_credentials", + "value": "User credentials" + }, + { + "name": "delete_success", + "value": "delete success" + }, + { + "name": "importCertDialogTitle", + "value": "importCertDialogTitle" + }, + { + "name": "importCertDialogMessage", + "value": "User trusted certificate is about to be installed. Whether or not to install?" + }, + { + "name": "importCredDialogTitle", + "value": "Install Public Credential" + }, + { + "name": "importCredDialogMessage", + "value": "Public credential is about to be installed. Whether or not to install?" + }, + { + "name": "invalidCertAndCredDialogTitle", + "value": "Cannot use This File " + }, + { + "name": "invalidCertAndCredDialogMessage", + "value": "Cannot use this file as a certificate and credential" + }, + { + "name": "importCancel", + "value": "Cancel" + }, + { + "name": "importConfirm", + "value": "Confirm" + }, + { + "name": "invalidCertAndCredConfirm", + "value": "Close" + }, + { + "name": "authenticationTitle", + "value": "Please authenticate" + }, + { + "name": "certInputPassword", + "value": "Enter the certificate password." + }, + { + "name": "inputConfirm", + "value": "Confirm" + }, + { + "name": "setCertName", + "value": "Set certificate name" + }, + { + "name": "setCertMessage", + "value": "The certificate issuer may check all traffic to and from the device, including: CA certificate" + }, + { + "name": "Install_Cert_Success", + "value": "The certificate is successfully installed." + }, + { + "name": "Install_Cred_Success", + "value": "Install credentials successfully" + }, + { + "name": "Install_Cert_Failed", + "value": "Install certificate failure" + }, + { + "name": "Install_Cred_Failed", + "value": "Install credentials failure" + }, + { + "name": "Install_Error_NOT_FOUND", + "value": "This file is not recognized." + }, + { + "name": "Install_ERROR_INCORRECT_FORMAT", + "value": "Certificate corrupted." + }, + { + "name": "Install_Error_MAX_QUANTITY_REACHED", + "value": "The number of certificates has reached the upper limit, Please clear the certificates." + }, + { + "name": "OK", + "value": "OK" + }, + { + "name": "Password_Message", + "value": "Password error" + }, + { + "name": "Identity_Authentication", + "value": "Please authenticate" + }, + { + "name": "inputAliasWarn", + "value": "Maximum length reached" + }, + { + "name": "system_credentials", + "value": "System credentials" + }, + { + "name": "cert_install_success", + "value": "Installation succeeded" + }, + { + "name": "cert_install_failed", + "value": "Installation failed" + }, + { + "name": "cert_install_tip", + "value": "\"%s\" is requesting to install the CA certificate" + }, + { + "name": "cert_install_success_tip", + "value": "CA certificate installed." + }, + { + "name":"cert_install_warning", + "value":"Enabling this certificate for a website will allow third parties to view any private data sent to the website." + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/phone/element/float.json b/entry/src/main/resources/phone/element/float.json new file mode 100755 index 0000000..ee764b7 --- /dev/null +++ b/entry/src/main/resources/phone/element/float.json @@ -0,0 +1,1012 @@ +{ + "float": [ + { + "name": "wh_value_0", + "value": "0vp" + }, + { + "name": "wh_value_1", + "value": "1vp" + }, + { + "name": "wh_value_1_5", + "value": "1.5vp" + }, + { + "name": "wh_value_3", + "value": "3vp" + }, + { + "name": "wh_value_4", + "value": "4vp" + }, + { + "name": "wh_value_5", + "value": "5vp" + }, + { + "name": "wh_value_6", + "value": "6vp" + }, + { + "name": "wh_value_8", + "value": "8vp" + }, + { + "name": "wh_value_9", + "value": "9vp" + }, + { + "name": "wh_value_10", + "value": "10vp" + }, + { + "name": "wh_value_11", + "value": "11vp" + }, + { + "name": "wh_value_12", + "value": "12vp" + }, + { + "name": "wh_value_13", + "value": "13vp" + }, + { + "name": "wh_value_15", + "value": "15vp" + }, + { + "name": "wh_value_16", + "value": "16vp" + }, + { + "name": "wh_value_18", + "value": "18vp" + }, + { + "name": "wh_value_19", + "value": "19vp" + }, + { + "name": "wh_value_20", + "value": "20vp" + }, + { + "name": "wh_value_21", + "value": "21vp" + }, + { + "name": "wh_value_22", + "value": "22vp" + }, + { + "name": "wh_value_24", + "value": "24vp" + }, + { + "name": "wh_value_26", + "value": "26vp" + }, + { + "name": "wh_value_28", + "value": "28vp" + }, + { + "name": "wh_value_30", + "value": "30vp" + }, + { + "name": "wh_value_32", + "value": "32vp" + }, + { + "name": "wh_value_33", + "value": "28vp" + }, + { + "name": "wh_value_40", + "value": "40vp" + }, + { + "name": "wh_value_44", + "value": "44vp" + }, + { + "name": "wh_value_48", + "value": "48vp" + }, + { + "name": "wh_value_50", + "value": "50vp" + }, + { + "name": "wh_value_52", + "value": "52vp" + }, + { + "name": "wh_value_56", + "value": "56vp" + }, + { + "name": "wh_value_60", + "value": "60vp" + }, + { + "name": "wh_value_64", + "value": "64vp" + }, + { + "name": "wh_value_65", + "value": "65vp" + }, + { + "name": "wh_value_67", + "value": "67vp" + }, + { + "name": "wh_value_70", + "value": "70vp" + }, + { + "name": "wh_value_80", + "value": "80vp" + }, + { + "name": "wh_value_96", + "value": "96vp" + }, + { + "name": "wh_value_100", + "value": "100vp" + }, + { + "name": "wh_value_104", + "value": "104vp" + }, + { + "name": "wh_value_109", + "value": "109vp" + }, + { + "name": "wh_value_118", + "value": "118vp" + }, + { + "name": "wh_value_119", + "value": "119vp" + }, + { + "name": "wh_value_120", + "value": "120vp" + }, + { + "name": "wh_value_130", + "value": "1300vp" + }, + { + "name": "wh_value_152", + "value": "152vp" + }, + { + "name": "wh_value_160", + "value": "144vp" + }, + { + "name": "wh_value_200", + "value": "200vp" + }, + { + "name": "wh_value_210", + "value": "210vp" + }, + { + "name": "wh_value_212", + "value": "280vp" + }, + { + "name": "wh_value_216", + "value": "216vp" + }, + { + "name": "wh_value_230", + "value": "230vp" + }, + { + "name": "wh_value_250", + "value": "250vp" + }, + { + "name": "wh_value_263", + "value": "150vp" + }, + { + "name": "wh_value_280", + "value": "280vp" + }, + { + "name": "wh_value_336", + "value": "336vp" + }, + { + "name": "wh_value_362", + "value": "288vp" + }, + { + "name": "wh_value_390", + "value": "390vp" + }, + { + "name": "wh_value_400", + "value": "400vp" + }, + { + "name": "wh_value_410", + "value": "336vp" + }, + { + "name": "wh_value_620", + "value": "620vp" + }, + { + "name": "wh_value_720", + "value": "720vp" + }, + { + "name": "font_35", + "value": "35fp" + }, + { + "name": "font_10", + "value": "10fp" + }, + { + "name": "font_14", + "value": "14fp" + }, + { + "name": "font_16", + "value": "16fp" + }, + { + "name": "font_18", + "value": "18fp" + }, + { + "name": "font_20", + "value": "20fp" + }, + { + "name": "font_21", + "value": "21fp" + }, + { + "name": "font_22", + "value": "22fp" + }, + { + "name": "font_24", + "value": "24fp" + }, + { + "name": "font_28", + "value": "28fp" + }, + { + "name": "font_30", + "value": "30fp" + }, + { + "name": "font_50", + "value": "50fp" + }, + { + "name": "lineHeight_19", + "value": "19vp" + }, + { + "name": "lineHeight_21", + "value": "21vp" + }, + { + "name": "lineHeight_22", + "value": "22vp" + }, + { + "name": "lineHeight_33", + "value": "33vp" + }, + { + "name": "lineHeight_41", + "value": "41vp" + }, + { + "name": "distance_1", + "value": "1vp" + }, + { + "name": "distance_2", + "value": "2vp" + }, + { + "name": "distance_4", + "value": "4vp" + }, + { + "name": "distance_6", + "value": "6vp" + }, + { + "name": "distance_8", + "value": "8vp" + }, + { + "name": "distance_9", + "value": "9vp" + }, + { + "name": "distance_9_5", + "value": "9.5vp" + }, + { + "name": "distance_10", + "value": "10vp" + }, + { + "name": "distance_11", + "value": "11vp" + }, + { + "name": "distance_11_5", + "value": "11.5vp" + }, + { + "name": "distance_12", + "value": "12vp" + }, + { + "name": "distance_13", + "value": "13vp" + }, + { + "name": "distance_14", + "value": "14vp" + }, + { + "name": "distance_15", + "value": "15vp" + }, + { + "name": "distance_16", + "value": "16vp" + }, + { + "name": "distance_17", + "value": "17vp" + }, + { + "name": "distance_18", + "value": "18vp" + }, + { + "name": "distance_19", + "value": "19vp" + }, + { + "name": "distance_19_5", + "value": "19.5vp" + }, + { + "name": "distance_20", + "value": "20vp" + }, + { + "name": "distance_21", + "value": "21vp" + }, + { + "name": "distance_22_5", + "value": "22.5vp" + }, + { + "name": "distance_24", + "value": "24vp" + }, + { + "name": "distance_26", + "value": "26vp" + }, + { + "name": "distance_30", + "value": "30vp" + }, + { + "name": "distance_32", + "value": "32vp" + }, + { + "name": "distance_36", + "value": "36vp" + }, + { + "name": "distance_56", + "value": "56vp" + }, + { + "name": "distance_64", + "value": "64vp" + }, + { + "name": "distance_66", + "value": "66vp" + }, + { + "name": "distance_80", + "value": "80vp" + }, + { + "name": "distance_96", + "value": "96vp" + }, + { + "name": "sys_corner_radius_clicked", + "value": "8vp" + }, + { + "name": "sys_elements_margin_vertical_l", + "value": "16vp" + }, + { + "name": "sys_elements_margin_vertical_m", + "value": "8vp" + }, + { + "name": "sys_elements_margin_horizontal_l", + "value": "16vp" + }, + { + "name": "sys_elements_margin_horizontal_m", + "value": "16vp" + }, + { + "name": "page_margin_horizontal", + "value": "24vp" + }, + { + "name": "square_click_image_size", + "value": "48vp" + }, + { + "name": "control_common_font_size", + "value": "20fp" + }, + { + "name": "item_common_vertical_margin", + "value": "20vp" + }, + { + "name": "item_common_horizontal_margin", + "value": "24vp" + }, + { + "name": "item_icon_size", + "value": "24vp" + }, + { + "name": "item_arrow_width", + "value": "12vp" + }, + { + "name": "slider_text_padding_left", + "value": "5vp" + }, + { + "name": "page_header_height", + "value": "56vp" + }, + { + "name": "volume_border_radius", + "value": "15vp" + }, + { + "name": "radius_12", + "value": "12vp" + }, + { + "name": "radius_20", + "value": "20vp" + }, + { + "name": "radius_24", + "value": "24vp" + }, + { + "name": "radius_32", + "value": "32vp" + }, + { + "name": "radius_40", + "value": "40vp" + }, + { + "name": "search_placeholder_font", + "value": "22fp" + }, + { + "name": "search_input_height", + "value": "40vp" + }, + { + "name": "search_input_margin_start", + "value": "64vp" + }, + { + "name": "search_input_horizontal_margin", + "value": "8vp" + }, + { + "name": "search_input_border_radius", + "value": "20vp" + }, + { + "name": "search_no_result_image_size", + "value": "160vp" + }, + { + "name": "search_no_result_text_font_size", + "value": "14fp" + }, + { + "name": "search_no_result_margin_top", + "value": "174vp" + }, + { + "name": "search_item_height", + "value": "72vp" + }, + { + "name": "search_result_item_title_font_size", + "value": "16vp" + }, + { + "name": "search_result_item_summary_font_size", + "value": "14fp" + }, + { + "name": "slider_image_width", + "value": "40vp" + }, + { + "name": "slider_image_height", + "value": "40vp" + }, + { + "name": "slider_image_margin", + "value": "15vp" + }, + { + "name": "audio_subtitle_font", + "value": "18fp" + }, + { + "name": "audio_subtitle_margin_left", + "value": "10vp" + }, + { + "name": "audio_subtitle_margin_bottom", + "value": "5vp" + }, + { + "name": "audio_title_summary_margin_left", + "value": "15vp" + }, + { + "name": "audio_summary_subtitle_margin_bottom", + "value": "10vp" + }, + { + "name": "audio_subtitle_margin_top", + "value": "10vp" + }, + { + "name": "audio_sound_mode_outer_height", + "value": "280vp" + }, + { + "name": "audio_sound_mode_inner_padding_top", + "value": "15vp" + }, + { + "name": "audio_sound_mode_inner_padding_bottom", + "value": "15vp" + }, + { + "name": "audio_sound_mode_outer_padding", + "value": "16vp" + }, + { + "name": "audio_sound_mode_item_inner_padding_top", + "value": "5vp" + }, + { + "name": "audio_sound_mode_border_width", + "value": "1vp" + }, + { + "name": "audio_sound_mode_border_radius", + "value": "15vp" + }, + { + "name": "audio_sound_mode_image_size", + "value": "40vp" + }, + { + "name": "audio_sound_mode_text_margin_top", + "value": "16vp" + }, + { + "name": "audio_sound_mode_font_size", + "value": "20fp" + }, + { + "name": "audio_sound_mode_radio_size", + "value": "36vp" + }, + { + "name": "audio_image_common_size", + "value": "50vp" + }, + { + "name": "audio_image_margin_right", + "value": "10vp" + }, + { + "name": "audio_subtitle_font_size", + "value": "18fp" + }, + { + "name": "audio_margin_left", + "value": "15vp" + }, + { + "name": "audio_start_end_margin_left", + "value": "15vp" + }, + { + "name": "audio_icon_height", + "value": "60vp" + }, + { + "name": "audio_no_icon_height", + "value": "70vp" + }, + { + "name": "audio_slider_height", + "value": "250vp" + }, + { + "name": "audio_volume_height", + "value": "280vp" + }, + { + "name": "audio_border_width", + "value": "1vp" + }, + { + "name": "audio_border_radius", + "value": "15vp" + }, + { + "name": "audio_volume_component_padding", + "value": "16vp" + }, + { + "name": "restore_factory_font_size", + "value": "20fp" + }, + { + "name": "restore_factory_button_width", + "value": "186vp" + }, + { + "name": "restore_factory_button_height", + "value": "40vp" + }, + { + "name": "switch_title_subtitle_size", + "value": "20fp" + }, + { + "name": "switch_component_margin", + "value": "12vp" + }, + { + "name": "switch_toggle_width", + "value": "36vp" + }, + { + "name": "switch_toggle_height", + "value": "20vp" + }, + { + "name": "switch_component_height", + "value": "70vp" + }, + { + "name": "application_common_size", + "value": "45vp" + }, + { + "name": "application_button_subtitle_size", + "value": "16fp" + }, + { + "name": "application_button_height", + "value": "40vp" + }, + { + "name": "storage_common_margin", + "value": "15vp" + }, + { + "name": "location_font_size", + "value": "16fp" + }, + { + "name": "location_common_margin", + "value": "15vp" + }, + { + "name": "storage_data_panel_height", + "value": "230vp" + }, + { + "name": "password_list_item_height", + "value": "48vp" + }, + { + "name": "password_list_item_title_font_size", + "value": "16vp" + }, + { + "name": "password_input_message_vertical_margin", + "value": "20vp" + }, + { + "name": "password_count_down_view_vertical_margin", + "value": "20vp" + }, + { + "name": "password_input_button_space", + "value": "10vp" + }, + { + "name": "password_input_button_margin_top", + "value": "40vp" + }, + { + "name": "radio_component_height", + "value": "30vp" + }, + { + "name": "radio_component_margin_bottom_right", + "value": "15vp" + }, + { + "name": "dataPanel_distance", + "value": "320vp" + }, + { + "name": "dataPanel_proportion_font_size", + "value": "66.67fp" + }, + { + "name": "dataPanel_percent_font_size", + "value": "17.78fp" + }, + { + "name": "dataPanel_used_font_size", + "value": "17.78fp" + }, + { + "name": "location_toggle_width", + "value": "36vp" + }, + { + "name": "location_toggle_height", + "value": "20vp" + }, + { + "name": "location_toggle_margin", + "value": "12vp" + }, + { + "name": "wh_value_72", + "value": "72vp" + }, + { + "name": "switch_summary_margin", + "value": "2vp" + }, + { + "name": "single_item_height", + "value": "48vp" + }, + { + "name": "wh_value_240", + "value": "240vp" + }, + { + "name": "wh_value_245", + "value": "245vp" + }, + { + "name": "wh_value_260", + "value": "260vp" + }, + { + "name": "wh_value_288", + "value": "288vp" + }, + { + "name": "wh_value_324", + "value": "324vp" + }, + { + "name": "divider_opacity", + "value": "0.2" + }, + { + "name": "paired_device_button_width", + "value": "186vp" + }, + { + "name": "component_button_width", + "value": "263vp" + }, + { + "name": "component_button_width_phone", + "value": "148vp" + }, + { + "name": "pinCode_font_size", + "value": "32fp" + }, + { + "name": "deviceName_button_width", + "value": "150vp" + }, + { + "name": "applicationInfo_button_width", + "value": "324vp" + }, + { + "name": "passwordSetting_button_width", + "value": "261vp" + }, + { + "name": "dialog_16", + "value": "16vp" + }, + { + "name": "dialog_118", + "value": "92vp" + }, + { + "name": "wh_4", + "value": "8vp" + }, + { + "name": "wh_10", + "value": "16vp" + }, + { + "name": "wh_20", + "value": "8vp" + }, + { + "name": "wh_23", + "value": "16vp" + }, + { + "name": "wh_24", + "value": "12vp" + }, + { + "name": "wh_35", + "value": "16vp" + }, + { + "name": "wh_37", + "value": "37vp" + }, + { + "name": "wh_42", + "value": "22vp" + }, + { + "name": "wh_48", + "value": "48vp" + }, + { + "name": "wh_192", + "value": "192vp" + }, + { + "name": "wh_205", + "value": "292vp" + }, + { + "name": "wh_263", + "value": "186vp" + }, + { + "name": "wh_hed_14", + "value": "14vp" + }, + { + "name": "wh_600", + "value": "600vp" + }, + { + "name": "padding_24", + "value": "24vp" + }, + { + "name": "padding_26", + "value": "14vp" + }, + { + "name": "wh_padding_32", + "value": "25vp" + }, + { + "name": "wh_padding_33", + "value": "20vp" + }, + { + "name": "wh_padding_35", + "value": "35vp" + }, + { + "name": "wh_padding_48", + "value": "24vp" + }, + { + "name": "wh_padding_112", + "value": "112vp" + }, + { + "name": "wh_padding_128", + "value": "128vp" + }, + { + "name": "wh_padding_212", + "value": "272vp" + }, + { + "name": "dataPanel_proportion_font_size_1", + "value": "60fp" + }, + { + "name": "dataPanel_percent_font_size_1", + "value": "16fp" + }, + { + "name": "dataPanel_used_font_size_1", + "value": "14fp" + }, + { + "name": "divider_wh", + "value": "0.5vp" + }, + { + "name": "progress_bottom", + "value": "127vp" + }, + { + "name": "head_font_24", + "value": "20vp" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/phone/element/string.json b/entry/src/main/resources/phone/element/string.json new file mode 100755 index 0000000..69b4742 --- /dev/null +++ b/entry/src/main/resources/phone/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "证书管理" + }, + { + "name": "mainability_description", + "value": "ETS_Empty Feature Ability" + }, + { + "name": "certificateTab", + "value": "证书与凭据" + }, + { + "name": "certificateManage", + "value": "证书管理" + }, + { + "name": "trustedEvidence", + "value": "受信任的证书" + }, + { + "name": "userEvidence", + "value": "凭据列表" + }, + { + "name": "installInStorageDevice", + "value": "从存储设备安装" + }, + { + "name": "installCertificateFailed", + "value": "从存储设备安装失败" + }, + { + "name": "certificatePwdTab", + "value": "提取证书" + }, + { + "name": "certificatePwdInfo", + "value": "输入证书密码以提取" + }, + { + "name": "deleteAllCred", + "value": "删除所有证书与凭据" + } + ] +} diff --git a/entry/src/main/resources/rawfile/security_privacy.json b/entry/src/main/resources/rawfile/security_privacy.json new file mode 100755 index 0000000..e704a16 --- /dev/null +++ b/entry/src/main/resources/rawfile/security_privacy.json @@ -0,0 +1,7 @@ +{ + "displayedMode": "list", + "mainTitleResource": "$string:certificateTab", + "dstAbilityMode": 1, + "dstAbilityName": "MainExtensionAbility", + "dstBundleName": "com.ohos.certmanager" +} \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json new file mode 100755 index 0000000..69b4742 --- /dev/null +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "证书管理" + }, + { + "name": "mainability_description", + "value": "ETS_Empty Feature Ability" + }, + { + "name": "certificateTab", + "value": "证书与凭据" + }, + { + "name": "certificateManage", + "value": "证书管理" + }, + { + "name": "trustedEvidence", + "value": "受信任的证书" + }, + { + "name": "userEvidence", + "value": "凭据列表" + }, + { + "name": "installInStorageDevice", + "value": "从存储设备安装" + }, + { + "name": "installCertificateFailed", + "value": "从存储设备安装失败" + }, + { + "name": "certificatePwdTab", + "value": "提取证书" + }, + { + "name": "certificatePwdInfo", + "value": "输入证书密码以提取" + }, + { + "name": "deleteAllCred", + "value": "删除所有证书与凭据" + } + ] +} diff --git a/entry/src/ohosTest/ets/test/Ability.test.ets b/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100755 index 0000000..33540a4 --- /dev/null +++ b/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { 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/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets new file mode 100755 index 0000000..da489eb --- /dev/null +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/entry/src/ohosTest/module.json5 b/entry/src/ohosTest/module.json5 new file mode 100755 index 0000000..aae3af2 --- /dev/null +++ b/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2024-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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 new file mode 100755 index 0000000..234611b --- /dev/null +++ b/hvigor/hvigor-config.json5 @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2024-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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + }, + "logging": { + }, + "debugging": { + } +} \ No newline at end of file diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100755 index 0000000..d911c06 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { 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/hvigorw b/hvigorw new file mode 100755 index 0000000..1f1f5e5 --- /dev/null +++ b/hvigorw @@ -0,0 +1,64 @@ + # Copyright (c) 2024-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. + + + + +#!/bin/bash + +# ---------------------------------------------------------------------------- +# Hvigor startup script, version 1.0.0 +# +# Required ENV vars: +# ------------------ +# NODE_HOME - location of a Node home dir +# or +# Add /usr/local/nodejs/bin to the PATH environment variable +# ---------------------------------------------------------------------------- + +HVIGOR_APP_HOME="`pwd -P`" +HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js +warn() { + echo "" + echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +error() { + echo "" + echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +fail() { + error "$@" + exit 1 +} + +# Determine node to start hvigor wrapper script +if [ -n "${NODE_HOME}" ];then + EXECUTABLE_NODE="${NODE_HOME}/bin/node" + if [ ! -x "$EXECUTABLE_NODE" ];then + fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed" + fi +else + EXECUTABLE_NODE="node" + which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path" +fi + +# Check hvigor wrapper script +if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then + fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}" +fi + +# start hvigor-wrapper script +exec "${EXECUTABLE_NODE}" \ + "${HVIGOR_WRAPPER_SCRIPT}" "$@" diff --git a/hvigorw.bat b/hvigorw.bat new file mode 100755 index 0000000..8459f3e --- /dev/null +++ b/hvigorw.bat @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2024-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. + */ + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Hvigor startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js +set NODE_EXE=node.exe + +goto start + +:start +@rem Find node.exe +if defined NODE_HOME goto findNodeFromNodeHome + +%NODE_EXE% --version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:findNodeFromNodeHome +set NODE_HOME=%NODE_HOME:"=% +set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE% + +if exist "%NODE_EXE_PATH%" goto execute +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:execute +@rem Execute hvigor +"%NODE_EXE%" "%WRAPPER_MODULE_PATH%" %* + +if "%ERRORLEVEL%" == "0" goto hvigorwEnd + +:fail +exit /b 1 + +:hvigorwEnd +if "%OS%" == "Windows_NT" endlocal + +:end diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100755 index 0000000..736c887 --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2024-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. + */ + +{ + "modelVersion": "5.0.0", + "license": "", + "devDependencies": { + "@ohos/hypium": "1.0.11" + }, + "author": "", + "name": "security_privacy_center", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": {} +} \ No newline at end of file diff --git a/signature/OpenHarmony.p12 b/signature/OpenHarmony.p12 new file mode 100755 index 0000000000000000000000000000000000000000..60f9bb48e677960e9dd7257e9f55c80915417ecf GIT binary patch literal 8252 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