HarmonyOS 鸿蒙Next应用如何读取剪切板内容(安全控件和自读取)

HarmonyOS 鸿蒙Next应用如何读取剪切板内容(安全控件和自读取)

鸿蒙应用如何读取剪切板内容(安全控件和自读取)

4 回复

通过[@ohos.pasteboard(剪贴板)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-pasteboard)模块提供的管理系统剪贴板能力,可以为系统复制、粘贴功能提供支持。系统剪贴板支持对文本、HTML、URI、Want、PixelMap等内容的操作。

示例如下:

  1. 使用pasteboard.createData创建剪贴板内容对象,传入数据类型和内容。
  2. 使用pasteboard.getSystemPasteboard获取系统剪贴板对象。
  3. 通过获取系统剪贴板对象并调用setData方法,将数据写入剪贴板。调用getData方法,读取剪贴板的内容。
import { pasteboard } from '@kit.BasicServicesKit';
@Entry
@Component
struct CopyText {
  private textContent: string = 'Copy me';
  aboutToAppear(): void {
    AppStorage.setOrCreate('context', this.getUIContext());
  }
  build() {
    Column() {
      Text(this.textContent)
        .fontSize(20)
        .borderRadius(9)
        .borderWidth(1)
        .padding({
          left: 8,
          right: 8
        })
        .fontColor($r('sys.color.ohos_id_color_text_primary'))
        .fontWeight(FontWeight.Medium)
        .opacity($r('sys.float.ohos_id_alpha_content_secondary'))
        .onClick(() => copyText(this.textContent))
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignSelf(ItemAlign.Center)
  }
}
/*
 * Copy the text to the system clipboard
 * @param text - text
 * @returns void
 */
function copyText(text: string) {
  const uiContext: UIContext | undefined = AppStorage.get('context');
  // Create clipboard content object
  const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);
  // Get system clipboard object
  const systemPasteboard = pasteboard.getSystemPasteboard();
  systemPasteboard.setData(pasteboardData); // Put data into clipboard
  systemPasteboard.getData().then((data) => { // Read clipboard content
    if (data) {
      uiContext?.getPromptAction().showToast({ message: 'Copy succeeded' });
    } else {
      uiContext?.getPromptAction().showToast({ message: 'Copy failed' });
    }
  })
}

更多关于HarmonyOS 鸿蒙Next应用如何读取剪切板内容(安全控件和自读取)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在鸿蒙中实现剪切板很简单,目前有两种方式,分别为:1.使用粘贴安全控件 2.申请用户授权,应用自己读取

解决方案

1.使用粘贴安全控件 鸿蒙系统提供了PasteButton安全组件,通过该按钮组件,用户点击即认为授权,不需要三方应用再自己申请权限。通过点击后的回调,再通过剪切板读取其中的内容pasteboard.getSystemPasteboard().getData。需要注意的时候,该按钮点击授权为临时授权,再app关闭,切到后台后授权就没有了,需要用户重新点击按钮。

所以一般该操作的设计都是,点击按钮后马上去读取剪切板内容,减少逻辑读取的链路。目前看读取虽然是异步,但是读取速度还是很快,影响不大

        PasteButton()
          .padding({top: 12, bottom: 12, left: 24, right: 24})
          .onClick((event: ClickEvent, result: PasteButtonOnClickResult) => {
            console.log(this.TAG, " PasteboardPage PasteButton result: " + JSON.stringify(result) + " event: " + JSON.stringify(event));
            if (PasteButtonOnClickResult.SUCCESS === result) {
              pasteboard.getSystemPasteboard().getData((err: BusinessError, pasteData: pasteboard.PasteData) => {
                console.log(this.TAG, " PasteboardPage getData err: " + JSON.stringify(err) + " pasteData: " + JSON.stringify(pasteData));
                if (err) {
                  return;
                }
                this.message = pasteData.getPrimaryText();
              });
            }
          })

----按钮样式需要显著,并且没有故意遮挡,透明度,UI叠加,误导用户等因素会导致按钮回调授权失败。原则是让用户能清晰感知此按钮是粘贴按钮。

2.申请用户授权,应用自读取 需要申请"ohos.permission.READ_PASTEBOARD"权限。该权限是管制权限,需要你的应用去通过场景申请,比如你有口令的场景,就可以申请该权限。

------如果应用涉及获取受限权限,在应用发布上架时,应用市场(AGC)将根据应用的使用场景审核是否可以使用对应的受限权限。如不符合,应用的上架申请将被驳回,审核方式请见发布HarmonyOS应用。

当你申请了该权限后,就不需要安全控件,直接通过系统剪切板可以读取到其中的内容。 需要注意的是,申请的是对应场景,比如口令场景。但是系统并不会对你读取的内容做出过滤,你能读取到用户复制的所有内容,并不只是口令。

DEMO示例:

PasteboardPage.ets

import { hilog } from '@kit.PerformanceAnalysisKit';
import { abilityAccessCtrl, bundleManager, common } from '@kit.AbilityKit';
import { pasteboard, BusinessError } from '@kit.BasicServicesKit';

/**
 * 剪切板
 */
@Entry
@Component
struct PasteboardPage {

  private TAG: string = "PasteboardPage";

  @State message: string = '';

  private requestPermissions(context: common.Context): void {
    // 进入页面时,向用户请求授权广告跨应用关联访问权限
    const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    try {
      atManager.requestPermissionsFromUser(context, ["ohos.permission.READ_PASTEBOARD"]).then((data) => {
        if (data.authResults[0] === 0) {
          this.readPasteBoardData();
        } else {
          hilog.error(0x0000, 'testTag', '%{public}s', 'user rejected');
        }
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, 'testTag', '%{public}s', `request permission failed, error: ${err.code} ${err.message}`);
      })
    } catch (err) {
      hilog.error(0x0000, 'testTag', '%{public}s', `catch err->${err.code}, ${err.message}`);
    }
  }


  onClickReadPasteboard = ()=>{
    this.requestPermissions(getContext());
  }

  private readPasteBoardData(){
    let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
    systemPasteboard.getData((err: BusinessError, pasteData: pasteboard.PasteData) => {
      if (err) {
        console.error('Failed to get PasteData. Cause: ' + err.message);
        return;
      }
      let text: string = pasteData.getPrimaryText();
      this.message = text;
    });
  }


  build() {
    Column({ space: 10 }) {
      Text("点击启用授权读取剪切板")
        .onClick(this.onClickReadPasteboard)

        TextInput({ placeholder: '请输入验证码', text: this.message })
          .onChange((value: string, previewText?: PreviewText)=>{
            console.log(this.TAG, " TextInput onChange value: " + JSON.stringify(value) + " previewText: " + JSON.stringify(previewText));
            this.message = value;
          })
        PasteButton()
          .padding({top: 12, bottom: 12, left: 24, right: 24})
          .onClick((event: ClickEvent, result: PasteButtonOnClickResult) => {
            console.log(this.TAG, " PasteboardPage PasteButton result: " + JSON.stringify(result) + " event: " + JSON.stringify(event));
            if (PasteButtonOnClickResult.SUCCESS === result) {
              pasteboard.getSystemPasteboard().getData((err: BusinessError, pasteData: pasteboard.PasteData) => {
                console.log(this.TAG, " PasteboardPage getData err: " + JSON.stringify(err) + " pasteData: " + JSON.stringify(pasteData));
                if (err) {
                  return;
                }
                this.message = pasteData.getPrimaryText();
              });
            }
          })
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
  }
}

module.json5

  "requestPermissions": [
  
      {
        "name": "ohos.permission.READ_PASTEBOARD",
        "usedScene": {
          "abilities": [
            "FormAbility"
          ],
          "when": "inuse"
        },
        "reason": "$string:module_desc",
      },

    ]

在HarmonyOS Next中,读取剪切板内容主要涉及安全控件和自读取两种方式。

安全控件方式:使用PasteDataPasteboard类。通过Pasteboard.getSystemPasteboard()获取系统剪切板实例,再调用getPasteData()获取数据。可读取文本、URI等类型。

自读取方式:应用内通过TextInputTextArea等组件,设置onPaste回调监听粘贴事件,从事件参数中直接获取内容。

两种方式均需在module.json5中声明ohos.permission.PASTEBOARD_READ权限。

在HarmonyOS Next中,读取剪切板内容主要通过@ohos.miscServices.pasteboard(剪贴板服务)模块实现,其设计遵循严格的安全和隐私规范。系统提供了两种主要方式:通过安全系统控件(如TextInput)自动读取,以及应用主动通过API读取。

1. 通过安全系统控件自动读取

这是最简单和安全的方式。当用户将焦点置于TextInputTextArea等系统提供的文本输入组件时,如果应用获得了焦点且剪切板中有文本内容,系统会自动显示粘贴提示。用户点击粘贴后,内容会填入控件。

这种方式无需应用主动调用读取API,完全由系统UI控件管理,符合最小权限原则,是推荐的首选方式。

2. 应用主动通过API读取

当需要以编程方式读取剪切板内容进行分析或处理时,需要使用pasteboard模块的API。主要步骤如下:

a. 申请权限module.json5文件中声明ohos.permission.PASTEBOARD权限。该权限级别为normal,但系统会向用户明示应用访问剪切板的行为。

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.PASTEBOARD"
      }
    ]
  }
}

b. 导入模块并创建PasteData对象

import { pasteboard } from '@kit.MiscServicesKit';

// 获取系统剪贴板单例
let systemPasteboard = pasteboard.getSystemPasteboard();

// 读取剪贴板内容,返回Promise<PasteData>
systemPasteboard.getPasteData().then((pasteData: pasteboard.PasteData) => {
  if (pasteData !== null) {
    // 成功获取到剪贴板数据
    // 接下来可以处理PasteData
  } else {
    // 剪贴板为空或数据不可访问
  }
}).catch((err: Error) => {
  // 处理错误,例如权限不足
});

c. 处理PasteData PasteData对象可能包含多种类型的数据(如文本、HTML、URI等)。通常读取文本内容:

if (pasteData !== null) {
  // 获取第一条文本记录
  let textRecord: pasteboard.PasteDataRecord = pasteData.getPrimaryText();
  if (textRecord !== null) {
    let clipText: string = textRecord.getPlainText().toString();
    // 使用clipText
  }

  // 或者遍历所有记录
  let count: number = pasteData.getRecordCount();
  for (let i = 0; i < count; i++) {
    let record: pasteboard.PasteDataRecord = pasteData.getRecordAt(i);
    let mimeType: string = record.mimeType;
    if (mimeType === pasteboard.MIMETYPE_TEXT_PLAIN) {
      let text: string = record.getPlainText().toString();
      // 处理文本
    }
    // 可检查其他MIME类型,如图片URI等
  }
}

关键安全机制

  • 显式用户感知:首次读取时,系统会触发提示,告知用户某应用正在访问剪切板。
  • 数据标签与来源检查PasteData可能包含来源应用信息,系统会标记敏感数据。
  • 后台限制:应用在后台时,对剪切板的访问会受到严格限制,防止静默读取。

总结

  • 优先使用系统文本控件实现粘贴功能,最安全便捷。
  • 需要主动读取时,务必声明权限,并通过getSystemPasteboard().getPasteData()异步获取。
  • 处理返回的PasteData对象时,注意检查数据是否存在及MIME类型。

这种设计确保了剪切板数据只能在用户知情或主动操作的情况下被访问,平衡了功能需求与隐私安全。

回到顶部