Intelligent Assistant
Chat with our virtual assistant to get answers promptly.
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.
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.
The development process of the cross-device pasteboard includes the following steps:
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.
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().
- let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_URI, 'PageABuilder');
- let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
- 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.
- onPageShow(): void {
- let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
- try {
- let result: boolean = systemPasteboard.hasDataType(pasteboard.MIMETYPE_TEXT_URI);
- if (result) {
- this.checkPermissionGrant();
- }
- } catch (error) {
- const err: BusinessError = error as BusinessError;
- hilog.error(0x0000, '[Home]',
- `Check data type failed. Code is ${err.code}, message is ${err.message}`);
- }
- }
-
- checkPermissionGrant(): void {
- let hasPermission = false;
- let tokenId: number = 0;
- try {
- let bundleInfo: bundleManager.BundleInfo =
- bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
- let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
- tokenId = appInfo.accessTokenId;
- } catch (error) {
- const err: BusinessError = error as BusinessError;
- hilog.error(0x0000, '[Home]',
- `Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
- }
-
- try {
- let atManager = abilityAccessCtrl.createAtManager();
- let approximatelyLocation = atManager.checkAccessTokenSync(tokenId, 'ohos.permission.READ_PASTEBOARD');
- hasPermission = approximatelyLocation === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
- } catch (error) {
- const err: BusinessError = error as BusinessError;
- hilog.error(0x0000, '[Home]', `Failed to check access token. Code is ${err.code}, message is ${err.message}`);
- }
- if (hasPermission) {
- let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
- systemPasteboard.getData().then((pasteData: pasteboard.PasteData) => {
- this.uri = pasteData.getPrimaryUri();
- if (this.uri === 'PageABuilder' || this.uri === 'PageBBuilder') {
- this.dialogControllerImage.open();
- }
- }).catch((err: BusinessError) => {
- hilog.error(0x0000, '[Home]', 'Failed to get PasteData. Cause: ' + err.message);
- })
- } else {
- hilog.error(0x0000, '[Home]', 'The app is not authorized.');
- this.requestPermissionsFn();
- }
- }
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.
- let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, '');
- let record: pasteboard.PasteDataRecord = pasteData.getRecord(0);
-
- let styledString: StyledString = this.editController.getStyledString();
- let html: string = StyledString.toHtml(styledString);
-
- record.addEntry(pasteboard.MIMETYPE_TEXT_HTML, html);
- record.addEntry(pasteboard.MIMETYPE_TEXT_PLAIN, styledString.getString());
- const htmlSpans = this.pastePixelMapController.fromStyledString(styledString);
- if (!htmlSpans || htmlSpans.length < 1) {
- return;
- }
- for (let i = 0; i < htmlSpans.length; i++) {
- const span = htmlSpans[i];
- if (!!(span as RichEditorImageSpanResult)?.valuePixelMap) {
- const pixelMap = (span as RichEditorImageSpanResult)?.valuePixelMap;
- record.addEntry(pasteboard.MIMETYPE_PIXELMAP, pixelMap);
- break;
- }
- }
-
- let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
- await systemPasteBoard.setData(pasteData).catch((err: BusinessError) => {
- hilog.error(0x0000, '[PasteBoard]',
- `Failed to set pastedata. Code: ${err.code}, message: ${err.message}`);
- });
Reading data in multiple styles
- PasteButton()
- // ...
- .onClick(async (_event: ClickEvent, result: PasteButtonOnClickResult) => {
- if (result === PasteButtonOnClickResult.SUCCESS) {
- try {
- // ...
- let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
- let gettedData = await systemPasteBoard.getData();
- // ...
- } catch {
- (err: BusinessError) => {
- hilog.error(0x0000, '[PasteBoard]', `getData error.code: ${err.code}, message: ${err.message}`);
- }
- }
- }
- })
- PasteButton()
- // ...
- .onClick(async (_event: ClickEvent, result: PasteButtonOnClickResult) => {
- if (result === PasteButtonOnClickResult.SUCCESS) {
- try {
- // ...
- let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
- let gettedData = await systemPasteBoard.getData();
- if (gettedData.getRecordCount() <= 0) {
- return;
- }
-
- let isPastedSuccess: boolean = false;
- for (let i = 0; i < gettedData.getRecordCount(); i++) {
- let record = gettedData.getRecord(i);
- let targetTypes: string[] = [
- pasteboard.MIMETYPE_TEXT_HTML,
- pasteboard.MIMETYPE_TEXT_PLAIN,
- pasteboard.MIMETYPE_PIXELMAP
- ];
- let tmpTypes: string[] = record.getValidTypes(targetTypes);
- for (let j = 0; j < tmpTypes.length; j++) {
- switch (tmpTypes[j]) {
- case pasteboard.MIMETYPE_TEXT_HTML: {
- let htmlRecord = await record.getData(pasteboard.MIMETYPE_TEXT_HTML);
- htmlRecord = htmlRecord as string;
- this.handlerPasteHtmlData(htmlRecord);
- isPastedSuccess = true;
- break;
- }
- case pasteboard.MIMETYPE_TEXT_PLAIN: {
- let textRecord = await record.getData(pasteboard.MIMETYPE_TEXT_PLAIN);
- textRecord = textRecord as string;
- let styledString = new StyledString(textRecord);
- this.pasteTextController.setStyledString(styledString);
- isPastedSuccess = true;
- break;
- }
- case pasteboard.MIMETYPE_PIXELMAP: {
- let pixelMapRecord = await record.getData(pasteboard.MIMETYPE_PIXELMAP);
- pixelMapRecord = pixelMapRecord as PixelMap;
- this.pastePixelMapController.deleteSpans();
- this.pastePixelMapController.addImageSpan(pixelMapRecord, {
- imageStyle: {
- size: ['57px', '57px']
- }
- });
- isPastedSuccess = true;
- break;
- }
- default:
- break;
- }
- }
- }
- // ...
- } catch {
- (err: BusinessError) => {
- hilog.error(0x0000, '[PasteBoard]', `getData error.code: ${err.code}, message: ${err.message}`);
- }
- }
- }
- })
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.
- let filesDir = uiContext?.getHostContext()!.filesDir;
- let fileFullName = filesDir + '/test1.txt';
- hilog.info(0x0000, '[ProgressBar]', 'The fileFullName of str is:' + fileFullName);
- let file = fileIo.openSync(fileFullName, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
- let writeLen = fileIo.writeSync(file.fd, VALUE_TEST_STRING_ELEMENT);
- hilog.info(0x0000, '[ProgressBar]', 'The size of str is: ' + writeLen);
- fileIo.closeSync(file);
- let pasteData =
- pasteboard.createData(pasteboard.MIMETYPE_TEXT_URI, fileUri.getUriFromPath(fileFullName));
-
- let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
- await systemPasteBoard.setData(pasteData).catch((err: BusinessError) => {
- hilog.error(0x0000, '[ProgressBar]',
- `Failed to set pastedata. Code: ${err.code}, message: ${err.message}`);
- });
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.
- PasteButton()
- // ...
- .onClick(async (event: ClickEvent, result: PasteButtonOnClickResult) => {
- if (result === PasteButtonOnClickResult.SUCCESS) {
- let systemPasteboard = pasteboard.getSystemPasteboard();
- let ProgressListener = (progress: pasteboard.ProgressInfo) => {
- this.progressInfo = progress.progress;
- hilog.info(0x0000, '[ProgressBar]', 'progressListener success, progress:' + progress.progress);
- // ...
- }
- let dstUri: string = fileUri.getUriFromPath(dstDir);
- hilog.info(0x0000, '[ProgressBar]', 'progressBar' + 'dstUri: ' + dstUri + ' length: ' + dstUri.length);
- let params: pasteboard.GetDataParams = {
- destUri: dstUri,
- progressIndicator: pasteboard.ProgressIndicator.NONE,
- progressListener: ProgressListener
- };
- try {
- await systemPasteboard.getDataWithProgress(params).then((data) => {
- hilog.info(0x0000, '[ProgressBar]', 'getDataWithProgress success');
- fileIo.unlink(dstUri);
- }).catch((error: BusinessError) => {
- hilog.error(0x0000, '[ProgressBar]', 'getDataWithProgress failed,error: ' + JSON.stringify(error));
- })
- } catch (err) {
- hilog.error(0x0000, '[ProgressBar]',
- 'getDataWithProgress fail,err: ' + err.code + ',message: ' + err.message);
- }
- }
- })