首页 鸿蒙 正文
  • 本文约10278字,阅读需51分钟
  • 150
  • 0

鸿蒙申请 user_grant 用户授权配置

温馨提示:本文最后更新于2024年10月3日 10:08,若内容或图片失效,请在下方留言或联系博主。

1. 配置声明权限:

1.1 在 module.json5 中添加权限说明(下面代码只展示了部分代码)

"requestPermissions": [
      { //申请网络权限  权限级别:normal  授权方式:system_grant
         "name": "ohos.permission.INTERNET"
      },
      { // 允许应用获取数据网络信息  权限级别:normal  授权方式:system_grant
        "name": "ohos.permission.GET_NETWORK_INFO"
      },
      // -------------------- user_grant --------------------
      {  // 允许应用读取日历信息 权限级别:normal 授权方式:user_grant(会弹窗)
        "name": "ohos.permission.READ_CALENDAR",
        // 授权的原因说明 (在resources/base/element/string)中添加 授权说明
        "reason": "$string:test",
        "usedScene": {}
      },
      {  // 日历写权限
        "name": "ohos.permission.WRITE_CALENDAR",
        "reason": '$string:test',
        "usedScene": {}
      },
      // 相机
      { // 获取相机管理器实例,同步返回结果
        "name": "ohos.permission.CAMERA",
        "reason": '$string:camera',
        "usedScene": {}
      },
      // 麦克风
      { // 获取音频管理器
        "name": "ohos.permission.MICROPHONE",
        "reason": '$string:microphone',
        "usedScene": {}
      },
      // 位置信息(权限组)
      { // 用于获取模糊位置,精确度为 5 公里
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": '$string:location',
        "usedScene": {}
      },
      { // 用于获取精准位置,精准度在米级别
        "name": "ohos.permission.LOCATION",
        "reason": '$string:permission_reason_location',
        "usedScene": {}
      },
      // -------------------- user_grant --------------------
    ]

1.2 为新增的权限 添加中英文说明(src/main/resources/base/element/string.json)

1.3 name = name reson = value

鸿蒙申请 user_grant 用户授权配置

2. 检查当前是否已经授权:

 // 检查是否授权
  checkPermissions(permissions: Permissions[]) {
    // 1. 程序访问控制管理
    const atManager = abilityAccessCtrl.createAtManager()
    // 2. 获取 bundle 包信息
    const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
    // 3. 获取 tokenID 应用唯一标识 通过动态获取来
    const tokenID = bundleInfo.appInfo.accessTokenId
    // 4. 核心 API (因为想一次传递多个 所哟使用一个数组 map 挨个进项权限检测)
    const grantStatus = permissions.map((item) => {
      return atManager.checkAccessTokenSync(tokenID, item)
    })
    // 5. 返回授权状态
    // every (条件) 遍历时所有条件都成立 返回 true 有一个不成立就返回 false
    // i => i == 0 也可以 只不过不标准,使用 abilityAccessCtrl 中有一个枚举值(更加正规)
    return grantStatus.every(i => i == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
  }

3. 动态想用户申请授权:

核心API:atManager.requestPermissionsFromUser()

注意:应用首次申请时才会弹窗。

// 动态申请授权(首次弹窗申请)
  async requestPermissions(permissions: Permissions[]) {
    // 1. 程序访问控制管理
    const atManager = abilityAccessCtrl.createAtManager()
    // 2. 动态向用户申请授权 ---获取上下文   数组格式的授权列表
    const requestResult = await atManager.requestPermissionsFromUser(getContext(), permissions)
    // 3. 判断是否成功---(abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED == 0 )
    const isAuth = requestResult.authResults.every(i => i == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
    // 4. 返回 授权状态
    // Promise.resolve(true)   标识成功, await 后面的代码可以继续执行
    // Promise.reject(false)   标识失败, await 后面的代码不会继续执行(并可以通过过 try()catch() 捕获到 reject的错误 信息)
    return isAuth == true ? Promise.resolve(true) : Promise.reject(false)
  }

4. 处理授权结果:

注意:如果用户在弹窗中拒绝授权,再次调用将没有任何反应。需要引导用户在系统设置页中开启应用权限。

  // 打开系统设置的权限管理页(处理授权结果)
  openPermissionSettingsPage() {
    // 获取上下文
    const context = getContext() as common.UIAbilityContext
    // 获取包信息 (动态获取 AppScope/app.json5 中的配置包信息) -- "bundleName": "com.itheima.hm_guardian",
    const bundleInfo =
      bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
    // 打开系统设置页
    context.startAbility({
      bundleName: 'com.huawei.hmos.settings', // 固定写法CV:设置页的包名
      abilityName: 'com.huawei.hmos.settings.MainAbility', // 固定写法CV:设置页的 ability 名
      uri: 'application_info_entry', // 固定写法CV:打开 设置->应用和元服务
      parameters: {
        // 按照包名打开对应设置页(对应的详情页面) -- "bundleName": "com.itheima.hm_guardian",
        pushParams: bundleInfo.name
      }
    })
  }

PS:附上完整的封装后的代码 使用时直接调用获取

// 封装一个 检测是否授权 动态申请授权的 打开系统设置的权限管理页(处理授权结果)
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';

class PermissionManager {
  // 检查是否授权
  checkPermissions(permissions: Permissions[]) {
    // 1. 程序访问控制管理
    const atManager = abilityAccessCtrl.createAtManager()
    // 2. 获取 bundle 包信息
    const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
    // 3. 获取 tokenID 应用唯一标识 通过动态获取来
    const tokenID = bundleInfo.appInfo.accessTokenId
    // 4. 核心 API (因为想一次传递多个 所哟使用一个数组 map 挨个进项权限检测)
    const grantStatus = permissions.map((item) => {
      return atManager.checkAccessTokenSync(tokenID, item)
    })
    // 5. 返回授权状态
    // every (条件) 遍历时所有条件都成立 返回 true 有一个不成立就返回 false
    // i => i == 0 也可以 只不过不标准,使用 abilityAccessCtrl 中有一个枚举值(更加正规)
    return grantStatus.every(i => i == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
  }

  // 动态申请授权(首次弹窗申请)
  async requestPermissions(permissions: Permissions[]) {
    // 1. 程序访问控制管理
    const atManager = abilityAccessCtrl.createAtManager()
    // 2. 动态向用户申请授权 ---获取上下文   数组格式的授权列表
    const requestResult = await atManager.requestPermissionsFromUser(getContext(), permissions)
    // 3. 判断是否成功---(abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED == 0 )
    const isAuth = requestResult.authResults.every(i => i == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
    // 4. 返回 授权状态
    // Promise.resolve(true)   标识成功, await 后面的代码可以继续执行
    // Promise.reject(false)   标识失败, await 后面的代码不会继续执行(并可以通过过 try()catch() 捕获到 reject的错误 信息)
    return isAuth == true ? Promise.resolve(true) : Promise.reject(false)
  }

  // 打开系统设置的权限管理页(处理授权结果)
  openPermissionSettingsPage() {
    // 获取上下文
    const context = getContext() as common.UIAbilityContext
    // 获取包信息 (动态获取 AppScope/app.json5 中的配置包信息) -- "bundleName": "com.itheima.hm_guardian",
    const bundleInfo =
      bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
    // 打开系统设置页
    context.startAbility({
      bundleName: 'com.huawei.hmos.settings', // 固定写法CV:设置页的包名
      abilityName: 'com.huawei.hmos.settings.MainAbility', // 固定写法CV:设置页的 ability 名
      uri: 'application_info_entry', // 固定写法CV:打开 设置->应用和元服务
      parameters: {
        // 按照包名打开对应设置页(对应的详情页面) -- "bundleName": "com.itheima.hm_guardian",
        pushParams: bundleInfo.name
      }
    })
  }
}
// 导出 Manager
export const permissionManager = new PermissionManager()

对手机本地相册的(获取,删除,获取相册,压缩图片)

// 获取用户相册图片需要申请权限 练习
import { permissionManager } from '../../manager/PermissionManager'
import { promptAction } from '@kit.ArkUI'
import { dataSharePredicates } from '@kit.ArkData'
import dayjs from 'dayjs'
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import { image } from '@kit.ImageKit'
import { fileIo } from '@kit.CoreFileKit'

@Entry
@Component
struct MediaLibraryKitTestPage {
  @State imageUri: string = ''
  @State imageSize: number = 0 // 大小
  @State imageWidth: number = 0 // 宽
  @State imageHeight: number = 0 // 高
  @State imageDateAdded: number = 0 // 图片添加时间
  // 相册
  @State albumCoverUri: string = ''
  @State albumAlbumName: string = ''
  @State albumCount: number = 0

  // 打开后执行
  aboutToAppear() {
    this.requestPermission()
  }

  // 动态申请授权(首次弹窗申请)
  async requestPermission() {
    try {
      await permissionManager.requestPermissions([
        'ohos.permission.READ_IMAGEVIDEO',
        'ohos.permission.WRITE_IMAGEVIDEO'])
    } catch (err) {
      // 未开启弹窗提示
      promptAction.showDialog({
        alignment: DialogAlignment.Center,
        title: '温馨提示',
        message: '手机瘦身功能需要获取权限,请在系统设置中打开相册开关',
        buttons: [
          { text: '取消', color: $r('app.color.font_sub') },
          { text: '立即开启', color: $r('app.color.brand') }
        ]
      })
        .then((res) => {
          // 打开设置页
          if (res.index === 1) {
            permissionManager.openPermissionSettingsPage()
          }
        })
    }
  }

  // 获取相册
  async example() {
    // 获取相册管理模块的实例  用于访问和修改相册中的媒体文件
    let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(getContext())
    // 查询条件
    let predicates = new dataSharePredicates.DataSharePredicates()
    // 获取图片和视频资源 核心 API :getAssets
    let fetchResult = await phAccessHelper.getAssets({
      fetchColumns: [photoAccessHelper.PhotoKeys.SIZE, // 监所条件 指定列名查询 如果该参数为空是时默认查询 uri name photoTyp
        photoAccessHelper.PhotoKeys.WIDTH, // 获取宽
        photoAccessHelper.PhotoKeys.HEIGHT, // 获取高
        photoAccessHelper.PhotoKeys.DATE_ADDED], // 获取 添加时间
      predicates: predicates // 谓词检查 显示过滤条件
    })
    // 获取第一个 图片/视频 资源
    let photoAsset = await fetchResult.getFirstObject()
    // 返回格式(图片的路径):uri格式: "file:// xxxx"
    // AlertDialog.show({ message: JSON.stringify(photoAsset.uri, null, 2) })
    this.imageUri = photoAsset.uri
    // photoAsset.photoType // 媒体类型 (1代表图片 2代表视频)
    // photoAsset.displayName // 媒体名称
    // ========= 获取文件的大小 和 宽高 =========
    this.imageSize = photoAsset.get(photoAccessHelper.PhotoKeys.SIZE) as number // 获取大小 K 转为KB 1000换算
    this.imageWidth = photoAsset.get(photoAccessHelper.PhotoKeys.WIDTH) as number // 获取 宽度
    this.imageHeight = photoAsset.get(photoAccessHelper.PhotoKeys.HEIGHT) as number // 获取 高度
    this.imageDateAdded = photoAsset.get(photoAccessHelper.PhotoKeys.DATE_ADDED) as number // 获取 图片添加时间
  }

  // 删除第一张图片放到回收站里
  async deletePhotos() {
    // 1. 找出待删除的图片资源,类型为 PhotoAsset
    // 1.1 获取相册管理实例
    const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(getContext())
    // 1.2 准备查询条件
    const predicates = new dataSharePredicates.DataSharePredicates()
    // 1.3 获取资源
    const fetchResult = await phAccessHelper.getAssets({
      fetchColumns: [],
      predicates
    })
    // 3. 获取第一张图片资源
    const photoAsset = await fetchResult.getFirstObject()
    AlertDialog.show({ message: JSON.stringify('11111', null, 2) })
    // 2. 调用 deleteAssets API 实现删除 (核心API )
    photoAccessHelper.MediaAssetChangeRequest.deleteAssets(getContext(), [photoAsset])
  }

  // 获取用户相册
  async getAlbums() {
    // 获取相册管理实例
    const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(getContext())
    // getAssets   获取 图片 / 视频
    // getAlbums   获取 相册
    const fetchResult = await phAccessHelper.getAlbums(
      photoAccessHelper.AlbumType.USER, // user 级别
      photoAccessHelper.AlbumSubtype.USER_GENERIC
    )
    const album = await fetchResult.getFirstObject()
    // 相册封面图
    this.albumCoverUri = album.coverUri
    this.albumAlbumName = album.albumName
    this.albumCount = album.count
    // 通过 PhotoAccessHelper.getAlbums 接口获取相册
  }

  // 图片压缩
  async compressImage() {
    // 压缩图片的话 首先获取图片
    await this.example()
    // 打开图片文件
    const file = fileIo.openSync(this.imageUri)
    const imageSource = image.createImageSource(file.fd)
    // 图片打包器(主要用于压缩)
    const imagePacker = image.createImagePacker()
    // 图片压缩核心 API imagePacker.packing
    // arrayBuffer 二进制文件数据流 quality 控制压缩的质量 值越大图片越大
    const arrayBuffer = await imagePacker.packing(imageSource, { format: 'image/jpeg', quality: 20 })
    AlertDialog.show({
      message: '压缩后的大小(单位字节B)' + JSON.stringify(arrayBuffer.byteLength, null, 2)
    }) // 压缩后的大小
    // =====================================
    // 把压缩后的图片写入相册中去
    const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(getContext())
    // 创建新的图片资源  返回图片的资源 uri
    const assetUri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg')
    // 根据 assetUri 打开文件 由于需要写入图片 arrayBuffer 所以再打开时 还需要设置打开模式为 可写
    const assetFile = fileIo.openSync(assetUri, fileIo.OpenMode.READ_WRITE)
    // 往打开的文件内写入 图片 arrayBuffer
    fileIo.writeSync(assetFile.fd, arrayBuffer)
    // 写完后 主动关闭文件
    fileIo.close(assetFile)
  }

  build() {
    Navigation() {
      Scroll() {
        Column({ space: 10 }) {
          // ====== 获取图片 ======
          Button('获取图片信息')
            .onClick(() => {
              this.example() // 调用定义方法 获取相册
            })
          Image(this.imageUri)// 显示图片 本地相册中的
            .width(200)
          Text('图片大小:' + (this.imageSize / 1000) + 'KB')
          Text('图片宽度::' + this.imageWidth)
          Text('图片高度:' + this.imageHeight)
          Text('图片添加时间:' + this.imageDateAdded)
          if (this.imageDateAdded > 0) {
            Text('添加时间格式化:' + dayjs(this.imageDateAdded * 1000).format('YYYY-MM-DD HH:mm:ss'))
          }
          Text('时间戳:' + Date.now())

          // ====== 删除图片 ======
          Button('删除图片 把第一张图片放到回收站')
            .onClick(() => {
              this.deletePhotos() // 删除第一张图片放到回收站里
            })

          // ====== 获取第一个相册 ======
          Button('获取第一个相册')
            .onClick(() => {
              this.getAlbums() // 获取用户相册
            })
          Image(this.albumCoverUri)
          Text('相册名字:' + this.albumAlbumName)
          Text('相册数量:' + this.albumCount.toString())

          // ======  图片压缩  ======
          Button('压缩图片')
            .onClick(() => {
              this.compressImage() // 图片压缩
            })
        }
        .constraintSize({ minHeight: '100%' }) // 设置约束尺寸,组件布局时,进行尺寸范围限制。
      }
      .width('100%')
      .height('100%')
    }
    .title('Media Library Kit(媒体文件管理服务)')
    .titleMode(NavigationTitleMode.Mini)
  }
}
评论