We use essential cookies for the website to function, as well as analytics cookies for analyzing and creating statistics of the website performance. To agree to the use of analytics cookies, click "Accept All". You can manage your preferences at any time by clicking "Cookie Settings" on the footer. More Information.

Only Essential Cookies
Accept All
Best PracticesHoppingMulti-Device CollaborationCommon Scenarios of Cross-Device Pasteboard

Common Scenarios of Cross-Device Pasteboard

Overview

Pasteboards are classified into local pasteboards and cross-device pasteboards. Local pasteboards allow users to copy and paste content within a device, and cross-device pasteboards allow users to copy and paste content across devices. By leveraging the all-scenario collaboration capability, cross-device pasteboards synchronize content across devices such as smart phones, PCs/2-in-1 devices, and tablets.

In multi-device scenarios, cross-device pasteboards allow copying app content from device A to pasting on device B, streamlining content sharing and enhancing collaboration efficiency.

How to Implement

Scenario Analysis

1. A user copies data on device A, and the data is written to the system pasteboard service.

2. The system pasteboard service processes and synchronizes relevant data, with developers unaware of these actions.

3. The user reads the system pasteboard content on device B and pastes the data from device A.

For details about the usage restrictions, see the constraints of the cross-device pasteboard.

Key Technologies

In developing the cross-device pasteboard feature, the system automatically transfers data across devices. Apps can connect to the cross-device pasteboard based on actual requirements. For details about the operation mechanism, see the working principles of the cross-device pasteboard.

How to Develop

The development process of the cross-device pasteboard includes the following steps:

  1. Copy data on device A and write the data to the pasteboard service: Convert the data to be stored into a common format that can be accepted by the system pasteboard, and call the SystemPasteboard.setData() method to write the data to the system pasteboard.
  2. Paste data on device B and read the pasteboard content: You can obtain temporary authorization to directly read the pasteboard data by referring to Using PasteButton. Alternatively, you can apply for the ohos.permission.READ_PASTEBOARD permission and call the @ohos.pasteboard API to read the pasteboard data. For details about the development procedure, see Accessing Pasteboard Content.

The following describes the implementation process during cross-device pasteboard development in common scenarios including sharing for direct access, multi-format copy and paste, and progress bar access.

Sharing for Direct Access

A user copies text containing a specific identifier (such as an activity password, order number, or link) in any app (such as SMS or browser), and then opens the target app (such as a shopping mall or service app). The target app can automatically identify the preset keyword or code in the copied text. After verifying the keyword format, validity, or user permission, the app displays a pop-up window or directly redirects to the associated activity page, order details page, or other specific functional modules. For example, a user copies an activity link in an SMS message and then opens a shopping mall app, the user is automatically redirected to the promotion activity page without manually searching for or tapping multiple menus.

Creating data and storing it to the pasteboard

Call createData() to create pasteData that contains a single URI. Write the pasteData data to the system pasteboard by calling setData().

Collapse
Word wrap
Dark theme
Copy code
  1. let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_URI, 'PageABuilder');
  2. let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
  3. await systemPasteboard.setData(pasteData);

Reading pasteboard data to implement redirection

The ohos.permission.READ_PASTEBOARD permission for an app to read data from the system pasteboard is subject to restricted access. Therefore, you need to apply for this permission on AppGallery Connect to obtain data from the system pasteboard. For details about how to apply for the permission, see Requesting a Profile File and Adding Permission Information.

When an app is started or switched from the background to the foreground, it checks whether the system pasteboard contains URI data during the onPageShow() lifecycle. If URI data exists, the app checks whether the ohos.permission.READ_PASTEBOARD permission has been granted. If the permission has been granted, it uses getData() to obtain data from the system pasteboard and getPrimaryUri() to obtain single URI-type data. After verifying that the data format is correct, a pop-up window is displayed to ask whether to perform redirection.

Collapse
Word wrap
Dark theme
Copy code
  1. onPageShow(): void {
  2. let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
  3. try {
  4. let result: boolean = systemPasteboard.hasDataType(pasteboard.MIMETYPE_TEXT_URI);
  5. if (result) {
  6. this.checkPermissionGrant();
  7. }
  8. } catch (error) {
  9. const err: BusinessError = error as BusinessError;
  10. hilog.error(0x0000, '[Home]',
  11. `Check data type failed. Code is ${err.code}, message is ${err.message}`);
  12. }
  13. }
  14. checkPermissionGrant(): void {
  15. let hasPermission = false;
  16. let tokenId: number = 0;
  17. try {
  18. let bundleInfo: bundleManager.BundleInfo =
  19. bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
  20. let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
  21. tokenId = appInfo.accessTokenId;
  22. } catch (error) {
  23. const err: BusinessError = error as BusinessError;
  24. hilog.error(0x0000, '[Home]',
  25. `Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
  26. }
  27. try {
  28. let atManager = abilityAccessCtrl.createAtManager();
  29. let approximatelyLocation = atManager.checkAccessTokenSync(tokenId, 'ohos.permission.READ_PASTEBOARD');
  30. hasPermission = approximatelyLocation === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
  31. } catch (error) {
  32. const err: BusinessError = error as BusinessError;
  33. hilog.error(0x0000, '[Home]', `Failed to check access token. Code is ${err.code}, message is ${err.message}`);
  34. }
  35. if (hasPermission) {
  36. let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
  37. systemPasteboard.getData().then((pasteData: pasteboard.PasteData) => {
  38. this.uri = pasteData.getPrimaryUri();
  39. if (this.uri === 'PageABuilder' || this.uri === 'PageBBuilder') {
  40. this.dialogControllerImage.open();
  41. }
  42. }).catch((err: BusinessError) => {
  43. hilog.error(0x0000, '[Home]', 'Failed to get PasteData. Cause: ' + err.message);
  44. })
  45. } else {
  46. hilog.error(0x0000, '[Home]', 'The app is not authorized.');
  47. this.requestPermissionsFn();
  48. }
  49. }

Multi-Format Copy and Paste

As rich text data is accessed across different devices, device A needs to store the same data in multiple styles in the pasteboard so that the copied data can be identified by more devices. For example, the rich text data with images and texts on device A can be stored in the pasteboard in multiple types, such as plain text ('text/plain'), HTML ('text/html'), and image-only ('pixelMap'). Device B reads data of the required type for paste. For details about the data types supported by the pasteboard, see @ohos.pasteboard (Pasteboard).

Creating data and storing it to the pasteboard

Create a PasteData object and obtain the data records PasteDataRecord by calling the PasteData.getRecord() method. In this example, the RichEditor component is used to initialize rich text data. You can select other rich text implementation solutions to read and paste rich text data. Use the getStyledString() method of the RichEditorStyledStringController to obtain data in the rich text box. Add data in multiple types to the data records using the PasteDataRecord.addEntry() method based on the required data type. Finally, call setData() to store the pasteData object that contains multi-format data to the system pasteboard. Note that PasteDataRecord supports only the transmission of a single pixel map. If multiple pixel maps need to be transmitted, you need to configure multiple paste data records in PasteData to save the pixel maps.

Collapse
Word wrap
Dark theme
Copy code
  1. let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, '');
  2. let record: pasteboard.PasteDataRecord = pasteData.getRecord(0);
  3. let styledString: StyledString = this.editController.getStyledString();
  4. let html: string = StyledString.toHtml(styledString);
  5. record.addEntry(pasteboard.MIMETYPE_TEXT_HTML, html);
  6. record.addEntry(pasteboard.MIMETYPE_TEXT_PLAIN, styledString.getString());
  7. const htmlSpans = this.pastePixelMapController.fromStyledString(styledString);
  8. if (!htmlSpans || htmlSpans.length < 1) {
  9. return;
  10. }
  11. for (let i = 0; i < htmlSpans.length; i++) {
  12. const span = htmlSpans[i];
  13. if (!!(span as RichEditorImageSpanResult)?.valuePixelMap) {
  14. const pixelMap = (span as RichEditorImageSpanResult)?.valuePixelMap;
  15. record.addEntry(pasteboard.MIMETYPE_PIXELMAP, pixelMap);
  16. break;
  17. }
  18. }
  19. let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
  20. await systemPasteBoard.setData(pasteData).catch((err: BusinessError) => {
  21. hilog.error(0x0000, '[PasteBoard]',
  22. `Failed to set pastedata. Code: ${err.code}, message: ${err.message}`);
  23. });

Reading data in multiple styles

  1. After data is written to the system pasteboard, obtain temporary authorization to read the pasteboard data by referring to Using PasteButton. You can click the paste button. The getData() method is called to read the content of the system pasteboard.
    Collapse
    Word wrap
    Dark theme
    Copy code
    1. PasteButton()
    2. // ...
    3. .onClick(async (_event: ClickEvent, result: PasteButtonOnClickResult) => {
    4. if (result === PasteButtonOnClickResult.SUCCESS) {
    5. try {
    6. // ...
    7. let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
    8. let gettedData = await systemPasteBoard.getData();
    9. // ...
    10. } catch {
    11. (err: BusinessError) => {
    12. hilog.error(0x0000, '[PasteBoard]', `getData error.code: ${err.code}, message: ${err.message}`);
    13. }
    14. }
    15. }
    16. })
  2. For multi-format data stored in the system pasteboard, call PasteDataRecord.getValidTypes() to get the array of data type intersections between the pasteboard's data types and the input parameter types. The intersection result indicates the requested type exists in the pasteboard. Traverse the data type array and use PasteDataRecord.getData() to obtain the data of the corresponding type. Different types of data are pasted through the controllers of different RichEditor components. If the requested data type does not exist in the pasteboard, the corresponding paste operation cannot be performed, and the content of the corresponding RichEditor component will not be updated.
    Collapse
    Word wrap
    Dark theme
    Copy code
    1. PasteButton()
    2. // ...
    3. .onClick(async (_event: ClickEvent, result: PasteButtonOnClickResult) => {
    4. if (result === PasteButtonOnClickResult.SUCCESS) {
    5. try {
    6. // ...
    7. let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
    8. let gettedData = await systemPasteBoard.getData();
    9. if (gettedData.getRecordCount() <= 0) {
    10. return;
    11. }
    12. let isPastedSuccess: boolean = false;
    13. for (let i = 0; i < gettedData.getRecordCount(); i++) {
    14. let record = gettedData.getRecord(i);
    15. let targetTypes: string[] = [
    16. pasteboard.MIMETYPE_TEXT_HTML,
    17. pasteboard.MIMETYPE_TEXT_PLAIN,
    18. pasteboard.MIMETYPE_PIXELMAP
    19. ];
    20. let tmpTypes: string[] = record.getValidTypes(targetTypes);
    21. for (let j = 0; j < tmpTypes.length; j++) {
    22. switch (tmpTypes[j]) {
    23. case pasteboard.MIMETYPE_TEXT_HTML: {
    24. let htmlRecord = await record.getData(pasteboard.MIMETYPE_TEXT_HTML);
    25. htmlRecord = htmlRecord as string;
    26. this.handlerPasteHtmlData(htmlRecord);
    27. isPastedSuccess = true;
    28. break;
    29. }
    30. case pasteboard.MIMETYPE_TEXT_PLAIN: {
    31. let textRecord = await record.getData(pasteboard.MIMETYPE_TEXT_PLAIN);
    32. textRecord = textRecord as string;
    33. let styledString = new StyledString(textRecord);
    34. this.pasteTextController.setStyledString(styledString);
    35. isPastedSuccess = true;
    36. break;
    37. }
    38. case pasteboard.MIMETYPE_PIXELMAP: {
    39. let pixelMapRecord = await record.getData(pasteboard.MIMETYPE_PIXELMAP);
    40. pixelMapRecord = pixelMapRecord as PixelMap;
    41. this.pastePixelMapController.deleteSpans();
    42. this.pastePixelMapController.addImageSpan(pixelMapRecord, {
    43. imageStyle: {
    44. size: ['57px', '57px']
    45. }
    46. });
    47. isPastedSuccess = true;
    48. break;
    49. }
    50. default:
    51. break;
    52. }
    53. }
    54. }
    55. // ...
    56. } catch {
    57. (err: BusinessError) => {
    58. hilog.error(0x0000, '[PasteBoard]', `getData error.code: ${err.code}, message: ${err.message}`);
    59. }
    60. }
    61. }
    62. })

Progress Bar Access

To enable the pasting of file data, you should use a progress bar to make the abstract process visible, allowing users to quickly check the current progress status.

Creating data and storing it to the pasteboard

Create a record that contains single-format data. Create a text file and save it to the pasteboard.

Collapse
Word wrap
Dark theme
Copy code
  1. let filesDir = uiContext?.getHostContext()!.filesDir;
  2. let fileFullName = filesDir + '/test1.txt';
  3. hilog.info(0x0000, '[ProgressBar]', 'The fileFullName of str is:' + fileFullName);
  4. let file = fileIo.openSync(fileFullName, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
  5. let writeLen = fileIo.writeSync(file.fd, VALUE_TEST_STRING_ELEMENT);
  6. hilog.info(0x0000, '[ProgressBar]', 'The size of str is: ' + writeLen);
  7. fileIo.closeSync(file);
  8. let pasteData =
  9. pasteboard.createData(pasteboard.MIMETYPE_TEXT_URI, fileUri.getUriFromPath(fileFullName));
  10. let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
  11. await systemPasteBoard.setData(pasteData).catch((err: BusinessError) => {
  12. hilog.error(0x0000, '[ProgressBar]',
  13. `Failed to set pastedata. Code: ${err.code}, message: ${err.message}`);
  14. });

Obtaining the pasteboard content and progress

After obtaining the temporary authorization by referring to Using PasteButton, call getDataWithProgress() to obtain the pasteboard content and progress. The params parameter supplies the necessary information required when the app utilizes the file copy feature of the pasteboard, including the target path, file conflict handling option, and progress bar type. ProgressListener listens for progress data changes, used to obtain the current data pasting progress. Once the progress status variable is updated, the Progress component is refreshed.

Collapse
Word wrap
Dark theme
Copy code
  1. PasteButton()
  2. // ...
  3. .onClick(async (event: ClickEvent, result: PasteButtonOnClickResult) => {
  4. if (result === PasteButtonOnClickResult.SUCCESS) {
  5. let systemPasteboard = pasteboard.getSystemPasteboard();
  6. let ProgressListener = (progress: pasteboard.ProgressInfo) => {
  7. this.progressInfo = progress.progress;
  8. hilog.info(0x0000, '[ProgressBar]', 'progressListener success, progress:' + progress.progress);
  9. // ...
  10. }
  11. let dstUri: string = fileUri.getUriFromPath(dstDir);
  12. hilog.info(0x0000, '[ProgressBar]', 'progressBar' + 'dstUri: ' + dstUri + ' length: ' + dstUri.length);
  13. let params: pasteboard.GetDataParams = {
  14. destUri: dstUri,
  15. progressIndicator: pasteboard.ProgressIndicator.NONE,
  16. progressListener: ProgressListener
  17. };
  18. try {
  19. await systemPasteboard.getDataWithProgress(params).then((data) => {
  20. hilog.info(0x0000, '[ProgressBar]', 'getDataWithProgress success');
  21. fileIo.unlink(dstUri);
  22. }).catch((error: BusinessError) => {
  23. hilog.error(0x0000, '[ProgressBar]', 'getDataWithProgress failed,error: ' + JSON.stringify(error));
  24. })
  25. } catch (err) {
  26. hilog.error(0x0000, '[ProgressBar]',
  27. 'getDataWithProgress fail,err: ' + err.code + ',message: ' + err.message);
  28. }
  29. }
  30. })

Sample Code

Search in Best Practices
Enter a keyword.