@react-native-ohos/react-native-contacts
v8.0.8
Published
React Native Contacts
Readme
模板版本:v0.4.1
本项目基于 react-native-contacts 开发。
该第三方库的仓库已迁移至 Gitcode,且支持直接从 npm 下载,新的包名为:@react-native-ohos/react-native-contacts 版本所属关系如下: | 三方库名称 | 三方库版本 | 发布信息 | 支持RN版本 | Autolink | 编译API版本 | 社区基线版本 | npm地址 | | ------------ | ------------ | ------------------------------ | ------------- | ------------- |------------------------ | ------------- | ------------- | | @react-native-ohos/react-native-contacts | ~8.0.8 | Gitcode Releases | 0.77.* | 否 | API12+ | 8.0.6 | Npm Address |
简介
react-native-contacts 是一个用于 React Native 的联系人管理库,提供对设备通讯录的读写能力。
下载安装
进入到工程目录并输入以下命令:
npm
npm install @react-native-ohos/react-native-contactsyarn
yarn add @react-native-ohos/react-native-contacts快速使用:
[!WARNING] 使用时 import 的库名不变。
import React, { useState } from "react";
import { ScrollView, Button, Alert } from "react-native";
import Contacts from "react-native-contacts";
export const ContactsDemo = () => {
let emailAddress: Contacts.EmailAddress = {
label: "emailAddress",
email: "[email protected]",
};
let phoneNumber: Contacts.PhoneNumber = {
label: "phoneNumber",
number: "13142536789",
};
let postalAddress: Contacts.PostalAddress = {
label: "label",
formattedAddress: "formattedAddress",
street: "street",
pobox: "pobox",
neighborhood: "neighborhood",
city: "city",
region: "region",
state: "state",
postCode: "postCode",
country: "country",
};
let birthday: Contacts.Birthday = {
day: 1,
month: 5,
year: 2024,
};
let instantMessageAddress: Contacts.InstantMessageAddress = {
username: "username",
service: "service",
};
let urlAddress: Contacts.UrlAddress = {
url: "url",
label: "label",
};
let contact: Contacts.Contact = {
company: "addcompany",
emailAddresses: [emailAddress],
displayName: "adddisplayName",
familyName: "addfamilyName",
givenName: "addgivenName",
middleName: "addmiddleName",
jobTitle: "addjobTitle",
phoneNumbers: [phoneNumber],
hasThumbnail: false,
thumbnailPath: "addthumbnailPath",
isStarred: false,
postalAddresses: [postalAddress],
prefix: "addprefix",
suffix: "addsuffix",
department: "adddepartment",
birthday: birthday,
imAddresses: [instantMessageAddress],
urlAddresses: [urlAddress],
note: "addnote",
};
return (
<ScrollView>
<Button
title="requestPermission"
onPress={() => {
Contacts.requestPermission().then((data) => {
console.log(`requestPermission:${JSON.stringify(data)}`);
Alert.alert(`requestPermission:${JSON.stringify(data)}`);
});
}}
/>
<Button
title="checkPermission"
onPress={() => {
Contacts.checkPermission().then((data) => {
console.log(`checkPermission:${JSON.stringify(data)}`);
Alert.alert(`checkPermission:${JSON.stringify(data)}`);
});
}}
/>
<Button
title="getAll"
onPress={() => {
Contacts.getAll().then((contacts: Contacts.Contact[]) => {
console.log(`getAll:${JSON.stringify(contacts)}`);
});
}}
/>
<Button
title="getAllWithoutPhotos"
onPress={() => {
Contacts.getAllWithoutPhotos().then(
(contacts: Contacts.Contact[]) => {
console.log(`getAllWithoutPhotos:${JSON.stringify(contacts)}`);
}
);
}}
/>
<Button
title="getContactById"
onPress={() => {
Contacts.getContactById("1").then(
(contact: Contacts.Contact | null) => {
console.log(`getContactById:${JSON.stringify(contact)}`);
}
);
}}
/>
<Button
title="getCount"
onPress={() => {
Contacts.getCount().then((count: number) => {
console.log(`getCount:${count}`);
});
}}
/>
<Button
title="getPhotoForId"
onPress={() => {
Contacts.getPhotoForId("1").then((photoUrl: string) => {
console.log(`getPhotoForId:${photoUrl}`);
});
}}
/>
<Button
title="addContact"
onPress={() => {
Contacts.addContact(contact).then((contact: Contacts.Contact) => {
console.log(`addContact:${JSON.stringify(contact)}`);
});
}}
/>
<Button
title="openContactForm"
onPress={() => {
Contacts.openContactForm(contact).then(
(contact: Contacts.Contact) => {
console.log(`openContactForm:${JSON.stringify(contact)}`);
Alert.alert(`openContactForm success`);
}
);
}}
/>
<Button
title="openExistingContact"
onPress={() => {
Contacts.openExistingContact({
recordID: "1",
phoneNumbers: [
{
label: "phoneNumber2",
number: "13521456721",
},
],
}).then((contact: Contacts.Contact) => {
console.log(`openExistingContact:${JSON.stringify(contact)}`);
Alert.alert(`openExistingContact success`);
});
}}
/>
<Button
title="viewExistingContact"
onPress={() => {
Contacts.viewExistingContact({
recordID: "1",
phoneNumbers: [
{
label: "phoneNumber2",
number: "13521456721",
},
],
}).then((contact: Contacts.Contact) => {
console.log(`viewExistingContact:${JSON.stringify(contact)}`);
Alert.alert(`viewExistingContact success`);
});
}}
/>
<Button
title="editExistingContact"
onPress={() => {
Contacts.editExistingContact({
recordID: "1",
phoneNumbers: [
{
label: "phoneNumber2",
number: "13521456721",
},
],
}).then((contact: Contacts.Contact) => {
console.log(`editExistingContact:${JSON.stringify(contact)}`);
Alert.alert(`editExistingContact success`);
});
}}
/>
<Button
title="updateContact"
onPress={() => {
Contacts.updateContact({
recordID: "1",
familyName: "updateContact",
givenName: "updateContact",
phoneNumbers: [
{
label: "phoneNumber2",
number: "13521456721",
},
{
label: "phoneNumber3",
number: "13521456222",
},
],
}).then(() => {
Alert.alert(`updateContact success`);
});
}}
/>
<Button
title="deleteContact"
onPress={() => {
Contacts.deleteContact({
recordID: "3",
}).then(() => {
Alert.alert(`deleteContact success`);
});
}}
/>
<Button
title="getContactsMatchingString"
onPress={() => {
Contacts.getContactsMatchingString("addfamilyName").then(
(contacts: Contacts.Contact[]) => {
console.log(
`getContactsMatchingString:${JSON.stringify(contacts)}`
);
}
);
}}
/>
<Button
title="getContactsByPhoneNumber"
onPress={() => {
Contacts.getContactsByPhoneNumber("789").then(
(contacts: Contacts.Contact[]) => {
console.log(
`getContactsByPhoneNumber:${JSON.stringify(contacts)}`
);
}
);
}}
/>
<Button
title="getContactsByEmailAddress"
onPress={() => {
Contacts.getContactsByEmailAddress("[email protected]").then(
(contacts: Contacts.Contact[]) => {
console.log(
`getContactsByEmailAddress:${JSON.stringify(contacts)}`
);
}
);
}}
/>
<Button
title="writePhotoToPath"
onPress={() => {
Contacts.writePhotoToPath("1", "file").then((data) => {
console.log(`writePhotoToPath:${JSON.stringify(data)}`);
});
}}
/>
<Button
title="iosEnableNotesUsage"
onPress={() => {
Contacts.iosEnableNotesUsage(true);
console.log(`iosEnableNotesUsage:true`);
}}
/>
</ScrollView>
);
};Link
| | 是否支持autolink | RN框架版本 | |--------------------------------------|-----------------|------------| | ~8.0.8 | No | 0.77 |
使用AutoLink的工程需要根据该文档配置,Autolink框架指导文档:https://gitcode.com/CPF-RN/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md
如您使用的版本支持 Autolink,并且工程已接入 Autolink,可跳过ManualLink配置。
首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 harmony
1.在工程根目录的 oh-package.json5 添加 overrides 字段
{
...
"overrides": {
"@rnoh/react-native-openharmony" : "./react_native_openharmony"
}
}2.引入原生端代码
目前有两种方法:
- 通过 har 包引入(在 IDE 完善相关功能后该方法会被遗弃,目前首选此方法);
- 直接链接源码。
方法一:通过 har 包引入
[!TIP] har 包位于三方库安装路径的
harmony文件夹下。
打开 entry/oh-package.json5,添加以下依赖
"dependencies": {
"@rnoh/react-native-openharmony" : "file:../react_native_openharmony",
"@react-native-ohos/react-native-contacts": "file:../../node_modules/@react-native-ohos/react-native-contacts/harmony/contacts.har"
}点击右上角的 sync 按钮
或者在终端执行:
cd entry
ohpm install方法二:直接链接源码
[!TIP] 如需使用直接链接源码,请参考直接链接源码说明
3.在 ArkTs 侧引入 ContactsPackage
打开 entry/src/main/ets/RNPackagesFactory.ts,添加:
...
+ import {ContactsPackage} from '@react-native-ohos/react-native-contacts/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [new SamplePackage(ctx),
+ new ContactsPackage(ctx)];
}4.配置 CMakeLists 和引入 ContactsPackage
若使用的是 <= 7.0.7-0.0.3 版本,请跳过本章。
打开 entry/src/main/cpp/CMakeLists.txt,添加:
project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
set(LOG_VERBOSITY_LEVEL 1)
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
add_compile_definitions(WITH_HITRACE_SYSTRACE)
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
# RNOH_BEGIN: manual_package_linking_1
add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package)
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-contacts/src/main/cpp" ./
contacts)
# RNOH_END: manual_package_linking_1
file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
add_library(rnoh_app SHARED
${GENERATED_CPP_FILES}
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
# RNOH_BEGIN: manual_package_linking_2
target_link_libraries(rnoh_app PUBLIC rnoh_sample_package)
+ target_link_libraries(rnoh_app PUBLIC rnoh_contacts)
# RNOH_END: manual_package_linking_2打开 entry/src/main/cpp/PackageProvider.cpp,添加:
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
#include "SamplePackage.h"
+ #include "ContactsPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
std::make_shared<SamplePackage>(ctx),
+ std::make_shared<ContactsPackage>(ctx)
};
}运行
点击右上角的 sync 按钮
或者在终端执行:
cd entry
ohpm install然后编译、运行即可。
约束与限制
兼容性
本文档内容基于以下版本验证通过:
- RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
权限要求
[!TIP] "ohos.permission.READ_CONTACTS","ohos.permission.WRITE_CONTACTS"权限等级为system_basic,授权方式为user_grant,使用 ACL 签名的配置指导
打开entry/src/main/module.json5,添加:
"requestPermissions": [
...
{
"name": "ohos.permission.READ_CONTACTS",
"reason": "$string:read_contacts_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.WRITE_CONTACTS",
"reason": "$string:write_contacts_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
}
]API
[!TIP] "Platform"列表示该属性在原三方库上支持的平台。
[!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。
| 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 |
| ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -------- | ------------ | ----------------- |
| getAll: Promise<Contact[]> | 以对象数组形式返回所有联系人 | function | no | Android,iOS | yes |
| getAllWithoutPhotos:Promise<Contact[]> | 与 getAll 相同,但在 iOS 上不会返回联系人照片的 URI(因为创建图片会产生显著的性能开销) | function | no | Android,iOS | yes |
| getContactById(contactId): Promise | 返回具有指定 contactId 的联系人(如果不存在则返回 null) | function | no | Android,iOS | yes |
| getCount(): Promise | 返回联系人总数 | function | no | Android,iOS | yes |
| getPhotoForId(contactId: string): Promise | 返回联系人照片的 URI(如果不存在则返回 null) | function | no | Android,iOS | yes |
| addContact(contact: Partial): Promise | 向通讯录中添加一个联系人 | function | no | Android,iOS | yes |
| openContactForm(contact: Partial): Promise<Contact | null> | 创建新联系人并在系统联系人界面中显示 | function | no | Android,iOS | partially |
| openExistingContact(contact: Contact): Promise | 打开现有联系人(编辑模式),其中 contact 是包含有效 recordID 的对象 | function | no | Android,iOS | partially |
| viewExistingContact(contact: { recordID: string }) | 打开现有联系人(查看模式),其中 contact 是包含有效 recordID 的对象 | function | no | Android,iOS | partially |
| editExistingContact(contact: Contact): Promise | 向联系人添加号码,其中 contact 是包含有效 recordID 和 phoneNumbers 数组的对象 | function | no | Android,iOS | no |
| updateContact(contact: Partial & {recordID: string}): Promise | 更新联系人,其中 contact 是包含有效 recordID 的对象 | function | no | Android,iOS | yes |
| deleteContact(contact: Contact): Promise | 删除联系人,其中 contact 是包含有效 recordID 的对象 | function | no | Android,iOS | yes |
| getContactsMatchingString(str: string): Promise<Contact[]> | 根据字符串匹配联系人姓名(名、中间名、姓) | function | no | Android,iOS | yes |
| getContactsByPhoneNumber(phoneNumber: string): Promise<Contact[]> | 根据电话号码匹配联系人 | function | no | Android,iOS | yes |
| getContactsByEmailAddress(emailAddress: string): Promise<Contact[]> | 根据电子邮件地址匹配联系人 | function | no | Android,iOS | yes |
| checkPermission(): Promise<'authorized' | 'denied' | 'undefined' | 'limited'> | 检查访问联系人的权限(仅 iOS) | function | no | iOS | partially |
| requestPermission(): Promise<'authorized' | 'denied' | 'undefined' | 'limited'> | 请求访问联系人的权限(仅 iOS) | function | no | iOS | partially |
| writePhotoToPath(contactId: string, file: string): Promise | 将联系人照片写入指定路径(仅 Android) | function | no | Android | no |
| getGroups(): Promise<Group[]> | 获取所有群组列表 | function | no | Android,iOS | no |
| getGroup(identifier: string): Promise<Group | null> | 通过标识符获取单个群组,不存在时返回 null | function | no | Android,iOS | no |
| deleteGroup(identifier: string): Promise | 删除指定群组 | function | no | Android,iOS | no |
| updateGroup(identifier: string, groupData: Object): Promise | 更新群组名称 | function | no | Android,iOS | no |
| addGroup(group: Object): Promise | 创建新群组,仅需提供 name 字段 | function | no | Android,iOS | no |
| contactsInGroup(identifier: string): Promise<Contact[]> | 获取指定群组内的所有联系人 | function | no | Android,iOS | no |
| addContactsToGroup(groupIdentifier: string, contactIdentifiers: string[]): Promise | 将多个联系人(通过 recordID 数组)添加到指定群组 | function | no | Android,iOS | no |
| removeContactsFromGroup(groupIdentifier: string, contactIdentifiers: string[]): Promise | 将多个联系人从指定群组中移除 | function | no | Android,iOS | no |
Contacts | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | recordID | 联系人id | string | no | Android,iOS | yes | | backTitle | 返回键标题 | string | no | Android,iOS | no | | company | 公司 | string|no | Android,iOS | yes | | emailAddresses | 电子邮箱地址 | EmailAddress[] | no | Android,iOS | yes | | displayName | 展示名 | string | no | Android,iOS | yes | | familyName | 姓氏 | string | no | Android,iOS | yes | | givenName | 名字 | string | no | Android,iOS | yes | | middleName | 中间名 | string | no | Android,iOS | yes | | jobTitle | 职位名称 | string | no | Android,iOS | yes | | phoneNumbers | 电话号码 | PhoneNumber[] | no | Android,iOS | yes | | hasThumbnail | 有头像 | boolean | no | Android,iOS | no | | thumbnailPath | 头像地址 | string | no | Android,iOS | no | | isStarred | 是否标记 | boolean | no | Android,iOS | yes | | postalAddresses | 邮件地址 | PostalAddress[] | no | Android,iOS | yes | | prefix | 前缀 | string | no | Android,iOS | yes | | suffix | 后缀 | string | no | Android,iOS | yes | | department | 部门 | string | no | Android,iOS | yes | | birthday | 生日 | Birthday | no | Android,iOS | yes | | imAddresses | 即时消息地址 | InstantMessageAddress[] | no | Android,iOS | yes | | urlAddresses | 图片地址 | UrlAddress[] | no | Android,iOS | no | | note | 备注 | string | no | Android,iOS | yes |
EmailAddress | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | label | 标题 | string | no | Android,iOS | yes | | email | 地址 | string | no | Android,iOS | yes |
PhoneNumber | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | label | 标题 | string | no | Android,iOS | yes | | number | 号码 | string | no | Android,iOS | yes |
PostalAddress | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | label | 标题 | string | no | Android,iOS | yes | | formattedAddress | 格式化地址 | string | no | Android,iOS | yes | | street | 街道 | string | no | Android,iOS | yes | | pobox | 信箱 | string | no | Android,iOS | yes | | neighborhood | 邻域 | string | no | Android,iOS | yes | | city | 城市 | string | no | Android,iOS | yes | | region | 区域 | string | no | Android,iOS | yes | | state | 州 | string | no | Android,iOS | yes | | postCode | 邮政编码 | string | no | Android,iOS | yes | | country | 国家/地区 | string | no | Android,iOS | yes |
InstantMessageAddress | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | username | 用户名 | string | no | Android,iOS | yes | | service | 服务地址 | string | no | Android,iOS | yes |
Birthday | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | day | 日 | number | no | Android,iOS | yes | | month | 月 | number | no | Android,iOS | yes | | year | 年 | number | no | Android,iOS | yes |
UrlAddress | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | url | 路径 | string | no | Android,iOS | yes | | label | 标题 | string | no | Android,iOS | yes |
Group | 名称 | 描述 | 类型 | 是否必填 | 平台 | HarmonyOS 支持 | | ---- | ----------- | ---- | -------- | -------- | ------------------ | | identifier | 群组唯一标识符 | string | no | Android,iOS | no | | name | 群组名称 | string | no | Android,iOS | no |
遗留问题
- [ ] openContactForm:跳转到系统联系人界面只支持姓名和电话参数传递,需要系统联系人应用支持所有属性,另外创建成功之后无法返回联系人信息,联系人应用目前不支持。issue#1
- [ ] openExistingContact:联系人应用新增和编辑是同一个界面,目前参数只支持姓名和电话传递,编辑成功之后也无法拿到联系人信息issue#1
- [ ] viewExistingContact:查看界面只有姓名和电话信息,需要联系人应用补齐所有属性issue#1
- [ ] editExistingContact:没有单独的只支持编辑电话号码的页面,目前同编辑页面issue#1
- [ ] writePhotoToPath:系统联系人应用不支持issue#1
- [ ] openExistingContact:打开现有联系人的编辑模式目前没有提供相关接口,现状是按照查看方式打开。如果现有联系人不存在返回空白页issue#9
- [ ] viewExistingContact:如果现有联系人不存在返回空白页issue#9
其他
- [ ] checkPermission跟requestPermission:鸿蒙获取的权限目前支持authorized跟denied,不支持部分权限limited.
目录结构
/rntpc_react-native-contacts # 项目根目录
├── harmony # 鸿蒙适配代码
│ └── contacts.har # har包
│ └── contacts # 鸿蒙适配核心代码
│ └── Index.ets # 鸿蒙适配代码入口
│ └── src/main/ets
│ └── ContactsPackage.ets # 鸿蒙侧Package注册
│ └── tm # TurboModule实现
│ └── ContactsTurboModule.ts # 联系人TurboModule
│ └── src/main/cpp # C++ TurboModule 桥接层
│ └── ContactsPackage.h # C++ 侧Package注册
│ └── generated/RNOH # 自动生成RNOH绑定代码
├── src # RN代码
│ └── NativeContacts.ts # 原生模块类型定义
├── index.js # JS入口文件
├── index.d.ts # TypeScript类型声明入口
├── README.md # 中文安装使用方法
├── README_en.md # 英文安装使用方法开源协议
本项目基于 The MIT License (MIT) ,请自由地享受和参与开源。
