Feat: finish database

This commit is contained in:
cheliangzhao 2023-10-08 17:49:17 +08:00
commit d358a2191d
78 changed files with 1415 additions and 0 deletions

11
.gitignore vendored Normal file
View File

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

10
AppScope/app.json5 Normal file
View File

@ -0,0 +1,10 @@
{
"app": {
"bundleName": "com.example.myapplication",
"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": "MyApplication"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# Template
## database
注意事项
1. 由于`constructor()`函数不能异步,因此在其中调用`getRdbStore()`
函数时,不能保证新建对象后,数据库初始化完毕。那么此时如果调用`insert`等方法时,`RdbStore`可能还是`undefined`,导致数据库操作失败。
因此,只能使用回调来处理。
```typescript
aboutToAppear() {
this.todoTable.getRdbStore(async () => {
const res = await this.todoTable.query();
this.todoList = res;
})
}
```

50
build-profile.json5 Normal file
View File

@ -0,0 +1,50 @@
{
"app": {
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"certpath": "C:\\Users\\Administrator\\.ohos\\config\\auto_debug_harmony4-template_com.example.myapplication_70086000144137060.cer",
"storePassword": "0000001BF0C7503EEB99D5360C10359536D3353B1F2C39787692D0526594DC53D8A9103366AB1B58CACBB6",
"keyAlias": "debugKey",
"keyPassword": "0000001BFE46D113BE907F9B0F067070188C870CD7DD7AF4E6CB9720F5CC73389CC6DE77567D43876B55FB",
"profile": "C:\\Users\\Administrator\\.ohos\\config\\auto_debug_harmony4-template_com.example.myapplication_70086000144137060.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "C:\\Users\\Administrator\\.ohos\\config\\auto_debug_harmony4-template_com.example.myapplication_70086000144137060.p12"
}
}
],
"products": [
{
"name": "default",
"signingConfig": "default",
"compileSdkVersion": "4.0.0(10)",
"compatibleSdkVersion": "4.0.0(10)",
"runtimeOS": "HarmonyOS",
}
],
"buildModeSet": [
{
"name": "debug",
},
{
"name": "release"
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{
"name": "default",
"applyToProducts": [
"default"
]
}
]
}
]
}

BIN
dependencies/hvigor-3.0.4-s.tgz vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dependencies/rollup.tgz vendored Normal file

Binary file not shown.

6
entry/.gitignore vendored Normal file
View File

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

31
entry/build-profile.json5 Normal file
View File

@ -0,0 +1,31 @@
{
"apiType": "stageMode",
"buildOption": {
"arkOptions": {
// "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
}
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"files": [
"./obfuscation-rules.txt"
]
}
}
}
},
],
"targets": [
{
"name": "default"
},
{
"name": "ohosTest",
}
]
}

6
entry/hvigorfile.ts Normal file
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,18 @@
# Define project specific obfuscation rules here.
# You can include the obfuscation configuration files in the current module's build-profile.json5.
#
# For more details, see
# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
# Obfuscation options:
# -disable-obfuscation: disable all obfuscations
# -enable-property-obfuscation: obfuscate the property names
# -enable-toplevel-obfuscation: obfuscate the names in the global scope
# -compact: remove unnecessary blank spaces and all line feeds
# -remove-log: remove all console.* statements
# -print-namecache: print the name cache that contains the mapping from the old names to new names
# -apply-namecache: reuse the given cache file
# Keep options:
# -keep-property-name: specifies property names that you want to keep
# -keep-global-name: specifies names that you want to keep in the global scope

10
entry/oh-package.json5 Normal file
View File

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

View File

@ -0,0 +1,23 @@
import relationalStore from '@ohos.data.relationalStore'
export const DatabaseConfig = {
RDB_TAG: '[Debug.Rdb]',
STORE_CONFIG: {
name: "RdbTest.db",
securityLevel: relationalStore.SecurityLevel.S1,
},
TODO_TABLE: {
tableName: 'todoTable',
sqlCreate: `CREATE TABLE IF NOT EXISTS todoTable(
id INTEGER PRIMARY KEY AUTOINCREMENT,
status INTEGER,
content TEXT
)`,
columns: ['id', 'status', 'content'],
update1to3: ``,
update2to3: ``,
},
}

View File

@ -0,0 +1,89 @@
import relationalStore from '@ohos.data.relationalStore';
import { Logger } from '../../utils/Logger';
import { DatabaseConfig } from '../config';
export default class Rdb {
private rdbStore: relationalStore.RdbStore = null;
private tableName: string;
private sqlCreateTable: string;
private columns: Array<string>;
constructor({tableName, sqlCreateTable, columns}: {
tableName: string,
sqlCreateTable: string,
columns: Array<string>
}) {
this.tableName = tableName;
this.sqlCreateTable = sqlCreateTable;
this.columns = columns;
}
getRdbStore(callback) {
const context = getContext(this)
relationalStore.getRdbStore(context, DatabaseConfig.STORE_CONFIG, (err, store) => {
if (err) {
console.error('getRdbStore() failed, error: ' + err)
return
}
this.rdbStore = store;
console.info('getRdbStore() finished.');
callback(store)
})
}
async insertData(data) {
const valueBucket = data
try {
const rowId = await this.rdbStore.insert(this.tableName, valueBucket);
console.info("insertData() finished: ", rowId)
return rowId;
} catch (err) {
console.error('insertData() failed, err: ' + err)
}
}
async updateData(predicates, data) {
const valueBucket = data;
try {
const rows = await this.rdbStore.update(valueBucket, predicates);
console.info('updateData() finished: ' + rows);
return rows;
} catch (err) {
console.error('update() failed, err: ' + err);
}
}
async deleteData(predicates) {
try {
const rows = await this.rdbStore.delete(predicates);
console.info('deleteData() finished: ' + rows);
return rows;
} catch (err) {
console.error('deleteData() failed, err: ' + err);
}
}
async query(predicates, mapFunc: Function) {
try {
const resSet = await this.rdbStore.query(predicates, this.columns)
const resList = [];
while (resSet.goToNextRow()) {
const resKeys = this.columns;
const resItem = mapFunc(resKeys, resSet);
resList.push(resItem);
}
resSet.close()
return resList;
}
catch (err) {
console.error('query() failed, err: ', err);
}
}
}

View File

@ -0,0 +1,89 @@
import { TodoData } from '../../types/Todo'
import { DatabaseConfig } from '../../config'
import Rdb from '../Rdb'
import relationalStore from '@ohos.data.relationalStore'
export default class TodoTable {
private todoTable = new Rdb({
tableName: DatabaseConfig.TODO_TABLE.tableName,
sqlCreateTable: DatabaseConfig.TODO_TABLE.sqlCreate,
columns: DatabaseConfig.TODO_TABLE.columns
})
constructor(callback: Function = () => {
}) {
this.getRdbStore(callback)
}
public getRdbStore(callback) {
// 假设当前数据库版本为3
this.todoTable.getRdbStore(store => {
// 当数据库创建时数据库版本默认为0
if (store.version == 0) {
store.executeSql(DatabaseConfig.TODO_TABLE.sqlCreate);
store.version = 3;
}
// 如果数据库版本不为0且和当前数据库的版本不匹配需要进行升降级操作
// 当数据库存在并假定版本为1时将其升级为2版本
if (store.version != 3 && store.version == 1) {
store.executeSql(DatabaseConfig.TODO_TABLE.update1to3);
store.version = 2;
}
// 当数据库存在并假定版本为2时将其升级为3版本
if (store.version != 3 && store.version == 2) {
store.executeSql(DatabaseConfig.TODO_TABLE.update2to3);
store.version = 3;
}
if (callback) callback()
})
}
async insertData(data: TodoData) {
return await this.todoTable.insertData(generateBucket(data));
}
async updateData(data: TodoData) {
const valueBucket = generateBucket(data)
const predicates = new relationalStore.RdbPredicates(DatabaseConfig.TODO_TABLE.tableName)
predicates.equalTo('id', data.id)
return await this.todoTable.updateData(predicates, valueBucket)
}
async deleteData(data: TodoData) {
const predicates = new relationalStore.RdbPredicates(DatabaseConfig.TODO_TABLE.tableName);
predicates.equalTo('id', data.id);
return await this.todoTable.deleteData(predicates);
}
async query(content: string = ''): Promise<TodoData[]> {
const predicates = new relationalStore.RdbPredicates(DatabaseConfig.TODO_TABLE.tableName)
if (content) {
predicates.contains('content', content)
} else {
predicates.glob('content', '*')
}
return await this.todoTable.query(predicates, (_, resSet: relationalStore.ResultSet): TodoData => {
return {
id: resSet.getLong(resSet.getColumnIndex('id')),
status: resSet.getLong(resSet.getColumnIndex('status')),
content: resSet.getString(resSet.getColumnIndex('content')),
}
})
}
}
function generateBucket(account: TodoData) {
let obj = {};
DatabaseConfig.TODO_TABLE.columns.forEach((item) => {
if (item != 'id') {
obj[item] = account[item];
}
});
return obj;
}

View File

@ -0,0 +1,5 @@
export interface TodoData {
id: number,
status: number,
content: string,
}

View File

@ -0,0 +1,45 @@
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
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/TodoPage', (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,17 @@
@Preview
@Entry
@Component
struct Index {
@State message: string = "This a Template Project"
build() {
Row() {
Text(this.message)
.fontSize(40)
.fontWeight(FontWeight.Bold)
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}

View File

@ -0,0 +1,207 @@
/**
* Database apply demo
*/
import TodoTable from '../common/database/tables/TodoTable';
import { TodoData } from '../common/types/Todo';
import { Logger } from '../utils/Logger';
@Preview
@Entry
@Component
struct TodoPage {
@State searchText: string = ''
@State isInput: boolean = false
@State isEdit: boolean = false
@State todoList: TodoData[] = []
@State selectedItems: number[] = []
private todoTable = new TodoTable()
searchController: SearchController = new SearchController();
addDialogController: CustomDialogController = new CustomDialogController({
builder: AddTodoDialog({ submit: (value) => this.handleAddSubmit(value) }),
cancel: () => this.isInput = false,
})
aboutToAppear() {
this.todoTable.getRdbStore(async () => {
const res = await this.todoTable.query();
this.todoList = res;
})
}
handleSearchSubmit(value: string) {
this.isInput = false
}
handleDelete() {
if (this.selectedItems.length === 0) return
AlertDialog.show({
message: '确认删除',
primaryButton: {
value: "取消",
action: () => {
},
defaultFocus: true,
},
secondaryButton: {
value: "删除",
action: () => {
this.todoList
.filter(item => this.selectedItems.includes(item.id))
.forEach(item => {
this.todoTable.deleteData(item);
this.todoList = this.todoList.filter(data => data !== item);
});
this.selectedItems = [];
this.isEdit = false;
},
}
})
}
async handleAddSubmit(value: string) {
this.isInput = false;
const data = {
id: 0,
content: value,
status: 0,
}
data.id = await this.todoTable.insertData(data);
this.todoList.push(data)
}
build() {
Stack() {
Column() {
Row() {
Text('Todo List')
.fontWeight(FontWeight.Bold)
.fontSize(24)
Image($rawfile('ic_public_edit.svg'))
.width(24)
.height(24)
.onClick(() => {
if (this.todoList.length > 0) {
this.isEdit = !this.isEdit;
}
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.margin({ top: 12, bottom: 12 })
Row() {
Search({
value: this.searchText,
placeholder: 'Search',
controller: this.searchController,
})
.enableKeyboardOnFocus(false)
.onChange(value => this.searchText = value)
.onSubmit(value => this.handleSearchSubmit(value))
}
.width('100%')
Column() {
ForEach(this.todoList, (item: TodoData) => {
TodoItem({ data: item, showSelect: this.isEdit, selectedItems: this.selectedItems })
})
}
.margin({ top: 12 })
.width('100%')
.alignItems(HorizontalAlign.Start)
}
.padding({ left: 12, right: 12 })
if (!this.isEdit && !this.isInput) {
Button() {
Image($rawfile('add.png'))
}
.width(48)
.height(48)
.markAnchor({ x: '50%', y: '50%' })
.position({ x: '90%', y: '95%' })
.onClick(() => {
this.isInput = true;
this.addDialogController.open()
})
}
if (this.isEdit && this.selectedItems.length > 0) {
Button() {
Image($rawfile('delete.png'))
}
.backgroundColor(Color.Red)
.width(48)
.height(48)
.markAnchor({ x: '50%', y: '50%' })
.position({ x: '90%', y: '95%' })
.onClick(() => this.handleDelete())
}
}
.height('100%')
.width('100%')
.alignContent(Alignment.Top)
}
}
@CustomDialog
struct AddTodoDialog {
submit: (value) => void = () => {
}
@State inputValue: string = ''
controller: CustomDialogController = new CustomDialogController({
builder: AddTodoDialog({
submit: this.submit,
}),
})
build() {
Column() {
Stack() {
TextInput({ placeholder: "please input" })
.onChange(value => this.inputValue = value)
Button("Submit")
.type(ButtonType.Normal)
.markAnchor({ x: "100%" })
.position({ x: '100%' })
.onClick(() => {
this.submit(this.inputValue);
this.controller.close();
})
}
}
}
}
@Preview
@Component
struct TodoItem {
@Prop data: TodoData = { id: 0, content: "test", status: 0 }
@Prop showSelect: boolean = true
@Link selectedItems: number[]
build() {
Row() {
Text(this.data.content)
.padding({ left: 8, right: 8 })
if (this.showSelect) {
Checkbox()
.onChange(value => {
if (value) {
this.selectedItems = [...this.selectedItems, this.data.id]
} else {
this.selectedItems = this.selectedItems.filter(i => i !== this.data.id)
}
})
}
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height(30)
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2022 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 { ILogger } from './ILogger';
export class ConsoleLogger implements ILogger {
private appIdentifier: string;
constructor(appIdentifier: string) {
this.appIdentifier = appIdentifier;
}
public verbose(tag: string, msg: string): void {
console.log(`[${this.appIdentifier}] [verbose] tag:${tag} msg:${msg}`);
}
public debug(tag: string, msg: string): void {
console.debug(`[${this.appIdentifier}] [debug] tag:${tag} msg:${msg}`);
}
public info(tag: string, msg: string): void {
console.info(`[${this.appIdentifier}] [info] tag:${tag} msg:${msg}`);
}
public warn(tag: string, msg: string): void {
console.warn(`[${this.appIdentifier}] [warn] tag:${tag} msg:${msg}`);
}
public error(tag: string, msg: string): void {
console.error(`[${this.appIdentifier}] [error] tag:${tag} msg:${msg}`);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License,Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import hilog from '@ohos.hilog';
import { ILogger } from './ILogger';
export class HiLogger implements ILogger {
private appIdentifier: string;
private appDomain: number = 0x0001;
constructor(appIdentifier: string) {
this.appIdentifier = appIdentifier;
}
public verbose(tag: string, msg: string): void {
hilog.debug(this.appDomain, tag, `[${this.appIdentifier}] [verbose] tag:${tag} msg:${msg}`);
}
public debug(tag: string, msg: string): void {
hilog.debug(this.appDomain, tag, `[${this.appIdentifier}] [debug] tag:${tag} msg:${msg}`);
}
public info(tag: string, msg: string): void {
hilog.info(this.appDomain, tag, `[${this.appIdentifier}] [info] tag:${tag} msg:${msg}`);
}
public warn(tag: string, msg: string): void {
hilog.warn(this.appDomain, tag, `[${this.appIdentifier}] [warn] tag:${tag} msg:${msg}`);
}
public error(tag: string, msg: string): void {
hilog.error(this.appDomain, tag, `[${this.appIdentifier}] [error] tag:${tag} msg:${msg}`);
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2022 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 interface ILogger {
verbose(tag: string, msg: string): void;
debug(tag: string, msg: string): void;
info(tag: string, msg: string): void;
warn(tag: string, msg: string): void;
error(tag: string, msg: string): void;
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2022 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 { ILogger } from './ILogger';
import { HiLogger } from './HiLogger';
import { ConsoleLogger } from './ConsoleLogger';
export class Logger implements ILogger {
private static isAppDebugMode: boolean = true;
private static appIdentifier: string = 'RDB';
// if true, use a ConsoleLogger, or HiLogger otherwise
private static useConsoleLogger: boolean = true;
public static verbose(tag: string, msg: string): void {
if (Logger.isAppDebugMode) {
Logger.getInstance().verbose(tag, msg);
}
}
public static debug(tag: string, msg: string): void {
if (Logger.isAppDebugMode) {
Logger.getInstance().debug(tag, msg);
}
}
public static info(tag: string, msg: string): void {
if (Logger.isAppDebugMode) {
Logger.getInstance().info(tag, msg);
}
}
public static warn(tag: string, msg: string): void {
if (Logger.isAppDebugMode) {
Logger.getInstance().warn(tag, msg);
}
}
public static error(tag: string, msg: string): void {
if (Logger.isAppDebugMode) {
Logger.getInstance().error(tag, msg);
}
}
private static logger: ILogger;
private constructor() {
}
public static getInstance(): Logger {
if (!Logger.logger) {
Logger.logger = Logger.useConsoleLogger
? new ConsoleLogger(Logger.appIdentifier)
: new HiLogger(Logger.appIdentifier);
}
return Logger.logger;
}
public verbose(tag: string, msg: string): void {
Logger.logger.verbose(tag, msg);
}
public debug(tag: string, msg: string): void {
Logger.debug(tag, msg);
}
public info(tag: string, msg: string): void {
Logger.info(tag, msg);
}
public warn(tag: string, msg: string): void {
Logger.warn(tag, msg);
}
public error(tag: string, msg: string): void {
Logger.error(tag, msg);
}
}

View File

@ -0,0 +1,10 @@
@Observed
export class ObservedArray<T> extends Array<T> {
constructor(args?: T[]) {
if (args instanceof Array) {
super(...args)
} else {
super()
}
}
}

View File

View File

@ -0,0 +1,38 @@
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
// "tablet",
// "2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
]
}
}

View File

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

View File

@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "label"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

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

View File

@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "label"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Public/ic_public_edit</title>
<defs>
<path d="M16.8722296,2 C17.8721999,2 18.5147251,2.05839169 19.0012003,2.17117304 L17.6599443,3.51379911 C17.4862664,3.50652642 17.2928183,3.5022817 17.0753381,3.50070655 L7.02453833,3.50017541 L6.82305804,3.501658 C5.6550816,3.5151767 5.20981608,3.61305534 4.75370688,3.8569852 C4.36325774,4.06579969 4.06579969,4.36325774 3.8569852,4.75370688 C3.59290159,5.24750032 3.5,5.72858421 3.5,7.1277704 L3.50017541,16.9754617 L3.501658,17.176942 C3.5151767,18.3449184 3.61305534,18.7901839 3.8569852,19.2462931 C4.06579969,19.6367423 4.36325774,19.9342003 4.75370688,20.1430148 C5.22281064,20.3938942 5.68044405,20.4902819 6.92466189,20.4992935 L16.8722296,20.5 C18.2714158,20.5 18.7524997,20.4070984 19.2462931,20.1430148 C19.6367423,19.9342003 19.9342003,19.6367423 20.1430148,19.2462931 C20.3938942,18.7771894 20.4902819,18.319556 20.4992935,17.0753381 L20.5,16.8722296 L20.5,7.1277704 C20.5,6.82616011 20.4956832,6.56721095 20.4862654,6.34159954 L21.8289375,4.9992768 C21.9416465,5.48569497 22,6.12812696 22,7.1277704 L22,16.8722296 L21.9989932,17.0937657 C21.9842401,18.674381 21.8076257,19.3015622 21.487765,19.91208 L21.4657346,19.9536914 C21.1337211,20.5745027 20.6538954,21.0680807 20.0458564,21.4148264 L19.9536914,21.4657346 C19.3018396,21.8143488 18.6552671,22 16.8722296,22 L7.1277704,22 L6.90623426,21.9989932 C5.32561899,21.9842401 4.69843783,21.8076257 4.08791999,21.487765 L4.04630859,21.4657346 C3.42549731,21.1337211 2.93191931,20.6538954 2.58517358,20.0458564 L2.53426541,19.9536914 C2.18565122,19.3018396 2,18.6552671 2,16.8722296 L2,7.1277704 C2,5.38266989 2.17783521,4.72618864 2.51223497,4.08791999 L2.53426541,4.04630859 C2.86627892,3.42549731 3.34610464,2.93191931 3.9541436,2.58517358 L4.04630859,2.53426541 C4.69816044,2.18565122 5.34473292,2 7.1277704,2 L16.8722296,2 Z M22.2300776,1.76992245 C22.6206018,2.16044674 22.6206018,2.79361172 22.2300776,3.18413601 L12.684136,12.7300776 C12.2936117,13.1206018 11.6604467,13.1206018 11.2699224,12.7300776 C10.8793982,12.3395533 10.8793982,11.7063883 11.2699224,11.315864 L20.815864,1.76992245 C21.2063883,1.37939815 21.8395533,1.37939815 22.2300776,1.76992245 Z" id="path-1"></path>
</defs>
<g id="Public/ic_public_edit" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="形状结合" fill="#182431" fill-rule="nonzero" xlink:href="#path-1"></use>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Public/ic_public_input_search</title>
<defs>
<path d="M13.2129889,12.2463949 L15.6099466,14.6430364 C15.9028398,14.9359296 15.9028398,15.4108033 15.6099466,15.7036966 C15.3170533,15.9965898 14.8421796,15.9965898 14.5492864,15.7036966 L12.1424668,13.2968868 C12.5355197,12.9856781 12.8945263,12.6333482 13.2129889,12.2463949 Z M7.78125,1.5 C11.2502886,1.5 14.0625,4.31221142 14.0625,7.78125 C14.0625,9.23090694 13.571411,10.5658639 12.7465202,11.6288338 L12.5852288,11.8281795 C12.5065389,11.9214937 12.4251798,12.0124834 12.3412691,12.1010309 L12.2391385,12.2063305 L12.2391385,12.2063305 L12.1042686,12.3381988 L12.1042686,12.3381988 L12.0040285,12.4312816 L12.0040285,12.4312816 L11.7500714,12.6500283 L11.7500714,12.6500283 L11.6103411,12.7608162 L11.6103411,12.7608162 C10.5503999,13.5770819 9.22251268,14.0625 7.78125,14.0625 C4.31221142,14.0625 1.5,11.2502886 1.5,7.78125 C1.5,4.31221142 4.31221142,1.5 7.78125,1.5 Z M7.78125,3 C5.14063854,3 3,5.14063854 3,7.78125 C3,10.4218615 5.14063854,12.5625 7.78125,12.5625 C10.4218615,12.5625 12.5625,10.4218615 12.5625,7.78125 C12.5625,5.14063854 10.4218615,3 7.78125,3 Z" id="path-1"></path>
</defs>
<g id="Public/ic_public_input_search" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="形状" fill-rule="nonzero"></g>
<g id="编组" mask="url(#mask-2)" fill="#000000" fill-opacity="0.9">
<g id="Symbol/color-light/colorPrimary">
<rect id="color/#000000" x="0" y="0" width="18" height="18"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_desc",
"value": "模块描述"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "label"
}
]
}

View File

@ -0,0 +1,35 @@
import hilog from '@ohos.hilog';
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);
})
})
}

View File

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

View File

@ -0,0 +1,50 @@
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
import hilog from '@ohos.hilog';
import { Hypium } from '@ohos/hypium';
import testsuite from '../test/List.test';
import window from '@ohos.window';
import Want from '@ohos.app.ability.Want';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
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, 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() {
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,47 @@
import hilog from '@ohos.hilog';
import TestRunner from '@ohos.application.testRunner';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
import Want from '@ohos.app.ability.Want';
let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator | undefined = undefined
let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs | undefined = undefined
async function onAbilityCreateCallback() {
hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
}
async function addAbilityMonitorCallback(err : Error) {
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() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
const bundleName = abilityDelegatorArguments.bundleName;
const testAbilityName = 'TestAbility';
let lMonitor: AbilityDelegatorRegistry.AbilityMonitor = {
abilityName: testAbilityName,
onAbilityCreate: onAbilityCreateCallback,
};
abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
const want: Want = {
bundleName: bundleName,
abilityName: testAbilityName
};
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.startAbility(want, (err, data) => {
hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');
hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? '');
})
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
}
}

View File

@ -0,0 +1,38 @@
{
"module": {
"name": "entry_test",
"type": "feature",
"description": "$string:module_test_desc",
"mainElement": "TestAbility",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"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: 6.6 KiB

View File

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

View File

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

View File

@ -0,0 +1,33 @@
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
export default function localUnitTest() {
describe('localUnitTest',() => {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(() => {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
});
beforeEach(() => {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
});
afterEach(() => {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
});
afterAll(() => {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
});
it('assertContain', 0, () => {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
let a = 'abc';
let b = 'b';
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
expect(a).assertContain(b);
expect(a).assertEqual(a);
});
});
}

View File

@ -0,0 +1,19 @@
{
"hvigorVersion": "file:../dependencies/hvigor-3.0.4-s.tgz",
"dependencies": {
"@ohos/hvigor-ohos-plugin": "file:../dependencies/hvigor-ohos-plugin-3.0.4-s.tgz",
"rollup": "file:../dependencies/rollup.tgz"
},
"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 */
}
}

1
hvigor/hvigor-wrapper.js Normal file

File diff suppressed because one or more lines are too long

6
hvigorfile.ts Normal file
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. */
}

48
hvigorw Normal file
View File

@ -0,0 +1,48 @@
#!/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}" "$@"

64
hvigorw.bat Normal file
View File

@ -0,0 +1,64 @@
@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

13
oh-package-lock.json5 Normal file
View File

@ -0,0 +1,13 @@
{
"lockfileVersion": 1,
"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": {
"resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.6.tgz",
"integrity": "sha512-bb3DWeWhYrFqj9mPFV3yZQpkm36kbcK+YYaeY9g292QKSjOdmhEIQR2ULPvyMsgSR4usOBf5nnYrDmaCCXirgQ=="
}
}
}

12
oh-package.json5 Normal file
View File

@ -0,0 +1,12 @@
{
"license": "",
"devDependencies": {
"@ohos/hypium": "1.0.6"
},
"author": "",
"name": "myapplication",
"description": "Please describe the basic information.",
"main": "",
"version": "1.0.0",
"dependencies": {}
}