@expo-harmony/expo-clipboard 55.0.13-harmony.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # @expo-harmony/expo-clipboard
2
+
3
+ [**GitHub 仓库**](https://github.com/renbaoshuo/expo-harmony/tree/master/packages/expo-clipboard) | [Expo 上游官方文档](https://docs.expo.dev/versions/v55.0.0/sdk/clipboard/)
4
+
5
+ 为 HarmonyOS 上的 React Native 应用提供 Expo Clipboard 的原生实现,与官方同版本的 `expo-clipboard` 配套使用。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install @expo-harmony/expo-clipboard expo-clipboard@55.0.13
11
+ ```
12
+
13
+ 鸿蒙适配会通过 Autolinking 自动接入,无需额外配置。最低支持 HarmonyOS 5.0.2(API 14),宿主的 `compatibleSdkVersion` 也需满足此要求。
14
+
15
+ 读取剪贴板需要 `ohos.permission.READ_PASTEBOARD`,本包已经声明并配置好用途说明(也可以参照下方的 `clipboardPermission` 配置自定义文本),正常情况下无需再次声明。这个权限只在读内容时用到,读之前如果没有就弹窗申请;弹窗只在前台出现,后台读取直接失败,用户拒绝后返回 `ERR_NO_PERMISSION`。权限声明挂在 `EntryAbility` 名下,Host 的入口 Ability 如果叫别的名字,需要改成自己的 Ability 名称。写入、查询内容类型和监听变化都不读内容,不会申请这个权限。
16
+
17
+ 业务代码依旧使用官方包:
18
+
19
+ ```ts
20
+ import * as Clipboard from 'expo-clipboard';
21
+
22
+ await Clipboard.setStringAsync('Hello HarmonyOS');
23
+ const text = await Clipboard.getStringAsync();
24
+ ```
25
+
26
+ ## Config Plugin
27
+
28
+ `clipboardPermission` 是 HarmonyOS 的构建期配置扩展,需要自定义读取权限的用途说明时,在应用配置中添加:
29
+
30
+ ```json
31
+ {
32
+ "expo": {
33
+ "plugins": [["@expo-harmony/expo-clipboard", { "clipboardPermission": "用于粘贴您复制的订单编号" }]]
34
+ }
35
+ }
36
+ ```
37
+
38
+ 该选项只接受非空字符串,不支持用 `false` 关闭权限。未配置时使用默认文案「用于粘贴您复制的文字、链接和图片」。修改配置后需要重新 prebuild 并构建应用。
39
+
40
+ ## API 对照表
41
+
42
+ ### Component
43
+
44
+ > **未实现的内容**
45
+ >
46
+ > - `ClipboardPasteButton`:官方只在 iOS 上提供该组件,HarmonyOS 上没有对应的系统粘贴控件,渲染结果为空。
47
+
48
+ ### Constants
49
+
50
+ #### `isPasteButtonAvailable`
51
+
52
+ 类型:`boolean`,HarmonyOS 上恒为 `false`。
53
+
54
+ ### Methods
55
+
56
+ #### `Clipboard.getStringAsync(options?)`
57
+
58
+ 返回 `Promise<string>`,读取剪贴板中的文本。`options` 见 `GetStringOptions`。
59
+
60
+ 默认返回纯文本。剪贴板中只有 HTML 时返回系统转换后的纯文本,只有 URL 时返回 URL 字符串。`preferredFormat` 为 `html` 时优先返回 HTML 原文,没有 HTML 时把纯文本或 URL 转换成 HTML。剪贴板中没有文本或 URL 时返回空字符串,不申请权限。读取需要读取权限。
61
+
62
+ HTML 转纯文本由系统完成,标签支持和换行行为遵循系统版本,可能与 Android、iOS 不同。系统在 API 20 起支持 `<br>` 换行标签;纯文本转 HTML 时会转义特殊字符,并使用段落保留换行,以兼容 API 14。
63
+
64
+ #### `Clipboard.setStringAsync(text, options?)`
65
+
66
+ 返回 `Promise<boolean>`,写入文本,HarmonyOS 上恒为 `true`。`options.inputFormat` 为 `html` 时按 HTML 写入,同时保存一份系统转换后的纯文本,之后按纯文本读取会得到转换结果。写入会替换剪贴板原有内容。并发发起的读写会排队依次执行,返回顺序与调用顺序一致。
67
+
68
+ #### `Clipboard.setString(text)`
69
+
70
+ 写入纯文本,无返回值。官方已标记废弃,行为与 `setStringAsync(text)` 相同,但不等待写入完成。
71
+
72
+ #### `Clipboard.hasStringAsync()`
73
+
74
+ 返回 `Promise<boolean>`,剪贴板中是否有文本,纯文本和 HTML 都算。不需要读取权限。
75
+
76
+ #### `Clipboard.getUrlAsync()`
77
+
78
+ 返回 `Promise<string | null>`,读取剪贴板中的 URL,没有则返回 `null`。官方标记为 iOS 专属,HarmonyOS 上可用。URL 使用系统 URI 类型,普通文本不会被识别为 URL。读取需要读取权限。
79
+
80
+ #### `Clipboard.setUrlAsync(url)`
81
+
82
+ 返回 `Promise<void>`,按 URL 类型写入,其他应用可以识别为链接。官方标记为 iOS 专属,HarmonyOS 上可用。
83
+
84
+ #### `Clipboard.hasUrlAsync()`
85
+
86
+ 返回 `Promise<boolean>`,剪贴板中是否有 URL 内容。官方标记为 iOS 专属,HarmonyOS 上可用。不需要读取权限。
87
+
88
+ #### `Clipboard.getImageAsync(options)`
89
+
90
+ 返回 `Promise<ClipboardImage | null>`,读取剪贴板中的图片,没有图片时返回 `null`,不申请权限。`options.format` 取 `png` 或 `jpeg`,`jpegQuality` 取 0 到 1,只对 JPEG 生效。`data` 已带 `data:image/...;base64,` 前缀,可直接用作 `Image` 组件的来源;`size` 是图片的像素尺寸。动画图片按系统默认帧处理,不保留动图。图片处理受设备内存、临时存储和系统编解码能力限制。读取需要读取权限,编码失败时抛出 `ERR_PASTE_FAILURE`。
91
+
92
+ #### `Clipboard.setImageAsync(base64Image)`
93
+
94
+ 返回 `Promise<void>`,写入图片。`base64Image` 是不带 `data:` 前缀的 Base64 字符串。内容不是可解码的图片时抛出 `ERR_INVALID_IMAGE`,写入失败时抛出 `ERR_COPY_FAILURE`。
95
+
96
+ #### `Clipboard.hasImageAsync()`
97
+
98
+ 返回 `Promise<boolean>`,剪贴板中是否有图片。不需要读取权限。
99
+
100
+ ### Event Subscriptions
101
+
102
+ #### `Clipboard.addClipboardListener(listener)`
103
+
104
+ 返回 `EventSubscription`,订阅剪贴板变化。回调收到 `ClipboardEvent`,`contentTypes` 列出当前内容类型。
105
+
106
+ 只在应用处于前台且存在订阅时发送事件,应用在后台发生的复制不会在回到前台后补发。取消订阅调用返回对象的 `remove()`。
107
+
108
+ #### `Clipboard.removeClipboardListener(subscription)`
109
+
110
+ 移除 `addClipboardListener` 添加的订阅,无返回值。官方已标记废弃,建议改用 `subscription.remove()`。
111
+
112
+ ### Interfaces
113
+
114
+ #### `Subscription`
115
+
116
+ `EventSubscription` 的别名,提供 `remove()` 用于取消订阅。
117
+
118
+ ### Types
119
+
120
+ #### `ClipboardEvent`
121
+
122
+ `contentTypes: ContentType[]`,剪贴板上可用的内容类型。
123
+
124
+ #### `ClipboardImage`
125
+
126
+ `data: string`,带 `data:` 前缀的 Base64 图片;`size: { width: number; height: number }`,图片的像素尺寸。
127
+
128
+ #### `GetImageOptions`
129
+
130
+ `format: 'png' | 'jpeg'`,输出格式;`jpegQuality?: number`,取值 0 到 1,默认 `1`,只对 JPEG 生效。
131
+
132
+ #### `GetStringOptions`
133
+
134
+ `preferredFormat?: StringFormat`,期望的字符串格式,默认 `StringFormat.PLAIN_TEXT`。
135
+
136
+ #### `SetStringOptions`
137
+
138
+ `inputFormat?: StringFormat`,写入字符串的格式,默认 `StringFormat.PLAIN_TEXT`。
139
+
140
+ > **未实现的内容**
141
+ >
142
+ > - `AcceptedContentType`、`CornerStyleType`、`DisplayModeType`、`PasteEventPayload`、`TextPasteEvent`、`ImagePasteEvent`:只在 `ClipboardPasteButton` 上使用,该组件在 HarmonyOS 上不可用。
143
+
144
+ ### Enums
145
+
146
+ #### `ContentType`
147
+
148
+ | 成员 | 值 |
149
+ | ------------ | -------------- |
150
+ | `PLAIN_TEXT` | `'plain-text'` |
151
+ | `HTML` | `'html'` |
152
+ | `IMAGE` | `'image'` |
153
+ | `URL` | `'url'` |
154
+
155
+ 官方把 `URL` 标记为 iOS 专属,HarmonyOS 上可用。
156
+
157
+ #### `StringFormat`
158
+
159
+ | 成员 | 值 |
160
+ | ------------ | ------------- |
161
+ | `PLAIN_TEXT` | `'plainText'` |
162
+ | `HTML` | `'html'` |
163
+
164
+ ## Author
165
+
166
+ **expo-harmony** © [Baoshuo](https://github.com/renbaoshuo), Released under the MIT License.<br>
167
+ Authored and maintained by Baoshuo with help from [contributors](https://github.com/renbaoshuo/expo-harmony/contributors).
168
+
169
+ > [Personal Website](https://baoshuo.ren) · [Blog](https://blog.baoshuo.ren) · GitHub [@renbaoshuo](https://github.com/renbaoshuo) · Twitter [@renbaoshuo](https://twitter.com/renbaoshuo)
package/app.plugin.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('./plugin/withClipboard');
@@ -0,0 +1,6 @@
1
+ {
2
+ "platforms": ["harmony"],
3
+ "harmony": {
4
+ "modules": ["ExpoClipboardModule"]
5
+ }
6
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@expo-harmony/expo-clipboard",
3
+ "version": "55.0.13-harmony.0",
4
+ "description": "HarmonyOS port of expo-clipboard",
5
+ "keywords": [
6
+ "react-native",
7
+ "expo",
8
+ "expo-harmony",
9
+ "harmonyos"
10
+ ],
11
+ "homepage": "https://github.com/renbaoshuo/expo-harmony/tree/master/packages/expo-clipboard#readme",
12
+ "repository": "https://github.com/renbaoshuo/expo-harmony",
13
+ "author": {
14
+ "name": "Baoshuo",
15
+ "email": "i@baoshuo.ren",
16
+ "url": "https://github.com/renbaoshuo"
17
+ },
18
+ "main": "Index.ets",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "@expo-harmony/expo-modules-core": "55.0.25-harmony.2"
22
+ }
23
+ }
Binary file
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = Object.freeze({});
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@expo-harmony/expo-clipboard",
3
+ "version": "55.0.13-harmony.0",
4
+ "description": "HarmonyOS port of expo-clipboard",
5
+ "keywords": [
6
+ "react-native",
7
+ "expo",
8
+ "expo-harmony",
9
+ "harmonyos"
10
+ ],
11
+ "homepage": "https://github.com/renbaoshuo/expo-harmony/tree/master/packages/expo-clipboard#readme",
12
+ "bugs": "https://github.com/renbaoshuo/expo-harmony/issues",
13
+ "license": "MIT",
14
+ "author": "Baoshuo <i@baoshuo.ren>",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/renbaoshuo/expo-harmony.git",
18
+ "directory": "packages/expo-clipboard"
19
+ },
20
+ "main": "index.js",
21
+ "types": "index.d.ts",
22
+ "scripts": {
23
+ "harmony:clean": "expo-harmony-module prepare --clean-only",
24
+ "harmony:build": "expo-harmony-module prepare",
25
+ "prepack": "expo-harmony-module prepack"
26
+ },
27
+ "dependencies": {
28
+ "@expo-harmony/config-plugins": "55.0.10-harmony.2",
29
+ "@expo/config-plugins": "55.0.10"
30
+ },
31
+ "peerDependencies": {
32
+ "@expo-harmony/expo-modules-core": "55.0.25-harmony.2",
33
+ "expo-clipboard": "55.0.13"
34
+ },
35
+ "devDependencies": {
36
+ "@expo-harmony/expo-module-scripts": "55.0.0-harmony.2",
37
+ "@expo-harmony/expo-modules-core": "55.0.25-harmony.2",
38
+ "@react-native-oh/react-native-harmony": "0.84.1",
39
+ "expo": "55.0.26",
40
+ "expo-clipboard": "55.0.13",
41
+ "expo-modules-core": "55.0.25",
42
+ "react": "19.2.3",
43
+ "react-native": "0.84.1"
44
+ },
45
+ "engines": {
46
+ "node": ">=20"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const { createRunOncePlugin } = require('@expo/config-plugins');
4
+ const { registerHarmonyConfigPlugin, withStrings } = require('@expo-harmony/config-plugins');
5
+
6
+ const pkg = require('../package.json');
7
+
8
+ const READ_PERMISSION_REASON = 'expo_clipboard_read_permission_reason';
9
+
10
+ function withHarmonyClipboard(config, { clipboardPermission } = {}) {
11
+ const enabled = config.harmony?.bundleName || config.platforms?.includes('harmony');
12
+ if (!enabled) return config;
13
+
14
+ if (clipboardPermission !== undefined
15
+ && (typeof clipboardPermission !== 'string' || clipboardPermission.trim().length === 0)) {
16
+ throw new TypeError('clipboardPermission must be a non-empty string.');
17
+ }
18
+
19
+ config = registerHarmonyConfigPlugin(config, pkg.name, {
20
+ resources: { strings: { entry: [READ_PERMISSION_REASON] } },
21
+ });
22
+
23
+ return withStrings(config, (mod) => {
24
+ const current = mod.modResults.entry?.string;
25
+ const strings = (Array.isArray(current) ? current : [])
26
+ .filter(resource => resource?.name !== READ_PERMISSION_REASON);
27
+
28
+ if (clipboardPermission !== undefined) {
29
+ strings.push({ name: READ_PERMISSION_REASON, value: clipboardPermission });
30
+ }
31
+
32
+ if (Array.isArray(current) || strings.length > 0) {
33
+ mod.modResults.entry ??= {};
34
+ mod.modResults.entry.string = strings;
35
+ }
36
+
37
+ return mod;
38
+ });
39
+ }
40
+
41
+ module.exports = createRunOncePlugin(withHarmonyClipboard, pkg.name, pkg.version);