add asset sample

Signed-off-by: 尹耀德 <yinyaode1@huawei.com>
This commit is contained in:
尹耀德 2024-03-19 21:06:15 +08:00
parent e25ec9d38d
commit ec411f9f54
52 changed files with 2856 additions and 0 deletions

11
OAT.xml
View File

@ -103,6 +103,17 @@ Note:If the text contains special characters, please escape them according to th
<filteritem type="filepath" name="network/Bluetooth/entry/src/main/java/ohos/samples/bluetooth/adapter/" desc="Not contains 3rd codelicense file is not required in this subdirectory."/> <filteritem type="filepath" name="network/Bluetooth/entry/src/main/java/ohos/samples/bluetooth/adapter/" desc="Not contains 3rd codelicense file is not required in this subdirectory."/>
</filefilter> </filefilter>
<filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies" > <filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies" >
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/AppScope/resources/base/media/app_icon.png" desc="Provided by the UX team."/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/AppScope/resources/base/media/forward.png" desc="Provided by the UX team."/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/AppScope/resources/base/media/back.png" desc="Provided by the UX team."/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/entry/src/ohosTest/resources/base/media/icon.png" desc="Provided by the UX team."/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/screenshots/update_edit.jpeg" desc="screenshot"/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/screenshots/single_query_result.jpeg" desc="screenshot"/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/screenshots/update_list.jpeg" desc="screenshot"/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/screenshots/delete.jpeg" desc="screenshot"/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/screenshots/save.jpeg" desc="screenshot"/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/screenshots/batch_query_result.jpeg" desc="screenshot"/>
<filteritem type="filepath" name="code/BasicFeature/Security/Asset/screenshots/query.jpeg" desc="screenshot"/>
<filteritem type="filepath" name="ability/DistributedMusicPlayer/entry/src/main/resources/rawfile/Homey.mp3" desc="Provided by the UX team."/> <filteritem type="filepath" name="ability/DistributedMusicPlayer/entry/src/main/resources/rawfile/Homey.mp3" desc="Provided by the UX team."/>
<filteritem type="filepath" name="ability/DistributedMusicPlayer/entry/src/main/resources/rawfile/Technology.mp3" desc="Provided by the UX team."/> <filteritem type="filepath" name="ability/DistributedMusicPlayer/entry/src/main/resources/rawfile/Technology.mp3" desc="Provided by the UX team."/>
<filteritem type="filepath" name="UI/JsAnimation/entry/src/main/js/default/common/animator/show.mp4" desc="Provided by the UX team."/> <filteritem type="filepath" name="UI/JsAnimation/entry/src/main/js/default/common/animator/show.mp4" desc="Provided by the UX team."/>

View File

@ -118,6 +118,7 @@
<td x:str><a href="code/BasicFeature/Security/Huks">通用密钥库系统huksAPI 10</a></td> <td x:str><a href="code/BasicFeature/Security/Huks">通用密钥库系统huksAPI 10</a></td>
<td x:str><a href="code/BasicFeature/Security/PaySecurely">支付</a></td> <td x:str><a href="code/BasicFeature/Security/PaySecurely">支付</a></td>
<td x:str><a href="code/BasicFeature/Security/CertManager">证书管理</a></td> <td x:str><a href="code/BasicFeature/Security/CertManager">证书管理</a></td>
<td x:str><a href="code/BasicFeature/Security/Asset">关键资产存储</a></td>
<td ></td> <td ></td>
</tr> </tr>
<tr height="18" style='height:13.50pt;'> <tr height="18" style='height:13.50pt;'>

11
code/BasicFeature/Security/Asset/.gitignore vendored Executable file
View File

@ -0,0 +1,11 @@
/node_modules
/oh_modules
/local.properties
/.idea
**/build
/.hvigor
.cxx
/.clangd
/.clang-format
/.clang-tidy
**/.test

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
{
"app": {
"bundleName": "com.example.asset_sample",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name"
}
}

View File

@ -0,0 +1,8 @@
{
"string": [
{
"name": "app_name",
"value": "asset_sample"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

View File

@ -0,0 +1,110 @@
# 关键资产存储服务
## 介绍
本示例使用@kit.AssetStoreKit相关接口实现了对敏感数据的保存、更新、查询、删除操作。
实现场景如下:
1用户使用本应用保存密码或信用卡号。
2用户更新本应用管理的密码或信用卡号。
3用户查询本应用管理的密码或信用卡号。
4用户删除本应用管理的密码或信用卡号。
## 效果预览
| **保存页面** | **删除页面** | **更新列表页面** | **更新编辑页面** |
|---------|----------|------------|----------|
|![](screenshots/save.jpeg)|![](screenshots/delete.jpeg)|![](screenshots/update_list.jpeg)|![](screenshots/update_edit.jpeg)|
| **查询页面** | **批量查询结果页面** | **单个查询结果页面** |
![](screenshots/query.jpeg)|![](screenshots/batch_query_result.jpeg)|![](screenshots/single_query_result.jpeg)|
使用说明:
* 保存敏感数据:
* 输入账号、密码、标签其中标签可以填写多个但不超过4个
* 点击“保存”按钮
* 删除敏感数据,可使用以下任意一种方式删除:
* 批量删除:不输入任何信息,或输入单个或多个标签),点击“删除”按钮
* 单个删除:输入账号、标签(可选),点击“删除”按钮
* 更新敏感数据:
* 点击标题栏的“更新”,进入更新页面
* 在显示的账号列表中,点击待更新的账号
* 输入锁屏密码,进入编辑界面
* 输入更新后的密码和标签,点击“更新”
* 查询敏感数据,可使用以下任意一种方式删除:
* 批量查询:不输入任何信息,或输入单个或多个标签),点击“查询”按钮,显示保存的信息列表,不含“密码/信用卡号”
* 单个查询:输入账号、标签(可选),点击“查询”按钮,输入锁屏密码,显示该账号详细信息,含“密码/信用卡号”
## 工程目录
```
entry/src/main/ets/
|---entryAbility
| |---EntryAbility.ts
|---model
| |---AssetModel.ets // 关键资产存储模型文件
|---pages
| |---Index.ets // 主页面
| |---QueryResultPage.ets // 查询结果界面
| |---UpdatePage.ets // 更新页面
```
## 具体实现
本应用分为四个主页面保存、删除、更新、查询通过TabBuilder组件实现页面直接的切换。
* **保存页面**:解析用户输入的账号、密码、标签信息,传入@kit.AssetStoreKit提供的add接口从而实现将短敏感数据存储到关键资产存储服务中。
* **删除页面**:解析用户输入的账号、标签信息,传入@kit.AssetStoreKit提供的remove接口删除满足条件的短敏感数据。
* **更新页面**
1调用@kit.AssetStoreKit提供的query接口查询关键资产中存储的全量账号。
2调用@kit.AssetStoreKit提供的preQuery接口将返回的challenge传给@kit.UserAuthenticationKit提供的on接口。
3待用户输入锁屏密码后将onResult接口返回的AuthToken传给@kit.AssetStoreKit提供的query接口用以查询旧的敏感数据明文。
4解析用户输入的账号、标签信息传入@kit.AssetStoreKit提供的update接口更新关键资产存储服务中存储的敏感数据。
5退出更新界面时调用@kit.AssetStoreKit提供的postQuery接口清理资源
* **查询页面**
用户未输入账号:
解析用户输入的标签信息,传入@kit.AssetStoreKit提供的query接口查询关键资产存储服务中存储的账号、标签。
用户输入了账号:
1调用@kit.AssetStoreKit提供的preQuery接口将返回的challenge传给@kit.UserAuthenticationKit提供的on接口。
2待用户输入锁屏密码后将onResult接口返回的AuthToken、用户输入的账号、标签信息传给@kit.AssetStoreKit提供的query接口用以查询敏感数据明文。
3调用@kit.AssetStoreKit提供的postQuery接口清理资源。
## 相关权限
ohos.permission.ACCESS_BIOMETRIC
ohos.permission.STORE_PERSISTENT_DATA
## 依赖
不涉及
## 约束与限制
1.本示例仅支持标准系统上运行。
2.本示例支持API11版本SDKSDK版本号(API Version 11 Release), 镜像版本号(4.1Release)。
3.本示例需要使用DevEco Studio版本号(4.1Release)及以上版本才可编译运行。
4.本示例需要在设备设置锁屏密码后使用。其中,更新、查询结果页面输入锁屏密码方可进入。
## 下载
如需单独下载本工程,执行如下命令:
```
git init
git config core.sparsecheckout true
echo code/BasicFeature/Security/Asset/ > .git/info/sparse-checkout
git remote add origin https://gitee.com/openharmony/applications_app_samples.git
git pull origin master
```

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
{
"app": {
"products": [
{
"name": "default",
"signingConfig": "default",
"compileSdkVersion": 11,
"compatibleSdkVersion": 11,
"runtimeOS": "OpenHarmony"
}
],
"signingConfigs": [
{
"name": "default",
"material": {
"certpath": "C:/Users/zengbenchong/.ohos/config/openharmony/default_Asset_WMjW1k3aXdGTv9zVP9gZHS6Q095UiwPg2bW9aR_b0Gw=.cer",
"storePassword": "0000001B53993973AD492CF6E3976586BBED74A3A3E4D725B2C0CDC71D63D0522A290CC9E86C861E213C97",
"keyAlias": "debugKey",
"keyPassword": "0000001B5B410B8FCF1DFC802D233B1A0AC012B8294A5BFB3BF3713733C3B5E2B68C890DC235987A625118",
"profile": "C:/Users/zengbenchong/.ohos/config/openharmony/default_Asset_WMjW1k3aXdGTv9zVP9gZHS6Q095UiwPg2bW9aR_b0Gw=.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "C:/Users/zengbenchong/.ohos/config/openharmony/default_Asset_WMjW1k3aXdGTv9zVP9gZHS6Q095UiwPg2bW9aR_b0Gw=.p12"
}
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{
"name": "default",
"applyToProducts": [
"default"
]
}
]
}
]
}

View File

@ -0,0 +1,6 @@
/node_modules
/oh_modules
/.preview
/build
/.cxx
/.test

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
{
"apiType": 'stageMode',
"targets": [
{
"name": "default",
"runtimeOS": "OpenHarmony"
},
{
"name": "ohosTest",
}
]
}

View File

@ -0,0 +1,6 @@
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}

View File

@ -0,0 +1,11 @@
{
"license": "",
"devDependencies": {},
"author": "",
"name": "entry",
"description": "Please describe the basic information.",
"main": "",
"version": "1.0.0",
"dynamicDependencies": {},
"dependencies": {}
}

View File

@ -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 { AbilityConstant } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { UIAbility } from '@kit.AbilityKit';
import { Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
onWindowStageDestroy() {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground() {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground() {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}

View File

@ -0,0 +1,329 @@
/*
* 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 { asset } from '@kit.AssetStoreKit';
import { promptAction } from '@kit.ArkUI';
import { util } from '@kit.ArkTS';
function removeDuplicateAndSort(arr: string[]): string[] {
let newArr = new Array<string>();
for (let i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1 && arr[i] != '') {
newArr.push(arr[i]);
}
}
return newArr.sort();
}
export function log(str: string) {
promptAction.showToast({ message: str })
console.log(str);
}
export function stringToArray(str: string): Uint8Array {
let textEncoder = new util.TextEncoder();
return textEncoder.encodeInto(str);
}
function newNullArray(): Uint8Array {
return new Uint8Array([0]);
}
function arrayToString(arr: Uint8Array): string {
let textDecoder = util.TextDecoder.create("utf-8", {
ignoreBOM: true
});
let str = textDecoder.decodeWithStream(arr, {
stream: false
})
return str;
}
export async function addAssetPromise(account: string, password: string, label: string) {
let labelList: Array<string> = label.split(';');
labelList = removeDuplicateAndSort(labelList);
if (labelList.length > 4) {
log("保存失败标签数量超过4个")
return;
}
let attr: asset.AssetMap = new Map();
attr.set(asset.Tag.ALIAS, stringToArray(account))
attr.set(asset.Tag.SECRET, stringToArray(password))
if (labelList[0]) {
attr.set(asset.Tag.DATA_LABEL_NORMAL_1, stringToArray(labelList[0]))
}
if (labelList[1]) {
attr.set(asset.Tag.DATA_LABEL_NORMAL_2, stringToArray(labelList[1]))
}
if (labelList[2]) {
attr.set(asset.Tag.DATA_LABEL_NORMAL_3, stringToArray(labelList[2]))
}
if (labelList[3]) {
attr.set(asset.Tag.DATA_LABEL_NORMAL_4, stringToArray(labelList[3]))
}
attr.set(asset.Tag.AUTH_TYPE, asset.AuthType.ANY)
attr.set(asset.Tag.IS_PERSISTENT, true)
try {
await asset.add(attr);
log("保存成功")
} catch (error) {
if (error.code === 24000003) {
log('保存失败,账号已存在');
} else {
log('保存失败')
}
}
}
export async function removeAssetPromise(deleteAccount: string, deleteLabel: string) {
let attr: asset.AssetMap = new Map();
if (deleteAccount.length > 0) {
attr.set(asset.Tag.ALIAS, stringToArray(deleteAccount))
}
if (deleteLabel.length == 0) {
try {
await asset.remove(attr);
log("删除成功")
} catch (error) {
if (error.code === 24000002) {
log('删除失败,账号不存在');
} else {
log('删除失败')
}
}
} else {
let labelList = deleteLabel.split(';');
labelList = removeDuplicateAndSort(labelList);
let assetList: asset.AssetMap[] = await getArrangedMap(deleteAccount, labelList)
if (assetList.length == 0) {
log('删除失败,账号不存在');
return;
}
let map: Map<asset.Tag, asset.Value> = new Map();
for (let i = 0; i < assetList.length; i++) {
map.set(asset.Tag.ALIAS, assetList[i].get(asset.Tag.ALIAS) as Uint8Array);
try {
await asset.remove(map)
} catch (error) {
log('删除失败')
return;
}
}
log("删除成功")
}
}
function compareArray(array1: Uint8Array, array2: Uint8Array): boolean {
return JSON.stringify(array1) == JSON.stringify(array2);
}
function addLabelInfo(label: string, tag: asset.Tag, account: asset.AssetMap, emptyArray: Uint8Array): string {
if (account.has(tag) && !compareArray(account.get(tag) as Uint8Array, emptyArray)) {
label += arrayToString(account.get(tag) as Uint8Array) + ';'
}
return label;
}
function addElement(attr: asset.AssetMap, label: string, key: asset.Tag) {
if (label){
attr.set(key, stringToArray(label))
} else {
attr.set(key, newNullArray())
}
}
let normalLabelList: Array<asset.Tag> = new Array<asset.Tag>(asset.Tag.DATA_LABEL_NORMAL_1,
asset.Tag.DATA_LABEL_NORMAL_2, asset.Tag.DATA_LABEL_NORMAL_3, asset.Tag.DATA_LABEL_NORMAL_4);
// asset获取的map转换为显示map
function convertAssetList(assetList: asset.AssetMap[], accountList: Map<string, string>[]): Array<Map<string, string>> {
for (let account of assetList) {
let map: Map<string, string> = new Map<string, string>();
let alias = arrayToString(account.get(asset.Tag.ALIAS) as Uint8Array);
if (account.has(asset.Tag.SECRET)) {
let secret = arrayToString(account.get(asset.Tag.SECRET) as Uint8Array);
map.set('secret', secret);
}
let label = ''
let emptyArray = newNullArray();
for (let currentLabel of normalLabelList) {
label = addLabelInfo(label, currentLabel, account, emptyArray);
}
label = label.slice(0, label.length - 1)
map.set('alias', alias);
map.set('label', label)
accountList.push(map);
}
return accountList;
}
export async function updateAssetPromise(reserveAccount: string, reservePassword: string, reserveLabel: string) {
let labelList = reserveLabel.split(';');
labelList = removeDuplicateAndSort(labelList);
if (labelList.length > 4) {
log("更新失败标签数量超过4个")
return;
}
let attr: asset.AssetMap = new Map();
attr.set(asset.Tag.SECRET, stringToArray(reservePassword))
normalLabelList.forEach((element: asset.Tag, index: number) => {
addElement(attr, labelList[index], element);
});
let attr2: asset.AssetMap = new Map();
attr2.set(asset.Tag.ALIAS, stringToArray(reserveAccount))
try {
await asset.update(attr2, attr)
log("更新成功")
} catch (error) {
log('更新失败');
}
}
async function getFirstQuery(alias: string, label: string): Promise<asset.AssetMap[]>
{
let res: Array<asset.AssetMap> = new Array<asset.AssetMap>();
let options: asset.Tag[] = [ asset.Tag.DATA_LABEL_NORMAL_1, asset.Tag.DATA_LABEL_NORMAL_2,
asset.Tag.DATA_LABEL_NORMAL_3, asset.Tag.DATA_LABEL_NORMAL_4 ];
for (let i = 0; i < options.length; i++) {
let map: asset.AssetMap = new Map();
if (alias.length > 0) {
map.set(asset.Tag.ALIAS, stringToArray(alias));
map.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL)
}
map.set(options[i], stringToArray(label));
try {
let temp: Array<asset.AssetMap> = await asset.query(map)
res = res.concat(temp);
} catch(e) {
// skip the error: not found
}
}
return res;
}
function getLabelList(input: asset.AssetMap): Array<string>
{
let tmp = new Array<string>();
if (input.has(asset.Tag.DATA_LABEL_NORMAL_1)) {
tmp.push(arrayToString(input.get(asset.Tag.DATA_LABEL_NORMAL_1) as Uint8Array));
}
if (input.has(asset.Tag.DATA_LABEL_NORMAL_2)) {
tmp.push(arrayToString(input.get(asset.Tag.DATA_LABEL_NORMAL_2) as Uint8Array));
}
if (input.has(asset.Tag.DATA_LABEL_NORMAL_3)) {
tmp.push(arrayToString(input.get(asset.Tag.DATA_LABEL_NORMAL_3) as Uint8Array));
}
if (input.has(asset.Tag.DATA_LABEL_NORMAL_4)) {
tmp.push(arrayToString(input.get(asset.Tag.DATA_LABEL_NORMAL_4) as Uint8Array));
}
return tmp;
}
async function getArrangedMap(alias: string, labels: string[]): Promise<asset.AssetMap[]>
{
let first_query: asset.AssetMap[] = await getFirstQuery(alias, labels[0]);
let res: asset.AssetMap[] = first_query.filter((map: asset.AssetMap) => {
let list: Array<string> = getLabelList(map);
for (let i = 1; i < labels.length; i++) {
if (list.indexOf(labels[i]) < 0) {
return false;
}
}
return true;
});
return res;
}
export async function queryAssetPromise(checkAccount: string, checkLabel: string): Promise<Array<Map<string, string>>> {
let accountList: Map<string, string>[] = new Array<Map<string, string>>();
let assetMap: asset.AssetMap = new Map();
if (checkAccount.length > 0) {
assetMap.set(asset.Tag.ALIAS, stringToArray(checkAccount))
assetMap.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL)
}
if (checkLabel.length == 0) { // query condition do not contain label.
try {
let assetList: Array<asset.AssetMap> = await asset.query(assetMap);
return convertAssetList(assetList, accountList);
} catch (error) {
if (error.code === 24000002) {
log('未查询到结果');
} else {
log('查询失败');
}
return new Array<Map<string, string>>();
}
} else {
let labelList: Array<string> = checkLabel.split(';');
labelList = removeDuplicateAndSort(labelList);
let assetList: asset.AssetMap[] = await getArrangedMap(checkAccount, labelList)
if (assetList.length == 0) {
log('未查询到结果');
return new Array<Map<string, string>>();
}
return convertAssetList(assetList, accountList);
}
}
export async function preQueryAssetPromise(checkAccount: string): Promise<Uint8Array> {
let assetMap: asset.AssetMap = new Map();
assetMap.set(asset.Tag.AUTH_VALIDITY_PERIOD, 10 * 60)
assetMap.set(asset.Tag.AUTH_TYPE, asset.AuthType.ANY)
if (checkAccount.length > 0) {
assetMap.set(asset.Tag.ALIAS, stringToArray(checkAccount))
}
try {
return await asset.preQuery(assetMap)
} catch (error) {
return new Uint8Array(0)
}
}
export async function queryAuthAssetPromise(checkAccount: string, challenge: Uint8Array,
authToken: Uint8Array): Promise<Map<string, string>> {
let attr: asset.AssetMap = new Map();
let account: Map<string, string> = new Map<string, string>();
if (checkAccount.length > 0) {
attr.set(asset.Tag.ALIAS, stringToArray(checkAccount));
attr.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);
attr.set(asset.Tag.AUTH_CHALLENGE, challenge);
attr.set(asset.Tag.AUTH_TOKEN, authToken)
}
try {
let data: Array<asset.AssetMap> = await asset.query(attr);
let accountList: Map<string, string>[] = new Array<Map<string, string>>();
accountList = convertAssetList(data, accountList);
return accountList[0]
} catch (error) {
if (error.code === 24000002) {
log('未查询到结果');
} else {
log('查询失败');
}
return account;
}
}
export async function postQueryAssetPromise(challenge: Uint8Array) {
let attr: asset.AssetMap = new Map();
attr.set(asset.Tag.AUTH_CHALLENGE, challenge);
try {
await asset.postQuery(attr);
} catch (error) {
}
}

View File

@ -0,0 +1,693 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { router } from '@kit.ArkUI';
import { promptAction } from '@kit.ArkUI';
import { userAuth } from '@kit.UserAuthenticationKit';
import { addAssetPromise, removeAssetPromise, queryAssetPromise, queryAuthAssetPromise, preQueryAssetPromise, log,
postQueryAssetPromise } from '../model/AssetModel';
interface AssetItem {
alias: string;
secret: string;
dataLabel: string;
}
function isReserveClickable(reserveAccount: string, reservePassword: string, reserveLabel: string): boolean {
return reserveAccount !== '' && reservePassword !== '' && reserveLabel != '';
}
async function getAuthTokenCallback(challenge: Uint8Array,
callback: (isSuccess: boolean, challenge: Uint8Array) => void) {
const authParam: userAuth.AuthParam = {
challenge: challenge,
authType: [userAuth.UserAuthType.PIN,userAuth.UserAuthType.FACE,userAuth.UserAuthType.FINGERPRINT],
authTrustLevel: userAuth.AuthTrustLevel.ATL1,
};
const widgetParam: userAuth.WidgetParam = {
title: '请输入锁屏密码',
};
try {
let userAuthInstance = userAuth.getUserAuthInstance(authParam, widgetParam);
userAuthInstance.on('result', {
onResult(result) {
console.log('userAuthInstance callback result = ' + JSON.stringify(result));
if (result.result == 12500000) {
console.log('userAuthInstance callback result success');
callback(true, result.token)
} else if (result.result == 12500010) {
promptAction.showToast({ message: $r('app.string.please_set_pwd') })
callback(false, new Uint8Array(0))
}
}
});
userAuthInstance.start();
} catch (error) {
console.log('auth catch error: ' + JSON.stringify(error));
promptAction.showToast({ message: $r('app.string.auth_failed') })
callback(false, new Uint8Array(0))
}
}
function convertMapArrayToMapItem(array: Array<Map<string, string>>): Array<AssetItem>
{
let res = new Array<AssetItem>();
for (let dataElement of array) {
let tmp: AssetItem = {
alias: dataElement.get('alias') as string,
secret: dataElement.get('secret') as string,
dataLabel: dataElement.get('label') as string,
};
res.push(tmp)
}
return res;
}
@Entry
@Component
struct Page {
private challenge: Uint8Array = new Uint8Array;
private authToken: Uint8Array = new Uint8Array;
@State reserveAccount: string = '';
@State reservePassword: string = '';
@State reserveLabel: string = '';
@State deleteAccount: string = '';
@State deleteLabel: string = '';
@State checkAccount: string = '';
@State checkLabel: string = '';
@State eventType: string = ''
@State index: number = 0
@State assetArr: string[] = new Array<string>();
private tabsController: TabsController = new TabsController();
@State view: Visibility[] = [Visibility.Hidden, Visibility.Visible, Visibility.None];
@State indexView: number = 0
@Builder TabBuilder(index: number, name: string) {
Column() {
Row().height(this.index === index ? 8 : 17)
Text(name)
.fontColor(0x182413)
.fontSize(this.index === index ? 30 : 20)
.fontWeight(this.index === index ? 700 : 500)
.lineHeight(this.index === index ? 41 : 28)
.opacity(this.index === index ? 1 : 0.7)
.fontFamily(this.index === index ? 'HarmonyHeiTi-Bold' : 'HarmonyHeiTi-Medium')
.margin(this.index == 0 ? {left: 24} : {left: 0})
Row().height(this.index === index ? 7 : 11)
}
.width('100%')
}
build() {
Tabs({
index: 0,
controller: this.tabsController }) {
/* 保存页面 */
TabContent() {
Column() {
Column() {
/* 账号 */
Row() {
Text("*").fontColor(0xFFE84026)
Text($r('app.string.account'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.margin({ top: "2.3%", bottom: "1.1%" })
}
.width('86.7%').height('7.14%')
.margin({ top: 4 })
/* 账号输入 */
Row() {
TextInput({ placeholder: $r('app.string.please_input_account') })
.fontColor(0x182431)
.defaultFocus(false)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0.00px' })
.backgroundColor(Color.Transparent)
.borderRadius(0)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.reserveAccount = value;
})
.onEditChange((isEditing: boolean) => {
if (isEditing === true) {
this.eventType = 'Down1';
}
})
}
.width('86.7%').height('7.14%')
if (this.eventType === 'Down1') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: 12,
right: 12
})
.backgroundColor(0x182431)
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
}
/* 密码 */
Row() {
Text("*").fontColor(0xFFE84026)
Text($r('app.string.secret_or_card_num'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.fontWeight(500)
.margin({ top: "2.3%", bottom: "1.1%" })
}
.width('86.7%').height('7.14%')
/* 密码输入 */
Row() {
TextInput({ placeholder: $r('app.string.please_input_secret_or_card_num') })
.type(InputType.Password)
.defaultFocus(false)
.fontColor(0x182431)
.padding({ top: '1.70%', right: '0', bottom: '1.70%', left: '0.00px' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.reservePassword = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down2';
}
})
.margin({ top: '0.00vp', right: '-12vp', bottom: '4.00vp', left: '0.00vp' })
}
.width('86.7%').height('7.14%')
if (this.eventType === 'Down2') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431)
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
}
/* 标签 */
Row() {
Text("*").fontColor(0xFFE84026)
Text($r('app.string.label'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.fontWeight(500)
.margin({ top: "2.3%", bottom: "1.1%" })
}
.width('86.7%').height('7.14%')
/* 标签输入 */
Row() {
TextInput({ placeholder: $r('app.string.please_input_label') })
.fontColor(0x182431)
.defaultFocus(false)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0.00px' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.reserveLabel = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down3';
}
})
}
.width('86.7%').height('7.14%')
if (this.eventType === 'Down3') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431).margin({ bottom: 4})
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431').margin({ bottom: 4})
}
}
.backgroundColor(0xFFFFFF).borderRadius(16)
Blank()
Row() {
/* 底部按钮 */
Button($r('app.string.save'), { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x007DFF).fontColor('white')
.borderRadius(20)
.width('86.7%')
.height(40)
.enabled(isReserveClickable(this.reserveAccount, this.reservePassword, this.reserveLabel))
.onClick(() => {
addAssetPromise(this.reserveAccount, this.reservePassword, this.reserveLabel)
})
}
.margin({ bottom: '8.1%' })
}
.height('100%')
}
.tabBar(this.TabBuilder(0, "保存"))
/* 删除页面 */
TabContent() {
Column() {
Column() {
/* 账号 */
Row() {
Text($r('app.string.account'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.margin({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('7.14%')
.margin({ top: 4 })
/* 用户名输入 */
Row() {
TextInput({ placeholder: $r('app.string.please_input_account') })
.fontColor(0x182431)
.defaultFocus(false)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.deleteAccount = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down4';
}
})
}
.width('86.7%').height('7.14%')
if (this.eventType === 'Down4') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431)
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
}
/* 标签 */
Row() {
Text($r('app.string.label'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.fontWeight(500)
.margin({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('7.14%')
/* 标签输入 */
Row() {
TextInput({ placeholder: $r('app.string.please_input_label') })
.fontColor(0x182431)
.defaultFocus(false)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.deleteLabel = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down5';
}
})
}
.width('86.7%').height('7.14%')
if (this.eventType === 'Down5') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431).margin({ bottom: 4})
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431').margin({ bottom: 4})
}
}
.backgroundColor(0xFFFFFF).borderRadius(16)
Blank()
Row() {
/* 底部按钮 */
Button($r('app.string.delete'), { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x007DFF).fontColor('white')
.borderRadius(20)
.width('86.7%')
.height(40)
.onClick(() => {
removeAssetPromise(this.deleteAccount, this.deleteLabel)
})
}
.margin({ bottom: '8.1%' })
}
.height('100%')
}
.tabBar(this.TabBuilder(1, "删除"))
/* 更新页面 */
TabContent() {
Column() {
Column() {
List({ space: 0, initialIndex: 0 }) { // 查询列表
ForEach(this.assetArr, (item: string) => {
ListItem() {
Column() {
/* 账号 */
Row() {
Row() {
Text(item)
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.textAlign(TextAlign.Start)
.margin({ top: '2.3%', bottom: '1.1%' })
}
Blank()
Row() {
Image($r('app.media.forward'))
.height(18)
.width(9)
.fillColor('#182431')
}
}
.width('100%').height('7.14%')
.onClick(async () => {
let account = await queryAuthAssetPromise(item, this.challenge, this.authToken);
router.pushUrl({
url: 'pages/UpdatePage',
params: { name: item, pwd: account.get('secret'), label: account.get("label") }
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
return;
}
});
});
}
.margin({ right: 12, left: 12 })
}
}, (item: string) => item)
}
.listDirection(Axis.Vertical) // 排列方向
.divider({ strokeWidth: 1, color: '#0D182431', startMargin: 12, endMargin: 12 }) // 每行之间的分界线
.edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果
}
.margin({ right: 12, left: 12 })
.backgroundColor(0xFFFFFF).borderRadius(16)
}
.height('100%')
}
.tabBar(this.TabBuilder(2, "更新"))
/* 查询页面 */
TabContent() {
Column() {
Column() {
/* 账号 */
Row() {
Text($r('app.string.account'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.textAlign(TextAlign.Start)
.margin({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('7.14%')
.margin({ top: 4 })
/* 用户名输入 */
Row() {
TextInput({ placeholder: $r('app.string.please_input_account') })
.fontColor(0x182431)
.defaultFocus(false)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.checkAccount = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down6';
}
})
}
.width('86.7%').height('7.14%')
if (this.eventType === 'Down6') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431)
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
}
/* 标签 */
Row() {
Text($r('app.string.label'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.textAlign(TextAlign.Start)
.margin({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('7.14%')
/* 标签输入 */
Row() {
TextInput({ placeholder: $r('app.string.please_input_label') })
.fontColor(0x182431)
.defaultFocus(false)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.checkLabel = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down7';
}
})
}
.width('86.7%').height('7.14%')
if (this.eventType === 'Down7') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431).margin({bottom: 4})
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431').margin({bottom: 4})
}
}
.backgroundColor(0xFFFFFF).borderRadius(16)
Blank()
Row() {
/* 底部按钮 */
Button($r('app.string.search'), { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x007DFF)
.borderRadius(20)
.width('86.7%')
.height(40)
.onClick(async () => {
// 需要执行的操作
// 如果查询的时候输入了账号 则表示精确查询 进行二次访问控制
if (this.checkAccount.length > 0) {
let challenge = await preQueryAssetPromise(this.checkAccount);
if (challenge.length == 0) {
log("查询失败")
return
}
this.challenge = challenge
getAuthTokenCallback(this.challenge, async (isSuccess: boolean, authToken: Uint8Array) => {
if (isSuccess) {
console.log('get auth token success!')
this.authToken = authToken
let account = await queryAuthAssetPromise(this.checkAccount, this.challenge, this.authToken);
postQueryAssetPromise(this.challenge)
this.challenge = new Uint8Array;
this.authToken = new Uint8Array;
router.pushUrl({
url: 'pages/QueryResultPage',
params: {
data_array_map: convertMapArrayToMapItem(new Array<Map<string, string>>(account))
}
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
return;
}
});
}
});
}
else {
let accountList = queryAssetPromise(this.checkAccount, this.checkLabel);
accountList.then(data => {
router.pushUrl({
url: 'pages/QueryResultPage', // 目标url
params: {
data_array_map: convertMapArrayToMapItem(data)
}
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
});
})
}
})
}
.margin({ bottom: '8.1%' })
}
.height('100%')
}
.tabBar(this.TabBuilder(3, "查询"))
}
.scrollable(true)
.backgroundColor(0xf1f3f5)
.barMode(BarMode.Fixed)
.barHeight(56)
.onChange(async (index: number) => {
if (index == 2 && this.index != 2) {
let challenge = await preQueryAssetPromise("");
console.log('get challenge success!')
this.challenge = challenge
// get auth token
getAuthTokenCallback(this.challenge, async (isSuccess: boolean, authToken: Uint8Array) => {
if (isSuccess) {
console.log('get auth token success!')
this.authToken = authToken
let resultList = await queryAssetPromise('', '');
for (let accountListElement of resultList) {
let str = accountListElement.get('alias');
if (str !== undefined) {
this.assetArr.push(str)
}
}
}
});
} else if (this.challenge.length > 0) {
postQueryAssetPromise(this.challenge)
this.challenge = new Uint8Array;
this.authToken = new Uint8Array;
this.assetArr = new Array<string>();
}
this.index = index;
})
}
}

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { router } from '@kit.ArkUI';
interface AssetItem {
alias: string;
secret: string;
dataLabel: string;
}
@Entry
@Component
struct QueryResultPage {
@State param: Record<string, Array<AssetItem>> = router.getParams() as Record<string, Array<AssetItem>>;
@State data: Array<AssetItem> = this.param.data_array_map as Array<AssetItem>;
build() {
Column() {
Row() {
Image($r('app.media.back'))
.height(18)
.width(20)
.onClick(() => {
// 返回到上一页面相当于pop操作
router.back();
})
Text($r('app.string.search_result'))
.fontFamily("HarmonyHeiTi")
.fontSize(20)
.fontColor(0x182431)
.lineHeight(28)
.fontWeight(700)
.margin({ left: '5%' })
.textAlign(TextAlign.Center)
}
.width('86.7%').height(56)
Column() {
List({ space: 0, initialIndex: 0 }) {
ForEach(this.data, (item: AssetItem) => {
ListItem() {
Column() {
Row() {
Row() {
Text($r('app.string.account'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.margin({ top: '2.3%', bottom: '1.1%' })
}
Blank()
Row() {
Text(item.alias.toString())
.opacity(0.6)
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(19)
.fontWeight(400)
.baselineOffset(0)
.margin({ top: '2.3%', bottom: '1.1%' })
}
}
.width('86.7%').height('7.14%')
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
/* 密码/信用卡号 */
if (item.secret) {
Row() {
Row() {
Text($r('app.string.secret_or_card_num'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.textAlign(TextAlign.Start)
.padding({ top: '2.3%', bottom: '1.1%' })
}
Blank()
Row() {
Text(item.secret.toString())
.opacity(0.6)
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(19)
.fontWeight(400)
.textAlign(TextAlign.End)
.padding({ top: '2.3%', bottom: '1.1%' })
}
}.width('86.7%').height('6.7%')
}
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
/* 标签 */
Row() {
Row() {
Text($r('app.string.label2'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.baselineOffset(0)
.textAlign(TextAlign.Start)
.padding({ top: '2.3%', bottom: '1.1%' })
}
Blank()
Row() {
Text(item.dataLabel.toString())
.opacity(0.6)
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(19)
.fontWeight(400)
.baselineOffset(0)
.textAlign(TextAlign.End)
.padding({ top: '2.3%', bottom: '1.1%' })
}
}.width('86.7%').height('7.14%')
}
.margin({ left: 12, right: -12, bottom: 12 })
.backgroundColor(0xFFFFFF).borderRadius(16)
}
}, (item: AssetItem) => JSON.stringify(item))
}
.listDirection(Axis.Vertical) // 排列方向
.edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果
}
}
.width("100%").height("100%").backgroundColor(0xf1f3f5)
}
pageTransition() {
// 定义页面进入时的效果从右侧滑入时长为1000ms页面栈发生push操作时该效果才生效
PageTransitionEnter({ type: RouteType.Push, duration: 300 })
.slide(SlideEffect.Right)
// 定义页面进入时的效果从左侧滑入时长为1000ms页面栈发生pop操作时该效果才生效
PageTransitionEnter({ type: RouteType.Pop, duration: 300 })
.slide(SlideEffect.Left)
// 定义页面退出时的效果向左侧滑出时长为1000ms页面栈发生push操作时该效果才生效
PageTransitionExit({ type: RouteType.Push, duration: 300 })
.slide(SlideEffect.Left)
// 定义页面退出时的效果向右侧滑出时长为1000ms页面栈发生pop操作时该效果才生效
PageTransitionExit({ type: RouteType.Pop, duration: 300 })
.slide(SlideEffect.Right)
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { router } from '@kit.ArkUI';
import { updateAssetPromise } from '../model/AssetModel';
function isUpdateClickable(reservePassword: string): boolean {
return reservePassword !== '';
}
@Entry
@Component
struct UpdatePage {
@State params: Record<string, string> = router.getParams() as Record<string, string>;
@State user: string = this.params.name as string;
@State pwd: string = this.params.pwd as string;
@State label: string = this.params.label as string;
@State reservePassword: string = this.pwd;
@State reserveLabel: string = this.label;
@State eventType: string = ''
build() {
Column() {
Row() {
Image($r('app.media.back'))
.height(18)
.width(20)
.onClick(() => {
// 返回到上一页面相当于pop操作
router.back();
})
Text($r('app.string.update'))
.fontFamily("HarmonyHeiTi")
.fontSize(20)
.fontColor(0x182431)
.lineHeight(28)
.fontWeight(700)
.margin({ left: '5%' })
.textAlign(TextAlign.Center)
}.width('86.7%').height(56)
Column() {
/* 账号 */
Row() {
Text($r('app.string.account'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.textAlign(TextAlign.Start)
.padding({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('6.7%')
/* 用户名输入 */
Row() {
Text(this.user)
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.opacity(0.6)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(400)
.textAlign(TextAlign.Start)
.padding({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('6.7%')
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
/* 密码 */
Row() {
Text("*").fontColor(0xFFE84026)
Text($r('app.string.secret_or_card_num'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.padding({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('6.7%')
/* 密码输入 */
Row() {
TextInput({ text: this.pwd })
.type(InputType.Password)
.defaultFocus(false)
.fontColor(0x182431)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.reservePassword = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down9';
}
})
.margin({ right: -12 })
}
.width('86.7%').height('6.7%')
if (this.eventType === 'Down9') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431)
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431')
}
/* 标签 */
Row() {
Text($r('app.string.label'))
.fontFamily("HarmonyHeiTi")
.fontSize(16)
.fontColor(0x182431)
.lineHeight(22)
.fontWeight(500)
.baselineOffset(0)
.textAlign(TextAlign.Start)
.padding({ top: '2.3%', bottom: '1.1%' })
}
.width('86.7%').height('6.7%')
/* 标签输入 */
Row() {
TextInput({ text: this.label })
.defaultFocus(false)
.fontColor(0x182431)
.padding({ top: '1.70%', right: '48.00px', bottom: '1.70%', left: '0' })
.backgroundColor(Color.Transparent)
.placeholderFont({ size: 16, weight: 400, family: 'HarmonyHeiTi', style: FontStyle.Normal })
.onChange((value: string) => {
this.reserveLabel = value;
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down10';
}
})
}
.width('86.7%').height('6.7%')
if (this.eventType === 'Down10') {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor(0x182431).margin({ bottom: 4 })
} else {
Line()
.width('86.7%')
.height('0.5vp')
.margin({
left: '12vp',
right: '12vp'
})
.backgroundColor('#0D182431').margin({ bottom: 4 })
}
}
.backgroundColor(0xFFFFFF).borderRadius(16)
Blank()
Row() {
/* 底部按钮 */
Button($r('app.string.update'), { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x007DFF).fontColor('white')
.borderRadius(20)
.width('86.7%')
.height(40)
.enabled(isUpdateClickable(this.reservePassword))
.onClick(() => {
updateAssetPromise(this.user, this.reservePassword, this.reserveLabel)
})
}
.margin({ bottom: '8.1%' })
}
// .margin({ right: 12, left: 12 })
.width("100%").height('100%').backgroundColor(0xf1f3f5)
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.
*/
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"default",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:app_icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:app_icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.ACCESS_BIOMETRIC"
},
{
"name": "ohos.permission.STORE_PERSISTENT_DATA"
}
]
}
}

View File

@ -0,0 +1,32 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
},
{
"name": "column_background_color",
"value": "#F5F5F5"
},
{
"name": "custom_color",
"value": "#182431"
},
{
"name": "custom_button_color",
"value": "#007DFF"
},
{
"name": "custom_blank_color",
"value": "#F2F2F2"
},
{
"name": "place_color",
"value": "#99000000"
},
{
"name": "divider_color",
"value": "#0D182431"
}
]
}

View File

@ -0,0 +1,188 @@
{
"float": [
{
"name": "image_back_size",
"value": "26vp"
},
{
"name": "avatar_size",
"value": "56vp"
},
{
"name": "personal_font_size",
"value": "16fp"
},
{
"name": "title_hobbies_size",
"value": "20fp"
},
{
"name": "title_line_height",
"value": "28vp"
},
{
"name": "label_size",
"value": "16fp"
},
{
"name": "options_height",
"value": "22vp"
},
{
"name": "toggle_size",
"value": "20vp"
},
{
"name": "options_top_distance",
"value": "13vp"
},
{
"name": "options_bottom_distance",
"value": "13vp"
},
{
"name": "divider_height",
"value": "0.5vp"
},
{
"name": "list_top_distance",
"value": "14vp"
},
{
"name": "list_bottom_distance",
"value": "8vp"
},
{
"name": "options_list_height",
"value": "242vp"
},
{
"name": "blank_width",
"value": "1vp"
},
{
"name": "blank_opacity",
"value": "1vp"
},
{
"name": "blank_height",
"value": "25vp"
},
{
"name": "dialog_radius",
"value": "32vp"
},
{
"name": "title_left_distance",
"value": "24vp"
},
{
"name": "label_left_distance",
"value": "24vp"
},
{
"name": "dialog_left_distance",
"value": "24vp"
},
{
"name": "dialog_top_distance",
"value": "14vp"
},
{
"name": "toggle_right_distance",
"value": "24vp"
},
{
"name": "dialog_bottom_distance",
"value": "16vp"
},
{
"name": "button_text_size",
"value": "16fp"
},
{
"name": "button_height",
"value": "40vp"
},
{
"name": "text_image_size",
"value": "24vp"
},
{
"name": "image_left_distance",
"value": "12vp"
},
{
"name": "text_size",
"value": "16vp"
},
{
"name": "content_left_distance",
"value": "16vp"
},
{
"name": "content_right_distance",
"value": "7vp"
},
{
"name": "arrow_image_width",
"value": "12vp"
},
{
"name": "arrow_image_height",
"value": "24vp"
},
{
"name": "arrow_right_distance",
"value": "14vp"
},
{
"name": "row_top_distance",
"value": "24vp"
},
{
"name": "row_radius",
"value": "24vp"
},
{
"name": "row_height",
"value": "64vp"
},
{
"name": "input_image_size",
"value": "24vp"
},
{
"name": "input_image_left",
"value": "12vp"
},
{
"name": "input_text_size",
"value": "16fp"
},
{
"name": "input_left_inside",
"value": "12vp"
},
{
"name": "input_right_inside",
"value": "32vp"
},
{
"name": "text_input_height",
"value": "48vp"
},
{
"name": "input_row_top",
"value": "24vp"
},
{
"name": "input_row_radius",
"value": "24vp"
},
{
"name": "input_row_height",
"value": "64vp"
}
]
}

View File

@ -0,0 +1,8 @@
{
"boolean": [
{
"name": "boolean_1",
"value": true
}
]
}

View File

@ -0,0 +1,72 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "Main program entry"
},
{
"name": "EntryAbility_label",
"value": "MultipleWindow"
},
{
"name": "please_set_pwd",
"value": "please set screen password"
},
{
"name": "auth_failed",
"value": "auth failed"
},
{
"name": "account",
"value": "account"
},
{
"name": "please_input_account",
"value": "please input account"
},
{
"name": "secret_or_card_num",
"value": "secret or card number"
},
{
"name": "please_input_secret_or_card_num",
"value": "please input secret or card number"
},
{
"name": "label",
"value": "label(max 4, split by ;)"
},
{
"name": "label2",
"value": "label"
},
{
"name": "please_input_label",
"value": "please input label"
},
{
"name": "save",
"value": "save"
},
{
"name": "delete",
"value": "delete"
},
{
"name": "search",
"value": "search"
},
{
"name": "search_result",
"value": "search result"
},
{
"name": "update",
"value": "update"
}
]
}

View File

@ -0,0 +1,35 @@
{
"strarray": [
{
"name": "sex_array",
"value": [
{
"value": "male"
},
{
"value": "female"
}
]
},
{
"name": "hobbies_data",
"value": [
{
"value": "Soccer"
},
{
"value": "Badminton"
},
{
"value": "Travelling"
},
{
"value": "Playing games"
},
{
"value": "Reading books"
}
]
}
]
}

View File

@ -0,0 +1,7 @@
{
"src": [
"pages/Index",
"pages/QueryResultPage",
"pages/UpdatePage"
]
}

View File

@ -0,0 +1,72 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "label"
},
{
"name": "please_set_pwd",
"value": "please set screen password"
},
{
"name": "auth_failed",
"value": "auth failed"
},
{
"name": "account",
"value": "account"
},
{
"name": "please_input_account",
"value": "please input account"
},
{
"name": "secret_or_card_num",
"value": "secret or card number"
},
{
"name": "please_input_secret_or_card_num",
"value": "please input secret or card number"
},
{
"name": "label",
"value": "label(max 4, split by ;)"
},
{
"name": "label2",
"value": "label"
},
{
"name": "please_input_label",
"value": "please input label"
},
{
"name": "save",
"value": "save"
},
{
"name": "delete",
"value": "delete"
},
{
"name": "search",
"value": "search"
},
{
"name": "search_result",
"value": "search result"
},
{
"name": "update",
"value": "update"
}
]
}

View File

@ -0,0 +1,72 @@
{
"string": [
{
"name": "module_desc",
"value": "模块描述"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "label"
},
{
"name": "please_set_pwd",
"value": "请设置锁屏密码"
},
{
"name": "auth_failed",
"value": "认证失败"
},
{
"name": "account",
"value": "账号"
},
{
"name": "please_input_account",
"value": "请输入账号"
},
{
"name": "secret_or_card_num",
"value": "密码/信用卡号"
},
{
"name": "please_input_secret_or_card_num",
"value": "请输入密码/信用卡号"
},
{
"name": "label",
"value": "标签最多4个以;分隔)"
},
{
"name": "label2",
"value": "标签"
},
{
"name": "please_input_label",
"value": "请输入标签名称"
},
{
"name": "save",
"value": "保存"
},
{
"name": "delete",
"value": "删除"
},
{
"name": "search",
"value": "查询"
},
{
"name": "search_result",
"value": "查询结果"
},
{
"name": "update",
"value": "更新"
}
]
}

View File

@ -0,0 +1,126 @@
import { hilog } from '@kit.PerformanceAnalysisKit';
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
import { addAssetPromise,
postQueryAssetPromise,
preQueryAssetPromise,
queryAssetPromise,
removeAssetPromise,
updateAssetPromise } from '../../../main/ets/model/AssetModel';
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);
})
it('assertAdd', 0, async () => {
// 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 account = 'asset_account';
let pwd = 'pwd';
let label ='label1;label2';
try {
await addAssetPromise(account, pwd, label);
let delete_label = 'label1';
await removeAssetPromise(account, delete_label);
expect(0).assertEqual(0);
} catch (error) {
expect(0).assertEqual(false);
}
})
it('assertQuery', 0, async () => {
// 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 account = 'asset_account';
let pwd = 'pwd';
let label ='label1;label2';
try {
await addAssetPromise(account, pwd, label);
let query_label = 'label1';
await queryAssetPromise(account, query_label);
let delete_label = 'label1';
await removeAssetPromise(account, delete_label);
expect(0).assertEqual(0);
} catch (error) {
expect(0).assertEqual(false);
}
})
it('assertUpdate', 0, async () => {
// 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 account = 'asset_account';
let pwd = 'pwd';
let label ='label1;label2';
try {
await addAssetPromise(account, pwd, label);
let reserve_label = 'label1';
let reserve_password = '123';
await updateAssetPromise(account, reserve_password, reserve_label);
let delete_label = 'label1';
await removeAssetPromise(account, delete_label);
expect(0).assertEqual(0);
} catch (error) {
expect(0).assertEqual(false);
}
})
it('assertDelete', 0, async () => {
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
let account = 'asset_account';
let pwd = 'pwd';
let label ='label1;label2';
try {
await addAssetPromise(account, pwd, label);
let delete_label = 'label1';
await removeAssetPromise(account, delete_label);
expect(0).assertEqual(0);
} catch (error) {
expect(0).assertEqual(false);
}
})
it('assertPreQuery', 0, async () => {
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
let account = 'asset_account';
let pwd = 'pwd';
let label ='label1;label2';
try {
await addAssetPromise(account, pwd, label);
let challenge = await preQueryAssetPromise(account);
await postQueryAssetPromise(challenge);
let delete_label = 'label1';
await removeAssetPromise(account, delete_label);
expect(0).assertEqual(0);
} catch (error) {
expect(0).assertEqual(false);
}
})
})
}

View File

@ -0,0 +1,5 @@
import abilityTest from './Ability.test';
export default function testsuite() {
abilityTest();
}

View File

@ -0,0 +1,47 @@
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { abilityDelegatorRegistry } from '@kit.TestKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { Hypium } from '@ohos/hypium';
import testsuite from '../test/List.test';
export default class TestAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator;
abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator();
let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs;
abilityDelegatorArguments = abilityDelegatorRegistry.getArguments();
hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite);
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
windowStage.loadContent('testability/pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
}
onForeground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
}
onBackground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
}
}

View File

@ -0,0 +1,17 @@
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}

View File

@ -0,0 +1,90 @@
import { abilityDelegatorRegistry, TestRunner } from '@kit.TestKit';
import { UIAbility, Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { resourceManager } from '@kit.LocalizationKit';
import { util } from '@kit.ArkTS';
let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator;
let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs;
let jsonPath: string = 'mock/mock-config.json';
let tag: string = 'testTag';
async function onAbilityCreateCallback(data: UIAbility) {
hilog.info(0x0000, 'testTag', 'onAbilityCreateCallback, data: ${}', JSON.stringify(data));
}
async function addAbilityMonitorCallback(err: BusinessError) {
hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
}
export default class OpenHarmonyTestRunner implements TestRunner {
constructor() {
}
onPrepare() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare');
}
async onRun() {
let tag = 'testTag';
hilog.info(0x0000, tag, '%{public}s', 'OpenHarmonyTestRunner onRun run');
abilityDelegatorArguments = abilityDelegatorRegistry.getArguments()
abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator()
let moduleName = abilityDelegatorArguments.parameters['-m'];
let context = abilityDelegator.getAppContext().getApplicationContext().createModuleContext(moduleName);
let mResourceManager = context.resourceManager;
await checkMock(abilityDelegator, mResourceManager);
const bundleName = abilityDelegatorArguments.bundleName;
const testAbilityName: string = 'TestAbility';
let lMonitor: abilityDelegatorRegistry.AbilityMonitor = {
abilityName: testAbilityName,
onAbilityCreate: onAbilityCreateCallback,
moduleName: moduleName
};
abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
const want: Want = {
bundleName: bundleName,
abilityName: testAbilityName,
moduleName: moduleName
};
abilityDelegator.startAbility(want, (err: BusinessError, data: void) => {
hilog.info(0x0000, tag, 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');
hilog.info(0x0000, tag, 'startAbility : data : %{public}s', JSON.stringify(data) ?? '');
})
hilog.info(0x0000, tag, '%{public}s', 'OpenHarmonyTestRunner onRun end');
}
}
async function checkMock(abilityDelegator: abilityDelegatorRegistry.AbilityDelegator, resourceManager: resourceManager.ResourceManager) {
let rawFile: Uint8Array;
try {
rawFile = resourceManager.getRawFileContentSync(jsonPath);
hilog.info(0x0000, tag, 'MockList file exists');
let mockStr: string = util.TextDecoder.create("utf-8", { ignoreBOM: true }).decodeWithStream(rawFile);
let mockMap: Record<string, string> = getMockList(mockStr);
try {
abilityDelegator.setMockList(mockMap)
} catch (error) {
let code = (error as BusinessError).code;
let message = (error as BusinessError).message;
hilog.error(0x0000, tag, `abilityDelegator.setMockList failed, error code: ${code}, message: ${message}.`);
}
} catch (error) {
let code = (error as BusinessError).code;
let message = (error as BusinessError).message;
hilog.error(0x0000, tag, `ResourceManager:callback getRawFileContent failed, error code: ${code}, message: ${message}.`);
}
}
function getMockList(jsonStr: string) {
let jsonObj: Record<string, Object> = JSON.parse(jsonStr);
let map: Map<string, object> = new Map<string, object>(Object.entries(jsonObj));
let mockList: Record<string, string> = {};
map.forEach((value: object, key: string) => {
let realValue: string = value['source'].toString();
mockList[key] = realValue;
});
hilog.info(0x0000, tag, '%{public}s', 'mock-json value:' + JSON.stringify(mockList) ?? '');
return mockList;
}

View File

@ -0,0 +1,36 @@
{
"module": {
"name": "entry_test",
"type": "feature",
"description": "$string:module_test_desc",
"mainElement": "TestAbility",
"deviceTypes": [
"default"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:test_pages",
"abilities": [
{
"name": "TestAbility",
"srcEntry": "./ets/testability/TestAbility.ets",
"description": "$string:TestAbility_desc",
"icon": "$media:icon",
"label": "$string:TestAbility_label",
"exported": true,
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"skills": [
{
"actions": [
"action.system.home"
],
"entities": [
"entity.system.home"
]
}
]
}
]
}
}

View File

@ -0,0 +1,8 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}

View File

@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_test_desc",
"value": "test ability description"
},
{
"name": "TestAbility_desc",
"value": "the test ability"
},
{
"name": "TestAbility_label",
"value": "test label"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,5 @@
{
"src": [
"testability/pages/Index"
]
}

View File

@ -0,0 +1,18 @@
{
"hvigorVersion": "4.0.1",
"dependencies": {
"@ohos/hvigor-ohos-plugin": "4.0.1"
},
"execution": {
// "daemon": true, /* Enable daemon compilation. Default: true */
// "incremental": true, /* Enable incremental compilation. Default: true */
// "parallel": true, /* Enable parallel compilation. Default: true */
// "typeCheck": false, /* Enable typeCheck. Default: false */
},
"logging": {
// "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
},
"debugging": {
// "stacktrace": false /* Disable stacktrace compilation. Default: false */
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
import { appTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}

View File

@ -0,0 +1,63 @@
#!/bin/bash
# ----------------------------------------------------------------------------
# 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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# 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}" "$@"

View File

@ -0,0 +1,77 @@
:: 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.
@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

View File

@ -0,0 +1,15 @@
{
"lockfileVersion": 3,
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
"specifiers": {
"@ohos/hypium@1.0.6": "@ohos/hypium@1.0.6"
},
"packages": {
"@ohos/hypium@1.0.6": {
"integrity": "sha512-bb3DWeWhYrFqj9mPFV3yZQpkm36kbcK+YYaeY9g292QKSjOdmhEIQR2ULPvyMsgSR4usOBf5nnYrDmaCCXirgQ==",
"resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.6.tgz",
"shasum": "3f5fed65372633233264b3447705b0831dfe7ea1",
"registryType": "ohpm"
}
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.
*/
{
"license": "",
"devDependencies": {
"@ohos/hypium": "1.0.6"
},
"author": "",
"name": "asset_sample",
"description": "Please describe the basic information.",
"main": "",
"version": "1.0.0",
"dynamicDependencies": {},
"dependencies": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB