npm package: @hadss/react_native_knock_share
Description: RN knock_share interface package
Latest version: 1.0.0-rc.0

# @hadss/react_native_knock_share

## 介绍

为 React Native 提供“碰一碰”分享能力，当前支持手机与手机之间的碰一碰分享。该库封装了鸿蒙能力，开发者可通过简洁接口在 RN 中实现鸿蒙的碰一碰分享功能。


## 工程目录

```
.
├─harmony                                             
│    ├─KnockShareHO                                  // Harmony
│    |   └─knock_share.har                           // Turbo Modules接口har包
│    ├─KnockShareOH                                  // OpenHarmony
│    │   └─knock_share.har                           // Turbo Modules接口har包
|    ├─knock_share
│    │  └─src
│    │      ├─main
│    │      │    └─ets
│    │      │      ├─KnockShareModule.ets            // Turbo Modules接口ArkTS实现
│    │      │      ├─KnockShareModulePackage.ets     // KnockShareModuleFactory
│    │      │      └─KnockShareModuleSpec.ts         // Turbo Modules接口
│    │      ├─HORealization                          
│    │      │      └─KnockShareModule.ets            // Harmony环境差异化源码
│    │      └─OHRealization
│    │             └─KnockShareModule.ets            // OpenHarmony环境差异化源码
|    └─build-profile.json5                           // 编译配置文件（多环境Harmony/OpenHarmony）
├─src
│  ├─index.ts                                        // RN模块导出
│  └─turbo    
|     └─KnockShareModule.ts                          // RN Turbo Modules接口
```

## 安装与使用

进入到工程目录并输入以下命令：

<!-- tabs:start -->

**npm**

```bash
npm install @hadss/react_native_knock_share
```

**yarn**

```bash
yarn add @hadss/react_native_knock_share
```
下面的代码展示了这个库的基本使用场景：

**碰一碰KnockShare使用示例** 
```jsx
import React, { useState } from 'react';
import {
  Alert,
  Button,
  SafeAreaView,
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  useColorScheme,
  View,
} from 'react-native';

import { Colors } from 'react-native/Libraries/NewAppScreen';

import { KnockShare, SharedRecord } from "@hadss/react_native_knock_share";

function App(): JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  const [isListening, setIsListening] = useState(false);
  const [canUseKnockShare, setCanUseKnockShare] = useState(false);
  const [hasChecked, setHasChecked] = useState(false);

  const handleCheckSupport = () => {
    const isSupported = KnockShare.canUseKnockShare()
    setCanUseKnockShare(isSupported);
    setHasChecked(true);
  };

  const handle = (shareData: Array<SharedRecord>) => {
    KnockShare.setKnockShareData(shareData)
  };

  const handleRemove = () => {
    KnockShare.removeKnockShareListener();
    setIsListening(false);
  }

  const addListener = () => {
    if (isListening) {
      Alert.alert('提示', '请先停止当前监听再开启新监听');
      return;
    }
    KnockShare.addKnockShareListener();
    setIsListening(true);
  }

  return (
    <SafeAreaView style={backgroundStyle}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundStyle.backgroundColor}
      />
      <ScrollView
        contentInsetAdjustmentBehavior="automatic"
        contentContainerStyle={[styles.container, { alignItems: 'center' }]}
        style={backgroundStyle}>
        <View style={styles.checkContainer}>
          <Text style={styles.tipText}>
            {hasChecked ?
              (canUseKnockShare ? '该设备支持碰一碰' : '该设备不支持碰一碰')
              : '请检测设备是否支持碰一碰'}
          </Text>
          <Button
            title="canUseKnockShare"
            onPress={handleCheckSupport}
          />
        </View>
        <Text style={styles.statusText}>
          {isListening ? '监听中' : '未监听'}
        </Text>
        <View style={styles.groupContainer}>
          <View style={styles.buttonRow}>
            <Button title="开启监听" onPress={addListener} />
            <Button title="停止监听" onPress={handleRemove} />
          </View>
        </View>
        <View style={styles.groupContainer}>
          <Text>纯图片</Text>
          <View style={styles.buttonRow}>
            <Button title="设置纯图片" onPress={() => {
              handle(
                [{
                  "utd": "general.jpeg",
                  "uri": "file://com.hadss.ohFeature/data/storage/el2/base/haps/entry/files/exampleKnock3.jpg",
                  "thumbnailUri": "file://com.hadss.ohFeature/data/storage/el2/base/haps/entry/files/exampleKnock3.jpg"
                }]
              )
            }} />
          </View>
        </View>

        <View style={styles.groupContainer}>
          <Text>沉浸式卡片</Text>
          <View style={styles.buttonRow}>
            <Button title="设置沉浸式卡片" onPress={() => {
              handle(
                [{
                  "utd": "general.hyperlink",
                  "content": "https://sharekitdemo.drcn.agconnect.link/ZB3p",
                  "thumbnailUri": "file://com.hadss.ohFeature/data/storage/el2/base/haps/entry/files/exampleKnock1.jpg",
                  "title": "中国唯一没有的地貌，峡湾地貌",
                  "description": "HarmonyShare · 220.8万播放"
                }]
              )
            }} />
          </View>
        </View>

        <View style={styles.groupContainer}>
          <Text>白卡上下布局</Text>
          <View style={styles.buttonRow}>
            <Button title="设置白卡上下布局卡片" onPress={() => {
              handle(
                [{
                  "utd": "general.hyperlink",
                  "content": "https://sharekitdemo.drcn.agconnect.link/ZB3p",
                  "thumbnailUri": "file://com.hadss.ohFeature/data/storage/el2/base/haps/entry/files/exampleKnock2.png",
                  "title": "海岸风光·天气晴朗",
                  "description": "HarmonyShare · 220.8万播放"
                }]
              )
            }} />
          </View>
        </View>

      </ScrollView>
    </SafeAreaView>
  );
}

// 样式
const styles = StyleSheet.create({
  container: {
    padding: 16,
  },
  buttonRow: {
    flexDirection: 'row',
    marginVertical: 8,
    justifyContent: 'space-between'
  },
  gap: {
    width: 16
  },
  statusText: {
    textAlign: 'center',
    marginBottom: 16,
    fontSize: 16
  },
  groupContainer: {
    width: '80%',
    alignItems: 'center',
    marginVertical: 10
  },
  checkContainer: {
    marginBottom: 20,
    alignItems: 'center'
  },
  tipText: {
    marginVertical: 10,
    color: '#666'
  }
});

export default App;

```

## Link

目前OpenHarmony暂不支持AutoLink，所以Link步骤需要手动配置。

首先需要使用DevEco Studio打开项目里的OpenHarmony工程，在工程根目录的`oh-package.json5`添加overrides字段：

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

### 引鸿蒙端代码

目前有两种方法：

1. **通过har包引入（在IDE完善相关功能后该方法会被遗弃，目前首选此方法）。**

    >**说明：**
    >har包位于三方库安装路径的`harmony`文件夹下。

    a. 打开`entry/oh-package.json5`，添加以下依赖：

    ```
    "dependencies":{
        "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
        "@hadss/react_native_knock_share": "file:../../node_modules/@hadss/react_native_knock_share/harmony/KnockShareHO/knock_share.har",
      }
    ```

    b. 配置CMakeLists和引入RNOHGeneratedPackage：

    打开`entry/src/main/cpp/CMakeLists.txt`，添加：

    ```diff
    project(rnapp)
    cmake_minimum_required(VERSION 3.4.1)
    set(CMAKE_SKIP_BUILD_RPATH TRUE)
    set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
    set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
    
    set(RNOH_CPP_DIR "${OH_MODULE_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
    set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
    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")
    add_compile_definitions(WITH_HITRACE_SYSTRACE)
    set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
    + file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
    
    add_subdirectory("${RNOH_CPP_DIR}" ./rn)
    
    add_library(rnoh_app SHARED
    +    ${GENERATED_CPP_FILES}
        "./PackageProvider.cpp"
        "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
    )
    
    target_link_libraries(rnoh_app PUBLIC rnoh)
    ```

    c. 打开`entry/src/main/cpp/PackageProvider.cpp`，添加：

    ```diff
    #include "RNOH/PackageProvider.h"
    + #include "generated/RNOHGeneratedPackage.h"
    
    using namespace rnoh;
    
    std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
        return {
    +        std::make_shared<RNOHGeneratedPackage>(ctx)
        };
    }
    ```

    d. 在ArkTs侧引入AvoidModulePackage：

    打开`entry/src/main/ets/RNPackagesFactory.ts`，添加：

    ```diff
      ...
    + import { KnockShareModulePackage} from '@hadss/react_native_knock_share/ts';;
    
    export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
      return [
    +   new KnockShareModulePackage(ctx),
      ];
    }
    ```
    e. 应用配置文件module.json5 配置新增：
       目标应用需要在应用配置文件中，配置支持的类型。
    ```
        "skills": [
          {
            "actions": [
              "ohos.want.action.sendData"
            ],
            "uris": [
              {
                "scheme": "file",
                "utd": "general.image",
                "maxFileSupported": 3
              },
            ]
          }
        ],
    ```

    f. 运行：

    点击右上角的`sync`按钮

    或者在终端执行：

    ```bash
    cd entry
    ohpm install
    ```
    然后编译、运行即可。
> **说明**
> 若项目启动时报错：can not find record '&@rnoh/react-native-openharmony/generated/ts&X.X.X'。需在entry\oh_modules@rnoh\react-native-openharmony\ts.ts文件中添加export * from './generated/ts'，并删除.cxx文件夹、build文件夹，然后执行sync操作同步代码。

2. **直接链接源码。**

    如需使用直接链接源码，请参考[直接链接源码说明](https://gitee.com/react-native-oh-library/usage-docs/blob/master/zh-cn/link-source-code.md)。

##  API

>**说明：**
>"Platform"列表示支持的平台，All表示支持所有平台。


### 碰一碰分享接口

**KnockShare**
| Name                | Description                                          | Type     | Platform  |
| ------------------- | -----------------------------------------------------| -------- | --------- |
| canUseKnockShare            | 判断设备是否支持碰一碰分享。                    | function | OpenHarmony|
| addKnockShareListener       | 开启碰一碰监听。                               | function | OpenHarmony|
| removeKnockShareListener    | 移除碰一碰监听。                               | function | OpenHarmony|
| setKnockShareData           | 设置碰一碰分享数据。                           | (records:Array<SharedRecord>)=>{} | OpenHarmony|

**说明：**
setKnockShareData入参的数据记录当前最大可支持500条，且需同时满足数据总大小不超过IPC传输上限200KB。
setKnockShareData方法中分享数据不支持多条文本类型分享。
常见问题见：https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/share-faq

**SharedRecord**
| Name        | Description                                                      | Type       | Platform  |
| :---------- | :----------------------------------------------------------------| :--------- | :-------- |
| utd         | 统一数据类型，参考@ohos.data.uniformTypeDescriptor (标准化数据定义与描述)建议开发者传入精准的数据类型，有助于匹配到精确的目标应用。                                                   | string     | OpenHarmony |
| title       | 如果是文本、链接等内容，建议填入title标识其标题。缺省时，若分享内容为文本类型，则文本内容作为title字段；若分享内容为文件类型，则文件名作为title字段。                                            | string     | OpenHarmony |
| label       | 标识当前数据记录类型的标签，在单选模式时生效。缺省为UniformDataType类型相应的标签。具体如下：HYPERLINK显示为链接；HTML显示为网页；TEXT显示为文本；VIDEO显示为视频；AUDIO显示为音乐；IMAGE显示为图片；FILE显示为文件。                         | string     | OpenHarmony |
| description   | 数据记录的描述。缺省为空字符串。                                                   | string     | OpenHarmony |
| thumbnail     | 数据记录缩略图                                                   | Uint8Array | OpenHarmony |
| thumbnailUri  | 数据记录缩略图。缺省时使用与分享内容类型匹配的图标作为缩略图。建议开发者传入符合数据记录的缩略图，如无，可传入应用图标。**说明：**限制图片大小：32KB以下。过大的图片可能导致want数据超限无法拉起分享，可使用ImagePacker.packing压缩图片质量。                                             | string      | OpenHarmony |
| uri           | 数据记录缩略图。缺省时使用与分享内容类型匹配的图标作为缩略图。支持的uri类型：应用文件uri，参见：应用文件分享 用户文件uri，参见：用户文件uri介绍 HarmonyOS起始版本：5.0.0(12)。**说明：**与thumbnail字段同时存在时，优先使用thumbnail字段。                                                    | string      | OpenHarmony |
| content       | 数据记录内容。链接（包含App Linking）、文本类型的内容通过该字段传递。**说明：**content和uri二者至少有一个不为空。  | string     | OpenHarmony |
| extraData     | 扩展数据，用于向目标应用/设备分享自定义的扩展内容                   | Record<string, string \| number \| boolean \| Array<string\| number \| boolean>>|OpenHarmony |

数据参考：https://developer.huawei.com/consumer/cn/doc/harmonyos-references/share-system-share#section20696483813


## 约束与限制
### 设备限制
支持的设备类型：当前仅支持手机碰手机。
手机应用发起碰一碰分享时，双端设备需要在亮屏、解锁的状态下并且都已开启华为分享服务（系统默认开启），设备顶部轻碰即可触发。如果用户已手动关闭华为分享服务开关，轻碰事件触发时，用户会接收到系统通知提示开启。
支持的手机系统：双端需HarmonyOS NEXT 5.0.0.102 SP6及以上版本，可使用canUseKnockShare进行判断。
集成开发环境：DevEco Studio NEXT Beta1及以上版本。
SDK版本：API12及以上。
### 使用限制
宿主应用和目标应用定义数据类型须遵照UDMF（统一数据管理框架）定义的UTD（统一类型描述符）规范。目标应用需要在应用配置文件中，配置支持的类型。如支持全部图片类型，可声明为：general.image。
统一数据类型，参考@ohos.data.uniformTypeDescriptor (标准化数据定义与描述)
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-uniformtypedescriptor
宿主应用配置分享数据描述信息总量不能超过200KB，且分享条目总量不能超过500条。