rntpc_react-native-contacts:基于 react-native-contacts 的 OpenHarmony 适配版,读写设备通讯录联系人

基于 react-native-contacts 的 OpenHarmony 适配版,读写设备通讯录联系人

分支4Tags3
文件最后提交记录最后更新时间
1 个月前
1 个月前
1 个月前
3 个月前
1 个月前
1 个月前
1 个月前
1 年前
7 年前
9 年前
3 个月前
3 个月前
1 个月前
1 个月前
3 个月前
3 个月前
11 个月前
1 个月前

模板版本:v0.4.2

react-native-contacts

本项目基于 react-native-contacts 开发。

该第三方库的仓库已迁移至 Gitcode,并支持直接从 npm 下载。新的包名为:@react-native-ohos/react-native-contacts。版本对应关系如下:

第三方库名称 第三方库版本(npm 地址) 发布信息 支持的 RN 版本 Autolink 编译 API 版本 社区基线版本 源码地址
@react-native-ohos/react-native-contacts ~8.1.0(开发中) Gitcode 发布 0.82.* / 0.84.* 是 API12+ 8.0.10 master
@react-native-ohos/react-native-contacts ~8.0.7 Gitcode 发布 0.77.* 否 API12+ 8.0.6 br_rnoh0.77
@react-native-ohos/react-native-contacts ~7.0.8 Gitcode 发布 0.72.* 是 API12+ 7.0.7 br_rnoh0.72
@react-native-oh-tpl/react-native-contacts <= 7.0.7-0.0.3@deprecated Github 发布 0.72.* 否 API12+ 7.0.7 sig

简介

react-native-contacts 是面向 React Native 的联系人管理库,提供设备通讯录的读取与写入能力。

下载安装

进入工程目录,并执行以下命令:

npm

npm install @react-native-ohos/react-native-contacts

yarn

yarn add @react-native-ohos/react-native-contacts

快速上手:

使用时,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: "test@163.com",
  };
  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("test@163.com").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>
  );
};

链接

是否支持 autolink RN 框架版本
~8.1.0 是 0.82/0.84

使用 AutoLink 的工程请根据该文档进行配置,Autolink 框架指导文档:https://gitcode.com/CPF-RN/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md

如果您使用的版本支持 Autolink,并且工程已经接入 Autolink,则可以跳过 ManualLink 配置。

ManualLink:此步骤用于指导手动配置原生依赖项

首先,请使用 DevEco Studio 打开项目中的 HarmonyOS 工程 harmony

1. 在工程根目录的 oh-package.json5 中添加 overrides 字段

{
  ...
  "overrides": {
    "@rnoh/react-native-openharmony" : "./react_native_openharmony"
  }
}

2.引入原生端代码

目前有两种引入方式:

  1. 通过 har 包引入(待 IDE 完善相关功能后,该方式将被弃用,目前优先采用此方式);
  2. 直接引入源码。

方法一:通过 har 包引入

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

方法二:直接链接源码

如需使用直接链接源码,请参考[直接链接源码说明](https://gitcode.com/CPF-RN/usage-docs/blob/master/zh-cn/link-source-code.md)

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

随后编译并运行即可。

约束与限制

兼容性

本文档内容已在以下版本中验证通过:

  1. RNOH: 0.82.1; SDK: HarmonyOS API Version 21 Release SDK; IDE: DevEco Studio 6.0.1.260; ROM: 6.0.0.130;
  2. RNOH: 0.84.2; SDK: HarmonyOS 6.0.1 Release SDK; IDE: DevEco Studio 26.0.0 Beta1; ROM: 6.0.0.120 SP7;

权限要求

[!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

“平台”列表示该属性在原始第三方库中支持的平台。

“HarmonyOS 支持”列为“是”表示 HarmonyOS 平台支持该属性;“否”表示不支持;“部分支持”表示部分支持。跨平台调用方式一致,效果对标 iOS 或 Android 平台的效果。

名称 描述 类型 是否必填 平台 HarmonyOS 支持
getAll: Promise<Contact[]> 以对象数组形式返回所有联系人 function 否 Android,iOS 是
getAllWithoutPhotos:Promise<Contact[]> 与 getAll 相同,但 iOS 平台不返回联系人照片的 URI,因为生成图片会造成明显性能开销 function 否 Android,iOS 是
getContactById(contactId): Promise 返回指定 contactId 对应的联系人;若不存在,则返回 null function 是 Android,iOS 是
getCount(): Promise 返回联系人总数 function 否 Android,iOS 是
getPhotoForId(contactId: string): Promise 返回指定联系人的照片 URI;若不存在,则返回 null function 是 Android,iOS 是
addContact(contact: Partial): Promise 向通讯录添加一个联系人 function 是 Android,iOS 是
openContactForm(contact: Partial): Promise<Contact | null> 创建新联系人,并在系统联系人界面中显示 function 是 Android,iOS 部分支持
openExistingContact(contact: Contact): Promise 以编辑模式打开已有联系人,contact 为包含有效 recordID 的对象 function 是 Android,iOS 部分支持
viewExistingContact(contact: { recordID: string }) 以查看模式打开已有联系人,contact 为包含有效 recordID 的对象 function 是 Android,iOS 部分支持
editExistingContact(contact: Contact): Promise 为联系人新增电话号码,contact 为包含有效 recordID 和 phoneNumbers 数组的对象 function 是 Android,iOS 否
updateContact(contact: Partial & {recordID: string}): Promise 更新联系人,contact 为包含有效 recordID 的对象 function 是 Android,iOS 是
deleteContact(contact: Contact): Promise 删除联系人,contact 为包含有效 recordID 的对象 function 是 Android,iOS 是
getContactsMatchingString(str: string): Promise<Contact[]> 根据字符串匹配联系人姓名(名字、中间名、姓氏) function 是 Android,iOS 是
getContactsByPhoneNumber(phoneNumber: string): Promise<Contact[]> 根据电话号码检索联系人 function 是 Android,iOS 是
getContactsByEmailAddress(emailAddress: string): Promise<Contact[]> 根据电子邮件地址检索联系人 function 是 Android,iOS 是
checkPermission(): Promise<'authorized' | 'denied' | 'undefined' | 'limited'> 检查联系人访问权限(仅 iOS) function 否 iOS 部分支持(authorized、denied)
requestPermission(): Promise<'authorized' | 'denied' | 'undefined' | 'limited'> 请求联系人访问权限(仅 iOS) function 否 iOS 部分支持(authorized、denied)
writePhotoToPath(contactId: string, file: string): Promise 将联系人照片写入指定路径(仅 Android) function 是 Android 否
getGroups(): Promise<Group[]> 获取全部群组列表 function 否 Android,iOS 是
getGroup(identifier: string): Promise<Group | null> 根据标识符获取单个群组;若不存在,则返回 null function 是 Android,iOS 是
deleteGroup(identifier: string): Promise 删除指定群组 function 否 Android,iOS 否
updateGroup(identifier: string, groupData: Object): Promise 更新群组名称 function 否 Android,iOS 否
addGroup(group: Object): Promise 创建新群组,仅需提供 name 字段 function 否 Android,iOS 否
contactsInGroup(identifier: string): Promise<Contact[]> 获取指定群组内的所有联系人 function 否 Android,iOS 否
addContactsToGroup(groupIdentifier: string, contactIdentifiers: string[]): Promise 将多个联系人添加到指定群组,contactIdentifiers 为 recordID 数组 function 否 Android,iOS 否
removeContactsFromGroup(groupIdentifier: string, contactIdentifiers: string[]): Promise 将多个联系人从指定群组中移除 function 否 Android,iOS 否
iosEnableNotesUsage(enabled: boolean): void 启用或禁用联系人备注(notes)的读写能力(仅 iOS,需特殊授权)。 function 是 iOS 否

Contacts

名称 描述 类型 是否必填 平台 HarmonyOS 支持
recordID 联系人标识符 string 否 Android,iOS 是
backTitle 返回键标题 string 否 Android,iOS 否
company 公司 string 否 Android,iOS 是
emailAddresses 电子邮件地址列表 EmailAddress[] 否 Android,iOS 是
displayName 显示名称 string 否 Android,iOS 是
familyName 姓氏 string 否 Android,iOS 是
givenName 名字 string 否 Android,iOS 是
middleName 中间名 string 否 Android,iOS 是
jobTitle 职位 string 否 Android,iOS 是
phoneNumbers 电话号码列表 PhoneNumber[] 否 Android,iOS 是
hasThumbnail 是否包含头像 boolean 否 Android,iOS 否
thumbnailPath 头像路径 string 否 Android,iOS 否
isStarred 是否星标 boolean 否 Android,iOS 部分支持(false)
postalAddresses 邮政地址列表 PostalAddress[] 否 Android,iOS 是
prefix 前缀 string 否 Android,iOS 是
suffix 后缀 string 否 Android,iOS 是
department 部门 string 否 Android,iOS 是
birthday 生日 Birthday 否 Android,iOS 是
imAddresses 即时消息地址列表 InstantMessageAddress[] 否 Android,iOS 是
urlAddresses 网址地址列表 UrlAddress[] 否 Android,iOS 否
note 备注 string 否 Android,iOS 是

EmailAddress

名称 描述 类型 是否必填 平台 HarmonyOS 支持
label 标签 string 否 Android,iOS 是
email 地址 string 否 Android,iOS 是

PhoneNumber

名称 描述 类型 是否必填 平台 HarmonyOS 支持
label 标签 string 否 Android,iOS 是
number 号码 string 否 Android,iOS 是

PostalAddress

名称 描述 类型 是否必填 平台 HarmonyOS 支持
label 标签 string 否 Android,iOS 是
formattedAddress 格式化地址 string 否 Android,iOS 是
street 街道 string 否 Android,iOS 是
pobox 信箱 string 否 Android,iOS 是
neighborhood 街区 string 否 Android,iOS 是
city 城市 string 否 Android,iOS 是
region 区域 string 否 Android,iOS 是
state 州 string 否 Android,iOS 是
postCode 邮政编码 string 否 Android,iOS 是
country 国家/地区 string 否 Android,iOS 是

InstantMessageAddress

名称 描述 类型 是否必填 平台 HarmonyOS 支持
username 用户名 string 否 Android,iOS 是
service 服务 string 否 Android,iOS 是

Birthday

名称 描述 类型 是否必填 平台 HarmonyOS 支持
day 日 number 否 Android,iOS 是
month 月 number 否 Android,iOS 是
year 年 number 否 Android,iOS 是

UrlAddress

名称 描述 类型 是否必填 平台 HarmonyOS 支持
url 网址 string 否 Android,iOS 是
label 标签 string 否 Android,iOS 是

Group

名称 描述 类型 是否必填 平台 HarmonyOS 支持
identifier 群组唯一标识符 string 否 Android,iOS 是
name 群组名称 string 否 Android,iOS 是

遗留问题

其他

目录结构

/rntpc_react-native-contacts        # 项目根目录
├── harmony                         # 鸿蒙适配代码
│   ├── contacts.har                # har包
│   └── contacts                    # 鸿蒙适配核心代码
│       ├── Index.ets               # 鸿蒙适配代码入口
│       └── src/main
│           ├── ets
│           │   ├── ContactsPackage.ets         # 鸿蒙侧Package注册
│           │   └── tm                          # TurboModule实现
│           │       └── ContactsTurboModule.ts  # 联系人TurboModule
│           └── 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                    # 英文安装使用方法

贡献代码

使用过程中发现任何问题,都可以提交 Issue,当然,也非常欢迎提交 PR。

开源协议

本项目基于 The MIT License (MIT),欢迎自由使用并参与开源。

项目介绍

基于 react-native-contacts 的 OpenHarmony 适配版,读写设备通讯录联系人

定制我的领域