鸿蒙申请 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
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)
}
}