@react-native-ohos/clipboard 1.16.3 → 1.16.4-beta.1
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.
|
@@ -33,6 +33,8 @@ import url from '@ohos.url';
|
|
|
33
33
|
const TAG = "RNCClipboardTurboModule"
|
|
34
34
|
const prefixPNG = "data:image/png;base64,"
|
|
35
35
|
const prefixJPG = "data:image/jpg;base64,"
|
|
36
|
+
const imageMimePrefix = "image/"
|
|
37
|
+
const defaultImageMimeType = "image/png"
|
|
36
38
|
const PERMISSIONS: Array<Permissions> = [
|
|
37
39
|
'ohos.permission.READ_PASTEBOARD'
|
|
38
40
|
]
|
|
@@ -181,7 +183,14 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
181
183
|
|
|
182
184
|
systemPasteboard.getData().then((pasteData) => {
|
|
183
185
|
let pixMap = pasteData.getPrimaryPixelMap();
|
|
184
|
-
|
|
186
|
+
if (!pixMap) {
|
|
187
|
+
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImagePNG,pixMap is null or undefined");
|
|
188
|
+
reject({
|
|
189
|
+
code: 1,
|
|
190
|
+
message: "Clipboard does not contain an image"
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
185
194
|
const imagePackerApi = image.createImagePacker();
|
|
186
195
|
let packOpt: image.PackingOption = {
|
|
187
196
|
format: "image/png",
|
|
@@ -196,11 +205,12 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
196
205
|
}).catch((err) => {
|
|
197
206
|
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImagePNG,failed to packing");
|
|
198
207
|
reject(err)
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
208
|
+
}).finally(() => {
|
|
209
|
+
imagePackerApi.release().then(() => {
|
|
210
|
+
logger.debug(TAG, "[RNOH]:RNCClipboardTurboModule call getImagePNG, releasing image packaging");
|
|
211
|
+
}).catch((error: BusinessError) => {
|
|
212
|
+
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImagePNG, releasing image packaging error");
|
|
213
|
+
})
|
|
204
214
|
})
|
|
205
215
|
|
|
206
216
|
}).catch((err) => {
|
|
@@ -213,6 +223,12 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
213
223
|
message: "User refuses authorization"
|
|
214
224
|
})
|
|
215
225
|
}
|
|
226
|
+
}).catch((err) => {
|
|
227
|
+
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImagePNG,failed to request permission");
|
|
228
|
+
reject({
|
|
229
|
+
code: 2,
|
|
230
|
+
message: "Failed to request permission"
|
|
231
|
+
})
|
|
216
232
|
});
|
|
217
233
|
});
|
|
218
234
|
}
|
|
@@ -227,8 +243,14 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
227
243
|
|
|
228
244
|
systemPasteboard.getData().then((pasteData) => {
|
|
229
245
|
let pixMap = pasteData.getPrimaryPixelMap();
|
|
230
|
-
|
|
231
|
-
|
|
246
|
+
if (!pixMap) {
|
|
247
|
+
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImageJPG,pixMap is null or undefined");
|
|
248
|
+
reject({
|
|
249
|
+
code: 1,
|
|
250
|
+
message: "Clipboard does not contain an image"
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
232
254
|
const imagePackerApi = image.createImagePacker();
|
|
233
255
|
let packOpt: image.PackingOption = {
|
|
234
256
|
format: "image/jpeg",
|
|
@@ -243,11 +265,12 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
243
265
|
}).catch((err) => {
|
|
244
266
|
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImageJPG,failed to packing");
|
|
245
267
|
reject(err)
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
268
|
+
}).finally(() => {
|
|
269
|
+
imagePackerApi.release().then(() => {
|
|
270
|
+
logger.debug(TAG, "[RNOH]:RNCClipboardTurboModule call getImageJPG, releasing image packaging");
|
|
271
|
+
}).catch((error: BusinessError) => {
|
|
272
|
+
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImageJPG, releasing image packaging error");
|
|
273
|
+
})
|
|
251
274
|
})
|
|
252
275
|
|
|
253
276
|
}).catch((err) => {
|
|
@@ -260,6 +283,12 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
260
283
|
message: "User refuses authorization"
|
|
261
284
|
})
|
|
262
285
|
}
|
|
286
|
+
}).catch((err) => {
|
|
287
|
+
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call getImageJPG,failed to request permission");
|
|
288
|
+
reject({
|
|
289
|
+
code: 2,
|
|
290
|
+
message: "Failed to request permission"
|
|
291
|
+
})
|
|
263
292
|
});
|
|
264
293
|
});
|
|
265
294
|
}
|
|
@@ -274,6 +303,10 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
274
303
|
let imageSource: image.ImageSource = image.createImageSource(arrayBuffer);
|
|
275
304
|
logger.debug(TAG, "[RNOH]:RNCClipboardTurboModule call setImage100");
|
|
276
305
|
|
|
306
|
+
// 获取图片格式信息
|
|
307
|
+
let imageFormat = this.getImageFormatFromArrayBuffer(arrayBuffer);
|
|
308
|
+
logger.debug(TAG, `[RNOH]:RNCClipboardTurboModule call setImage, detected format: ${imageFormat}`);
|
|
309
|
+
|
|
277
310
|
imageSource.getImageInfo().then(value => {
|
|
278
311
|
let hValue = Math.round(value.size.height);
|
|
279
312
|
let wValue = Math.round(value.size.width);
|
|
@@ -293,11 +326,17 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
293
326
|
|
|
294
327
|
let systemPasteboard = pasteboard.getSystemPasteboard();
|
|
295
328
|
systemPasteboard.getData().then((pasteData) => {
|
|
329
|
+
// 使用 MIMETYPE_PIXELMAP 创建记录,确保兼容性
|
|
296
330
|
let record = pasteboard.createRecord(pasteboard.MIMETYPE_PIXELMAP, iconPixelMap)
|
|
331
|
+
// 在 record 的 data 中保存原始格式信息,供 getImage 读取
|
|
332
|
+
if (!record.data) {
|
|
333
|
+
record.data = {};
|
|
334
|
+
}
|
|
335
|
+
record.data[imageFormat] = arrayBuffer;
|
|
297
336
|
pasteData.addRecord(record)
|
|
298
337
|
|
|
299
338
|
systemPasteboard.setData(pasteData).then((data: void) => {
|
|
300
|
-
logger.debug(TAG,
|
|
339
|
+
logger.debug(TAG, `[RNOH]:RNCClipboardTurboModule call setImage305,successed in setting pasteData with format: ${imageFormat}`);
|
|
301
340
|
}).catch((err) => {
|
|
302
341
|
logger.error(TAG, "[RNOH]:RNCClipboardTurboModule call setImage305,failed in setting pasteData");
|
|
303
342
|
})
|
|
@@ -309,13 +348,252 @@ export class RNCClipboardTurboModule extends TurboModule {
|
|
|
309
348
|
|
|
310
349
|
}
|
|
311
350
|
|
|
351
|
+
// 从 ArrayBuffer 中检测图片格式
|
|
352
|
+
private getImageFormatFromArrayBuffer(buffer: ArrayBuffer): string {
|
|
353
|
+
let uint8Array = new Uint8Array(buffer);
|
|
354
|
+
|
|
355
|
+
// 检查 JPEG (FF D8 FF)
|
|
356
|
+
if (uint8Array.length >= 3 &&
|
|
357
|
+
uint8Array[0] === 0xFF &&
|
|
358
|
+
uint8Array[1] === 0xD8 &&
|
|
359
|
+
uint8Array[2] === 0xFF) {
|
|
360
|
+
return "image/jpeg";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 检查 PNG (89 50 4E 47 0D 0A 1A 0A)
|
|
364
|
+
if (uint8Array.length >= 8 &&
|
|
365
|
+
uint8Array[0] === 0x89 &&
|
|
366
|
+
uint8Array[1] === 0x50 &&
|
|
367
|
+
uint8Array[2] === 0x4E &&
|
|
368
|
+
uint8Array[3] === 0x47 &&
|
|
369
|
+
uint8Array[4] === 0x0D &&
|
|
370
|
+
uint8Array[5] === 0x0A &&
|
|
371
|
+
uint8Array[6] === 0x1A &&
|
|
372
|
+
uint8Array[7] === 0x0A) {
|
|
373
|
+
return "image/png";
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// 检查 WEBP (52 49 46 46 ... 57 45 42 50)
|
|
377
|
+
if (uint8Array.length >= 12 &&
|
|
378
|
+
uint8Array[0] === 0x52 &&
|
|
379
|
+
uint8Array[1] === 0x49 &&
|
|
380
|
+
uint8Array[2] === 0x46 &&
|
|
381
|
+
uint8Array[3] === 0x46 &&
|
|
382
|
+
uint8Array[8] === 0x57 &&
|
|
383
|
+
uint8Array[9] === 0x45 &&
|
|
384
|
+
uint8Array[10] === 0x42 &&
|
|
385
|
+
uint8Array[11] === 0x50) {
|
|
386
|
+
return "image/webp";
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 检查 GIF (47 49 46 38)
|
|
390
|
+
if (uint8Array.length >= 4 &&
|
|
391
|
+
uint8Array[0] === 0x47 &&
|
|
392
|
+
uint8Array[1] === 0x49 &&
|
|
393
|
+
uint8Array[2] === 0x46 &&
|
|
394
|
+
uint8Array[3] === 0x38) {
|
|
395
|
+
return "image/gif";
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// 检查 BMP (42 4D)
|
|
399
|
+
if (uint8Array.length >= 2 &&
|
|
400
|
+
uint8Array[0] === 0x42 &&
|
|
401
|
+
uint8Array[1] === 0x4D) {
|
|
402
|
+
return "image/bmp";
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// 默认返回 PNG
|
|
406
|
+
logger.debug(TAG, "[RNOH]:RNCClipboardTurboModule getImageFormatFromArrayBuffer, unknown format, defaulting to image/png");
|
|
407
|
+
return "image/png";
|
|
408
|
+
}
|
|
409
|
+
|
|
312
410
|
getImage(): Promise<string> {
|
|
313
411
|
return new Promise<string>((resolve, reject) => {
|
|
314
412
|
logger.debug(TAG, "[RNOH]:RNCClipboardTurboModule call getImage");
|
|
315
|
-
|
|
413
|
+
this.requestPermission().then(async res => {
|
|
414
|
+
if (!res) {
|
|
415
|
+
reject({
|
|
416
|
+
code: 0,
|
|
417
|
+
message: "User refuses authorization"
|
|
418
|
+
})
|
|
419
|
+
return
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
let systemPasteboard = pasteboard.getSystemPasteboard();
|
|
424
|
+
let pasteData = await systemPasteboard.getData();
|
|
425
|
+
|
|
426
|
+
// 检查剪贴板中是否有 JPEG 或 PNG 格式的数据
|
|
427
|
+
// 如果有,使用与 getImageJPG/getImagePNG 相同的逻辑:通过 getPrimaryPixelMap 重新打包
|
|
428
|
+
let imageFormat = this.getImageFormatFromPasteData(pasteData);
|
|
429
|
+
if (imageFormat) {
|
|
430
|
+
logger.debug(TAG, `[RNOH]:RNCClipboardTurboModule getImage, detected format: ${imageFormat}, using getImageJPG/getImagePNG logic`);
|
|
431
|
+
let pixelMapObj = pasteData.getPrimaryPixelMap();
|
|
432
|
+
if (pixelMapObj) {
|
|
433
|
+
// 使用与 getImageJPG/getImagePNG 相同的参数重新打包
|
|
434
|
+
let imageDataUri = await this.packPixelMapAsDataUri(pixelMapObj, imageFormat);
|
|
435
|
+
resolve(imageDataUri);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
let imageDataUri = await this.getImageDataUriFromPasteData(pasteData);
|
|
441
|
+
resolve(imageDataUri)
|
|
442
|
+
} catch (err) {
|
|
443
|
+
logger.error(TAG, `[RNOH]:RNCClipboardTurboModule call getImage,failed to get image. Cause:${err.message}`);
|
|
444
|
+
reject(err)
|
|
445
|
+
}
|
|
446
|
+
}).catch((err) => {
|
|
447
|
+
logger.error(TAG, `[RNOH]:RNCClipboardTurboModule call getImage,failed to request permission. Cause:${err.message}`);
|
|
448
|
+
reject(err)
|
|
449
|
+
})
|
|
316
450
|
});
|
|
317
451
|
}
|
|
318
452
|
|
|
453
|
+
// 从剪贴板数据中检测图片格式
|
|
454
|
+
private getImageFormatFromPasteData(pasteData: pasteboard.PasteData): string | null {
|
|
455
|
+
let count = pasteData.getRecordCount();
|
|
456
|
+
for (let index = 0; index < count; index++) {
|
|
457
|
+
let record = pasteData.getRecord(index);
|
|
458
|
+
// 检查 record.mimeType
|
|
459
|
+
let mimeType = record.mimeType?.toLowerCase() || "";
|
|
460
|
+
if (mimeType === "image/jpeg" || mimeType === "image/jpg") {
|
|
461
|
+
return "image/jpeg";
|
|
462
|
+
}
|
|
463
|
+
if (mimeType === "image/png") {
|
|
464
|
+
return "image/png";
|
|
465
|
+
}
|
|
466
|
+
// 检查 record.data 中的 mimeType
|
|
467
|
+
if (record.data) {
|
|
468
|
+
for (let mt of Object.keys(record.data)) {
|
|
469
|
+
let lowerMt = mt.toLowerCase();
|
|
470
|
+
if (lowerMt === "image/jpeg" || lowerMt === "image/jpg") {
|
|
471
|
+
return "image/jpeg";
|
|
472
|
+
}
|
|
473
|
+
if (lowerMt === "image/png") {
|
|
474
|
+
return "image/png";
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private async getImageDataUriFromPasteData(pasteData: pasteboard.PasteData): Promise<string> {
|
|
483
|
+
let count = pasteData.getRecordCount();
|
|
484
|
+
for (let index = 0; index < count; index++) {
|
|
485
|
+
let record = pasteData.getRecord(index);
|
|
486
|
+
let dataUri = await this.getImageDataUriFromRecord(record);
|
|
487
|
+
if (dataUri.length > 0) {
|
|
488
|
+
return dataUri
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
let pixelMapObj = pasteData.getPrimaryPixelMap();
|
|
494
|
+
if (pixelMapObj) {
|
|
495
|
+
return await this.packPixelMapAsDataUri(pixelMapObj, defaultImageMimeType)
|
|
496
|
+
}
|
|
497
|
+
} catch (err) {
|
|
498
|
+
logger.debug(TAG, `[RNOH]:RNCClipboardTurboModule getImage,no primary PixelMap. Cause:${err.message}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return ""
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private async getImageDataUriFromRecord(record: pasteboard.PasteDataRecord): Promise<string> {
|
|
505
|
+
if (this.isImageMimeType(record.mimeType)) {
|
|
506
|
+
let dataUri = await this.getImageDataUriByMimeType(record, record.mimeType);
|
|
507
|
+
if (dataUri.length > 0) {
|
|
508
|
+
return dataUri
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (record.data) {
|
|
513
|
+
let customMimeTypes = Object.keys(record.data);
|
|
514
|
+
for (let index = 0; index < customMimeTypes.length; index++) {
|
|
515
|
+
let mimeType = customMimeTypes[index];
|
|
516
|
+
if (this.isImageMimeType(mimeType)) {
|
|
517
|
+
return this.createDataUri(mimeType, record.data[mimeType])
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (record.pixelMap) {
|
|
523
|
+
let mimeType = this.isImageMimeType(record.mimeType) ? record.mimeType : defaultImageMimeType;
|
|
524
|
+
return await this.packPixelMapAsDataUri(record.pixelMap, mimeType)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return ""
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private async getImageDataUriByMimeType(record: pasteboard.PasteDataRecord, mimeType: string): Promise<string> {
|
|
531
|
+
if (record.data && record.data[mimeType]) {
|
|
532
|
+
return this.createDataUri(mimeType, record.data[mimeType])
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
try {
|
|
536
|
+
let value = await record.getData(mimeType);
|
|
537
|
+
if (value instanceof ArrayBuffer) {
|
|
538
|
+
return this.createDataUri(mimeType, value)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (this.isPixelMap(value)) {
|
|
542
|
+
return await this.packPixelMapAsDataUri(value as image.PixelMap, mimeType)
|
|
543
|
+
}
|
|
544
|
+
} catch (err) {
|
|
545
|
+
logger.debug(TAG, `[RNOH]:RNCClipboardTurboModule getImage,failed to read mimeType:${mimeType}. Cause:${err.message}`);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return ""
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
private async packPixelMapAsDataUri(pixelMapObj: image.PixelMap, mimeType: string): Promise<string> {
|
|
552
|
+
const imagePackerApi = image.createImagePacker();
|
|
553
|
+
let packingMimeType = this.getPackingMimeType(mimeType);
|
|
554
|
+
let packOpt: image.PackingOption = {
|
|
555
|
+
format: packingMimeType,
|
|
556
|
+
quality: 96
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
try {
|
|
560
|
+
let data = await imagePackerApi.packing(pixelMapObj, packOpt);
|
|
561
|
+
return this.createDataUri(packingMimeType, data)
|
|
562
|
+
} finally {
|
|
563
|
+
imagePackerApi.release().then(() => {
|
|
564
|
+
logger.debug(TAG, "[RNOH]:RNCClipboardTurboModule call getImage, releasing image packaging");
|
|
565
|
+
}).catch((error: BusinessError) => {
|
|
566
|
+
logger.error(TAG, `[RNOH]:RNCClipboardTurboModule call getImage, releasing image packaging error. Cause:${error.message}`);
|
|
567
|
+
})
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private createDataUri(mimeType: string, data: ArrayBuffer): string {
|
|
572
|
+
let uint8Array = new Uint8Array(data);
|
|
573
|
+
let base64Helper = new util.Base64Helper();
|
|
574
|
+
let base64Str = base64Helper.encodeToStringSync(uint8Array, util.Type.BASIC);
|
|
575
|
+
return `data:${mimeType};base64,${base64Str}`
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
private getPackingMimeType(mimeType: string): string {
|
|
579
|
+
let normalizedMimeType = mimeType.toLowerCase();
|
|
580
|
+
if (normalizedMimeType == "image/jpeg" || normalizedMimeType == "image/jpg") {
|
|
581
|
+
return "image/jpeg"
|
|
582
|
+
}
|
|
583
|
+
if (normalizedMimeType == "image/webp") {
|
|
584
|
+
return "image/webp"
|
|
585
|
+
}
|
|
586
|
+
return defaultImageMimeType
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private isImageMimeType(mimeType: string): boolean {
|
|
590
|
+
return mimeType != undefined && mimeType.length > 0 && mimeType.toLowerCase().indexOf(imageMimePrefix) == 0
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private isPixelMap(value: pasteboard.ValueType): boolean {
|
|
594
|
+
return value != undefined && typeof value == "object" && "getImageInfo" in value
|
|
595
|
+
}
|
|
596
|
+
|
|
319
597
|
setStrings(content: string[]) {
|
|
320
598
|
logger.debug(TAG, "[RNOH]:RNCClipboardTurboModule call setStrings fun");
|
|
321
599
|
let systemPasteboard = pasteboard.getSystemPasteboard();
|
package/harmony/clipboard.har
CHANGED
|
Binary file
|