您的当前位置:首页正文

HarmonyOS 实战开发 —— 基于@ohos.pasteboard实现剪贴板功能

2024-11-30 来源:个人技术集锦

?往期笔录记录?:

?
?
?
?
?
?
?
?


场景描述

本模块主要提供管理系统剪贴板的能力,为系统复制、粘贴功能提供支持。系统剪贴板支持对文本、HTML、URI、Want、PixelMap等内容的操作。

场景一:手动点击按钮进行复制粘贴

使用场景:安全控件会在点击粘贴按钮时给业务临时授予ohos.permission.SECURE_PASTE权限,因此使用安全控件访问剪贴板的业务不会受此次非兼容性变更的影响,暂未使用安全控件的业务也可以通过使用安全控件访问剪贴板内容。

效果图

实现方案

核心代码

import { pasteboard, BusinessError } from '@kit.BasicServicesKit';
 
@Entry
@Component
struct Index {
  @State message: string = '';
 
  build() {
    Row() {
      Column({ space: 10 }) {
        TextInput({ placeholder: '请输入验证码', text: this.message })
          .onChange((value: string) => {
            this.message = value;
          })
        PasteButton().onClick((event: ClickEvent, result: PasteButtonOnClickResult) => {
          if (PasteButtonOnClickResult.SUCCESS === result) {
            pasteboard.getSystemPasteboard().getData((err: BusinessError, pasteData: pasteboard.PasteData) => {
              if (err) {
                console.error(`Failed to get paste data. Code is ${err.code}, message is ${err.message}`);
                return;
              }
              this.message = pasteData.getPrimaryText();
            });
          }
        })
        Text("复制")
          .onClick(() => {
            //剪贴板数据对应的MIME类型,可以是常量中已定义的类型,也可以是自定义的MIME类型,开发者可自定义此参数值,
            let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.message);
            let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
            systemPasteboard.setData(pasteData, (err, data) => {
              if (err) {
                console.error('Failed to set PasteData. Cause: ' + err.message);
                return;
              }
              console.info('Succeeded in setting PasteData.');
            });
 
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

场景二:读取系统剪贴板数据并实现页面跳转

使用场景:在应用A复制口令(使用按钮复制),打开应用b后直接读取到系统剪贴板里面的口令,然后跳转到口令里面的页面。

当前可申请剪贴板读取权限的场景与功能:

  1. 2in1设备上的应用均可申请。
  2. 其它设备符合以下场景可申请:
  • 银行卡号复制:银行类应用需要读取剪贴板中的银行卡号自动生成卡片。
  • 口令复制:应用需要读取剪贴板中特定格式口令,自动打开应用内对应页面。

在其他场景下,请应用使用粘贴控件读取剪贴板数据,使用方式参考:场景一。

实现方案

  1. 在module.json5中按需声明ohos.permission.READ_PASTEBOARD权限以访问剪贴板。
  2. 在进行权限申请之前,需要先检查当前应用程序是否已经被授予权限。可以通过调用checkAccessToken()方法来校验当前是否已经授权,如果已经授权,则可以直接访问目标操作,否则需要进行下一步操作,即向用户申请授权。
  3. 通过调用reqPermissionsFromUser( )接口向用户申请授权。
  4. 当权限被授予时,尝试从系统剪贴板获取数据,如果成功则从pasteData中获取主要文本数据。如果权限未被授予,则根据this.permission_state的状态来决定是否向用户请求权限或者进行其他处理。
  5. 页面跳转

核心代码

// 在module.json5中声明ohos.permission.READ_PASTEBOARD权限
"requestPermissions": [
  {
    "name": "ohos.permission.READ_PASTEBOARD",
    "reason": "$string:app_name",
    "usedScene": {
      "abilities": [
        "EntryAbility",
      ],
      "when": "always"
    }
  }
]

// 检查当前应用程序是否已经被授予权限
async function checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
  let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  //初始化grantStatus 变量,表示权限被拒绝的初始状态
  let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
 
  // 获取应用程序的accessTokenID
  let tokenId: number = 0;
  try {
    //获取应用程序信息
    let bundleInfo: bundleManager.BundleInfo =
      await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
    tokenId = appInfo.accessTokenId;
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
  }
 
  // 校验应用是否被授予权限
  try {
    grantStatus = await atManager.checkAccessToken(tokenId, permission);
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
  }
  return grantStatus;
}

// 向用户申请授权
reqPermissionsFromUser(permissions: Array<Permissions> = ['ohos.permission.READ_PASTEBOARD']): void {
  let context = getContext(this) as common.UIAbilityContext;
  let atManager = abilityAccessCtrl.createAtManager();
  // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
  atManager.requestPermissionsFromUser(context, permissions).then((data) => {
    let grantStatus: Array<number> = data.authResults;
    let length: number = grantStatus.length;
    for (let i = 0; i < length; i++) {
      if (grantStatus[i] === 0) {
        // 用户授权,可以继续访问目标操作
        this.permission_state = true;
      } else {
        // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能
        console.error("user did not grant!")
        this.permission_state = false;
      }
    }
  })
}

//获取系统剪贴板的内容
async getPaste(): Promise < string > {
  const permissions: Array<Permissions> =['ohos.permission.READ_PASTEBOARD'];
  // 调用前面定义的 checkAccessToken 函数来检查是否已授予读取剪贴板的权限
  let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions[0]);
  //检查 grantStatus 是否为 PERMISSION_GRANTED,即检查是否已授予权限
  if(grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
    try {
      //获取系统剪贴板对象
      let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();
      let pasteData = await systemPasteboard.getData();
      return pasteData.getPrimaryText();
    } catch (err) {
      hilog.error(0x0000, 'testTag', '%{public}s', `get oaid by promise catch error: ${err.code} ${err.message}`);
      return '';
    }
  } else {
    // 申请权限
    if (this.permission_state) {
      this.reqPermissionsFromUser(permissions);
    }
    return '';
  }
}

//页面跳转
function onJumpClick(pageJump: string): void {
  router.pushUrl({
    url: pageJump // 目标url
  }, router.RouterMode.Standard, (err) => {
    if (err) {
      console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
      return;
    }
    console.info('Invoke pushUrl succeeded.');
  });
}

//口令识别
let text = await this.getPaste();
if (text.includes('口令')) {
  onJumpClick('pages/Second');
} else {
  onJumpClick('pages/Third');
}
显示全文