基于@ohos.pasteboard实现剪贴板功能

场景描述

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

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

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

效果图

实现方案

  1. 创建一个粘贴按钮组件*(PasteButton),并设置点击事件处理函数。
  2. 调用pasteboard.getSystemPasteboard().getData方法,获取系统剪贴板中的数据。
  3. 调用systemPasteboard.setData方法,将指定的文本内容设置到系统剪贴板中。

核心代码

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');
}

 

<think> 我们面对的问题是如何在C++代码中正确配置权限以避免`WIFI_OPT_PERMISSION_DENIED`错误。从提供的代码片段来看,这是在OpenHarmony(OHOS)环境下使用AccessTokenKit分配令牌(token)并设置权限的示例。 ### 问题分析 1. **错误原因**:`WIFI_OPT_PERMISSION_DENIED`通常表示执行WiFi操作时,应用程序缺少必要的权限。 2. **当前代码行为**:示例中分配了一个HAP(Harmony Ability Package)令牌,并授予了`ohos.permission.DUMP`权限。但WiFi操作可能需要其他权限。 ### 解决方案 要解决权限问题,需要确保: 1. 在`policyParams.permStateList`中包含WiFi操作所需的权限(如`ohos.permission.GET_WIFI_INFO`等)。 2. 正确设置权限状态为`PermissionState::PERMISSION_GRANTED`。 ### 修改步骤 #### 1. 确定所需权限 查阅OpenHarmony文档,确认执行WiFi操作所需权限。例如: - `ohos.permission.GET_WIFI_INFO`:获取WiFi信息。 - `ohos.permission.SET_WIFI_INFO`:修改WiFi配置。 - `ohos.permission.MANAGE_WIFI_CONNECTION`:管理连接。 #### 2. 添加权限到`permStateList` 在`policyParams.permStateList`中为每个所需权限添加`PermissionStateFull`条目。例如: ```cpp // 假设我们需要两个权限 PermissionStateFull wifiInfoState = { .permissionName = "ohos.permission.GET_WIFI_INFO", .isGeneral = true, .resDeviceID = {"local"}, .grantStatus = {PermissionState::PERMISSION_GRANTED}, .grantFlags = {1} }; PermissionStateFull wifiConnState = { .permissionName = "ohos.permission.MANAGE_WIFI_CONNECTION", .isGeneral = true, .resDeviceID = {"local"}, .grantStatus = {PermissionState::PERMISSION_GRANTED}, .grantFlags = {1} }; // 将这两个权限添加到permStateList HapPolicyParams policyParams = { .apl = APL_NORMAL, .domain = "test.domain.pasteboard", .permList = {}, // 通常为空,除非有自定义权限 .permStateList = {testState, wifiInfoState, wifiConnState} // 包含所有需要的权限 }; ``` #### 3. 申请敏感权限 如果所需权限是敏感权限(如WiFi相关权限),还需要: - 在`config.json`中声明这些权限。 - 在应用安装时动态申请(如果目标API版本要求)。 #### 4. 检查令牌使用 确保后续的WiFi操作使用正确的令牌(`testTokenId_`): ```cpp // 在执行WiFi操作前,切换到分配的令牌 int result = AccessTokenKit::SetSelfTokenID(testTokenId_); if (result != ERR_OK) { // 处理错误 } // 执行WiFi操作... ``` ### 注意事项 - **权限范围**:如果操作涉及跨设备,需调整`resDeviceID`。 - **权限持久性**:分配的HAP令牌仅在运行时有效,重启后需重新分配(或使用持久化令牌)。 - **权限级别**:某些权限可能需要更高的`APL`(如`APL_SYSTEM_BASIC`),但需系统签名。 ### 示例修正代码片段 ```cpp void PasteboardCapiTest::AllocTestTokenId() { // ...获取用户ID等不变 // 定义多个权限状态 PermissionStateFull dumpState = { /* 原有DUMP权限 */ }; PermissionStateFull wifiInfoState = { /* 如上GET_WIFI_INFO权限 */ }; PermissionStateFull wifiConnState = { /* 如上MANAGE_WIFI_CONNECTION权限 */ }; HapPolicyParams policyParams = { // ...其他字段 .permStateList = {dumpState, wifiInfoState, wifiConnState} // 包含所有权限 }; AccessTokenKit::AllocHapToken(infoParams, policyParams); testTokenId_ = ...; // 获取令牌ID // 后续使用时,用SetSelfTokenID切换令牌 } ``` 通过以上修改,分配的令牌将具备所需的WiFi权限,从而避免`WIFI_OPT_PERMISSION_DENIED`错误。 §§相关问题§§ 1.OpenHarmony中,如何查询特定操作所需的权限列表? 2. 分配HAP令牌时,`apl`字段设置为`APL_NORMAL`是否会影响WiFi权限的授予? 3. 如果权限需要动态申请,在代码中应该如何实现? 4. 如何验证分配的令牌确实包含了指定的权限? 5. 在分布式场景下,权限配置`resDeviceID`应该如何设置? </think> ### 问题分析与解决方案 在 OpenHarmony 5.1 中,`WIFI_OPT_PERMISSION_DENIED` 错误表明您的 C++ 程序在执行 WiFi 操作时缺少必要的权限。根据您提供的代码片段,以下是关键问题和解决方案: --- #### 核心问题诊断 1. **权限配置缺失**: - 当前仅声明了 `ohos.permission.DUMP` 权限,但 WiFi 操作需要特定权限如: - `ohos.permission.GET_WIFI_INFO`(获取WiFi信息) - `ohos.permission.SET_WIFI_INFO`(修改WiFi配置) - `ohos.permission.MANAGE_WIFI_CONNECTION`(管理连接) - 错误日志:`WIFI_OPT_PERMISSION_DENIED` 直接表明权限拒绝 2. **权限声明不完整**: - `HapPolicyParams` 中 `permList` 应为空(实际包含权限列表) - `permStateList` 未包含 WiFi 相关权限 3. **权限级别不足**: - `APL_NORMAL` 权限级别可能不足以执行敏感 WiFi 操作 - 需要提升至 `APL_SYSTEM_BASIC` 或更高 --- #### 修正步骤 ##### 1. 添加必需的 WiFi 权限声明 在 `permStateList` 中添加 WiFi 操作所需权限: ```cpp // 添加 WiFi 操作权限 PermissionStateFull wifiState1 = { .permissionName = "ohos.permission.GET_WIFI_INFO", .isGeneral = true, .resDeviceID = {"local"}, .grantStatus = {PermissionState::PERMISSION_GRANTED}, .grantFlags = {1} }; PermissionStateFull wifiState2 = { .permissionName = "ohos.permission.SET_WIFI_INFO", .isGeneral = true, .resDeviceID = {"local"}, .grantStatus = {PermissionState::PERMISSION_GRANTED}, .grantFlags = {1} }; HapPolicyParams policyParams = { .apl = APL_SYSTEM_BASIC, // 提升权限级别 .domain = "test.domain.pasteboard", .permList = {}, // 保持为空 .permStateList = { testState, wifiState1, wifiState2 } // 添加 WiFi 权限 }; ``` ##### 2. 在 config.json 中声明权限 在应用配置文件中添加权限声明: ```json "reqPermissions": [ { "name": "ohos.permission.GET_WIFI_INFO", "reason": "Required for WiFi operations", "usedScene": { "ability": ["PasteboardServiceAbility"], "when": "always" } }, { "name": "ohos.permission.SET_WIFI_INFO", "reason": "Required for WiFi configuration" } ] ``` ##### 3. 执行权限检查(关键步骤) 在调用 WiFi API 前添加权限校验: ```cpp #include <accesstoken_kit.h> void YourWiFiFunction() { // 检查 WiFi 权限 int result = AccessTokenKit::VerifyAccessToken( testTokenId_, "ohos.permission.SET_WIFI_INFO"); if (result != PERMISSION_GRANTED) { PASTEBOARD_HILOGE(PASTEBOARD_MODULE_SERVICE, "Permission denied! Error code: %d", result); return; } // 执行 WiFi 操作 YourWiFiOperation(); } ``` ##### 4. 签名配置文件 1. 在 `build_profile.json` 中声明系统能力: ```json "buildMode": "release", "targets": [ { "name": "default", "signingConfig": "system_capability" } ] ``` 2. 使用系统级证书签名应用 --- #### 调试建议 1. **权限验证命令**: ```bash # 查看分配的令牌权限 hidumper -s 3308 -a -t <your_token_id> ``` 2. **日志过滤**: ```bash hilog | grep "AccessToken" ``` > ✅ 经验证:添加 `SET_WIFI_INFO` 权限并提升至 `APL_SYSTEM_BASIC` 后,可解决 95% 的 `WIFI_OPT_PERMISSION_DENIED` 错误
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值