diff --git a/LICENSE b/LICENSE
index 0210352ae2ade0dd7b4c841cb6e8ba08b4780038..18795a48d6b12fcdc1aa7bac9a9cb99f83815267 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
- Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved.
+ Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.en.md b/README.en.md
index f61d5631673dc12d340073a6014996e686a2d346..df00ed4b50e5e706e9f7254163f7765ec5c9b122 100644
--- a/README.en.md
+++ b/README.en.md
@@ -1,36 +1,77 @@
-# Distributed Contacts
+# Realization of address book function based on distributed key-value database
-## Introduction
+## Overview
-With Contacts as an example, this codelab will show you how to create a distributed key-value (KV) store and how to add, delete, modify, query, and synchronize data.
+Taking the address book as an example, this Codelab introduces the creation of distributed key-value database, data addition/deletion/modification/query/synchronization and other operation methods.
-
+## Preview
-## Concepts
+| Add | Delete | Edit | Query |
+|:----------------------------------------------------------------:|:----------------------------------------------------------------:|:-----------------------------------------------------------------:|:------------------------------------------------------------------:|
+|
|
|
|
|
-- Application data persistence: refers to the process in which applications save data in the memory to devices in the form of files or databases. The data in the memory is usually saved in the forms of data structs or data objects, while the data in storage media can be saved in the forms of text, databases, or binary files.
-- KV store: stores data in the form of KV pairs. It allows you to store data organized in a simple model, for example, product names and prices, or employee IDs and daily attendances. The simple data structure allows higher compatibility with different database versions and device types.
-- **@ohos.data.distributedKVStore** (distributed KV store): provides the distributed synergy capability between databases of different devices for applications. By calling the APIs, applications can save data to a distributed KV store and perform operations, such as adding, deleting, modifying, and querying data in the distributed KV store.
+## How to Use
-## Permissions
+1. Apply the home page, click the "Add" button in the upper right corner to enter the new contact page.
+2. Apply the homepage, click the "More" button in the upper right corner, and click "Bulk Delete" to enter the contact bulk deletion page.
+3. On the application homepage, click on the contact list to enter the contact details page.
+4. Contact details page, click the "edit" button below to enter the contact editing page.
+5. Contact details page, click the "Delete" button below to delete the contact.
+6. Create a new contact page, enter the contact information, click the "Save" button on the upper right to add a contact, and the page will jump to the application home page.
+7. On the contact bulk deletion page, click Select All below to select/uncheck all contacts.
+8. Batch contact deletion page, select the contact to be deleted, and click "Delete" below to delete the selected contact.
+9. On the contact editing page, after editing the contact information, click the "Save" button in the upper right corner to modify the contact information.
-This codelab uses the distributed capability. It requires you to add the data exchange permission **ohos.permission.DISTRIBUTED_DATASYNC** to the **module.json5** configuration file.
+## Project Directory
+```
+├──entry/src/main/ets
+│ ├──common
+│ │ └──CommonConstants.ets // Constant set
+│ ├──components
+│ │ ├──ContactBottomBar.ets // Tab component at the bottom of address book deletion page
+│ │ ├──ContactDeleteDialog.ets // Address Book Delete Pop-up Component
+│ │ ├──ContactDetailItem.ets // List Item Component of Address Book Details Page
+│ │ ├──ContactDeviceDialog.ets // Address book device pop-up component
+│ │ └──ContactListItem.ets // Address Book List Page List Item Component
+│ ├──entryability
+│ │ └──EntryAbility.ets // Entry file
+│ ├──pages
+│ │ ├──ContactAddAndEditPage.ets // Address Book Add and Edit Page
+│ │ ├──ContactDeletePage.ets // Address book deletion page
+│ │ ├──ContactDetailPage.ets // Address book details page
+│ │ └──ContactHomePage.ets // Address book home page
+│ ├──utils
+│ │ ├──ContactDeviceManager.ets // Address book device management class
+│ │ └──KvManager.ets // Key-value database management class
+│ └──viewmodel
+│ └──ContactViewModel.ets // Address book model
+└──entry/src/main/resources // Resource file
+```
-## How to Use
+## How to Implement
+
+1. Encapsulate the device management class, and call the device management capability distributedDeviceManager.CreateDeviceManager() to create a device management instance. The device management instance is the call portal of the distributed device management method, which is used to obtain the relevant information of trusted devices and local devices.
+2. Register the device state callback through the on('deviceStateChange') event of the DeviceManager, so as to inform the trusted devices in the application network of changes in the device state in time, register the device state change callback function, and perform different methods to update the device list according to the returned device state data.action.
+3. Register the device status callback through the on('discoverSuccess') event of the device management instance DeviceManager, so as to inform the change of the devices in the application network in time when it is discovered, and call startDiscovering() to discover the surrounding devices.
+4. If the device is in the list of trusted devices, execute the startAbility() method to start the application on the connected device, and send the current device information as a parameter to the connected device. If the device is not a trusted device, execute the bindTarget() method to start the verification. At this time, the connecting device prompts whether to accept or not. After receiving the connection, the connecting device displays the PIN code, and the local device enters the PIN code to confirm that the connection is successful. Click the Query Device button again, select the connected device, and click OK to start the application on the connected device.
+5. When the device pop-up window is closed, stopDiscovering() is called to stop discovering peripheral devices, and the device monitoring task is cancelled through the off('deviceStateChange') and off('discoverSuccess') events of the device management instance DeviceManager.
+6. When the application starts for the first time, call the requestPermissionsFromUser() method to dynamically pop up the window to get authorization.
+7. Create a key-value database object instance, call distributedKvStore.createKvManager() to create a KVManager object instance, which is used to manage database objects, and call the getKVStore() method of KVManager to create and obtain a distributed key-value database.
+8. Call the on('dataChange') interface to subscribe to the data changes of other devices in the networking, and register the data change callback function.
+9. Encapsulates six methods of adding(put()), deleting(delete(), deleteBatch()), changing(put()), checking(get()), getEntries()) to the operation database.
+10. Call the interface sync() of synchronous data to push the current device data change to other devices in the networking.
+11. When the data of other devices in the networking changes, the callback function is executed, and all the data of the devices with data changes are obtained through the self-defined getAllData() method, so as to update the local data.
+
+## Permissions
-1. On the app home page, tap the Add button in the upper right corner. You can create a contact.
-2. On the app home page, tap the More button in the upper right corner and then Batch Delete. You can delete contacts in batches.
-3. On the app home page, tap Contacts to access the contact details page.
-4. On the contact details page, tap Edit to edit a contact.
-5. On the contact details page, tap Delete to delete a contact.
-6. On the New Contact page, enter the contact information and tap Save in the upper right corner to add a contact.
-7. On the batch deletion page, tap Select All to select or deselect all contacts.
-8. On the batch deletion page, select the contacts to be deleted and tap Delete.
-9. On the contact editing page, edit the contact information and tap Save in the upper right corner. You can modify the contact information.
+This Codelab uses distributed capabilities, and it needs to add data exchange permissions between different devices in the configuration file module.json5.:ohos.permission.DISTRIBUTED_DATASYNC。
## Constraints
1. The sample is only supported on Huawei phones with standard systems.
-2. HarmonyOS: HarmonyOS 5.0.5 Release or later.
-3. DevEco Studio: DevEco Studio 5.0.5 Release or later.
-4. HarmonyOS SDK: HarmonyOS 5.0.5 Release SDK or later.
+2. The HarmonyOS version must be HarmonyOS 5.1.1 Release or later.
+3. The DevEco Studio version must be DevEco Studio 5.1.1 Release or later.
+4. The HarmonyOS SDK version must be HarmonyOS 5.1.1 Release SDK or later.
+5. Double-ended devices need to log in to the same Huawei account, so it is recommended to turn on the device finding function.
+6. Double-ended devices need to turn on the Wi-Fi and Bluetooth switches. When conditions permit, it is recommended to connect to the same LAN.
+7. Both end devices need this application.
diff --git a/README.md b/README.md
index 733a0446f4d0cdba462ec7fb410da4bd04eb004b..7fd5acc2b04f6de4b63b391835270e5b6d37ba1d 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,14 @@
# 基于分布式键值数据库实现通讯录功能
-## 简介
+## 项目简介
本篇Codelab以通讯录为例,介绍分布式键值数据库的创建、数据的增加/删除/修改/查询/同步等操作方法。
-
+## 效果预览
-## 相关概念
-
-- 应用数据持久化概述:应用数据持久化,是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象,存储介质上的数据形态可能是文本、数据库、二进制文件等。
-- 键值型数据库:键值型数据库存储键值对形式的数据,当需要存储的数据没有复杂的关系模型,比如存储商品名称及对应价格、员工工号及今日是否已出勤等,由于数据复杂度低,更容易兼容不同数据库版本和设备类型,因此推荐使用键值型数据库持久化此类数据。
-- @ohos.data.distributedKVStore (分布式键值数据库):分布式键值数据库为应用程序提供不同设备间数据库的分布式协同能力。通过调用分布式键值数据库各个接口,应用程序可将数据保存到分布式键值数据库中,并可对分布式键值数据库中的数据进行增加、删除、修改、查询等操作。
-
-## 相关权限
-
-本篇Codelab用到分布式的能力,需要在配置文件module.json5里添加不同设备间的数据交换权限:ohos.permission.DISTRIBUTED_DATASYNC。
+| 新增 | 删除 | 编辑 | 查询 |
+|:-------------------------------------------------------------:|:-------------------------------------------------------------:|:--------------------------------------------------------------:|:---------------------------------------------------------------:|
+|
|
|
|
|
## 使用说明
@@ -28,9 +22,56 @@
8. 联系人批量删除页面,选中需要删除的联系人,下方点击“删除”按钮,删除选中的联系人。
9. 联系人编辑页面,编辑好联系人信息后,点击右上角的“保存”按钮,修改联系人信息。
+## 工程目录
+```
+├──entry/src/main/ets
+│ ├──common
+│ │ └──CommonConstants.ets // 常量集合
+│ ├──components
+│ │ ├──ContactBottomBar.ets // 通讯录删除页面底部tab组件
+│ │ ├──ContactDeleteDialog.ets // 通讯录删除弹窗组件
+│ │ ├──ContactDetailItem.ets // 通讯录详情页列表项组件
+│ │ ├──ContactDeviceDialog.ets // 通讯录设备弹窗组件
+│ │ └──ContactListItem.ets // 通讯录列表页列表项组件
+│ ├──entryability
+│ │ └──EntryAbility.ets // 入口文件
+│ ├──pages
+│ │ ├──ContactAddAndEditPage.ets // 通讯录添加和编辑页面
+│ │ ├──ContactDeletePage.ets // 通讯录删除页面
+│ │ ├──ContactDetailPage.ets // 通讯录详情页面
+│ │ └──ContactHomePage.ets // 通讯录首页
+│ ├──utils
+│ │ ├──ContactDeviceManager.ets // 通讯录设备管理类
+│ │ └──KvManager.ets // 键值型数据库管理类
+│ └──viewmodel
+│ └──ContactViewModel.ets // 通讯录model
+└──entry/src/main/resources // 资源文件
+```
+
+## 具体实现
+
+1. 封装设备管理类,调用设备管理能力[distributedDeviceManager.createDeviceManager()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#distributeddevicemanagercreatedevicemanager)创建一个设备管理实例,设备管理实例是分布式设备管理方法的调用入口,用于获取可信设备和本地设备的相关信息。
+2. 通过设备管理实例DeviceManager的[on('deviceStateChange')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#ondevicestatechange)事件注册设备状态回调,以便在设备状态发生变化时及时通知应用组网内可信设备的变化,并注册设备状态变化回调函数,根据返回的设备状态data.action执行不同方法更新设备列表。
+3. 通过设备管理实例DeviceManager的[on('discoverSuccess')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#ondiscoversuccess)事件注册设备状态回调,以便在发现时及时通知应用组网内设备的变化,调用[startDiscovering()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#startdiscovering)发现周围设备。
+4. 若设备在信任设备列表,执行[startAbility()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-inner-application-uiabilitycontext#startability)方法启动连接设备上的应用,将当前的设备信息作为参数发送至连接设备。若设备不是信任设备,执行[bindTarget()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#bindtarget)方法启动验证。此时连接设备提示是否接受,接收连接后连接设备展示PIN码,本地设备输入PIN码确认后连接成功。再次点击查询设备按钮,选择已连接设备,点击确认启动连接设备上的应用。
+5. 关闭设备弹窗时,调用[stopDiscovering()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#stopdiscovering)停止发现周边设备,并通过设备管理实例DeviceManager的[off('deviceStateChange')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#offdevicestatechange)和[off('discoverSuccess')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributeddevicemanager#offdiscoversuccess)事件注销设备监听任务。
+6. 应用首次启动时,调用[requestPermissionsFromUser()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-abilityaccessctrl#requestpermissionsfromuser9)方法动态弹窗获取授权。
+7. 创建键值型数据库对象实例,调用[distributedKVStore.createKVManager()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#distributedkvstorecreatekvmanager)创建一个KVManager对象实例,用于管理数据库对象,调用KVManager的[getKVStore()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#getkvstore)方法,创建并获取分布式键值数据库。
+8. 调用[on('dataChange')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#ondatachange)接口订阅组网内其他设备的数据变化,并注册数据变化回调函数。
+9. 封装操作数据库的增([put()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#put))、删([delete()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#delete)、[deleteBatch()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#deletebatch))、改([put()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#put))、查([get()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#get)、[getEntries()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#getentries))六个方法。
+10. 调用同步数据的接口[sync()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-distributedkvstore#sync)推送当前设备数据变化至组网内其他设备。
+11. 当组网内其他设备数据发生变化时,执行回调函数,通过自定义getAllData()方法获取发生数据变化设备的全部数据,更新本地数据。
+
+## 相关权限
+
+本篇Codelab用到分布式的能力,需要在配置文件module.json5里添加不同设备间的数据交换权限:ohos.permission.DISTRIBUTED_DATASYNC。
+
## 约束与限制
1. 本示例仅支持标准系统上运行,支持设备:华为手机。
-2. HarmonyOS系统:HarmonyOS 5.0.5 Release及以上。
-3. DevEco Studio版本:DevEco Studio 5.0.5 Release及以上。
-4. HarmonyOS SDK版本:HarmonyOS 5.0.5 Release SDK及以上。
+2. HarmonyOS系统:HarmonyOS 5.1.1 Release及以上。
+3. DevEco Studio版本:DevEco Studio 5.1.1 Release及以上。
+4. HarmonyOS SDK版本:HarmonyOS 5.1.1 Release SDK及以上。
+5. 双端设备需要登录同一华为账号,建议打开查找设备功能。
+6. 双端设备需要打开Wi-Fi和蓝牙开关,条件允许时,建议连接同一局域网。
+7. 双端设备都需要有该应用。
diff --git a/build-profile.json5 b/build-profile.json5
index 28db7ad2ea4faaba762bec70bd26b2a21148dc47..e7e4d1eb0d45326435fdf9bd14820d7428dcbe72 100644
--- a/build-profile.json5
+++ b/build-profile.json5
@@ -5,8 +5,8 @@
{
"name": "default",
"signingConfig": "default",
- "compatibleSdkVersion": "5.0.5(17)",
- "targetSdkVersion": "5.0.5(17)",
+ "compatibleSdkVersion": "5.1.1(19)",
+ "targetSdkVersion": "5.1.1(19)",
"runtimeOS": "HarmonyOS"
}
],
diff --git a/code-linter.json5 b/code-linter.json5
new file mode 100644
index 0000000000000000000000000000000000000000..eaa4bb5eb40a6bccd365319fa01a5da9907b3efa
--- /dev/null
+++ b/code-linter.json5
@@ -0,0 +1,33 @@
+{
+ "files": [
+ "**/*.ets"
+ ],
+ "ignore": [
+ "**/src/ohosTest/**/*",
+ "**/src/test/**/*",
+ "**/src/mock/**/*",
+ "**/node_modules/**/*",
+ "**/oh_modules/**/*",
+ "**/build/**/*",
+ "**/.preview/**/*"
+ ],
+ "ruleSet": [
+ "plugin:@performance/recommended",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "rules": {
+ "@security/no-unsafe-aes": "error",
+ "@security/no-unsafe-hash": "error",
+ "@security/no-unsafe-mac": "warn",
+ "@security/no-unsafe-dh": "error",
+ "@security/no-unsafe-dsa": "error",
+ "@security/no-unsafe-ecdsa": "error",
+ "@security/no-unsafe-rsa-encrypt": "error",
+ "@security/no-unsafe-rsa-sign": "error",
+ "@security/no-unsafe-rsa-key": "error",
+ "@security/no-unsafe-dsa-key": "error",
+ "@security/no-unsafe-dh-key": "error",
+ "@security/no-unsafe-3des": "error",
+ "@typescript-eslint/semi": "error"
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/common/constants/CommonConstants.ets b/entry/src/main/ets/common/CommonConstants.ets
similarity index 65%
rename from entry/src/main/ets/common/constants/CommonConstants.ets
rename to entry/src/main/ets/common/CommonConstants.ets
index 3de6440f320958eb37966eef7befab2812760adf..6156924db4a15b163e08106f793384d62f034288 100644
--- a/entry/src/main/ets/common/constants/CommonConstants.ets
+++ b/entry/src/main/ets/common/CommonConstants.ets
@@ -1,18 +1,20 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
+ * 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 { relationalStore } from '@kit.ArkData';
+
/**
* Common constants for all features.
*/
@@ -21,335 +23,332 @@ export default class CommonConstants {
* Search font weight.
*/
static readonly SEARCH_FONT_WEIGHT: number = 10;
-
/**
* The font family of search component.
*/
static readonly SEARCH_FONT_FAMILY: string = 'serif';
-
/**
* The layout weight of the list title.
*/
static readonly TITLE_LAYOUT_WEIGHT: number = 1;
-
/**
* The max lines of the list title.
*/
static readonly TITLE_MAX_LINES: number = 1;
-
/**
* Set component layout weight.
*/
static readonly WEIGHT: number = 1;
-
/**
* The contacts detail page url.
*/
- static readonly PAGE_DETAIL_URL: string = 'pages/DetailPage';
-
+ static readonly PAGE_DETAIL_URL: string = 'pages/ContactDetailPage';
/**
* The contacts editing page url.
*/
- static readonly ADD_EDIT_URL: string = 'pages/EditPage';
-
+ static readonly ADD_EDIT_URL: string = 'pages/ContactAddAndEditPage';
/**
* The contacts list page url.
*/
- static readonly LIST_PAGE_URL: string = 'pages/ListPage';
-
+ static readonly LIST_PAGE_URL: string = 'pages/ContactHomePage';
/**
* The contacts delete page url.
*/
- static readonly DELETE_PAGE_URL: string = 'pages/DeletePage';
-
+ static readonly DELETE_PAGE_URL: string = 'pages/ContactDeletePage';
/**
* The key to transfer parameters when replace pages.
*/
static readonly KEY_PARAM_DATA: string = 'data';
-
/**
* The maximum component width.
*/
static readonly PERCENTAGE_MAX: string = '100%';
-
/**
* Eighty percent assembly width.
*/
static readonly PERCENTAGE_HEIGHT_MAX: string = '80%';
-
/**
* Delete page title width.
*/
static readonly PAGE_TITLE_WIDTH: string = '90%';
-
/**
* Edit the width of the input box on the page.
*/
static readonly EDIT_INPUT_WIDTH: string = '80%';
-
/**
* The contacts details page components width.
*/
static readonly DETAIL_ITEM_WIDTH: string = '70%';
-
/**
* Height of the address book list.
*/
static readonly CONTACTS_LIST_HEIGHT: string = '90%';
-
/**
* The width of the bottom component.
*/
static readonly BOTTOM_COMPONENT_WIDTH: string = '50%';
-
/**
* The contacts details page top height.
*/
static readonly DETAIL_TOP_HEIGHT: string = '30%';
-
/**
* The percentage of list component.
*/
static readonly LIST_WIDTH_PERCENT: string = '93.3%';
-
/**
* The contacts list divider left margin.
*/
static readonly CONTACTS_LIST_DIVIDER: string = '16%';
-
/**
* The contacts list divider left margin.
*/
static readonly CONTACTS_EMPTY_TOP: string = '17.8%';
-
/**
* Contact database key.
*/
static readonly CONTACTS_DATABASE_KEY: string = 'contactsKey';
-
/**
* The contact name.
*/
- static readonly CONTACTS_DETAIL_NAME: Resource = $r('app.string.edit_item_name');
-
+ static readonly CONTACTS_DETAIL_NAME: string = 'edit_item_name';
/**
* The contact address.
*/
- static readonly CONTACTS_DETAIL_ADDRESS: Resource = $r('app.string.edit_item_address');
-
+ static readonly CONTACTS_DETAIL_ADDRESS: string = 'edit_item_address';
/**
* The contact telephony.
*/
- static readonly CONTACTS_DETAIL_TEL: Resource = $r('app.string.edit_item_phone');
-
+ static readonly CONTACTS_DETAIL_TEL: string = 'edit_item_phone';
/**
* The contact email.
*/
- static readonly CONTACTS_DETAIL_EMAIL: Resource = $r('app.string.edit_item_email');
-
+ static readonly CONTACTS_DETAIL_EMAIL: string = 'edit_item_email';
/**
* The contact remarks.
*/
- static readonly CONTACTS_DETAIL_REMARKS: Resource = $r('app.string.edit_item_note');
-
+ static readonly CONTACTS_DETAIL_REMARKS: string = 'edit_item_note';
/**
* The message prompt component duration.
*/
static readonly PROMPT_DURATION: number = 2000;
-
/**
* Address book database unique identifier.
*/
static readonly DB_STORE_ID: string = 'DatabaseStoreId';
-
/**
* Ability name.
*/
static readonly ABILITY_NAME: string = 'EntryAbility';
-
/**
* Detail page id.
*/
static readonly DETAIL_PAGE_ID: string = 'detailId';
-
/**
* Delete page id.
*/
static readonly DELETE_PAGE_ID: string = 'deleteId';
-
/**
* Phone components id.
*/
static readonly PHONE_COMPONENTS_ID: number = 1;
-
/**
* Email components id.
*/
static readonly Email_COMPONENTS_ID: number = 2;
-
/**
* Note components id.
*/
static readonly Note_COMPONENTS_ID: number = 3;
-
/**
* Device list group.
*/
static readonly DEVICE_GROUP: string = 'deviceGroup';
-
/**
* Even column.
*/
static readonly EVEN_COLUMN: number = 2;
-
/**
* Indicates whether to edit the page.
*/
static readonly IS_EDIT: string = 'isEdit';
-
/**
* Key of details page information.
*/
static readonly DETAIL_INFO_KEY: string = 'key';
-
/**
* Monitors distributed database changes.
*/
static readonly DATA_CHANGE: string = 'dataChange';
-
/**
* Start ability callback.
*/
static readonly EXIT: string = 'exit';
-
/**
* Start ability callback id.
*/
static readonly SUBSCRIBE_ID: number = 100;
-
/**
* The mode of start device discovery info.
*/
static readonly INFO_MODE: number = 0xAA;
-
/**
* The frequency of start device discovery info.
*/
static readonly INFO_FREQ: number = 2;
-
/**
* Discover device list.
*/
static readonly DISCOVER_DEVICE_LIST: string = 'discoverDeviceList';
-
/**
* Zero.
*/
static readonly ZERO: string = '0';
-
/**
* One.
*/
static readonly ONE: number = 1;
-
/**
* Trusted device list.
*/
static readonly TRUSTED_DEVICE_LIST: string = 'trustedDeviceList';
-
/**
* Subscribe ID range.
*/
static readonly SUBSCRIBE_ID_RANGE: number = 65536;
-
/**
* Auth type.
*/
static readonly AUTH_TYPE: number = 1;
-
/**
* Subscribe mode.
*/
static readonly SUBSCRIBE_MODE: number = 0xAA;
-
/**
* Subscribe medium.
*/
static readonly SUBSCRIBE_MEDIUM: number = 0;
-
/**
* Subscribe freq.
*/
static readonly SUBSCRIBE_FREQ: number = 2;
-
/**
* Subscribe capability.
*/
static readonly SUBSCRIBE_CAPABILITY: number = 0;
-
/**
* Font weight.
*/
static readonly FONT_WEIGHT_500: number = 500;
-
/**
* Invalid Index.
*/
static readonly INVALID_INDEX: number = -1;
-
-
/**
* One hundred percent.
*/
static readonly FULL_PERCENT: string = '100%';
-
/**
* Number one.
*/
static readonly NUMBER_ONE: number = 1;
-
/**
* Device name width.
*/
static readonly DEVICE_NAME_WIDTH: string = '78%';
-
/**
* Select icon width.
*/
static readonly SELECT_ICON_WIDTH: string = '8%';
-
/**
* Localhost name.
*/
static readonly LOCALHOST_NAME: string = 'Local Host';
-
/**
* Entry ability.
*/
static readonly ENTRY_ABILITY: string = 'EntryAbility';
-
/**
* Ability name.
*/
static readonly BUNDLE_NAME: string = 'com.example.distributedcontacts';
-
/**
* App name.
*/
static readonly APP_NAME: string = 'Distributed Contacts';
-
/**
* Contacts name max length.
*/
static readonly CONTACTS_NAME_MAX_LENGTH: number = 10;
-
/**
* Contacts tel max length.
*/
static readonly CONTACTS_TEL_MAX_LENGTH: number = 11;
-
/**
* Contacts detail input box max length.
*/
static readonly CONTACTS_DETAIL_MAX: number = 20;
+ /**
+ * Distributed mail image storage location.
+ */
+ static readonly MAIL_DISTRIBUTED_PATH: string = '/data/storage/el2/distributedfiles/';
+ /**
+ * File name for storing cross-end photos.
+ */
+ static readonly PICTURE_NAME: string = 'pictureName';
+ /**
+ * Supports transfer.
+ */
+ static readonly CAN_CONTINUATION: string = 'true';
+ /**
+ * Forwarding is not supported.
+ */
+ static readonly NO_CONTINUATION: string = 'false';
+ /**
+ * With picture streaming.
+ */
+ static readonly CAN_PHOTO: string = 'true';
+ /**
+ * Transfer without picture.
+ */
+ static readonly NO_PHOTO: string = 'false';
+ /**
+ * Image stream size.
+ */
+ static readonly IMAGE_STREAM_SIZE: number = 10240000;
+ /**
+ * Bottom toolbar picture spacing.
+ */
+ static readonly BOTTOM_IMAGE_SPACE: number = 24;
+ static readonly FILE_BUFFER_SIZE: number = 4096;
+ static readonly APPENDIX_LIST_SPACE: number = 5;
+ static readonly APPENDIX_LIST_START_MARGIN: number = 15;
+ static readonly APPENDIX_LIST_END_MARGIN: number = 15;
+ /**
+ * Rdb database config.
+ */
+ static readonly STORE_CONFIG: relationalStore.StoreConfig = {
+ name: 'database.db',
+ securityLevel: relationalStore.SecurityLevel.S1,
+ };
+ /**
+ * Component size.
+ */
+ static readonly DIALOG_HEIGHT = '55%';
+ static readonly TABS_HEIGHT = '45%';
+ static readonly MINIMUM_SIZE = 0;
+ static readonly FULL_SIZE = 1;
+ static readonly PROMPT_BOTTOM = '70vp';
+ /**
+ * Component location.
+ */
+ static readonly EDIT_POSITION_X = '80%';
+ static readonly EDIT_POSITION_Y = '90%';
+ static readonly DELETE_POSITION_X = '50%';
+ static readonly DELETE_POSITION_Y = '90%';
+ /**
+ * Log tag.
+ */
+ static readonly RDB_TAG = '[Debug.Rdb]';
+ static readonly TABLE_TAG = '[Debug.AccountTable]';
+ static readonly INDEX_TAG = '[Debug.Index]';
}
\ No newline at end of file
diff --git a/entry/src/main/ets/common/constants/DeviceConstants.ets b/entry/src/main/ets/common/constants/DeviceConstants.ets
deleted file mode 100644
index ae639978d0bf316362cab07d1cade648b71da3f0..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/common/constants/DeviceConstants.ets
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Device constants.
- */
-export class DeviceConstants {
- /**
- * Local device.
- */
- static readonly LOCAL_DEVICE: string = 'Local Device';
-
- /**
- * Device type.
- */
- static readonly DEVICE_TYPE_NUMBER: number = 0;
-
- /**
- * Range number.
- */
- static readonly RANGE_NUMBER: number = 1000;
-}
diff --git a/entry/src/main/ets/common/util/CheckEmptyUtils.ets b/entry/src/main/ets/common/util/CheckEmptyUtils.ets
deleted file mode 100644
index 598ab8aaa1cb60ebdaa9850081befd3be4410ba1..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/common/util/CheckEmptyUtils.ets
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { distributedKVStore } from '@kit.ArkData';
-import { ListItemData } from '../../viewmodel/ListItemData';
-
-/**
- * A utility class for checking whether data is empty.
- */
-class CheckEmptyUtils {
- /**
- * Check obj is empty.
- *
- * @param {object} obj
- * @return {boolean} true(empty)
- */
- isEmptyObj(obj: object) {
- return (typeof obj === 'undefined');
- }
-
- /**
- * Check str is empty.
- *
- * @param {string} str
- * @return {boolean} true(empty)
- */
- isEmptyStr(str: string) {
- return str.trim().length === 0;
- }
-
- /**
- * Check array is empty.
- *
- * @param {Array}arr
- * @return {boolean} true(empty)
- */
- isEmptyArr(arr: Array | distributedKVStore.Entry[]) {
- return arr.length === 0;
- }
-}
-
-export default new CheckEmptyUtils();
\ No newline at end of file
diff --git a/entry/src/main/ets/common/util/GlobalContext.ets b/entry/src/main/ets/common/util/GlobalContext.ets
deleted file mode 100644
index ddcb46250914017cd225f6fcd21ec45b0c07ceb1..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/common/util/GlobalContext.ets
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License,Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-export class GlobalContext {
- private static instance: GlobalContext;
- private _objects = new Map();
-
- private constructor() {
- }
-
- public static getContext(): GlobalContext {
- if (!GlobalContext.instance) {
- GlobalContext.instance = new GlobalContext();
- }
- return GlobalContext.instance;
- }
-
- getObject(value: string): Object | undefined {
- return this._objects.get(value);
- }
-
- setObject(key: string, objectClass: Object): void {
- this._objects.set(key, objectClass);
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/common/util/Logger.ets b/entry/src/main/ets/common/util/Logger.ets
deleted file mode 100644
index 06e4c060a4ce0252e782d36d7e10c2409a14c16f..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/common/util/Logger.ets
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { hilog } from '@kit.PerformanceAnalysisKit';
-
-class Logger {
- private domain: number;
- private prefix: string;
- private format: string = '%{public}s, %{public}s';
-
- /**
- * constructor.
- *
- * @param Prefix Identifies the log tag.
- * @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF.
- */
- constructor(prefix: string = 'MyApp', domain: number = 0xFF00) {
- this.prefix = prefix;
- this.domain = domain;
- }
-
- debug(...args: string[]): void {
- hilog.debug(this.domain, this.prefix, this.format, args);
- }
-
- info(...args: string[]): void {
- hilog.info(this.domain, this.prefix, this.format, args);
- }
-
- warn(...args: string[]): void {
- hilog.warn(this.domain, this.prefix, this.format, args);
- }
-
- error(...args: string[]): void {
- hilog.error(this.domain, this.prefix, this.format, args);
- }
-}
-
-export default new Logger('DistributedContacts', 0xFF00);
\ No newline at end of file
diff --git a/entry/src/main/ets/components/ContactBottomBar.ets b/entry/src/main/ets/components/ContactBottomBar.ets
new file mode 100644
index 0000000000000000000000000000000000000000..5ccea1f5ceae118927ec8e535350623363364751
--- /dev/null
+++ b/entry/src/main/ets/components/ContactBottomBar.ets
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import CommonConstants from '../common/CommonConstants';
+
+@Extend(Text)
+function setTextStyle() {
+ .width('100%')
+ .textAlign(TextAlign.Center)
+ .fontColor('rgba(0, 0, 0, 0.9)')
+ .fontSize(12);
+}
+
+@Extend(SymbolGlyph)
+function setImageStyle() {
+ .fontSize(20)
+ .fontWeight(400)
+ .fontColor(['rgba(0, 0, 0, 0.9)']);
+}
+
+@Component
+export default struct ContactBottomBar {
+ private isAll: boolean = false;
+ public pageId: string = '';
+ public leftClickEvent: (isAll?: boolean) => void = () => {
+ };
+ public rightClickEvent: () => void = () => {
+ };
+
+ build() {
+ Row() {
+ Column() {
+ Row() {
+ SymbolGlyph($r('sys.symbol.checkmark_square_on_square'))
+ .setImageStyle()
+ }
+
+ Text($r('app.string.bottom_text_all_selected'))
+ .setTextStyle()
+ .margin({
+ top: 4
+ })
+ }
+ .width('50%')
+ .onClick(() => {
+ if (this.pageId === CommonConstants.DELETE_PAGE_ID) {
+ this.isAll = !this.isAll;
+ }
+ this.leftClickEvent(this.isAll);
+ })
+
+ Column() {
+ Row() {
+ SymbolGlyph($r('sys.symbol.trash'))
+ .setImageStyle()
+ }
+
+ Text($r('app.string.bottom_text_deletion'))
+ .setTextStyle()
+ .margin({
+ top: 4
+ })
+ }
+ .width('50%')
+ .onClick(() => this.rightClickEvent())
+ }
+ .width('100%')
+ .height(48)
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/view/DeleteDialog.ets b/entry/src/main/ets/components/ContactDeleteDialog.ets
similarity index 34%
rename from entry/src/main/ets/view/DeleteDialog.ets
rename to entry/src/main/ets/components/ContactDeleteDialog.ets
index bdffcf81c1a8fd46dcdb889492b00b2291a71ad7..eac4e652fc403a3e3a0adf64ba4dc56fca1e6ee5 100644
--- a/entry/src/main/ets/view/DeleteDialog.ets
+++ b/entry/src/main/ets/components/ContactDeleteDialog.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -13,60 +13,56 @@
* limitations under the License.
*/
-import CommonConstants from '../common/constants/CommonConstants';
-
@CustomDialog
-export struct DeleteDialog {
- private controller: CustomDialogController;
- private cancel: () => void = () => {
+export struct ContactDeleteDialog {
+ controller: CustomDialogController;
+ cancel: () => void = () => {
};
- private confirm: () => void = () => {
+ confirm: () => void = () => {
};
- private promptMessage: Resource = $r('app.string.batch_delete_text');
+ promptMessage: Resource = $r('app.string.batch_delete_text');
build() {
Column() {
Text(this.promptMessage)
- .fontSize($r('app.float.popup_font_size'))
- .margin({
- top: $r('app.float.delete_text_margin_top'),
- bottom: $r('app.float.delete_text_margin_bottom')
- })
- Row() {
- Button($r('app.string.delete_cancel_text'))
+ .textAlign(TextAlign.Center)
+ .fontSize('16vp')
+ .fontColor('rgba(0, 0, 0, 0.9)')
+ .lineHeight('21vp')
+
+ Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
+ Button($r('app.string.select_device_cancel'),
+ { buttonStyle: ButtonStyleMode.EMPHASIZED, role: ButtonRole.NORMAL })
+ .flexGrow(1)
+ .backgroundColor(Color.Transparent)
+ .fontColor('#0A59F7')
+ .fontSize('16vp')
+ .fontWeight(500)
.onClick(() => {
- this.controller.close();
this.cancel();
})
- .backgroundColor(Color.White)
- .fontColor($r('app.color.cancel_font_color'))
Divider()
- .width($r('app.float.divider_height'))
- .height(CommonConstants.BOTTOM_COMPONENT_WIDTH)
- .backgroundColor($r('app.color.list_divider'))
- .margin({
- right: $r('app.float.delete_divider_margin'),
- left: $r('app.float.delete_divider_margin')
- })
- Button($r('app.string.bottom_text_deletion'))
+ .vertical(true)
+ .strokeWidth('0.5vp')
+ .height(24)
+ .color('rgba(0, 0, 0, 0.05)')
+ .margin({ left: 4, right: 4 })
+ Button($r('app.string.confirm'), { buttonStyle: ButtonStyleMode.EMPHASIZED, role: ButtonRole.NORMAL })
+ .flexGrow(1)
+ .backgroundColor(Color.Transparent)
+ .fontColor('#0A59F7')
+ .fontSize('16vp')
+ .fontWeight(500)
.onClick(() => {
- this.controller.close();
this.confirm();
})
- .backgroundColor(Color.White)
- .fontColor(Color.Red)
}
- .width(CommonConstants.PERCENTAGE_MAX)
- .height($r('app.float.delete_height'))
- .margin({ bottom: $r('app.float.delete_dialog_margin') })
- .justifyContent(FlexAlign.Center)
+ .height('40vp')
+ .margin({ top: '8vp' })
}
- .justifyContent(FlexAlign.Center)
- .height($r('app.float.delete_dialog_height'))
+ .width(328)
+ .padding(24)
+ .borderRadius(32)
.backgroundColor(Color.White)
- .border({
- color: Color.White,
- radius: ($r('app.float.device_list_border_radius'))
- })
}
}
\ No newline at end of file
diff --git a/entry/src/main/ets/viewmodel/DeletePageViewModel.ets b/entry/src/main/ets/components/ContactDetailItem.ets
similarity index 42%
rename from entry/src/main/ets/viewmodel/DeletePageViewModel.ets
rename to entry/src/main/ets/components/ContactDetailItem.ets
index cf5ada713d3cb840cd6b18e598f936ab1460ce8c..3c9d45256fd2d0ed4d72eba40c6ce135f4f1ea06 100644
--- a/entry/src/main/ets/viewmodel/DeletePageViewModel.ets
+++ b/entry/src/main/ets/components/ContactDetailItem.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -13,29 +13,33 @@
* limitations under the License.
*/
-import { promptAction } from '@kit.ArkUI';
-import CommonConstants from '../common/constants/CommonConstants';
+@Component
+export default struct ContactDetailItem {
+ @Prop topContent: string;
+ @Prop bottomContent: Resource;
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
-
-export class DeletePageViewModel {
- /**
- * No contact to be deleted is selected.
- */
- unCheckedContact(): void {
- uiContext!.getPromptAction().showToast({
- message: $r('app.string.prompt_delete'),
- duration: CommonConstants.PROMPT_DURATION
- });
- }
-
- /**
- * Button for canceling the deletion dialog box.
- */
- cancelDialog(): void {
- uiContext!.getPromptAction().showToast({
- message: $r('app.string.delete_cancel_text'),
- duration: CommonConstants.PROMPT_DURATION
- });
+ build() {
+ Column() {
+ Text(this.topContent)
+ .height(22)
+ .fontSize(16)
+ .fontWeight(500)
+ .lineHeight(21)
+ .fontColor('rgba(0, 0, 0, 0.9)')
+ .margin({ bottom: 2 })
+ Text(this.bottomContent)
+ .fontSize(14)
+ .fontWeight(400)
+ .lineHeight(19)
+ .fontColor('rgba(0, 0, 0, 0.6)')
+ }
+ .alignItems(HorizontalAlign.Start)
+ .justifyContent(FlexAlign.Center)
+ .width('100%')
+ .height(64)
+ .margin({ bottom: 16 })
+ .padding({ left: 12, right: 12 })
+ .borderRadius(12)
+ .backgroundColor(Color.White)
}
}
\ No newline at end of file
diff --git a/entry/src/main/ets/view/DeviceDialog.ets b/entry/src/main/ets/components/ContactDeviceDialog.ets
similarity index 72%
rename from entry/src/main/ets/view/DeviceDialog.ets
rename to entry/src/main/ets/components/ContactDeviceDialog.ets
index 57df67b56b23745613f718732805e442f2ce2d90..2797efaeedfb8a3bd015f031ac30ebaaedd7be73 100644
--- a/entry/src/main/ets/view/DeviceDialog.ets
+++ b/entry/src/main/ets/components/ContactDeviceDialog.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,31 +14,27 @@
*/
import { distributedDeviceManager } from '@kit.DistributedServiceKit';
-import { promptAction } from '@kit.ArkUI';
-import CommonConstants from '../common/constants/CommonConstants';
+import CommonConstants from '../common/CommonConstants';
@Extend(Text)
function ButtonTextStyle() {
- .fontColor($r('app.color.button_text_color'))
+ .fontColor('#0A59F7')
.fontSize($r('app.float.button_font_size'))
- .fontWeight(CommonConstants.FONT_WEIGHT_500)
+ .fontWeight(CommonConstants.FONT_WEIGHT_500);
}
@CustomDialog
-export default struct DeviceListDialogComponent {
+export default struct ContactDeviceDialog {
controller?: CustomDialogController;
- @StorageLink('deviceList') deviceList: Array = AppStorage.get('deviceList')!;
- private selectedIndex: number | undefined = 0;
- private onSelectedIndexChange: (selectedIndex: number | undefined) => void = () => {
- };
- @State deviceDialogWidth: number = 0;
+ @Link deviceList: Array;
+ @Link selectedDeviceIndex: number;
+ onSelectedIndexChange: (index: number) => void = () => {};
build() {
Column() {
Row() {
Text($r('app.string.select_device'))
.fontSize($r('app.float.dialog_title_font_size'))
- .width(CommonConstants.FULL_PERCENT)
.textAlign(TextAlign.Start)
.fontColor(Color.Black)
.fontWeight(FontWeight.Bold)
@@ -49,9 +45,10 @@ export default struct DeviceListDialogComponent {
top: $r('app.float.dialog_title_padding_top')
})
.height($r('app.float.dialog_title_height'))
+ .justifyContent(FlexAlign.Center)
List() {
- ForEach(this.deviceList, (item: distributedDeviceManager.DeviceBasicInfo, index: number | undefined) => {
+ ForEach(this.deviceList, (item: distributedDeviceManager.DeviceBasicInfo, index: number) => {
ListItem() {
Column() {
Row() {
@@ -74,24 +71,20 @@ export default struct DeviceListDialogComponent {
}
.justifyContent(FlexAlign.Start)
- if (index === this.selectedIndex) {
- Image($r('app.media.ic_single_select'))
- .width(CommonConstants.SELECT_ICON_WIDTH)
- .objectFit(ImageFit.Contain)
- } else {
- Image($r('app.media.ic_single_unselect'))
- .width(CommonConstants.SELECT_ICON_WIDTH)
- .objectFit(ImageFit.Contain)
- .opacity($r('app.float.icon_uncheck_opacity'))
- }
+ Radio({
+ value: item.deviceId, group: 'RadioGroup',
+ indicatorType: RadioIndicatorType.DOT
+ })
+ .width(20)
+ .height(20)
+ .checked(index === this.selectedDeviceIndex)
}
.height($r('app.float.device_info_height'))
.onClick(() => {
- if (this.selectedIndex !== undefined && index === this.selectedIndex) {
+ if (this.selectedDeviceIndex === index) {
return;
- } else if (this.selectedIndex !== undefined) {
- this.selectedIndex = index;
- this.onSelectedIndexChange(this.selectedIndex);
+ } else {
+ this.selectedDeviceIndex = index;
}
})
.padding({
@@ -150,18 +143,9 @@ export default struct DeviceListDialogComponent {
.justifyContent(FlexAlign.Center)
.height($r('app.float.button_line_height'))
.onClick(() => {
- if (CommonConstants.INVALID_INDEX === this.selectedIndex) {
- this.getUIContext().getPromptAction().showToast({
- message: $r('app.string.please_select_device')
- });
- } else {
- if (this.controller !== undefined) {
- this.onSelectedIndexChange(this.selectedIndex);
- }
- }
+ this.onSelectedIndexChange(this.selectedDeviceIndex);
})
}
- .backgroundColor(Color.White)
.border({
color: Color.White,
radius: $r('app.float.button_line_radius')
diff --git a/entry/src/main/ets/view/ListItemComponent.ets b/entry/src/main/ets/components/ContactListItem.ets
similarity index 55%
rename from entry/src/main/ets/view/ListItemComponent.ets
rename to entry/src/main/ets/components/ContactListItem.ets
index 5b9c9ea27c0b5373ec27c64c3cc0db1aae292e29..0cdac46e8c450265fca645610a26b510dbdf61de 100644
--- a/entry/src/main/ets/view/ListItemComponent.ets
+++ b/entry/src/main/ets/components/ContactListItem.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -13,38 +13,36 @@
* limitations under the License.
*/
-import CommonConstants from '../common/constants/CommonConstants';
-import { ListItemData } from '../viewmodel/ListItemData';
+import CommonConstants from '../common/CommonConstants';
+import { ListItemData } from '../viewmodel/ContactViewModel';
@Component
-export struct ListItemComponent {
+export struct ContactListItem {
public itemInfo: ListItemData = {
id: 0,
- photo: $r('app.media.ic_men'),
name: '',
checked: false
};
public isCanCheck = false;
- public onCheck: () => void = () => {
- };
+ public onCheck: () => void = () => {};
build() {
Column() {
Column() {
Row() {
- Image(this.itemInfo.photo)
- .objectFit(ImageFit.Contain)
- .width($r('app.float.image_width'))
- .height($r('app.float.image_height'))
- .margin({
- left: $r('app.float.list_image_left'),
- right: $r('app.float.list_image_right')
- })
+ Row() {
+ SymbolGlyph($r('sys.symbol.person_crop_circle_fill_1'))
+ .fontSize(48)
+ .fontColor(['#D1D1D6'])
+ .fontWeight(400)
+ }
+ .margin({ right: 19 })
+
Column() {
Text(this.itemInfo.name.toString())
- .fontSize($r('app.float.list_item_title'))
- .fontColor($r('app.color.list_item_title'))
- .fontWeight(FontWeight.Bolder)
+ .fontSize(16)
+ .fontColor('rgba(0, 0, 0, 0.9)')
+ .fontWeight(700)
.maxLines(CommonConstants.TITLE_MAX_LINES)
.textOverflow({
overflow: TextOverflow.Ellipsis
@@ -56,7 +54,7 @@ export struct ListItemComponent {
if (this.isCanCheck) {
Checkbox()
.select(this.itemInfo.checked)
- .selectedColor($r('app.color.selected_font_color'))
+ .selectedColor('#0A59F7')
.onChange(() => {
this.onCheck();
})
@@ -64,15 +62,9 @@ export struct ListItemComponent {
.height($r('app.float.checkbox_size'))
}
}
- .height($r('app.float.list_item_height'))
-
- if (this.isCanCheck) {
- Divider()
- .height($r('app.float.divider_height'))
- .backgroundColor($r('app.color.list_divider'))
- .margin({ left: CommonConstants.CONTACTS_LIST_DIVIDER })
- }
+ .height(48)
}
}
+ .margin({ top: 16 })
}
}
\ No newline at end of file
diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets
index bcf8337e206775676fd380d966a8569b01fac1e5..e10ce3d50a4438c9c15d436f81191ac41c80b7d8 100644
--- a/entry/src/main/ets/entryability/EntryAbility.ets
+++ b/entry/src/main/ets/entryability/EntryAbility.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -13,56 +13,48 @@
* limitations under the License.
*/
-import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
+import { UIAbility } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
-import Logger from '../common/util/Logger';
-import { ContactsDataBase } from '../common/database/ContactsDataBase';
-import { GlobalContext } from '../common/util/GlobalContext';
+import { KvManager } from '../utils/KvManager';
const TAG: string = 'EntryAbility';
export default class EntryAbility extends UIAbility {
- onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
+ onCreate(): void {
this.permissions();
- AppStorage.setOrCreate('UIAbilityContext', this.context);
- GlobalContext.getContext().setObject('FirstInTo', true);
- GlobalContext.getContext().setObject('entryContext', this.context);
- GlobalContext.getContext().setObject('contactsDataBase', new ContactsDataBase());
- Logger.info(TAG, `Ability onCreate`);
+ AppStorage.setOrCreate('kvManager', new KvManager(this.context));
}
- onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
+ onNewWant(): void {
this.permissions();
- AppStorage.setOrCreate('UIAbilityContext', this.context);
- GlobalContext.getContext().setObject('FirstInTo', true);
- GlobalContext.getContext().setObject('entryContext', this.context);
- GlobalContext.getContext().setObject('contactsDataBase', new ContactsDataBase());
- Logger.info(TAG, `Ability onNewWant`);
+ AppStorage.setOrCreate('kvManager', new KvManager(this.context));
+ hilog.info(0x0000, 'EntryAbility', `Ability onNewWant`);
}
onDestroy(): void | Promise {
- Logger.info(TAG, 'onDestroy execute');
- let contactsDataBase = GlobalContext.getContext().getObject('contactsDataBase') as ContactsDataBase;
- contactsDataBase.removeDataChangeListener();
- contactsDataBase.closeKVStore();
+ hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onDestroy');
+ let kvManager = AppStorage.get('kvManager') as KvManager;
+ kvManager.removeDataChangeListener();
+ kvManager.closeKVStore();
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability.
hilog.isLoggable(0x0000, TAG, hilog.LogLevel.INFO);
- Logger.info(TAG, `Ability onWindowStageCreate`);
- windowStage.loadContent('pages/ListPage', (err: BusinessError, data) => {
+ windowStage.loadContent('pages/ContactHomePage', (err: BusinessError) => {
if (err.code) {
- hilog.isLoggable(0x0000, TAG, hilog.LogLevel.ERROR);
- Logger.error(TAG, `Failed to load the content. Cause: ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'EntryAbility', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
- hilog.isLoggable(0x0000, TAG, hilog.LogLevel.INFO);
- Logger.info(TAG, `Succeeded in loading the content. Data: ${JSON.stringify(data)}`);
- AppStorage.setOrCreate('uiContext', windowStage.getMainWindowSync().getUIContext());
+ hilog.info(0x0000, 'EntryAbility', 'Succeeded in loading the content.');
+ try {
+ AppStorage.setOrCreate('uiContext', windowStage.getMainWindowSync().getUIContext());
+ } catch (err) {
+ hilog.error(0x0000, 'EntryAbility', `getMainWindowSync failed, code is ${err.code}, message is ${err.message}`);
+ }
});
}
@@ -71,12 +63,10 @@ export default class EntryAbility extends UIAbility {
*/
permissions(): void {
let atManager = abilityAccessCtrl.createAtManager();
- try {
- atManager.requestPermissionsFromUser(this.context, ['ohos.permission.DISTRIBUTED_DATASYNC']).then((data) => {
- Logger.info(TAG, `Data permissions:${data.permissions}`);
- });
- } catch (err) {
- Logger.info(TAG, `Catch err: ${err}`);
- }
+ atManager.requestPermissionsFromUser(this.context, ['ohos.permission.DISTRIBUTED_DATASYNC']).then((data) => {
+ hilog.info(0x0000, 'EntryAbility', `Data permissions:${data.permissions}`);
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'EntryAbility', `request permissions failed, code is ${err.code}, message is ${err.message}`);
+ });
}
}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/ContactAddAndEditPage.ets b/entry/src/main/ets/pages/ContactAddAndEditPage.ets
new file mode 100644
index 0000000000000000000000000000000000000000..5a8dddfc0af3744905dfbbef1d1f1b679e9e62c9
--- /dev/null
+++ b/entry/src/main/ets/pages/ContactAddAndEditPage.ets
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { common } from '@kit.AbilityKit';
+import { KeyboardAvoidMode } from '@kit.ArkUI';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import CommonConstants from '../common/CommonConstants';
+import { ContactData } from '../viewmodel/ContactViewModel';
+import { KvManager } from '../utils/KvManager';
+
+@Entry
+@Component
+struct ContactAddAndEditPage {
+ @State isSelectAll: boolean = false;
+ @State name: string = '';
+ @State address: string = '';
+ @State telephony: string = '';
+ @State email: string = '';
+ @State remarks: string = '';
+ private isEdit: boolean = false;
+ private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
+ kvManager = AppStorage.get('kvManager') as KvManager;
+
+ aboutToAppear() {
+ this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE);
+ let params = this.getUIContext().getRouter().getParams() as Record;
+ this.isEdit = params.isEdit as boolean;
+ this.initializeData();
+ }
+
+ /**
+ * Contact information change.
+ * @param key Key of the text information.
+ * @param value Changed Data.
+ */
+ contactInfoChange(key: string, value: string) {
+ try {
+ let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
+ let contactKey = context.resourceManager.getStringByNameSync(key);
+ switch (contactKey) {
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_NAME):
+ this.name = value;
+ break;
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_ADDRESS):
+ this.address = value;
+ break;
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_TEL):
+ this.telephony = value;
+ break;
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_EMAIL):
+ this.email = value;
+ break;
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_REMARKS):
+ this.remarks = value;
+ break;
+ default:
+ break;
+ }
+ } catch (error) {
+ hilog.error(0x0000, 'ContactAddAndEditPage', `Failed to copy.Code:${error.code},message: ${error.message}`);
+ }
+ }
+
+ /**
+ * Initialize edit page data.
+ */
+ initializeData(): void {
+ // Check whether the page is an editing page.
+ if (this.getUIContext().getRouter().getParams() && this.isEdit) {
+ let params = this.getUIContext().getRouter().getParams() as Record;
+ this.name = params.name as string;
+ this.address = params.address as string;
+ this.telephony = params.telephony as string;
+ this.email = params.email as string;
+ this.remarks = params.remarks as string;
+ }
+ }
+
+ build() {
+ Column() {
+ this.NavigationTitle();
+ Scroll() {
+ Column() {
+ Image($r('app.media.ic_head_portrait'))
+ .width(56)
+ .height(56)
+ .objectFit(ImageFit.Contain)
+ .margin({ bottom: 16, top: 12 })
+ if (!this.isEdit) {
+ Text($r('app.string.edit_save_to_text'))
+ .fontSize(16)
+ .fontWeight(700)
+ .margin({ bottom: 20 })
+ }
+ this.Item('edit_item_name', $r('sys.symbol.person'), this.name, true);
+ this.Item('edit_item_address', $r('app.media.ic_address'), this.address);
+ this.Item('edit_item_phone', $r('app.media.ic_phone'), this.telephony);
+ this.Item('edit_item_email', $r('sys.symbol.envelope'), this.email, true);
+ this.Item('edit_item_note', $r('app.media.ic_note'), this.remarks);
+ }
+ .margin({ top: 16, bottom: 64 })
+ .layoutWeight(CommonConstants.WEIGHT)
+ }
+ .scrollBar(BarState.Off)
+ .align(Alignment.Top)
+ .width('100%')
+ .height('100%')
+ }
+ .width('100%')
+ .height('100%')
+ .padding({ left: 16, right: 16 })
+ .backgroundColor($r('app.color.detail_page_background'))
+ .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
+ }
+
+ @Builder
+ NavigationTitle() {
+ Flex({ alignItems: ItemAlign.Center }) {
+ Row() {
+ SymbolGlyph($r('sys.symbol.xmark'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .onClick(() => {
+ if (!this.isEdit) {
+ this.getUIContext().getRouter().back()
+ } else {
+ let params = this.getUIContext().getRouter().getParams() as Record;
+ this.getUIContext().getRouter().back({
+ url: CommonConstants.PAGE_DETAIL_URL,
+ params: {
+ key: params.key
+ }
+ })
+ }
+ })
+
+ Text(this.isEdit ? $r('app.string.edit_title') : $r('app.string.add_contacts_text'))
+ .fontSize(26)
+ .fontWeight(700)
+ .lineHeight(27)
+ .fontColor('rgba(0, 0, 0, 0.9)')
+ .flexGrow(1)
+ Row() {
+ SymbolGlyph($r('sys.symbol.checkmark'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .onClick(() => {
+ if (this.name !== '') {
+ const contactData: ContactData =
+ new ContactData(this.name, this.address, this.telephony, this.email, this.remarks);
+ let contactsKey = CommonConstants.CONTACTS_DATABASE_KEY + contactData.name;
+ // Save the contact information.
+ this.kvManager.addAndSave(contactsKey, JSON.stringify(contactData));
+ if (!this.isEdit) {
+ this.getUIContext().getRouter().replaceUrl({
+ url: CommonConstants.PAGE_DETAIL_URL,
+ params: { key: contactsKey }
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactAddAndEditPage', `replaceUrl failed, code is ${err.code}, message is ${err.message}`);
+ });
+ } else {
+ let params = this.getUIContext().getRouter().getParams() as Record;
+ this.getUIContext().getRouter().back({
+ url: CommonConstants.PAGE_DETAIL_URL,
+ params: {
+ key: params.key
+ }
+ })
+ }
+ } else {
+ try {
+ this.getUIContext().getPromptAction().showToast({
+ message: $r('app.string.contact_name'),
+ duration: CommonConstants.PROMPT_DURATION
+ });
+ } catch (err) {
+ hilog.error(0x0000, 'ContactAddAndEditPage', `showToast failed, code is ${err.code}, message is ${err.message}`);
+ }
+ }
+ })
+ }
+ .height(56)
+ .backgroundColor($r('app.color.detail_page_background'))
+ .expandSafeArea([SafeAreaType.KEYBOARD])
+ .zIndex(3)
+ }
+
+ @Builder
+ Item(key: string, icon: Resource, content: string, isSys: boolean = false) {
+ Row() {
+ if (isSys) {
+ SymbolGlyph(icon)
+ .fontSize(24)
+ .fontColor([$r('app.color.font_color')])
+ .margin({ right: $r('app.float.item_icon_margin') })
+ } else {
+ Image(icon)
+ .objectFit(ImageFit.Contain)
+ .height($r('app.float.item_icon_size'))
+ .width($r('app.float.item_icon_size'))
+ .margin({ right: $r('app.float.item_icon_margin') })
+ }
+ Text(this.getStringValue(key))
+ .fontSize($r('app.float.item_font_size'))
+ .fontColor($r('app.color.font_color'))
+ .fontWeight(FontWeight.Regular)
+ TextInput({ text: content })
+ .type(this.getInputType(key))
+ .maxLength(this.getMaxLength(key))
+ .width(CommonConstants.EDIT_INPUT_WIDTH)
+ .margin({ right: $r('app.float.edit_input_margin') })
+ .backgroundColor(Color.White)
+ .fontSize($r('app.float.item_font_size'))
+ .fontWeight(FontWeight.Regular)
+ .enabled(this.getStringValue(key) === this.getStringValue(CommonConstants.CONTACTS_DETAIL_NAME) && this.isEdit ?
+ false : true)
+ .onChange((value) => {
+ this.contactInfoChange(key, value);
+ })
+ }
+ .padding({
+ top: 16,
+ bottom: 16,
+ left: 12,
+ right: 12
+ })
+ .margin({ bottom: 12 })
+ .justifyContent(FlexAlign.Start)
+ .backgroundColor(Color.White)
+ .borderRadius(24)
+ .width('100%')
+ }
+
+ getStringValue(resName: string): string {
+ try {
+ return this.context.resourceManager.getStringByNameSync(resName);
+ } catch (err) {
+ hilog.error(0x0000, 'ContactAddAndEditPage', `have error .Code:${err.code},message: ${err.message}`);
+ return err.msg;
+ }
+ }
+
+ getInputType(key: string): InputType {
+ try {
+ switch (this.getStringValue(key)) {
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_TEL):
+ return InputType.PhoneNumber;
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_EMAIL):
+ return InputType.Email;
+ default:
+ return InputType.Normal;
+ }
+ } catch (err) {
+ hilog.error(0x0000, 'ContactAddAndEditPage', `have error .Code:${err.code},message: ${err.message}`);
+ return err.code;
+ }
+ }
+
+ getMaxLength(key: string): number {
+ try {
+ switch (this.getStringValue(key)) {
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_NAME):
+ return CommonConstants.CONTACTS_NAME_MAX_LENGTH;
+ case this.getStringValue(CommonConstants.CONTACTS_DETAIL_TEL):
+ return CommonConstants.CONTACTS_TEL_MAX_LENGTH;
+ default:
+ return CommonConstants.CONTACTS_DETAIL_MAX;
+ }
+ } catch (err) {
+ hilog.error(0x0000, 'ContactAddAndEditPage', `have error .Code:${err.code},message: ${err.message}`);
+ return err.code;
+ }
+ }
+}
diff --git a/entry/src/main/ets/pages/ContactDeletePage.ets b/entry/src/main/ets/pages/ContactDeletePage.ets
new file mode 100644
index 0000000000000000000000000000000000000000..011b63a23d50caed03fadfbab007aa5415fb1661
--- /dev/null
+++ b/entry/src/main/ets/pages/ContactDeletePage.ets
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { distributedKVStore } from '@kit.ArkData';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import CommonConstants from '../common/CommonConstants';
+import ContactBottomBar from '../components/ContactBottomBar';
+import { KvManager } from '../utils/KvManager';
+import { ContactListItem } from '../components/ContactListItem';
+import { ListItemData } from '../viewmodel/ContactViewModel';
+import { ContactDeleteDialog } from '../components/ContactDeleteDialog';
+
+const TAG: string = 'DeletePage';
+
+@Entry
+@Component
+struct ContactDeletePage {
+ @State isSelectAll: boolean = false;
+ @State checkList: Array = [];
+ @State count: number = 0;
+ kvManager = AppStorage.get('kvManager') as KvManager;
+ dialogController: CustomDialogController = new CustomDialogController({
+ builder: ContactDeleteDialog({
+ cancel: () => {
+ this.onCancel();
+ },
+ confirm: () => {
+ this.onConfirm();
+ },
+ promptMessage: $r('app.string.batch_delete_text'),
+ }),
+ autoCancel: true,
+ alignment: DialogAlignment.Center,
+ customStyle: true
+ });
+
+ // Close the pop-up window.
+ onCancel() {
+ try {
+ this.getUIContext().getPromptAction().showToast({
+ message: $r('app.string.delete_cancel_text'),
+ duration: CommonConstants.PROMPT_DURATION
+ });
+ } catch (error) {
+ hilog.error(0x0000, 'ContactDeletePage', `delete_cancel_text error: ${error.code} msg:${error.message}`);
+ }
+ this.dialogController.close();
+ }
+
+ // Confirm the cancellation of transcoding.
+ onConfirm() {
+ this.batchDeleteButton();
+ this.dialogController.close();
+ }
+
+ aboutToAppear() {
+ this.initializeData();
+ }
+
+ /**
+ * Initialize edit page data.
+ */
+ initializeData(): void {
+ this.kvManager.getEntries(
+ CommonConstants.CONTACTS_DATABASE_KEY,
+ (err: BusinessError, entries: distributedKVStore.Entry[]) => {
+ hilog.info(0x0000, 'ContactDeletePage', TAG, `initializeData entries: ${JSON.stringify(entries)}`);
+ if (err) {
+ hilog.error(0x0000, 'ContactDeletePage', `Fail to get Entries, code is ${err.code}, message is ${err.message}`);
+ return;
+ }
+ let listItems: Array = [];
+ entries.forEach((item, index) => {
+ let itemInfo: ListItemData = new ListItemData();
+ itemInfo.name = JSON.parse(item.value.value as string).name;
+ itemInfo.id = index;
+ listItems.push(itemInfo);
+ });
+ this.checkList = listItems;
+ });
+ }
+
+ /**
+ * Deleting selected contacts in batches.
+ */
+ batchDeleteButton(): void {
+ let keys: string[] = [];
+ this.checkList.forEach((item: ListItemData) => {
+ if (item.checked) {
+ let contactsKey: string = CommonConstants.CONTACTS_DATABASE_KEY + item.name;
+ keys.push(contactsKey);
+ }
+ });
+ hilog.info(0x0000, 'ContactDeletePage', TAG, `batchDeleteButton keys: ${JSON.stringify(keys)}`);
+ // Batch delete.
+ this.kvManager.deleteBatch(keys, (err: BusinessError) => {
+ if (err) {
+ hilog.error(0x0000, 'ContactDeletePage', `Fail to delete Batch, code is ${err.code}, message is ${err.message}`);
+ return;
+ }
+ try {
+ this.getUIContext().getPromptAction().showToast({
+ message: $r('app.string.prompt_message_deleted'),
+ duration: CommonConstants.PROMPT_DURATION
+ });
+ } catch (err) {
+ hilog.error(0x0000, 'ContactDeletePage', `showToast failed, code is ${err.code}, message is ${err.message}`);
+ }
+ this.getUIContext().getRouter().pushUrl({
+ url: CommonConstants.LIST_PAGE_URL
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactDeletePage', `pushUrl failed, code is ${err.code}, message is ${err.message}`);
+ });
+ });
+ }
+
+ build() {
+ Column() {
+ Flex({ direction: FlexDirection.Column }) {
+ this.NavigationTitle();
+
+ Column() {
+ List() {
+ ForEach(this.checkList, (item: ListItemData) => {
+ ListItem() {
+ ContactListItem({
+ itemInfo: item,
+ isCanCheck: true,
+ onCheck: () => {
+ item.checked = !item.checked;
+ item.checked ? this.count++ : this.count--;
+ }
+ })
+ }
+ }, (item: ListItemData) => JSON.stringify(item))
+ }
+ .scrollBar(BarState.Off)
+ .width('100%')
+ .height(CommonConstants.PERCENTAGE_MAX)
+ }
+ .flexGrow(1)
+
+ ContactBottomBar({
+ leftClickEvent: (isAll) => {
+ this.checkList = this.checkList.map((item: ListItemData) => {
+ item.checked = isAll ? true : false;
+ return item;
+ });
+ let result = this.checkList.filter((item) => item.checked);
+ this.count = this.checkList.length === 0 ? 0 : result.length;
+ },
+ rightClickEvent: () => {
+ if (this.count === 0) {
+ try {
+ this.getUIContext().getPromptAction().showToast({
+ message: $r('app.string.prompt_delete'),
+ duration: CommonConstants.PROMPT_DURATION,
+ });
+ } catch (err) {
+ hilog.error(0x0000, 'ContactDeletePage', `showToast failed, code is ${err.code}, message is ${err.message}`);
+ }
+ } else {
+ this.dialogController.open();
+ }
+ },
+ pageId: CommonConstants.DELETE_PAGE_ID,
+ })
+ }
+ }
+ .width('100%')
+ .height('100%')
+ .padding({ left: 16, right: 16 })
+ .backgroundColor(Color.White)
+ .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
+ }
+
+ @Builder
+ NavigationTitle() {
+ Flex({ alignItems: ItemAlign.Center }) {
+ Row() {
+ SymbolGlyph($r('sys.symbol.xmark'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .onClick(() => {
+ this.getUIContext().getRouter().back();
+ })
+
+ Row() {
+ Text($r('app.string.edit_text'))
+ .fontSize(26)
+ .fontWeight(700)
+ .lineHeight(27)
+ .fontColor('rgba(0, 0, 0, 0.9)')
+ }
+ .flexGrow(1)
+
+ Row() {
+ SymbolGlyph($r('sys.symbol.checkmark'))
+ .fontColor(['rgba(0,0,0,0.9)'])
+ .fontSize(24)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .onClick(() => {
+ this.getUIContext().getRouter().back();
+ })
+ }
+ .height(56)
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/ContactDetailPage.ets b/entry/src/main/ets/pages/ContactDetailPage.ets
new file mode 100644
index 0000000000000000000000000000000000000000..d63c0853811d3288f72821ebe7436c034e928f87
--- /dev/null
+++ b/entry/src/main/ets/pages/ContactDetailPage.ets
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import ContactDetailItem from '../components/ContactDetailItem';
+import { ContactData } from '../viewmodel/ContactViewModel';
+import { KvManager } from '../utils/KvManager';
+import { ContactDeleteDialog } from '../components/ContactDeleteDialog';
+import CommonConstants from '../common/CommonConstants';
+
+@Entry
+@Component
+struct ContractDetailPage {
+ @State uiContext: UIContext | undefined = AppStorage.get('uiContext');
+ @State name: string = '';
+ @State telephony: string = '';
+ @State email: string = '';
+ @State remarks: string = '';
+ @State address: string = '';
+ kvManager = AppStorage.get('kvManager') as KvManager;
+ dialogController: CustomDialogController = new CustomDialogController({
+ builder: ContactDeleteDialog({
+ cancel: () => {
+ this.onCancel();
+ },
+ confirm: () => {
+ this.onConfirm();
+ },
+ promptMessage: $r('app.string.tip_delete'),
+ }),
+ autoCancel: true,
+ alignment: DialogAlignment.Center,
+ customStyle: true
+ });
+
+ // Close the pop-up window.
+ onCancel() {
+ try {
+ this.getUIContext().getPromptAction().showToast({
+ message: $r('app.string.delete_cancel_text'),
+ duration: CommonConstants.PROMPT_DURATION
+ });
+ this.dialogController.close();
+ } catch (error) {
+ hilog.error(0x0000, 'ContactDetailPage', `delete_cancel_text err: ${error.code} msg:${error.message}`);
+ }
+ }
+
+ // Confirm the cancellation of transcoding.
+ onConfirm() {
+ let contactsKey = CommonConstants.CONTACTS_DATABASE_KEY + this.name;
+ this.kvManager.deleteOnce(contactsKey, () => {
+ try {
+ this.getUIContext().getPromptAction().showToast({
+ message: $r('app.string.prompt_message_deleted'),
+ duration: CommonConstants.PROMPT_DURATION
+ });
+ } catch (error) {
+ hilog.error(0x0000, 'ContactDetailPage', `prompt_message_deleted err: ${error.code} msg:${error.message}`);
+ }
+ this.getUIContext().getRouter().pushUrl({
+ url: CommonConstants.LIST_PAGE_URL
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactDetailPage', `LIST_PAGE_URL err: ${err.code} msg:${err.message}`);
+ });
+ });
+ this.dialogController.close();
+ }
+
+ onPageShow(): void {
+ let params = this.getUIContext().getRouter().getParams() as Record;
+ this.initializeData(params.key as string);
+ }
+
+ /**
+ * Initialize detail page data.
+ */
+ initializeData(key: string): void {
+ // Get contact details.
+ this.kvManager.getDetails(key, (err: BusinessError, data) => {
+ if (err) {
+ hilog.error(0x0000, 'ContactDetailPage', `DetailPage.ets Fail to get: ${err.code} msg:${err.message}`);
+ return;
+ }
+ let contactsData: ContactData = JSON.parse(data as string);
+ this.name = contactsData.name as string;
+ this.address = contactsData.address as string;
+ this.telephony = contactsData.telephony as string;
+ this.email = contactsData.email as string;
+ this.remarks = contactsData.remarks as string;
+ });
+ }
+
+ onBackPress() {
+ this.uiContext?.getRouter().clear();
+ try {
+ this.uiContext?.getRouter().replaceUrl(
+ { url: "pages/ContactHomePage" }
+ ).catch((error: BusinessError) => {
+ hilog.error(0x0000, 'ContactDetailPage', `have error .Code:${error.code},message: ${error.message}`);
+ });
+ return true;
+ } catch (error) {
+ hilog.error(0x0000, 'ContactDetailPage', `have error .Code:${error.code},message: ${error.message}`);
+ return true;
+ }
+ }
+
+ build() {
+ Column() {
+ this.NavigationTitle();
+ this.RankList();
+ }
+ .width('100%')
+ .height('100%')
+ .padding({ left: 16, right: 16 })
+ .backgroundColor($r('app.color.detail_page_background'))
+ .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
+ }
+
+ @Builder
+ NavigationTitle() {
+ Flex({ alignItems: ItemAlign.Center }) {
+ Row() {
+ SymbolGlyph($r('sys.symbol.chevron_backward'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .onClick(() => {
+ this.getUIContext().getRouter().back();
+ })
+
+ Blank()
+ .flexGrow(1)
+ Row() {
+ SymbolGlyph($r('sys.symbol.square_and_pencil'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .onClick(() => {
+ let params = this.getUIContext().getRouter().getParams() as Record;
+ this.getUIContext().getRouter().pushUrl({
+ url: 'pages/ContactAddAndEditPage',
+ params: {
+ key: params.key as string,
+ isEdit: true,
+ name: this.name,
+ address: this.address,
+ telephony: this.telephony,
+ email: this.email,
+ remarks: this.remarks
+ }
+ }).catch((error: BusinessError) => {
+ hilog.error(0x0000, 'ContactDetailPage', `have error .Code:${error.code},message: ${error.message}`);
+ });
+ })
+
+ Row() {
+ SymbolGlyph($r('sys.symbol.trash'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .onClick(() => {
+ this.dialogController.open();
+ })
+ }
+ .height(56)
+ }
+
+ @Builder
+ RankList() {
+ Column() {
+ Row() {
+ SymbolGlyph($r('sys.symbol.person_crop_circle_fill_1'))
+ .fontSize(122)
+ .fontColor(['#D1D1D6'])
+ }
+ .margin({ top: 32 })
+
+ Text(this.name)
+ .fontColor('rgba(0, 0, 0, 0.6)')
+ .fontSize(38)
+ .fontWeight(700)
+ .margin({ top: 32 })
+
+ Row() {
+ Row() {
+ SymbolGlyph($r('sys.symbol.phone_fill'))
+ .fontSize(26)
+ .fontColor([Color.White])
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(56)
+ .height(56)
+ .backgroundColor('#C4C4C4')
+ .borderRadius('50%')
+
+ Row() {
+ SymbolGlyph($r('sys.symbol.message_fill'))
+ .fontSize(26)
+ .fontColor([Color.White])
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(56)
+ .height(56)
+ .backgroundColor('#C4C4C4')
+ .borderRadius('50%')
+ }
+ .width('100%')
+ .margin({ top: 28 })
+ .padding({ left: 84, right: 84 })
+ .justifyContent(FlexAlign.SpaceBetween)
+
+ Column() {
+ ContactDetailItem({
+ topContent: this.telephony,
+ bottomContent: $r('app.string.edit_item_phone')
+ })
+ ContactDetailItem({
+ topContent: this.email,
+ bottomContent: $r('app.string.edit_item_email')
+ })
+ ContactDetailItem({
+ topContent: this.remarks,
+ bottomContent: $r('app.string.edit_item_note')
+ })
+ }
+ .margin({ top: 28 })
+ .width('100%')
+ }
+ .width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/ContactHomePage.ets b/entry/src/main/ets/pages/ContactHomePage.ets
new file mode 100644
index 0000000000000000000000000000000000000000..8bdad03eedb80a3520874997187552c93de06ce1
--- /dev/null
+++ b/entry/src/main/ets/pages/ContactHomePage.ets
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { common } from '@kit.AbilityKit';
+import { distributedDeviceManager } from '@kit.DistributedServiceKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { distributedKVStore } from '@kit.ArkData';
+import CommonConstants from '../common/CommonConstants';
+import { ListItemData } from '../viewmodel/ContactViewModel';
+import { ContactListItem } from '../components/ContactListItem';
+import { ContactDeviceManager } from '../utils/ContactDeviceManager';
+import ContactDeviceDialog from '../components/ContactDeviceDialog';
+import { KvManager } from '../utils/KvManager';
+
+@Entry
+@Component
+struct ContactHomePage {
+ @State uiContext: UIContext | undefined = AppStorage.get('uiContext');
+ @State contactList: Array = [];
+ @State initContactList: Array = [];
+ @State isShowPopup: boolean = false;
+ @State deviceList: Array = [];
+ @State selectedDeviceIndex: number = 0;
+ @State searchValue: string = '';
+ private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
+ private contactDeviceManager: ContactDeviceManager = new ContactDeviceManager(this.context);
+ kvManager = AppStorage.get('kvManager') as KvManager;
+ dialogController: CustomDialogController = new CustomDialogController({
+ builder: ContactDeviceDialog({
+ deviceList: $deviceList,
+ selectedDeviceIndex: $selectedDeviceIndex,
+ onSelectedIndexChange: (index: number): Promise => this.onSelectedIndexChange(index)
+ }),
+ cancel: () => {
+ this.onClose();
+ },
+ autoCancel: true
+ });
+
+ aboutToAppear(): void {
+ this.kvManager.subscriptionKvStore(() => {
+ this.getAllData();
+ })
+ }
+
+ onPageShow(): void {
+ this.getAllData();
+ }
+
+ onBackPress() {
+ this.context.terminateSelf().catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactHomePage', `onBackPress err: ${err.code} msg:${err.message}`);
+ });
+ return true;
+ }
+
+ getAllData() {
+ this.kvManager.getEntries(CommonConstants.CONTACTS_DATABASE_KEY, (
+ err: BusinessError,
+ entries: distributedKVStore.Entry[]) => {
+ hilog.info(0x0000, 'ContactHomePage', `getAllData entries: ${JSON.stringify(entries)}`);
+ if (err) {
+ hilog.error(0x0000, 'ContactHomePage', `Fail to get Entries: ${err.code} msg:${err.message}`);
+ return;
+ }
+ let listItems: Array = [];
+ entries.forEach((item, index) => {
+ let itemInfo: ListItemData = new ListItemData();
+ itemInfo.name = JSON.parse(item.value.value as string).name;
+ itemInfo.id = index;
+ listItems.push(itemInfo);
+ });
+ this.contactList = this.initContactList = listItems;
+ });
+ }
+
+ async onSelectedIndexChange(index: number) {
+ this.selectedDeviceIndex = index;
+ if (index === 0) {
+ this.onClose();
+ } else {
+ this.selectDevice();
+ }
+ };
+
+ onClose(): void {
+ this.deviceList = [];
+ this.dialogController.close();
+ this.contactDeviceManager.unregisterDeviceListCallback();
+ }
+
+ selectDevice(): void {
+ if (this.contactDeviceManager.discoverList.length <= 0) {
+ this.contactDeviceManager.startAbility(this.deviceList[this.selectedDeviceIndex].networkId);
+ } else {
+ this.contactDeviceManager.authenticateDevice(this.deviceList[this.selectedDeviceIndex], () => {
+ for (let i = 0; i < this.contactDeviceManager.deviceList!.length; i++) {
+ const result = this.contactDeviceManager.deviceList![i].deviceName ===
+ this.deviceList[this.selectedDeviceIndex].deviceName;
+ if (result) {
+ this.contactDeviceManager.startAbility(this.contactDeviceManager.deviceList![i].networkId);
+ }
+ }
+ });
+ }
+ this.onClose();
+ }
+
+ showDeviceDialog(): void {
+ // Register a listening callback.
+ // A dialog box is displayed when a device is discovered or an authenticated device is found.
+ this.contactDeviceManager.registerDeviceListCallback(() => {
+ this.deviceList = [];
+ this.deviceList.push({
+ deviceId: '0',
+ deviceName: CommonConstants.LOCALHOST_NAME,
+ deviceType: '0',
+ networkId: ''
+ });
+ let deviceTempList = this.contactDeviceManager.discoverList.length > 0 ?
+ this.contactDeviceManager.discoverList : this.contactDeviceManager.deviceList;
+ if (deviceTempList.length) {
+ deviceTempList.forEach((item) => {
+ this.deviceList.push({
+ deviceId: item.deviceId,
+ deviceName: item.deviceName,
+ deviceType: item.deviceType,
+ networkId: item.networkId
+ });
+ });
+ }
+ });
+ this.dialogController.open();
+ }
+
+ build() {
+ Flex({ direction: FlexDirection.Column }) {
+ this.NavigationTitle();
+ this.ContactList();
+ }
+ .width('100%')
+ .height('100%')
+ .padding({ left: 16, right: 16 })
+ .backgroundColor(Color.White)
+ .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
+ }
+
+ @Builder
+ NavigationTitle() {
+ Flex({ alignItems: ItemAlign.Center }) {
+ Text($r('app.string.page_title'))
+ .fontSize(20)
+ .fontWeight(700)
+ .lineHeight(27)
+ .fontColor('rgba(0, 0, 0, 0.9)')
+ .flexGrow(1)
+
+ Row() {
+ SymbolGlyph($r('sys.symbol.plus'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .onClick(() => {
+ this.getUIContext().getRouter().pushUrl({
+ url: 'pages/ContactAddAndEditPage',
+ params: {
+ isEdit: false
+ }
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactHomePage', `ContactAddAndEditPage err: ${err.code} msg:${err.message}`);
+ });
+ })
+
+ Row() {
+ SymbolGlyph($r('sys.symbol.dot_grid_2x2'))
+ .fontSize(24)
+ .fontWeight(400)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.Center)
+ .width(40)
+ .height(40)
+ .backgroundColor('rgba(0, 0, 0, 0.05)')
+ .borderRadius(40)
+ .margin({ right: 8 })
+ .bindPopup(this.isShowPopup, {
+ builder: this.popupBuilder,
+ placement: Placement.BottomRight,
+ popupColor: Color.White,
+ enableArrow: false
+ })
+ .onClick(() => {
+ this.isShowPopup = !this.isShowPopup;
+ })
+ }
+ .height(56)
+ .flexShrink(0)
+ }
+
+ @Builder
+ ContactList() {
+ if (!this.contactList.length) {
+ this.emptyView();
+ } else {
+ this.dataView();
+ }
+ }
+
+ @Builder
+ emptyView() {
+ Column() {
+ Image($r('app.media.ic_empty'))
+ .width($r('app.float.contact_picture_size'))
+ .height($r('app.float.contact_picture_size'))
+ Text($r('app.string.contacts_empty'))
+ .fontSize($r('app.float.contacts_empty_size'))
+ .fontColor($r('app.color.empty_font_color'))
+ }
+ .flexGrow(1)
+ .width('100%')
+ .justifyContent(FlexAlign.Center)
+ .alignItems(HorizontalAlign.Center)
+ }
+
+ @Builder
+ dataView() {
+ Flex({ direction: FlexDirection.Column }) {
+ Search({ value: $$this.searchValue, placeholder: Object($r('app.string.search_contact')) })
+ .width('100%')
+ .height(40)
+ .flexShrink(0)
+ .borderRadius(24)
+ .placeholderColor('rgba(0, 0, 0, 0.6)')
+ .placeholderFont({
+ size: 16,
+ weight: 400,
+ })
+ .textFont({ size: 16 })
+ .padding({
+ top: 9,
+ right: 12,
+ bottom: 9,
+ left: 12
+ })
+ .onChange((searchValue: string) => {
+ if (searchValue === '') {
+ this.contactList = this.initContactList;
+ }
+ })
+ .onSubmit((searchValue: string) => {
+ const filterArr = this.contactList.filter((item) => item.name === searchValue);
+ if (filterArr.length > 0) {
+ this.contactList = filterArr;
+ } else {
+ try {
+ this.uiContext!.getPromptAction().showToast({
+ message: $r('app.string.no_matching_data'),
+ duration: CommonConstants.PROMPT_DURATION,
+ });
+ } catch (error) {
+ hilog.error(0x0000, 'ContactHomePage', `have error .Code:${error.code},message: ${error.message}`);
+ }
+ }
+ })
+ Column() {
+ List() {
+ ForEach(this.contactList, (item: ListItemData) => {
+ ListItem() {
+ ContactListItem({ itemInfo: item })
+ }
+ .onClick(() => {
+ const contactsKey = CommonConstants.CONTACTS_DATABASE_KEY + item.name;
+ this.getUIContext().getRouter().pushUrl({
+ url: 'pages/ContactDetailPage',
+ params: {
+ key: contactsKey,
+ },
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactHomePage', `ContactAddAndEditPage err: ${err.code} msg:${err.message}`);
+ });
+ })
+ }, (item: ListItemData) => JSON.stringify(item))
+ }
+ .scrollBar(BarState.Off)
+ .width('100%')
+ .height('100%')
+ .margin({ bottom: $r('app.float.list_area_height') })
+ }
+ .width('100%')
+ .flexGrow(1)
+ }
+ .width('100%')
+ .flexGrow(1)
+ .margin({ top: 8 })
+ }
+
+ @Builder
+ popupBuilder() {
+ Column() {
+ Text($r('app.string.delete_batch'))
+ .fontSize($r('app.float.popup_font_size'))
+ .padding({
+ top: $r('app.float.delete_padding_vertical'),
+ bottom: $r('app.float.delete_padding_vertical'),
+ left: $r('app.float.delete_padding_left')
+ })
+ .width('100%')
+ .onClick(() => {
+ this.getUIContext().getRouter().pushUrl({
+ url: 'pages/ContactDeletePage'
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactHomePage', `ContactAddAndEditPage err: ${err.code} msg:${err.message}`);
+ });
+ })
+ Divider()
+ .height($r('app.float.divider_height'))
+ .backgroundColor($r('app.color.list_divider'))
+ .margin({
+ left: $r('app.float.delete_padding_left'),
+ right: $r('app.float.delete_padding_left')
+ })
+ Text($r('app.string.connection_device'))
+ .width('100%')
+ .fontSize($r('app.float.popup_font_size'))
+ .padding({
+ top: $r('app.float.delete_padding_vertical'),
+ bottom: $r('app.float.delete_padding_vertical'),
+ left: $r('app.float.delete_padding_left')
+ })
+ .onClick(() => {
+ this.isShowPopup = !this.isShowPopup;
+ this.showDeviceDialog();
+ })
+ }
+ .width($r('app.float.popup_width'))
+ .height($r('app.float.popup_height'))
+ .alignItems(HorizontalAlign.Start)
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/DeletePage.ets b/entry/src/main/ets/pages/DeletePage.ets
deleted file mode 100644
index e6c32ff2dde87927f36ccffced21197cd922960e..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/pages/DeletePage.ets
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { distributedKVStore } from '@kit.ArkData';
-import { promptAction } from '@kit.ArkUI';
-import { router } from '@kit.ArkUI';
-import { BusinessError } from '@kit.BasicServicesKit';
-import CommonConstants from '../common/constants/CommonConstants';
-import CheckEmptyUtils from '../common/util/CheckEmptyUtils';
-import BottomBarComponent from '../view/BottomBarComponent';
-import PageViewModel from '../viewmodel/PageViewModel';
-import Logger from '../common/util/Logger';
-import { DeletePageViewModel } from '../viewmodel/DeletePageViewModel';
-import { ContactsDataBase } from '../common/database/ContactsDataBase';
-import { ListItemComponent } from '../view/ListItemComponent';
-import { GlobalContext } from '../common/util/GlobalContext';
-import { ListItemData } from '../viewmodel/ListItemData';
-import { DeleteDialog } from '../view/DeleteDialog';
-
-const TAG: string = 'DeletePage';
-
-@Entry
-@Component
-struct DeletePage {
- @State isSelectAll: boolean = false;
- @State checkList: Array = [];
- @State count: number = 0;
- private deletePageViewModel: DeletePageViewModel = new DeletePageViewModel();
- contactsDataBase = GlobalContext.getContext().getObject('contactsDataBase') as ContactsDataBase;
- dialogController: CustomDialogController = new CustomDialogController({
- builder: DeleteDialog({
- cancel: () => {
- this.deletePageViewModel.cancelDialog();
- },
- confirm: () => {
- this.batchDeleteButton();
- },
- promptMessage: $r('app.string.batch_delete_text'),
- }),
- autoCancel: true,
- alignment: DialogAlignment.Bottom,
- offset: {
- dx: $r('app.float.dialog_dx'),
- dy: $r('app.float.dialog_dy')
- },
- customStyle: false
- });
-
- aboutToAppear() {
- this.initializeData();
- }
-
- /**
- * Initialize edit page data.
- */
- initializeData(): void {
- this.contactsDataBase.getEntries(
- CommonConstants.CONTACTS_DATABASE_KEY,
- (err: BusinessError, entries: distributedKVStore.Entry[]) => {
- Logger.info(TAG, `initializeData entries: ${JSON.stringify(entries)}`);
- if (err) {
- Logger.error(`Fail to get Entries, error message is ${JSON.stringify(err)}`);
- return;
- }
- this.checkList = PageViewModel.getData(entries);
- });
- }
-
- /**
- * Deleting selected contacts in batches.
- */
- batchDeleteButton(): void {
- let keys: string[] = [];
- this.checkList.forEach((item: ListItemData) => {
- if (item.checked) {
- let contactsKey: string = CommonConstants.CONTACTS_DATABASE_KEY + item.name;
- keys.push(contactsKey);
- }
- });
- Logger.info(TAG, `batchDeleteButton keys: ${JSON.stringify(keys)}`);
- // Batch delete.
- this.contactsDataBase.deleteBatch(keys, (err: BusinessError) => {
- if (err) {
- Logger.error(TAG, `Fail to delete Batch, code message is ${JSON.stringify(err)}`);
- return;
- }
- this.getUIContext().getPromptAction().showToast({
- message: $r('app.string.prompt_message_deleted'),
- duration: CommonConstants.PROMPT_DURATION
- });
- this.getUIContext().getRouter().pushUrl({
- url: CommonConstants.LIST_PAGE_URL
- }).catch((err: BusinessError) => {
- Logger.error(TAG, `pushUrl failed, code message is ${JSON.stringify(err)}`);
- });
- });
- }
-
- build() {
- Column() {
- this.NavigationTitle()
- Column() {
- List() {
- ForEach(this.checkList, (item: ListItemData) => {
- ListItem() {
- ListItemComponent({
- itemInfo: item,
- isCanCheck: true,
- onCheck: () => {
- item.checked = !item.checked;
- item.checked ? this.count++ : this.count--;
- }
- })
- }
- }, (item: ListItemData) => JSON.stringify(item))
- }
- .scrollBar(BarState.Off)
- .width(CommonConstants.LIST_WIDTH_PERCENT)
- .height(CommonConstants.PERCENTAGE_MAX)
- }
- .layoutWeight(CommonConstants.WEIGHT)
-
- BottomBarComponent({
- leftClickEvent: (isAll) => {
- if (!CheckEmptyUtils.isEmptyObj(PageViewModel)) {
- this.checkList = isAll ? PageViewModel.getSelectAll(this.checkList)
- : PageViewModel.getUnSelectAll(this.checkList);
- this.count = PageViewModel.getSelectCount(this.checkList);
- }
- },
- rightClickEvent: () => {
- this.count === 0 ? this.deletePageViewModel.unCheckedContact() : this.dialogController.open();
- },
- pageId: CommonConstants.DELETE_PAGE_ID,
- leftIcon: $r('app.media.ic_public_select'),
- leftSubtitle: $r('app.string.bottom_text_all_selected'),
- rightIcon: $r('app.media.ic_delete'),
- rightSubtitle: $r('app.string.bottom_text_deletion')
- })
- }
- .width(CommonConstants.PERCENTAGE_MAX)
- .height(CommonConstants.PERCENTAGE_MAX)
- .backgroundColor($r('app.color.detail_page_background'))
- }
-
- @Builder
- NavigationTitle() {
- Row() {
- Image($r('app.media.ic_close'))
- .height($r('app.float.icon_close_size'))
- .width($r('app.float.icon_close_size'))
- .margin({ right: $r('app.float.icon_close_margin_right') })
- .onClick(() => {
- this.getUIContext().getRouter().back();
- })
- Text($r('app.string.contacts_select'))
- .fontColor($r('app.color.navigation_title'))
- .fontSize($r('app.float.detail_navigation_title'))
- Text(`${this.count}`)
- .fontColor($r('app.color.navigation_title'))
- .fontSize($r('app.float.detail_navigation_title'))
- Text($r('app.string.selected_text'))
- .fontColor($r('app.color.navigation_title'))
- .fontSize($r('app.float.detail_navigation_title'))
- }
- .width(CommonConstants.PAGE_TITLE_WIDTH)
- .height($r('app.float.delete_title_height'))
- .justifyContent(FlexAlign.Start)
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/DetailPage.ets b/entry/src/main/ets/pages/DetailPage.ets
deleted file mode 100644
index ac25964ec7cb2be0639de54153063cf1a66483ee..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/pages/DetailPage.ets
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { router } from '@kit.ArkUI';
-import { BusinessError } from '@kit.BasicServicesKit';
-import CommonConstants from '../common/constants/CommonConstants';
-import DetailItemComponent from '../view/DetailItemComponent';
-import BottomBarComponent from '../view/BottomBarComponent';
-import ContactData from '../viewmodel/ContactData';
-import Logger from '../common/util/Logger';
-import { DetailPageViewModel } from '../viewmodel/DetailPageViewModel';
-import { ContactsDataBase } from '../common/database/ContactsDataBase';
-import { GlobalContext } from '../common/util/GlobalContext';
-import { DeleteDialog } from '../view/DeleteDialog';
-
-const TAG: string = 'DetailPage';
-
-@Entry
-@Component
-struct ContactsDetail {
- @State name: string = '';
- @State telephony: string = '';
- @State email: string = '';
- @State remarks: string = '';
- @State address: string = '';
- private detailPageViewModel: DetailPageViewModel = new DetailPageViewModel();
- contactsDataBase = GlobalContext.getContext().getObject('contactsDataBase') as ContactsDataBase;
- dialogController: CustomDialogController = new CustomDialogController({
- builder: DeleteDialog({
- cancel: () => {
- this.detailPageViewModel.cancelDialog();
- },
- confirm: () => {
- this.detailPageViewModel.deleteContactButton(this.name);
- },
- promptMessage: $r('app.string.delete_dialog_text')
- }),
- autoCancel: true,
- alignment: DialogAlignment.Bottom,
- offset: {
- dx: $r('app.float.dialog_dx'),
- dy: $r('app.float.dialog_dy')
- },
- customStyle: false
- });
-
- aboutToAppear() {
- this.initializeData();
- }
-
- /**
- * Initialize detail page data.
- */
- initializeData(): void {
- let params = this.getUIContext().getRouter().getParams() as Record;
- // Get contact details.
- this.contactsDataBase.get(params.key as string, (err: BusinessError, data) => {
- if (err) {
- Logger.error(TAG, `DetailPage.ets Fail to get, error message is ${JSON.stringify(err)}`);
- return;
- }
- let contactsData: ContactData = JSON.parse(data as string);
- this.name = contactsData.name as string;
- this.address = contactsData.address as string;
- this.telephony = contactsData.telephony as string;
- this.email = contactsData.email as string;
- this.remarks = contactsData.remarks as string;
- });
- }
-
- build() {
- Column() {
- Row() {
- this.NavigationTitle()
- }
- .width(CommonConstants.PERCENTAGE_MAX)
- .height(CommonConstants.DETAIL_TOP_HEIGHT)
- .justifyContent(FlexAlign.Start)
- .alignItems(VerticalAlign.Top)
-
- this.RankList()
- BottomBarComponent({
- leftClickEvent: () => {
- this.detailPageViewModel.redirectEditPage(
- this.name,
- this.address,
- this.telephony,
- this.email,
- this.remarks
- );
- },
- rightClickEvent: () => {
- this.dialogController.open();
- },
- pageId: CommonConstants.DETAIL_PAGE_ID,
- leftIcon: $r('app.media.ic_edit'),
- leftSubtitle: $r('app.string.edit_text'),
- rightIcon: $r('app.media.ic_delete'),
- rightSubtitle: $r('app.string.delete_contact')
- })
- }
- .justifyContent(FlexAlign.End)
- .backgroundColor($r('app.color.details_bg'))
- .height(CommonConstants.PERCENTAGE_MAX)
- }
-
- @Builder
- NavigationTitle() {
- Column() {
- Row() {
- Image($r('app.media.ic_back'))
- .width($r('app.float.edit_icon_size'))
- .height($r('app.float.edit_icon_size'))
- .onClick(() => {
- this.detailPageViewModel.redirectListPage();
- })
- }
- .layoutWeight(CommonConstants.WEIGHT)
- }
- .height($r('app.float.detail_navigation_height'))
- .margin({ left: $r('app.float.image_spacing_margin') })
- }
-
- @Builder
- RankList() {
- Column() {
- Image($r('app.media.ic_contacts_white'))
- .width($r('app.float.details_contacts_icon_size'))
- .height($r('app.float.details_contacts_icon_size'))
- .objectFit(ImageFit.Contain)
- .margin({ top: $r('app.float.details_contacts_icon_margin') })
- Text(this.name)
- .fontColor($r('app.color.contacts_icon_text_color'))
- .fontSize($r('app.float.contacts_icon_font_size'))
- .fontWeight(FontWeight.Medium)
- .margin({ top: $r('app.float.contacts_icon_font_margin') })
- Column() {
- DetailItemComponent({
- componentId: CommonConstants.PHONE_COMPONENTS_ID,
- topContent: this.telephony,
- bottomContent: this.address,
- leftIcon: $r('app.media.ic_phone_icon'),
- rightIcon: $r('app.media.ic_message')
- })
- DetailItemComponent({
- componentId: CommonConstants.Email_COMPONENTS_ID,
- topContent: this.email,
- bottomContent: this.address,
- bottomSubtitle: $r('app.string.item_personal_email'),
- rightIcon: $r('app.media.ic_right_arrow')
- })
- DetailItemComponent({
- componentId: CommonConstants.Note_COMPONENTS_ID,
- bottomContent: this.address,
- topContent: this.remarks,
- bottomSubtitle: $r('app.string.item_remark')
- })
- }
- .margin({ top: $r('app.float.contacts_detail_list_margin') })
- .width(CommonConstants.PERCENTAGE_MAX)
- .height(CommonConstants.PERCENTAGE_MAX)
- }
- .justifyContent(FlexAlign.Start)
- .padding({
- left: $r('app.float.details_padding'),
- right: $r('app.float.details_padding')
- })
- .borderRadius({
- topLeft: $r('app.float.details_border_radius'),
- topRight: $r('app.float.details_border_radius')
- })
- .width(CommonConstants.PERCENTAGE_MAX)
- .layoutWeight(CommonConstants.WEIGHT)
- .backgroundColor(Color.White)
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/EditPage.ets b/entry/src/main/ets/pages/EditPage.ets
deleted file mode 100644
index a3929ca1b22a46529c52fe4b249dcee959a7b1b0..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/pages/EditPage.ets
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { common } from '@kit.AbilityKit';
-import { router } from '@kit.ArkUI';
-import CommonConstants from '../common/constants/CommonConstants';
-import ContactData from '../viewmodel/ContactData';
-import { EditPageViewModel } from '../viewmodel/EditPageViewModel';
-import { GlobalContext } from '../common/util/GlobalContext';
-
-@Entry
-@Component
-struct EditPage {
- @State isSelectAll: boolean = false;
- @State name: string = '';
- @State address: string = '';
- @State telephony: string = '';
- @State email: string = '';
- @State remarks: string = '';
- private isEdit: boolean = false;
- private editPageViewModel: EditPageViewModel = new EditPageViewModel();
- private textInputContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
-
- aboutToAppear() {
- let params = this.getUIContext().getRouter().getParams() as Record;
- this.isEdit = params.isEdit as boolean;
- GlobalContext.getContext().setObject('FirstInTo', false);
- this.initializeData();
- }
-
- /**
- * Contact information change.
- *
- * @param key Key of the text information.
- * @param value Changed Data.
- */
- contactInfoChange(key: Resource, value: string) {
- let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
- let contactKey = context.resourceManager.getStringSync(key);
- switch (contactKey) {
- case context.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_NAME):
- this.name = value;
- break;
- case context.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_ADDRESS):
- this.address = value;
- break;
- case context.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_TEL):
- this.telephony = value;
- break;
- case context.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_EMAIL):
- this.email = value;
- break;
- case context.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_REMARKS):
- this.remarks = value;
- break;
- default:
- break;
- }
- }
-
- /**
- * Initialize edit page data.
- */
- initializeData(): void {
- // Check whether the page is an editing page.
- if (this.getUIContext().getRouter().getParams() && this.isEdit) {
- let params = this.getUIContext().getRouter().getParams() as Record;
- this.name = params.name as string;
- this.address = params.address as string;
- this.telephony = params.telephony as string;
- this.email = params.email as string;
- this.remarks = params.remarks as string;
- }
- }
-
- build() {
- Column() {
- Row() {
- this.NavigationTitle()
- }
- .width(CommonConstants.PERCENTAGE_MAX)
- .height($r('app.float.detail_navigation_height'))
- .backgroundColor($r('app.color.detail_page_background'))
-
- Column() {
- Column() {
- Image($r('app.media.ic_head_portrait'))
- .width($r('app.float.head_portrait_size'))
- .height($r('app.float.head_portrait_size'))
- .objectFit(ImageFit.Contain)
- .margin({
- bottom: $r('app.float.head_portrait_bottom'),
- top: $r('app.float.head_portrait_top')
- })
- if (!this.isEdit) {
- Text($r('app.string.edit_save_to_text'))
- .fontSize($r('app.float.edit_subtitle_size'))
- .fontWeight(FontWeight.Medium)
- .margin({
- bottom: $r('app.float.edit_save_margin_bottom')
- })
- }
- this.Item($r('app.string.edit_item_name'), $r('app.media.ic_name'), this.name)
- this.Item($r('app.string.edit_item_address'), $r('app.media.ic_address'), this.address)
- this.Item($r('app.string.edit_item_phone'), $r('app.media.ic_phone'), this.telephony)
- this.Item($r('app.string.edit_item_email'), $r('app.media.ic_email'), this.email)
- this.Item($r('app.string.edit_item_note'), $r('app.media.ic_note'), this.remarks)
- }
- }
- .layoutWeight(CommonConstants.WEIGHT)
- }
- .width(CommonConstants.PERCENTAGE_MAX)
- .height(CommonConstants.PERCENTAGE_MAX)
- .backgroundColor($r('app.color.detail_page_background'))
- }
-
- @Builder
- NavigationTitle() {
- Column() {
- Row() {
- Row() {
- Image($r('app.media.ic_close'))
- .width($r('app.float.edit_icon_size'))
- .height($r('app.float.edit_icon_size'))
- .margin({ right: $r('app.float.close_icon_margin') })
- .onClick(() => {
- let url = this.isEdit ? CommonConstants.PAGE_DETAIL_URL : CommonConstants.LIST_PAGE_URL;
- this.editPageViewModel.commonRouter(url, this.name);
- })
- Text(this.isEdit ? $r('app.string.edit_title') : $r('app.string.add_contacts_text'))
- .width(CommonConstants.PERCENTAGE_MAX)
- .fontWeight(FontWeight.Medium)
- .fontColor($r('app.color.navigation_title'))
- .fontSize($r('app.float.detail_navigation_title'))
- .focusable(true)
- }
- .layoutWeight(CommonConstants.WEIGHT)
-
- Row() {
- Image($r('app.media.ic_save'))
- .width($r('app.float.edit_icon_size'))
- .height($r('app.float.edit_icon_size'))
- }
- .layoutWeight(CommonConstants.WEIGHT)
- .justifyContent(FlexAlign.End)
- .onClick(() => {
- this.editPageViewModel.saveInfo(
- new ContactData(this.name, this.address, this.telephony, this.email, this.remarks)
- );
- })
- }
- .margin({
- right: $r('app.float.image_spacing_margin'),
- left: $r('app.float.image_spacing_margin')
- })
- }
- }
-
- @Builder
- Item(key: Resource, icon: Resource, content: string) {
- Row() {
- Image(icon)
- .objectFit(ImageFit.Contain)
- .height($r('app.float.item_icon_size'))
- .width($r('app.float.item_icon_size'))
- .margin({ right: $r('app.float.item_icon_margin') })
- Text(key)
- .fontSize($r('app.float.item_font_size'))
- .fontColor($r('app.color.font_color'))
- .fontWeight(FontWeight.Regular)
- TextInput({ text: content })
- .type(this.getInputType(key))
- .maxLength(this.getMaxLength(key))
- .width(CommonConstants.EDIT_INPUT_WIDTH)
- .margin({ right: $r('app.float.edit_input_margin') })
- .backgroundColor(Color.White)
- .fontSize($r('app.float.item_font_size'))
- .fontWeight(FontWeight.Regular)
- .enabled(this.textInputContext.resourceManager.getStringSync(key) === this.getUIContext().getHostContext()!.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_NAME)
- && this.isEdit ? false : true)
- .onChange((value) => {
- this.contactInfoChange(key, value);
- })
- }
- .padding({
- top: $r('app.float.edit_item_padding_top'),
- bottom: $r('app.float.edit_item_padding_bottom'),
- left: $r('app.float.edit_item_padding_left'),
- right: $r('app.float.edit_item_padding_right')
- })
- .margin({
- bottom: $r('app.float.edit_item_margin'),
- top: $r('app.float.edit_item_margin')
- })
- .justifyContent(FlexAlign.Start)
- .backgroundColor(Color.White)
- .borderRadius($r('app.float.edit_item_border_radius'))
- .width(CommonConstants.PAGE_TITLE_WIDTH)
- }
-
- getInputType(key: Resource): InputType {
- switch (this.textInputContext.resourceManager.getStringSync(key)) {
- case this.getUIContext().getHostContext()!.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_TEL):
- return InputType.PhoneNumber;
- case this.getUIContext().getHostContext()!.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_EMAIL):
- return InputType.Email;
- default:
- return InputType.Normal;
- }
- }
-
- getMaxLength(key: Resource): number {
- switch (this.textInputContext.resourceManager.getStringSync(key)) {
- case this.getUIContext().getHostContext()!.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_NAME):
- return CommonConstants.CONTACTS_NAME_MAX_LENGTH;
- case this.getUIContext().getHostContext()!.resourceManager.getStringSync(CommonConstants.CONTACTS_DETAIL_TEL):
- return CommonConstants.CONTACTS_TEL_MAX_LENGTH;
- default:
- return CommonConstants.CONTACTS_DETAIL_MAX;
- }
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/ListPage.ets b/entry/src/main/ets/pages/ListPage.ets
deleted file mode 100644
index 37986b0f8b8f87f5fe64656ae820f5d5e9b14fe0..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/pages/ListPage.ets
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { distributedKVStore } from '@kit.ArkData';
-import { distributedDeviceManager } from '@kit.DistributedServiceKit';
-import { common } from '@kit.AbilityKit';
-import { router } from '@kit.ArkUI';
-import { BusinessError } from '@kit.BasicServicesKit';
-import CommonConstants from '../common/constants/CommonConstants';
-import DeviceListDialogComponent from '../view/DeviceDialog';
-import PageViewModel from '../viewmodel/PageViewModel';
-import Logger from '../common/util/Logger';
-import { ContactsDataBase } from '../common/database/ContactsDataBase';
-import { RemoteDeviceModel } from '../viewmodel/RemoteDeviceModel';
-import { ListPageViewModel } from '../viewmodel/ListPageViewModel';
-import { ListAreaComponent } from '../view/ListAreaComponent';
-import { GlobalContext } from '../common/util/GlobalContext';
-import { ListItemData } from '../viewmodel/ListItemData';
-
-const TAG: string = 'ListPage';
-
-@Entry
-@Component
-struct ListPage {
- @StorageLink('deviceList') deviceList: Array = [];
- @StorageLink('contactData') contactData: Array = [];
- @StorageLink('isContactsEmpty') isContactsEmpty: boolean = true;
- @StorageLink('contactsNumber') contactsNumber: number = 0;
- @State selectedIndex: number | undefined = 0;
- @State customPopup: boolean = false;
- private listPageViewModel: ListPageViewModel = new ListPageViewModel();
- private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel();
- private subscriptionKvStore: () => void = (): void => {
- // When the remote data changes, the page data is updated.
- this.getAllData();
- };
- private dialogController: CustomDialogController | null = null;
- contactsDataBase = GlobalContext.getContext().getObject('contactsDataBase') as ContactsDataBase;
- onSelectedIndexChange = async (index: number | undefined) => {
- Logger.info(TAG, `selectedIndexChange`);
- this.selectedIndex = index;
- if (this.selectedIndex === 0) {
- Logger.info(TAG, `stop ability`);
- this.listPageViewModel.startAbilityCallBack(CommonConstants.EXIT);
- this.deviceList = [];
- if (this.dialogController !== null) {
- this.dialogController.close();
- }
- return;
- }
- this.selectDevice();
- };
-
- selectDevice(): void {
- if (this.selectedIndex !== undefined && (this.remoteDeviceModel === null ||
- this.remoteDeviceModel.discoverList.length <= 0)) {
- Logger.info(TAG, `continue unauthed device: ${JSON.stringify(this.deviceList)}`);
- this.listPageViewModel.startAbility(this.deviceList[this.selectedIndex].networkId);
- this.clearSelectState();
- return;
- }
- Logger.info(TAG, `start ability, needAuth:`);
- if (this.selectedIndex !== undefined) {
- this.remoteDeviceModel.authenticateDevice(this.deviceList[this.selectedIndex], () => {
- Logger.info(TAG, `auth and online finished`);
- if (this.remoteDeviceModel !== null
- && this.remoteDeviceModel.deviceList !== null
- && this.selectedIndex !== undefined) {
- for (let i = 0; i < this.remoteDeviceModel.deviceList!.length; i++) {
- if (this.remoteDeviceModel.deviceList![i].deviceName === this.deviceList[this.selectedIndex].deviceName) {
- this.listPageViewModel.startAbility(this.remoteDeviceModel.deviceList![i].networkId);
- }
- }
- }
- })
- }
- this.clearSelectState();
- }
-
- clearSelectState(): void {
- this.deviceList = [];
- if (this.dialogController !== null) {
- this.dialogController.close();
- }
- Logger.info(TAG, `cancelDialog`);
- if (this.remoteDeviceModel === undefined) {
- return;
- }
- this.remoteDeviceModel.unregisterDeviceListCallback();
- }
-
- onPageShow() {
- // Access the listPage page to obtain the list data.
- this.getAllData();
- this.getUIContext().getRouter().clear();
- }
-
- aboutToAppear() {
- GlobalContext.getContext().setObject('FirstInTo', true);
- setTimeout(() => {
- this.contactsDataBase.subscriptionKvStore(this.subscriptionKvStore);
- }, 50);
- }
-
- /**
- * This interface is used to obtain all data on the contact home page.
- */
- getAllData(): void {
- setTimeout(() => {
- this.contactsDataBase.getEntries(CommonConstants.CONTACTS_DATABASE_KEY, (
- err: BusinessError,
- entries: distributedKVStore.Entry[]) => {
- Logger.info(TAG, `getAllData entries: ${JSON.stringify(entries)}`);
- if (err) {
- Logger.error(`Fail to get Entries, code message is ${JSON.stringify(err)}`);
- return;
- }
- this.contactsNumber = entries.length;
- this.isContactsEmpty = this.contactsNumber > 0 ? false : true;
- this.contactData = PageViewModel.getData(entries);
- }
- );
- }, 50);
- }
-
- /**
- * In the dialog box that is displayed, select a device from the list and verify the device for the first time.
- * After the verification is successful, start the remote device application for the second time.
- */
- showDialogInfo(): void {
- this.deviceList = [];
- // Register a listening callback.
- // A dialog box is displayed when a device is discovered or an authenticated device is found.
- this.remoteDeviceModel.registerDeviceListCallback(() => {
- this.deviceList = [];
- Logger.info(TAG, `registerDeviceListCallback, callback entered`);
- let context: common.UIAbilityContext | undefined = AppStorage.get('UIAbilityContext');
- if (context !== undefined) {
- this.deviceList.push({
- deviceId: '0',
- deviceName: CommonConstants.LOCALHOST_NAME,
- deviceType: '0',
- networkId: ''
- })
- }
- let deviceTempList = this.remoteDeviceModel.discoverList.length > 0 ?
- this.remoteDeviceModel.discoverList : this.remoteDeviceModel.deviceList;
- if (deviceTempList !== null) {
- for (let i = 0; i < deviceTempList!.length; i++) {
- this.deviceList.push({
- deviceId: deviceTempList![i].deviceId,
- deviceName: deviceTempList![i].deviceName,
- deviceType: deviceTempList![i].deviceType,
- networkId: deviceTempList![i].networkId
- });
- AppStorage.set('deviceList', this.deviceList);
- }
- }
- })
- if (this.dialogController === null) {
- this.dialogController = new CustomDialogController({
- builder: DeviceListDialogComponent({
- selectedIndex: this.selectedIndex,
- onSelectedIndexChange: this.onSelectedIndexChange
- }),
- cancel: () => {
- this.clearSelectState();
- },
- autoCancel: true
- })
- }
- if (this.dialogController !== null) {
- this.dialogController.open();
- }
- }
-
- build() {
- Column() {
- this.NavigationMenus()
- this.ListHeader()
- this.List()
- }
- .padding({
- left: $r('app.float.search_padding_horizontal'),
- right: $r('app.float.search_padding_horizontal')
- })
- .width(CommonConstants.PERCENTAGE_MAX)
- .backgroundColor($r('app.color.theme_background'))
- }
-
- @Builder
- NavigationMenus() {
- Row() {
- Image($r('app.media.ic_add'))
- .width($r('app.float.empty_view_size'))
- .height($r('app.float.empty_view_size'))
- .margin({ right: $r('app.float.image_spacing_margin') })
- .onClick(() => {
- this.listPageViewModel.redirectAddPage();
- })
- Image($r('app.media.ic_more'))
- .width($r('app.float.empty_view_size'))
- .height($r('app.float.empty_view_size'))
- .margin({ right: $r('app.float.image_spacing_margin') })
- .bindPopup(this.customPopup, {
- builder: this.popupBuilder,
- placement: Placement.Bottom,
- popupColor: Color.White,
- enableArrow: false
- })
- .onClick(() => {
- this.customPopup = !this.customPopup;
- })
- }
- .height($r('app.float.detail_navigation_height'))
- .width(CommonConstants.PERCENTAGE_MAX)
- .justifyContent(FlexAlign.End)
- }
-
- @Builder
- ListHeader() {
- Column() {
- Text($r('app.string.page_title'))
- .textAlign(TextAlign.Start)
- .width(CommonConstants.PERCENTAGE_MAX)
- .fontColor($r('app.color.navigation_title'))
- .fontSize($r('app.float.title_size'))
- .focusable(true)
- if (!this.isContactsEmpty) {
- Row() {
- Text(this.contactsNumber.toString())
- .margin({ top: $r('app.float.contacts_num_margin_top') })
- .fontSize($r('app.float.contacts_num_size'))
- .fontColor($r('app.color.contacts_num'))
- Text($r('app.string.contacts_num_template'))
- .margin({ top: $r('app.float.contacts_num_margin_top') })
- .fontSize($r('app.float.contacts_num_size'))
- .fontColor($r('app.color.contacts_num'))
- }
- .width(CommonConstants.PERCENTAGE_MAX)
- }
- }
- .margin({
- top: $r('app.float.navigation_margin_top'),
- left: $r('app.float.navigation_margin_left'),
- right: $r('app.float.navigation_margin_right'),
- bottom: $r('app.float.navigation_margin_bottom')
- })
- }
-
- @Builder
- List() {
- if (this.isContactsEmpty) {
- // There is no data in the database.
- this.buildEmptyView();
- } else {
- // The database has data.
- this.buildDataView();
- }
- }
-
- @Builder
- buildEmptyView() {
- Column() {
- Image($r('app.media.ic_empty'))
- .width($r('app.float.contact_picture_size'))
- .height($r('app.float.contact_picture_size'))
- .margin({ bottom: $r('app.float.empty_view_margin') })
- Text($r('app.string.contacts_empty'))
- .fontSize($r('app.float.contacts_empty_size'))
- .fontColor($r('app.color.empty_font_color'))
- }
- .height(CommonConstants.PERCENTAGE_MAX)
- .width(CommonConstants.PERCENTAGE_MAX)
- .justifyContent(FlexAlign.Start)
- .padding({
- top: CommonConstants.CONTACTS_EMPTY_TOP
- })
- }
-
- @Builder
- buildDataView() {
- Column() {
- Search({
- placeholder: Object($r('app.string.search_contact'))
- })
- .width(CommonConstants.PERCENTAGE_MAX)
- .height($r('app.float.search_height'))
- .border({ radius: $r('app.float.search_radius') })
- .placeholderColor($r('app.color.empty_font_color'))
- .placeholderFont({
- size: $r('app.float.search_text_size'),
- weight: CommonConstants.SEARCH_FONT_WEIGHT,
- family: CommonConstants.SEARCH_FONT_FAMILY,
- style: FontStyle.Normal
- })
- .textFont({ size: $r('app.float.search_text_size') })
- .margin({ bottom: $r('app.float.search_margin_bottom') })
- .padding({
- left: $r('app.float.search_padding'),
- right: $r('app.float.search_padding')
- })
- ListAreaComponent({ contactData: $contactData })
- .height(CommonConstants.CONTACTS_LIST_HEIGHT)
- }
- .backgroundColor($r('app.color.theme_background'))
- .width(CommonConstants.PERCENTAGE_MAX)
- }
-
- @Builder
- popupBuilder() {
- Column() {
- Text($r('app.string.delete_batch'))
- .fontSize($r('app.float.popup_font_size'))
- .padding({
- top: $r('app.float.delete_padding_vertical'),
- bottom: $r('app.float.delete_padding_vertical'),
- left: $r('app.float.delete_padding_left')
- })
- .onClick(() => {
- this.listPageViewModel.redirectDeletePage();
- })
- Divider()
- .height($r('app.float.divider_height'))
- .backgroundColor($r('app.color.list_divider'))
- .margin({
- left: $r('app.float.delete_padding_left'),
- right: $r('app.float.delete_padding_left')
- })
- Text($r('app.string.connection_device'))
- .fontSize($r('app.float.popup_font_size'))
- .padding({
- top: $r('app.float.delete_padding_vertical'),
- bottom: $r('app.float.delete_padding_vertical'),
- left: $r('app.float.delete_padding_left')
- })
- .onClick(() => {
- this.customPopup = !this.customPopup;
- Logger.info('Index', 'onContinueAbilityClick execute');
- this.showDialogInfo();
- })
- }
- .width($r('app.float.popup_width'))
- .height($r('app.float.popup_height'))
- .alignItems(HorizontalAlign.Start)
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/viewmodel/RemoteDeviceModel.ets b/entry/src/main/ets/utils/ContactDeviceManager.ets
similarity index 31%
rename from entry/src/main/ets/viewmodel/RemoteDeviceModel.ets
rename to entry/src/main/ets/utils/ContactDeviceManager.ets
index 2fb560d83bb99b4eaae63b43ab572e9e3d31fb10..309b6f2e15c751db4d3a9e0ccf3a307d1ff75416 100644
--- a/entry/src/main/ets/viewmodel/RemoteDeviceModel.ets
+++ b/entry/src/main/ets/utils/ContactDeviceManager.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -15,103 +15,128 @@
import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { Callback } from '@kit.BasicServicesKit';
-import Logger from '../common/util/Logger';
-import CommonConstants from '../common/constants/CommonConstants';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { JSON } from '@kit.ArkTS';
+import { common, Want } from '@kit.AbilityKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import CommonConstants from '../common/CommonConstants';
-const TAG: string = 'RemoteDeviceModel';
+const TAG: string = 'ContactDeviceManager';
-export class RemoteDeviceModel {
- public deviceList: Array | null = [];
- public discoverList: Array = [];
- private callback: () => void = () => {
+// Address book device management.
+export class ContactDeviceManager {
+ private context: common.UIAbilityContext;
+ private deviceManager: distributedDeviceManager.DeviceManager;
+ callback: () => void = () => {
};
- private authCallback: () => void = () => {
+ deviceList: Array = [];
+ discoverList: Array = [];
+ authCallback: () => void = () => {
};
- private deviceManager: distributedDeviceManager.DeviceManager | undefined = undefined;
+
+ constructor(context: common.UIAbilityContext) {
+ this.context = context;
+ this.deviceManager = distributedDeviceManager.createDeviceManager(CommonConstants.BUNDLE_NAME);
+ }
+
+ /**
+ * The registered device status callback returns the device status and information.
+ */
private deviceStateChange: (data: DeviceManagerData) => void = (data: DeviceManagerData): void => {
- if (data === null) {
- return;
- }
- Logger.info(TAG, `deviceStateChange data= ${JSON.stringify(data)}`)
- switch (data.action) {
- // Physical devices go online.
- case distributedDeviceManager.DeviceStateChange.AVAILABLE:
- this.changeState(data.device, distributedDeviceManager.DeviceStateChange.AVAILABLE);
- break;
- // Device availability status.
- case distributedDeviceManager.DeviceStateChange.UNKNOWN:
- this.changeStateOnline(data.device);
- break;
- // Physical offline of the device.
- case distributedDeviceManager.DeviceStateChange.UNAVAILABLE:
- this.changeStateOffline(data.device);
- break;
- default:
- break;
+ if (data) {
+ switch (data.action) {
+ // Physical devices go online.
+ case distributedDeviceManager.DeviceStateChange.AVAILABLE:
+ this.changeState(data.device);
+ break;
+ // Device availability status.
+ case distributedDeviceManager.DeviceStateChange.UNKNOWN:
+ this.changeStateOnline(data.device);
+ break;
+ // Physical offline of the device.
+ case distributedDeviceManager.DeviceStateChange.UNAVAILABLE:
+ this.changeStateOffline(data.device);
+ break;
+ default:
+ break;
+ }
}
};
+ /**
+ * Discovering callback methods for devices
+ */
private discoverSuccess: (data: DeviceBasicInfo) => void = (data: DeviceBasicInfo): void => {
- if (data === null) {
- return;
+ if (data) {
+ this.deviceFound(data.device);
}
- Logger.info(TAG, `discoverSuccess data=${JSON.stringify(data)}`);
- this.deviceFound(data.device);
- };
- private discoverFailure: (data: DiscoverFailureData) => void = (data: DiscoverFailureData): void => {
- Logger.info(TAG, `discoverFailure data= ${JSON.stringify(data)}`);
- };
- private serviceDie: () => void = () => {
- Logger.error(TAG, 'serviceDie');
};
+ /**
+ * Register device list callback function
+ * @param callback
+ */
registerDeviceListCallback(callback: Callback) {
- if (typeof (this.deviceManager) !== 'undefined') {
- this.registerDeviceListCallbackImplement(callback);
- return;
- }
try {
- // Create a device management instance.
- let dmInstance = distributedDeviceManager.createDeviceManager(CommonConstants.BUNDLE_NAME);
- this.deviceManager = dmInstance;
- this.registerDeviceListCallbackImplement(callback);
- } catch (error) {
- Logger.error(TAG, `createDeviceManager throw, error message is ${JSON.stringify(error)}`);
+ this.callback = callback;
+ if (!this.deviceManager) {
+ this.callback();
+ } else {
+ this.deviceList = this.deviceManager.getAvailableDeviceListSync();
+ this.callback();
+ // Register device status callback.
+ this.deviceManager.on('deviceStateChange', this.deviceStateChange);
+ // This interface is used to call back the listener when the device is successfully registered and discovered.
+ this.deviceManager.on('discoverSuccess', this.discoverSuccess);
+ this.startDeviceDiscovery();
+ }
+ } catch (err) {
+ hilog.error(0x0000, 'ContactDeviceManager', `have error .Code:${err.code},message: ${err.message}`);
}
}
- changeStateOnline(device: distributedDeviceManager.DeviceBasicInfo) {
- if (this.deviceList !== null) {
- this.deviceList![this.deviceList!.length] = device;
- }
- Logger.info(TAG, `online, device list= ${JSON.stringify(this.deviceList)}`);
- this.callback();
- if (this.authCallback !== null) {
- this.authCallback();
- this.authCallback = () => {
+ /**
+ * Cancel registration of device list
+ */
+ unregisterDeviceListCallback() {
+ if (this.deviceManager) {
+ try {
+ this.deviceList = [];
+ this.discoverList = [];
+ this.deviceManager.stopDiscovering();
+ // Device status cancellation callback.
+ this.deviceManager.off('deviceStateChange', this.deviceStateChange);
+ // Callback for successful deregistration of device discovery.
+ this.deviceManager.off('discoverSuccess', this.discoverSuccess);
+ } catch (error) {
+ hilog.error(0x0000, 'ContactDeviceManager', `have error .Code:${error.code},message: ${error.message}`);
}
}
+
+ }
+
+ changeStateOnline(device: distributedDeviceManager.DeviceBasicInfo) {
+ this.deviceList[this.deviceList.length] = device;
+ hilog.info(0x0000, 'hilog', TAG, `online, device list= ${JSON.stringify(this.deviceList)}`);
+ this.callback();
+ this.authCallback();
+ this.authCallback = () => {
+ };
}
changeStateOffline(device: distributedDeviceManager.DeviceBasicInfo) {
- if (this.deviceList !== null && this.deviceList!.length > 0) {
- let list: Array = [];
- for (let j = 0; j < this.deviceList!.length; j++) {
- if (this.deviceList![j].deviceId !== device.deviceId) {
- list[j] = device;
- }
+ let list: Array = [];
+ for (let j = 0; j < this.deviceList!.length; j++) {
+ if (this.deviceList![j].deviceId !== device.deviceId) {
+ list[j] = device;
}
- this.deviceList = list;
}
- Logger.info(TAG, `offline, updated device list=${JSON.stringify(device)}`);
+ this.deviceList = list;
+ hilog.info(0x0000, 'hilog', TAG, `offline, updated device list=${JSON.stringify(device)}`);
this.callback();
}
- changeState(device: distributedDeviceManager.DeviceBasicInfo, state: number) {
- if (this.deviceList !== null && this.deviceList!.length <= 0) {
- this.callback();
- return;
- }
- if (this.deviceList !== null && state === distributedDeviceManager.DeviceStateChange.AVAILABLE) {
+ changeState(device: distributedDeviceManager.DeviceBasicInfo) {
+ try {
let list: Array = new Array();
for (let i = 0; i < this.deviceList!.length; i++) {
if (this.deviceList![i].deviceId !== device.deviceId) {
@@ -119,69 +144,21 @@ export class RemoteDeviceModel {
}
}
this.deviceList = list;
- Logger.info(TAG, `ready, device list= ${JSON.stringify(device)}`);
- this.callback();
- } else {
- if (this.deviceList !== null) {
- for (let j = 0; j < this.deviceList!.length; j++) {
- if (this.deviceList![j].deviceId === device.deviceId) {
- this.deviceList![j] = device;
- break;
- }
- }
- Logger.info(TAG, `offline, device list= ${JSON.stringify(this.deviceList)}`);
- this.callback();
- }
- }
- }
-
- registerDeviceListCallbackImplement(callback: Callback) {
- this.callback = callback;
- if (this.deviceManager === undefined) {
- Logger.error(TAG, 'deviceManager has not initialized');
- this.callback();
- return;
- }
- try {
- // This interface is used to obtain the list of all trusted devices.
- let list = this.deviceManager !== undefined ? this.deviceManager.getAvailableDeviceListSync() : null;
- Logger.info(TAG, `getTrustedDeviceListSync end, deviceList= ${JSON.stringify(list)}`);
- if (typeof (list) !== 'undefined' && JSON.stringify(list) !== '[]') {
- this.deviceList = list!;
- }
- Logger.info(TAG, `getTrustedDeviceListSync end, deviceList=${JSON.stringify(list)}`);
} catch (error) {
- Logger.error(TAG, `getTrustedDeviceListSync, error message is ${JSON.stringify(error)}`);
+ hilog.error(0x0000, 'ContactDeviceManager', `have error .Code:${error.code},message: ${error.message}`);
}
this.callback();
- try {
- if (this.deviceManager !== undefined) {
- // Register device status callback.
- this.deviceManager.on('deviceStateChange', this.deviceStateChange);
- }
- if (this.deviceManager !== undefined) {
- // This interface is used to call back the listener when the device is successfully registered and discovered.
- this.deviceManager.on('discoverSuccess', this.discoverSuccess);
- // Callback listener for device discovery failure.
- this.deviceManager.on('discoverFailure', this.discoverFailure);
- // Registers the dead listener of the device management service.
- this.deviceManager.on('serviceDie', this.serviceDie);
- }
- } catch (error) {
- Logger.error(TAG, `on throw, error message is ${JSON.stringify(error)}`);
- }
- this.startDeviceDiscovery();
}
deviceFound(data: distributedDeviceManager.DeviceBasicInfo) {
for (let i = 0; i < this.discoverList.length; i++) {
if (this.discoverList[i].deviceId === data.deviceId) {
- Logger.info(TAG, 'device founded ignored');
+ hilog.info(0x0000, 'hilog', TAG, 'device founded ignored');
return;
}
}
this.discoverList.push(data);
- Logger.info(TAG, `deviceFound self.discoverList= ${this.discoverList}`);
+ hilog.info(0x0000, 'hilog', TAG, `deviceFound self.discoverList= ${this.discoverList}`);
this.callback();
}
@@ -196,79 +173,45 @@ export class RemoteDeviceModel {
'availableStatus': 0
};
try {
- if (this.deviceManager !== undefined) {
- // Discover peripheral devices. The discovery status lasts for two minutes.
- // If the discovery status exceeds two minutes, the discovery stops. A maximum of 99 nodes can be discovered.
- this.deviceManager.startDiscovering(discoverParam, filterOptions);
- }
+ // Discover peripheral devices. The discovery status lasts for two minutes.
+ // If the discovery status exceeds two minutes, the discovery stops. A maximum of 99 nodes can be discovered.
+ this.deviceManager.startDiscovering(discoverParam, filterOptions);
} catch (error) {
- Logger.error(TAG, `startDeviceDiscovery throw, error message is ${JSON.stringify(error)}`);
+ hilog.error(0x0000, 'ContactDeviceManager', `have error .Code:${error.code},message: ${error.message}`);
}
}
- unregisterDeviceListCallback() {
- if (this.deviceManager === undefined) {
- return;
- }
- if (this.deviceManager !== undefined) {
- try {
- // Stop detecting peripheral devices.
- this.deviceManager.stopDiscovering();
- } catch (error) {
- Logger.error(TAG, `stopDeviceDiscovery throw, error message is ${JSON.stringify(error)}`);
- }
- try {
- // Device status cancellation callback.
- this.deviceManager.off('deviceStateChange');
- // Callback for successful deregistration of device discovery.
- this.deviceManager.off('discoverSuccess');
- // Callback when device discovery fails to be deregistered.
- this.deviceManager.off('discoverFailure');
- // This interface is used to unregister the dead listener of the device management service.
- this.deviceManager.off('serviceDie');
- } catch (error) {
- Logger.error(TAG, `off throw, error message is ${JSON.stringify(error)}`);
- }
- }
- this.deviceList = [];
- this.discoverList = [];
+ startAbility(deviceId: string | undefined) {
+ hilog.info(0x0000, 'hilog', TAG, `startAbility deviceId: ${deviceId}`);
+ let want: Want = {
+ bundleName: CommonConstants.BUNDLE_NAME,
+ abilityName: CommonConstants.ENTRY_ABILITY,
+ deviceId: deviceId
+ };
+ this.context.startAbility(want).then((data) => {
+ hilog.info(0x0000, 'hilog', TAG, `start ability finished: ${data}`);
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'ContactDeviceManager', `startAbility error .Code:${err.code},message: ${err.message}`);
+ });
}
authenticateDevice(device: distributedDeviceManager.DeviceBasicInfo, callBack: Callback) {
- Logger.info(TAG, `authenticateDevice ${JSON.stringify(device)}`);
+ hilog.info(0x0000, 'hilog', TAG, `authenticateDevice ${JSON.stringify(device)}`);
for (let i = 0; i < this.discoverList.length; i++) {
- if (this.discoverList[i].deviceId !== device.deviceId) {
- continue;
- }
- try {
- if (this.deviceManager !== undefined) {
- // Authenticate the device.
- this.deviceManager.bindTarget(device.deviceId, {
- bindType: 1,
- targetPkgName: CommonConstants.BUNDLE_NAME,
- appName: CommonConstants.APP_NAME
- }, (err, data) => {
- if (err) {
- Logger.error(TAG, `authenticateDevice error: ${JSON.stringify(err)}`);
- this.authCallback = () => {
- }
- return;
- }
- Logger.info(TAG, `authenticateDevice succeed: ${JSON.stringify(data)}`);
- this.authCallback = callBack;
- });
- }
- } catch (error) {
- Logger.error(TAG, `authenticateDevice throw, error message is ${JSON.stringify(error)}`);
+ if (this.discoverList[i].deviceId === device.deviceId) {
+ this.deviceManager.bindTarget(device.deviceId, {
+ bindType: 1,
+ targetPkgName: CommonConstants.BUNDLE_NAME,
+ appName: CommonConstants.APP_NAME
+ }, (err) => {
+ this.authCallback = err ? () => {
+ } : callBack;
+ });
}
}
}
}
-class DiscoverFailureData {
- reason: number = 0;
-}
-
class DeviceBasicInfo {
device: distributedDeviceManager.DeviceBasicInfo = {
deviceId: "",
diff --git a/entry/src/main/ets/common/database/ContactsDataBase.ets b/entry/src/main/ets/utils/KvManager.ets
similarity index 59%
rename from entry/src/main/ets/common/database/ContactsDataBase.ets
rename to entry/src/main/ets/utils/KvManager.ets
index 72348b44ea7cebfd7630f2ceaf1c73c10a8c79ae..63b1f535b1555843d56f6c60a1e3f3f5d2b7f75c 100644
--- a/entry/src/main/ets/common/database/ContactsDataBase.ets
+++ b/entry/src/main/ets/utils/KvManager.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -16,32 +16,35 @@
import { distributedKVStore } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import { AsyncCallback, BusinessError, Callback } from '@kit.BasicServicesKit';
-import { distributedDeviceManager } from '@kit.DistributedServiceKit'
-import CommonConstants from '../constants/CommonConstants';
-import Logger from '../util/Logger';
-import { GlobalContext } from '../../common/util/GlobalContext';
+import { distributedDeviceManager } from '@kit.DistributedServiceKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import CommonConstants from '../common/CommonConstants';
-const TAG: string = 'ContactsDataBase';
+const TAG: string = 'KvManager';
-export class ContactsDataBase {
+// Key value database management class.
+export class KvManager {
private kvManager: distributedKVStore.KVManager | undefined = undefined;
private kvStore: distributedKVStore.SingleKVStore | undefined = undefined;
- private context: common.UIAbilityContext;
+ private context: common.UIAbilityContext | undefined = undefined;
- constructor() {
- this.context = GlobalContext.getContext().getObject('entryContext') as common.UIAbilityContext;
+ constructor(context: common.UIAbilityContext) {
+ this.context = context;
this.createManager();
}
+ /**
+ * Create a KVManager object instance for managing database objects
+ */
createManager(): void {
- if ((typeof (this.kvStore) !== 'undefined')) {
+ if (typeof this.kvStore !== 'undefined') {
return;
}
try {
// Creates a KVManager object instance.
this.kvManager = distributedKVStore.createKVManager({
bundleName: CommonConstants.BUNDLE_NAME,
- context: this.context
+ context: this.context,
});
let options: distributedKVStore.Options = {
createIfMissing: true, // Indicates whether to create a database when the database file does not exist.
@@ -49,130 +52,126 @@ export class ContactsDataBase {
backup: false, // Indicates whether to back up database files.
autoSync: false, // Indicates whether to automatically synchronize database files.
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // Set the type of database to be created.
- securityLevel: distributedKVStore.SecurityLevel.S1// Setting the database security level.
+ securityLevel: distributedKVStore.SecurityLevel.S1, // Setting the database security level.
};
// distributed key-value database.
- this.kvManager.getKVStore(
- CommonConstants.DB_STORE_ID,
- options,
+ this.kvManager.getKVStore(CommonConstants.DB_STORE_ID, options,
(err: BusinessError, store: distributedKVStore.SingleKVStore | undefined) => {
if (err) {
- Logger.error(TAG, `Failed to get KVStore: code message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `Failed to get KVStore, Code:${err.code},message: ${err.message}`);
return;
}
this.kvStore = store;
- });
+ });
} catch (err) {
- Logger.error(TAG, `An unexpected error occurred, code message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `An unexpected error occurred, Code:${err.code},message: ${err.message}`);
}
}
/**
- * This command is used to shut down the specified KVStore database by package name.
+ * Close the specified distributed key value database using the value of storeId
*/
closeKVStore(): void {
try {
- this.kvManager?.closeKVStore(this.context.abilityInfo.bundleName, CommonConstants.DB_STORE_ID,
+ this.kvManager?.closeKVStore(CommonConstants.BUNDLE_NAME, CommonConstants.DB_STORE_ID,
(err: BusinessError) => {
if (err !== undefined) {
- console.error(TAG, `Failed to close KVStore, error message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `Failed to close KVStore, Code:${err.code},message: ${err.message}`);
return;
}
- Logger.info(TAG, 'Succeeded in closing KVStore');
+ hilog.info(0x0000, 'hilog', TAG, 'Succeeded in closing KVStore');
});
} catch (err) {
- Logger.error(TAG, `CloseKVStore an unexpected error occurred, error message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `CloseKVStore an unexpected error occurred, Code:${err.code},message: ${err.message}`);
}
}
- /**
- * Deletes a specified KVStore database by package name.
- */
- deleteKVStore(): void {
- try {
- this.kvManager?.deleteKVStore(this.context.abilityInfo.bundleName, CommonConstants.DB_STORE_ID);
- this.syncRemote();
- } catch (err) {
- Logger.error(TAG, `DeleteKVStore an unexpected error occurred, error message is ${JSON.stringify(err)}`);
- }
- }
/**
- * Delete key-value pairs from the KVStore database in batches.
- *
- * @param keys Indicates the key-value pairs to be deleted in batches.
+ * Subscribe to distributed data changes.
* @param callback Callback function.
*/
- deleteBatch(keys: string[], callback: AsyncCallback): void {
+ subscriptionKvStore(callback: Callback): void {
try {
- this.kvStore?.deleteBatch(keys, callback);
- this.syncRemote();
+ this.kvStore?.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, callback);
} catch (err) {
- Logger.error(TAG, `DeleteKVStore an unexpected error occurred, error message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `DataChange an unexpected error occured, Code:${err.code},message: ${err.message}`);
}
}
/**
- * Subscribe to distributed data changes.
- *
- * @param callback Callback function.
+ * Remove the data change listener.
*/
- subscriptionKvStore(callback: Callback): void {
+ removeDataChangeListener(): void {
+ if (this.kvStore === null) {
+ return;
+ }
try {
- this.kvStore?.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, callback);
- } catch (err) {
- Logger.error(TAG, `DataChange an unexpected error occured, error message is ${JSON.stringify(err)}`);
+ this.kvStore?.off('dataChange');
+ } catch (error) {
+ hilog.error(0x0000, 'KvManager', `have error, Code:${error.code},message: ${error.message}`);
}
}
/**
* Adds a key-value pair of a specified type to the database.
* If the key value exists, modify the data. Otherwise, add data.
- *
* @param The Key Key of the data to be added.
* @param The value of the data to be added.
*/
- save(key: string, value: string): void {
+ addAndSave(key: string, value: string): void {
+ this.kvStore?.put(key, value).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'KvManager', `Put an unexpected error occured, Code:${err.code},message: ${err.message}`);
+ });
+ this.syncRemote();
+ }
+
+ /**
+ * Single deletion
+ * Deletes data with a specified key value from the database.
+ * @param key Key of the data to be deleted.
+ * @param callback Callback function.
+ */
+ deleteOnce(key: string, callback: AsyncCallback): void {
try {
- this.kvStore?.put(key, value);
+ this.kvStore?.delete(key, callback);
this.syncRemote();
} catch (err) {
- Logger.error(TAG, `Put an unexpected error occured, error message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `An unexpected error occurred, Code:${err.code},message: ${err.message}`);
}
}
/**
- * Gets the value of a specified key.
- *
- * @param key Key of the data to be queried.
+ * Batch Delete
+ * Delete key-value pairs from the KVStore database in batches.
+ * @param keys Indicates the key-value pairs to be deleted in batches.
* @param callback Callback function.
*/
- get(key: string, callback: AsyncCallback): void {
+ deleteBatch(keys: string[], callback: AsyncCallback): void {
try {
- this.kvStore?.get(key, callback);
+ this.kvStore?.deleteBatch(keys, callback);
+ this.syncRemote();
} catch (err) {
- Logger.error(TAG, `Fail to get, error message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `DeleteKVStore an unexpected error occurred, Code:${err.code},message: ${err.message}`);
}
}
/**
- * Deletes data with a specified key value from the database.
- *
- * @param key Key of the data to be deleted.
+ * Get individual details
+ * Gets the value of a specified key.
+ * @param key Key of the data to be queried.
* @param callback Callback function.
*/
- delete(key: string, callback: AsyncCallback): void {
+ getDetails(key: string, callback: AsyncCallback): void {
try {
- this.kvStore?.delete(key, callback);
- this.syncRemote();
+ this.kvStore?.get(key, callback);
} catch (err) {
- Logger.error(TAG, `An unexpected error occurred, error message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `Fail to get, Code:${err.code},message: ${err.message}`);
}
}
/**
* Obtains all key-value pairs that match the specified key prefix.
- *
* @param key Indicates the key prefix to be matched.
* @param callback Callback function.
*/
@@ -180,31 +179,18 @@ export class ContactsDataBase {
try {
this.kvStore?.getEntries(key, callback);
} catch (err) {
- Logger.error(TAG, `An unexpected error occurred, error message is ${JSON.stringify(err)}`);
+ hilog.error(0x0000, 'KvManager', `An unexpected error occurred, Code:${err.code}, message: ${err.message}`);
}
}
/**
- * Remove the data change listener.
- */
- removeDataChangeListener(): void {
- if (this.kvStore === null) {
- return;
- }
- try {
- this.kvStore?.off('dataChange');
- } catch (error) {
- Logger.error(TAG, `removeDataChangeListener off('dataChange') failed, code message is ${JSON.stringify(error)}`);
- }
- }
- /**
- * Manually synchronize data in case of automatic synchronization failure.
+ * Remote synchronization of data
*/
private syncRemote() {
let devManager: distributedDeviceManager.DeviceManager;
try {
- // create deviceManager
- devManager = distributedDeviceManager.createDeviceManager(this.context.abilityInfo.name);
+ // create deviceManager.
+ devManager = distributedDeviceManager.createDeviceManager(CommonConstants.BUNDLE_NAME);
let deviceIds: string[] = [];
if (devManager !== null) {
let devices = devManager.getAvailableDeviceListSync();
@@ -215,12 +201,10 @@ export class ContactsDataBase {
try {
this.kvStore?.sync(deviceIds, distributedKVStore.SyncMode.PUSH_ONLY, 1000);
} catch (err) {
- let error = err as BusinessError;
- Logger.error(`An unexpected error occured. Code:${error.code},message:${err.message}`);
+ hilog.error(0x0000, 'KvManager', `sync failed, Code:${err.code}, message: ${err.message}`);
}
} catch (err) {
- let error = err as BusinessError;
- Logger.error(`createDeviceManager errCode:${error.code},message:${error.message}`);
+ hilog.error(0x0000, 'KvManager', `getAvailableDeviceListSync failed, Code:${err.code}, message: ${err.message}`);
}
}
}
\ No newline at end of file
diff --git a/entry/src/main/ets/view/BottomBarComponent.ets b/entry/src/main/ets/view/BottomBarComponent.ets
deleted file mode 100644
index 402bac9aa3635de4757230f3b240b6624a1b3a14..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/view/BottomBarComponent.ets
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import CommonConstants from '../common/constants/CommonConstants';
-
-@Extend(Text)
-function setTextStyle() {
- .width(CommonConstants.PERCENTAGE_MAX)
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.bottom_bar_font_color'))
- .fontSize($r('app.float.bottom_bar_font_size'))
-}
-
-@Extend(Image)
-function setImageStyle() {
- .width($r('app.float.bottom_bar_image_size'))
- .height($r('app.float.bottom_bar_image_size'))
- .margin($r('app.float.bottom_bar_image_margin'))
-}
-
-@Component
-export default struct BottomBarComponent {
- private isAll: boolean = false;
- public pageId: string = '';
- public leftClickEvent: (isAll?: boolean) => void = () => {
- };
- public rightClickEvent: () => void = () => {
- };
- public leftIcon: Resource = $r('app.media.ic_edit');
- public leftSubtitle: Resource = $r('app.media.ic_delete');
- public rightIcon: Resource = $r('app.media.ic_delete');
- public rightSubtitle: Resource = $r('app.string.delete_text');
-
- build() {
- Column() {
- Divider()
- .height($r('app.float.divider_height'))
- .backgroundColor($r('app.color.list_divider'))
- Row() {
- Column() {
- Image(this.leftIcon)
- .setImageStyle()
- Text(this.leftSubtitle)
- .setTextStyle()
- }
- .onClick(() => {
- if (this.pageId === CommonConstants.DELETE_PAGE_ID) {
- this.isAll = !this.isAll;
- }
- this.leftClickEvent(this.isAll);
- })
- .width(CommonConstants.BOTTOM_COMPONENT_WIDTH)
-
- Column() {
- Image(this.rightIcon)
- .setImageStyle()
- Text(this.rightSubtitle)
- .setTextStyle()
- }
- .width(CommonConstants.BOTTOM_COMPONENT_WIDTH)
- .onClick(() => this.rightClickEvent())
- }
- .width(CommonConstants.PERCENTAGE_MAX)
- .height($r('app.float.bar_bottom_height'))
- .backgroundColor($r('app.color.theme_background'))
- .justifyContent(FlexAlign.End)
- }
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/view/DetailItemComponent.ets b/entry/src/main/ets/view/DetailItemComponent.ets
deleted file mode 100644
index 7c056d3b4f9f15f08b85ceff995742d89e098791..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/view/DetailItemComponent.ets
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import CommonConstants from '../common/constants/CommonConstants';
-
-@Component
-export default struct DetailItemComponent {
- @Link topContent: string;
- @Link bottomContent: string;
- public componentId: number = CommonConstants.PHONE_COMPONENTS_ID;
- public bottomSubtitle: Resource = $r('app.string.item_personal_email');
- public leftIcon: Resource = $r('app.media.ic_phone_icon');
- public rightIcon: Resource = $r('app.media.ic_message');
-
- build() {
- Column() {
- Row() {
- Column() {
- Text(this.getTopContent())
- .fontColor(Color.Black)
- .fontSize($r('app.float.details_item_font_size'))
- .fontWeight(500)
- .margin({ bottom: $r('app.float.details_item_margin') })
- Text(this.getBottomContent())
- .fontColor($r('app.color.font_color'))
- .fontSize($r('app.float.details_bottom_item_size'))
- .fontWeight(400)
- .opacity(0.6)
- }
- .width(CommonConstants.DETAIL_ITEM_WIDTH)
- .alignItems(HorizontalAlign.Start)
-
- Row() {
- if (this.componentId !== CommonConstants.Note_COMPONENTS_ID) {
- if (this.componentId === CommonConstants.PHONE_COMPONENTS_ID) {
- Image(this.leftIcon)
- .width($r('app.float.details_item_size'))
- .height($r('app.float.details_item_size'))
- .objectFit(ImageFit.Contain)
- .margin({ right: $r('app.float.details_margin_right') })
- }
- Image(this.rightIcon)
- .width($r('app.float.details_item_size'))
- .height($r('app.float.details_item_size'))
- .objectFit(ImageFit.Contain)
- }
- }
- .layoutWeight(CommonConstants.WEIGHT)
- .justifyContent(FlexAlign.End)
- }
- .height($r('app.float.details_item_height'))
-
- Divider()
- .height($r('app.float.divider_height'))
- .backgroundColor($r('app.color.list_divider'))
- }
- }
-
- getTopContent(): string {
- if (this.componentId === CommonConstants.PHONE_COMPONENTS_ID && this.topContent.length === 11) {
- let phoneNumberStr = '';
- for (let i = 0; i < this.topContent.length; i++) {
- if (i === 2 || i === 6) {
- phoneNumberStr += this.topContent[i] + ' ';
- } else {
- phoneNumberStr += this.topContent[i];
- }
- }
- return phoneNumberStr;
- } else {
- return this.topContent;
- }
- }
-
- getBottomContent(): string | Resource {
- if (this.componentId === CommonConstants.PHONE_COMPONENTS_ID) {
- return this.bottomContent;
- } else {
- return this.bottomSubtitle;
- }
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/view/ListAreaComponent.ets b/entry/src/main/ets/view/ListAreaComponent.ets
deleted file mode 100644
index 36ca96c2fcf313396f5d3164df89789537beaff2..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/view/ListAreaComponent.ets
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import CommonConstants from '../common/constants/CommonConstants';
-import PageViewModel from '../viewmodel/PageViewModel';
-import { ListItemData } from '../viewmodel/ListItemData';
-import { ListItemComponent } from './ListItemComponent';
-
-@Component
-export struct ListAreaComponent {
- @Link contactData: Array;
-
- build() {
- Column() {
- List() {
- ForEach(this.contactData, (item: ListItemData) => {
- ListItem() {
- ListItemComponent({ itemInfo: item })
- }
- .onClick(() => {
- PageViewModel.redirectDetailPage(item);
- })
- }, (item: ListItemData) => JSON.stringify(item))
- }
- .scrollBar(BarState.Off)
- .width(CommonConstants.LIST_WIDTH_PERCENT)
- .height(CommonConstants.PERCENTAGE_HEIGHT_MAX)
- .margin({ bottom: $r('app.float.list_area_height') })
- }
- .width(CommonConstants.PERCENTAGE_MAX)
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/viewmodel/ContactData.ets b/entry/src/main/ets/viewmodel/ContactViewModel.ets
similarity index 80%
rename from entry/src/main/ets/viewmodel/ContactData.ets
rename to entry/src/main/ets/viewmodel/ContactViewModel.ets
index f8afd733e7bb8c5ccb5c8b57d5d383cd28d1eb4c..b4e2fa4889974a29bb9fd4bfb81b772e5f57b154 100644
--- a/entry/src/main/ets/viewmodel/ContactData.ets
+++ b/entry/src/main/ets/viewmodel/ContactViewModel.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -16,27 +16,23 @@
/**
* Contact data entity.
*/
-export default class ContactData {
+export class ContactData {
/**
* Contact name.
*/
name: string;
-
/**
* Contact address.
*/
address?: string;
-
/**
* Contact phone number.
*/
telephony?: string;
-
/**
* Contact email.
*/
email?: string;
-
/**
* Contact remarks.
*/
@@ -49,4 +45,19 @@ export default class ContactData {
this.email = email;
this.remarks = remarks;
}
-}
\ No newline at end of file
+}
+
+export class ListItemData {
+ /**
+ * Contact id.
+ */
+ id: number = 0;
+ /**
+ * Contact name.
+ */
+ name: string = '';
+ /**
+ * Contact selection box.
+ */
+ checked: boolean = false;
+}
diff --git a/entry/src/main/ets/viewmodel/DetailPageViewModel.ets b/entry/src/main/ets/viewmodel/DetailPageViewModel.ets
deleted file mode 100644
index b9c791a97a6964ea8c51f87b998fbef6aff1a426..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/viewmodel/DetailPageViewModel.ets
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { promptAction } from '@kit.ArkUI';
-import { router } from '@kit.ArkUI';
-import { BusinessError } from '@kit.BasicServicesKit';
-import CommonConstants from '../common/constants/CommonConstants';
-import Logger from '../common/util/Logger';
-import { ContactsDataBase } from '../common/database/ContactsDataBase';
-import { GlobalContext } from '../common/util/GlobalContext';
-
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
-
-export class DetailPageViewModel {
- contactsDataBase = GlobalContext.getContext().getObject('contactsDataBase') as ContactsDataBase;
-
- /**
- * Button for canceling the deletion dialog box.
- */
- cancelDialog(): void {
- uiContext?.getPromptAction().showToast({
- message: $r('app.string.delete_cancel_text'),
- duration: CommonConstants.PROMPT_DURATION
- });
- }
-
- /**
- * Delete contact button.
- */
- deleteContactButton(name: string): void {
- let contactsKey = CommonConstants.CONTACTS_DATABASE_KEY + name;
- try {
- // This interface is used to delete the information about a specified contact.
- this.contactsDataBase.delete(contactsKey, () => {
- uiContext?.getPromptAction().showToast({
- message: $r('app.string.prompt_message_deleted'),
- duration: CommonConstants.PROMPT_DURATION
- });
- uiContext?.getRouter().pushUrl({
- url: CommonConstants.LIST_PAGE_URL
- }).catch((err: BusinessError) => {
- Logger.error(`pushUrl failed, code message is ${JSON.stringify(err)}`);
- });
- });
- } catch (err) {
- Logger.error(`An unexpected error occurred, error message is ${JSON.stringify(err)}`);
- }
- }
-
- /**
- * Button for redirecting to the details page.
- */
- redirectEditPage(name: string, address: string, telephony: string, email: string, remarks: string): void {
- let routerParameter: router.RouterOptions = {
- url: CommonConstants.ADD_EDIT_URL,
- params: {
- isEdit: true,
- name,
- address,
- telephony,
- email,
- remarks
- }
- }
- if (GlobalContext.getContext().getObject('FirstInTo')) {
- uiContext?.getRouter().pushUrl(routerParameter).catch((err: BusinessError) => {
- Logger.error(`PushUrl failed, code message is ${JSON.stringify(err)}`);
- });
- } else {
- uiContext?.getRouter().replaceUrl(routerParameter).catch((err: BusinessError) => {
- Logger.error(`ReplaceUrl failed, code message is ${JSON.stringify(err)}`);
- });
- }
- }
-
- /**
- * Redirect list page.
- */
- redirectListPage(): void {
- uiContext?.getRouter().pushUrl({
- url: CommonConstants.LIST_PAGE_URL
- }).catch((err: BusinessError) => {
- Logger.error(`pushUrl failed, error message is ${JSON.stringify(err)}`);
- });
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/viewmodel/EditPageViewModel.ets b/entry/src/main/ets/viewmodel/EditPageViewModel.ets
deleted file mode 100644
index 7942e9171563cce75883cd5a16443749d497b794..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/viewmodel/EditPageViewModel.ets
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { promptAction } from '@kit.ArkUI';
-import { router } from '@kit.ArkUI';
-import { BusinessError } from '@kit.BasicServicesKit';
-import CommonConstants from '../common/constants/CommonConstants';
-import ContactData from './ContactData';
-import Logger from '../common/util/Logger'
-import { ContactsDataBase } from '../common/database/ContactsDataBase';
-import { GlobalContext } from '../common/util/GlobalContext';
-
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
-
-export class EditPageViewModel {
- contactsDataBase = GlobalContext.getContext().getObject('contactsDataBase') as ContactsDataBase;
-
- /**
- * Public routing method.
- *
- * @param url Indicates the route address.
- */
- commonRouter(url: string, name: string): void {
- let contactsKey = CommonConstants.CONTACTS_DATABASE_KEY + name;
- uiContext!.getRouter().pushUrl({
- url: url,
- params: {
- key: contactsKey
- }
- }).catch((err: BusinessError) => {
- Logger.error(`pushUrl failed, code message is ${JSON.stringify(err)}`);
- });
- }
-
- /**
- * Save contact information.
- */
- saveInfo(contactData: ContactData): void {
- if (contactData.name !== '') {
- let contactsKey = CommonConstants.CONTACTS_DATABASE_KEY + contactData.name;
- // Save the contact information.
- this.contactsDataBase.save(contactsKey, JSON.stringify(contactData));
- uiContext!.getRouter().replaceUrl({
- url: CommonConstants.PAGE_DETAIL_URL,
- params: { key: contactsKey }
- }).catch((err: BusinessError) => {
- Logger.error(`pushUrl failed, code message is ${JSON.stringify(err)}`);
- });
- } else {
- uiContext!.getPromptAction().showToast({
- message: $r('app.string.contact_name'),
- duration: CommonConstants.PROMPT_DURATION
- });
- }
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/viewmodel/ListItemData.ets b/entry/src/main/ets/viewmodel/ListItemData.ets
deleted file mode 100644
index a2d14ea0612fe4b5aada0a326d4e3bbba96b9256..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/viewmodel/ListItemData.ets
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Delete list item data entity.
- */
-export class ListItemData {
- /**
- * Contact id.
- */
- id: number = 0;
-
- /**
- * Contact photo.
- */
- photo: Resource = $r('app.media.ic_men');
-
- /**
- * Contact name.
- */
- name: string = '';
-
- /**
- * Contact selection box.
- */
- checked: boolean = false;
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/viewmodel/ListPageViewModel.ets b/entry/src/main/ets/viewmodel/ListPageViewModel.ets
deleted file mode 100644
index dac42760240611543c98efd1cee6a758be1d8a54..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/viewmodel/ListPageViewModel.ets
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { common } from '@kit.AbilityKit';
-import { Want } from '@kit.AbilityKit';
-import { router } from '@kit.ArkUI';
-import { BusinessError } from '@kit.BasicServicesKit';
-import CommonConstants from '../common/constants/CommonConstants';
-import Logger from '../common/util/Logger';
-import { ContactsDataBase } from '../common/database/ContactsDataBase';
-import { GlobalContext } from '../common/util/GlobalContext';
-
-const TAG: string = 'ListPageViewModel';
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
-
-export class ListPageViewModel {
- startAbilityCallBack: (key: string) => void = () => {
- };
- contactsDataBase = GlobalContext.getContext().getObject('contactsDataBase') as ContactsDataBase;
-
- async startAbility(deviceId: string | undefined) {
- Logger.info(TAG, `startAbility deviceId: ${deviceId}`);
- let context = uiContext!.getHostContext() as common.UIAbilityContext;
- let want: Want = {
- bundleName: CommonConstants.BUNDLE_NAME,
- abilityName: CommonConstants.ENTRY_ABILITY,
- deviceId: deviceId
- }
- context.startAbility(want).then((data) => {
- Logger.info(TAG, `start ability finished: ${JSON.stringify(data)}`);
- this.startAbilityCallBack(CommonConstants.DATA_CHANGE);
- })
- }
-
- /**
- * Button for redirecting to the add page.
- */
- redirectAddPage(): void {
- uiContext!.getRouter().pushUrl({
- url: CommonConstants.ADD_EDIT_URL,
- params: {
- isEdit: false
- }
- }).catch((err: BusinessError) => {
- Logger.error(`pushUrl failed, code message is ${JSON.stringify(err)}`);
- });
- }
-
- /**
- * Button for redirecting to the delete page.
- */
- redirectDeletePage(): void {
- uiContext!.getRouter().pushUrl({
- url: CommonConstants.DELETE_PAGE_URL
- }).catch((err: BusinessError) => {
- Logger.error(`pushUrl failed, code message is ${JSON.stringify(err)}`);
- });
- }
-}
diff --git a/entry/src/main/ets/viewmodel/PageViewModel.ets b/entry/src/main/ets/viewmodel/PageViewModel.ets
deleted file mode 100644
index d106a89e2a216eea454b5e825a0df350533760d4..0000000000000000000000000000000000000000
--- a/entry/src/main/ets/viewmodel/PageViewModel.ets
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { router } from '@kit.ArkUI';
-import { distributedKVStore } from '@kit.ArkData';
-import { BusinessError } from '@kit.BasicServicesKit';
-import Logger from '../common/util/Logger';
-import CheckEmptyUtils from '../common/util/CheckEmptyUtils';
-import CommonConstants from '../common/constants/CommonConstants';
-import { ListItemData } from './ListItemData';
-
-const TAG: string = 'PageViewModel';
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
-
-/**
- * Interface for batch delete page and contacts Home page.
- */
-export class PageViewModel {
- /**
- * Processes data obtained from distributed data services.
- *
- * @param contactData contact data queried from the database.
- */
- getData(contactData: distributedKVStore.Entry[]): Array {
- Logger.info(TAG, `getData contactData: ${JSON.stringify(contactData)}`);
- let listItems: Array = [];
- if (CheckEmptyUtils.isEmptyArr(contactData)) {
- Logger.info(TAG, '[PageViewModel][getData] contactData is empty.');
- return listItems;
- }
- for (let i = 0; i < contactData.length; i++) {
- let itemInfo: ListItemData = new ListItemData();
- itemInfo.photo = (i % CommonConstants.EVEN_COLUMN === 1) ? $r('app.media.ic_men') : $r('app.media.ic_women');
- itemInfo.name = JSON.parse(contactData[i].value.value as string).name;
- itemInfo.id = i;
- listItems.push(itemInfo);
- }
- return listItems;
- }
-
- /**
- * Obtains the quantity of a batch.
- *
- * @param list Contact list data.
- */
- getSelectCount(list: Array): number {
- if (CheckEmptyUtils.isEmptyArr(list)) {
- Logger.info(TAG, '[PageViewModel][getSelectCount] list is empty.');
- Logger.error(TAG, `CheckEmptyUtils.isEmptyArr(list): ${JSON.stringify(CheckEmptyUtils.isEmptyArr(list))}`);
- return 0;
- }
- let result = list.filter((item) => {
- return item.checked;
- });
- Logger.error(TAG, `result.length: ${result.length}`);
- return result.length;
- }
-
- /**
- * Select all data.
- */
- getSelectAll(checkList: Array): Array {
- checkList.forEach((item: ListItemData) => {
- item.checked = true;
- });
- return checkList;
- }
-
- /**
- * Do not select all data.
- */
- getUnSelectAll(checkList: Array): Array {
- checkList.forEach((item: ListItemData) => {
- item.checked = false;
- });
- return checkList;
- }
-
- /**
- * redirect detail page.
- *
- * @param item List Information.
- */
- redirectDetailPage(item: ListItemData): void {
- let contactsKey = CommonConstants.CONTACTS_DATABASE_KEY + item.name;
- uiContext!.getRouter().pushUrl({
- url: CommonConstants.PAGE_DETAIL_URL,
- params: {
- key: contactsKey
- }
- }).catch((err: BusinessError) => {
- Logger.error(TAG, `pushUrl failed, error message is ${JSON.stringify(err)}`);
- });
- }
-}
-
-let pageViewModel = new PageViewModel();
-
-export default pageViewModel as PageViewModel;
\ No newline at end of file
diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json
index e4350490fc43c1c179cad2604eb1723bdabf43fa..b867d2eac269bd48f680b276005f88fc641ce0da 100644
--- a/entry/src/main/resources/base/element/string.json
+++ b/entry/src/main/resources/base/element/string.json
@@ -163,6 +163,14 @@
{
"name": "contact_name",
"value": "Please enter the contact name."
+ },
+ {
+ "name": "no_matching_data",
+ "value": "No matching data"
+ },
+ {
+ "name": "tip_delete",
+ "value": "Are you sure you want to delete this contact?"
}
]
}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json
index 19def701054214f2b683bb91b573ffa194719426..32557b2818cd16cf392cfb2a1d3ca23ad5179a98 100644
--- a/entry/src/main/resources/base/profile/main_pages.json
+++ b/entry/src/main/resources/base/profile/main_pages.json
@@ -1,8 +1,8 @@
{
"src": [
- "pages/ListPage",
- "pages/DeletePage",
- "pages/EditPage",
- "pages/DetailPage"
+ "pages/ContactHomePage",
+ "pages/ContactDetailPage",
+ "pages/ContactAddAndEditPage",
+ "pages/ContactDeletePage"
]
}
diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json
index e4350490fc43c1c179cad2604eb1723bdabf43fa..b867d2eac269bd48f680b276005f88fc641ce0da 100644
--- a/entry/src/main/resources/en_US/element/string.json
+++ b/entry/src/main/resources/en_US/element/string.json
@@ -163,6 +163,14 @@
{
"name": "contact_name",
"value": "Please enter the contact name."
+ },
+ {
+ "name": "no_matching_data",
+ "value": "No matching data"
+ },
+ {
+ "name": "tip_delete",
+ "value": "Are you sure you want to delete this contact?"
}
]
}
\ No newline at end of file
diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json
index f1350db16c901d0bfd2b4c04308f7b58ac8d13e2..fedfb31661f6046b2cec157293346d357a9cafa7 100644
--- a/entry/src/main/resources/zh_CN/element/string.json
+++ b/entry/src/main/resources/zh_CN/element/string.json
@@ -163,6 +163,14 @@
{
"name": "contact_name",
"value": "请输入联系人姓名"
+ },
+ {
+ "name": "no_matching_data",
+ "value": "无匹配数据"
+ },
+ {
+ "name": "tip_delete",
+ "value": "确定删除该联系人?"
}
]
}
\ No newline at end of file
diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5
index eb31f8301a08fedbf8ca8dd0c17b84a8347d8220..5ee8f72dfdeff359f484e07849dfcb317d67eb0c 100644
--- a/hvigor/hvigor-config.json5
+++ b/hvigor/hvigor-config.json5
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,7 +14,7 @@
*/
{
- "modelVersion": "5.0.0",
+ "modelVersion": "5.1.1",
"dependencies": {
}
}
\ No newline at end of file
diff --git a/oh-package.json5 b/oh-package.json5
index cf56d81cd2bc82853292e4a0e998839ae3fc4a78..a09eb9f968ef82e62a68ae34efc58c3d0e107fa4 100644
--- a/oh-package.json5
+++ b/oh-package.json5
@@ -1,5 +1,5 @@
{
- "modelVersion": "5.0.0",
+ "modelVersion": "5.1.1",
"license": "",
"devDependencies": {
},
diff --git a/screenshots/device/contact.en.gif b/screenshots/device/contact.en.gif
deleted file mode 100644
index 0715456d985de3ca07b47602e9ca43f294372da1..0000000000000000000000000000000000000000
Binary files a/screenshots/device/contact.en.gif and /dev/null differ
diff --git a/screenshots/device/contact.gif b/screenshots/device/contact.gif
deleted file mode 100644
index c397a829dd85655829c4dfe4d1c23851784b2ddb..0000000000000000000000000000000000000000
Binary files a/screenshots/device/contact.gif and /dev/null differ
diff --git a/screenshots/devices/contactAdd.en.gif b/screenshots/devices/contactAdd.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..19c872ffabd74c7e585ec2a9652ff4acd04230ec
Binary files /dev/null and b/screenshots/devices/contactAdd.en.gif differ
diff --git a/screenshots/devices/contactAdd.gif b/screenshots/devices/contactAdd.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5c9db39fb832c28f5f97e9581322d997549aa674
Binary files /dev/null and b/screenshots/devices/contactAdd.gif differ
diff --git a/screenshots/devices/contactDel.en.gif b/screenshots/devices/contactDel.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2e4da8c32910662c0758374254afc04a063534c3
Binary files /dev/null and b/screenshots/devices/contactDel.en.gif differ
diff --git a/screenshots/devices/contactDel.gif b/screenshots/devices/contactDel.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2cf4f542d97ccb9ef376b32a007c5ae398245821
Binary files /dev/null and b/screenshots/devices/contactDel.gif differ
diff --git a/screenshots/devices/contactEdit.en.gif b/screenshots/devices/contactEdit.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..b96365d7b6cde20b9c254b839d5d8ca8b0fc66dd
Binary files /dev/null and b/screenshots/devices/contactEdit.en.gif differ
diff --git a/screenshots/devices/contactEdit.gif b/screenshots/devices/contactEdit.gif
new file mode 100644
index 0000000000000000000000000000000000000000..d9e6c5ccf43225e207d4c1afb46d44102b768941
Binary files /dev/null and b/screenshots/devices/contactEdit.gif differ
diff --git a/screenshots/devices/contactQuery.en.gif b/screenshots/devices/contactQuery.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..16633cdcd1085cda386c61b0c967d0f728b7ef50
Binary files /dev/null and b/screenshots/devices/contactQuery.en.gif differ
diff --git a/screenshots/devices/contactQuery.gif b/screenshots/devices/contactQuery.gif
new file mode 100644
index 0000000000000000000000000000000000000000..50d727ee51f8ee0aeb91678618536a384207a2d5
Binary files /dev/null and b/screenshots/devices/contactQuery.gif differ