首页 > 其他分享 >《鸿蒙/Harmony | 开发日志》请求用户权限 & 打开应用设置界面

《鸿蒙/Harmony | 开发日志》请求用户权限 & 打开应用设置界面

时间:2024-09-14 13:52:23浏览次数:14  
标签:abilityAccessCtrl const err 鸿蒙 await Harmony error 日志 权限

鸿蒙的请求用户权限相对比较好用,下面的代码,基本是来源华为官方的文档,只需要封装一下,挺好用。

鸿蒙的权限分类

鸿蒙的权限分为两种

  1. 系统权限(直接配置文件配置申请,不需要询问用户)
  2. 需要用户手动确认的权限(必须提示用户主动授权)

参考:

应用权限管控概述

在配置文件中设置需要申请的权限

在配置文件module.json5中配置requestPermissions。同时一定要配置usedScenereason。注意reason字段,不可以直接写字符串原因。因为要求这个是可以翻译的字段。

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  }, {
    "name": "ohos.permission.LOCATION",
    "reason": "$string:location_reason",
    "usedScene": {
      "when": "inuse"
    }
  }, {
    "name": "ohos.permission.APPROXIMATELY_LOCATION",
    "reason": "$string:location_reason",
    "usedScene": {
      "when": "inuse"
    }
  }
]

参考:

  1. 申请应用权限

弹出界面向用户申请权限

检查有没有之前是不是已经拥有权限

async function checkPermissionGrant(permission: Permissions): Promise<boolean> {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    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) {
        const 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) {
        const err: BusinessError = error as BusinessError;
        console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
    }

    return grantStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
}


//

checkPermissionGrant("ohos.permission.LOCATION") //true or false

第一次请求用户权限

如上图,就是弹出给用户授权的界面。requestPermissionsFromUser方法就是弹出上面的界面的。注意,如果用户拒绝了,再此调用requestPermissionsFromUser是不会再此弹出来的。

async function firstReqPermissionFormUser(permissions: Array<Permissions>, context: common.UIAbilityContext) {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();

    const data = await atManager.requestPermissionsFromUser(context, permissions)

    let grantStatus: Array<number> = data.authResults;

    return grantStatus.every(s => s == 0)
}


//

const p = "ohos.permission.LOCATION"
let isAuth = await checkPermissionGrant(p)
if(!isAuth) {
	isAuth = await firstReqPermissionFormUser([p], context); // true or false
}

当用户第一次申请权限的时候,用户拒绝了,下次再次调用此代码。将不会弹出界面让用户选择了。

如果还需要选项,就需要引导用户去设置里面授权。

二次向用户申请授权

requestPermissionOnSetting打开应用设置权限界面,可以参考打开应用设置权限界面。此节不详细说明。

第一次被拒绝后,那么需要引导用户去设置中,授权。

const secondAuthResults = await atManager.requestPermissionOnSetting(context, permissions)
const isSecondAuth = secondAuthResults.every(s => s == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)

注意在这里设置会有三种情况。

设置中授权界面一般会有三个选项:

  1. 仅使用期间允许
  2. 每次使用询问
  3. 禁止

只有第一个选项,结果会返回授权了。第三个选项,好理解,就是不授权。但是要注意了,第二个选项也是返回不授权。但是我们明显是知道用户是授权了的。所以上面的代码返回false之后,还需要再次调用第一次授权的代码,再次询问用户是否授权了。

下面是完整二次授权代码

const secondAuthResults = await atManager.requestPermissionOnSetting(context, permissions)
const isSecondAuth = secondAuthResults.every(s => s == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
if (!isSecondAuth) {
		const isAuth = await firstReqPermissionFormUser(permissions, context)
		resolve(isAuth)
} else {
		resolve(true)
}

完整请求权限工具类代码

import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';
import { UIContext } from '@kit.ArkUI';

import { BusinessError } from '@kit.BasicServicesKit';

async function checkPermissionGrant(permission: Permissions): Promise<boolean> {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    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) {
        const 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) {
        const err: BusinessError = error as BusinessError;
        console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
    }

    return grantStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
}


async function firstReqPermissionFormUser(permissions: Array<Permissions>, context: common.UIAbilityContext) {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();

    const data = await atManager.requestPermissionsFromUser(context, permissions)

    let grantStatus: Array<number> = data.authResults;

    return grantStatus.every(s => s == 0)
}

async function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext,
    uiContext: UIContext) {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    try {
        // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
        const isAuth = await firstReqPermissionFormUser(permissions, context)
        if (isAuth) {
            return true
        }

        return await new Promise((resolve, reject) => {
            uiContext.showAlertDialog({
                title: "系统提示",
                message: "必须要授权才能使用,是否前往应用进行授权",
                autoCancel: false,
                primaryButton: {
                    value: "取消",
                    action: () => {
                        resolve(false)
                    }
                },
                secondaryButton: {
                    value: "前往授权",
                    action: async () => {
                        const secondAuthResults = await atManager.requestPermissionOnSetting(context, permissions)
                        const isSecondAuth =
                            secondAuthResults.every(s => s == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
                        if (!isSecondAuth) {
                            const isAuth = await firstReqPermissionFormUser(permissions, context)
                            resolve(isAuth)
                        } else {
                            resolve(true)
                        }
                    }
                }
            })
        })

    } catch (err) {
        const error = err as BusinessError
        console.error(`Failed to request permissions from user. Code is ${error.code}, message is ${error.message}`);
    }

    return true
}


export async function requestPermissions(uiAbilityContext: common.UIAbilityContext, uiContext: UIContext,
    permissions: Permissions[]) {
    const ps = permissions.map((s) => {
        return checkPermissionGrant(s).then(isSuccess => {
            if (isSuccess) {
                return true
            }
            return Promise.reject(s)
        })
    })
    const s = await Promise.allSettled(ps)
    const rejectedPs =
        s.filter(item => item.status == "rejected").map((item: PromiseRejectedResult) => item.reason as Permissions)

    if (rejectedPs.length == 0) {
        return true
    }

    return await reqPermissionsFromUser(rejectedPs, uiAbilityContext, uiContext)
}
import { requestPermissions } from './permission';

const isAuth =
            await this.requestPermissions(['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'])

	if (isAuth) {
		 // 
	} else {
		//
	}

参考:

  1. 向用户申请授权
  2. 二次向用户申请授权

打开应用设置权限界面

很多的时候,判断 APP 没有权限的时候,会提示让用户去设置界面设置权限,但是这样用户不方便。那么这个时候最好的做法是提示后,直接 APP 打开当前应用的设置界面,让用户直接设置,然后后退回来 APP 就能用了。

打开应用设置页

参考《https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/scenario-fusion-functionalbutton-V5

使用FunctionalButton 组件

import {FunctionalButton, functionalButtonComponentManager} from '@kit.ScenarioFusionKit';

// 声明FunctionalButton
FunctionalButton({
		params: {
				// OpenType.OPEN_SETTING表示Button为打开授权设置页类型
				openType: functionalButtonComponentManager.OpenType.OPEN_SETTING,
				label: '打开授权设置页',
				// 调整Button样式
				styleOption: {
						styleConfig: new functionalButtonComponentManager.ButtonConfig()
								.fontSize(20)
								.fontColor(Color.Black)
				}
		},
		// OpenType为“OPEN_SETTING”时,回调必须选择“onOpenSetting”
		controller: new functionalButtonComponentManager.FunctionalButtonController().onOpenSetting((err,
				data) => {
				if (err) {
						// 错误日志处理
						hilog.error(0x0000, "testTag", "error: %{public}d %{public}s", err.code, err.message);
						return;
				}
				// 成功日志处理,终止设置应用程序时触发
				hilog.info(0x0000, "testTag", "succeeded in opening setting");
				data.permissions!.forEach((value, key) => {
						hilog.info(0x0000, "testTag", "key: %{public}s value: %{public}s", String(key), value);
				})
		})
})

效果如下:

但是注意,这个界面中,没有什么定位、相册权限。

定位、相册在鸿蒙中的设置是在【隐私和安全】当中设置的。那如何打开定位权限设置呢。

打开当前应用用户授权的权限设置界面

<font style="color:rgba(0, 0, 0, 0.9);">requestPermissionOnSetting</font>方法。参考《requestPermissionOnSetting API》。

const permissions = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']
const secondAuthResults = await atManager.requestPermissionOnSetting(context, permissions)
const isSecondAuth =
		secondAuthResults.every(s => s == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)

打开的界面如下:

参考

  1. 程序访问控制
  2. 申请应用权限
  3. 向用户申请授权
  4. 二次向用户申请授权
  5. 程序访问控制管理

在开发过程中遇到其它使用的,会更新此章节

标签:abilityAccessCtrl,const,err,鸿蒙,await,Harmony,error,日志,权限
From: https://www.cnblogs.com/xakoy/p/18413817

相关文章

  • 《鸿蒙/Harmony | 开发日志》DevEco Studio 使用NewUI
    DevEco也是基于Jetbrain的IntelliJIDEA社区版开发。用习惯了Idea和AndroidStudio的新UI界面。看着旧版的界面风格太难了。官方没有提供改NewUI的方式,不过还是有其它的方法可以开启。只是有些功能可能没有那么好用,需要注意一下。不过在我用下来,目前的问题都能解决。开启N......
  • 2024/9/13日 日志
    今天,系统的对代码进行了修改优化,对原有代码进行全面修改,并实现了工序对接功能。点击查看代码//信2305-2--20234023--张一衡importjava.awt.*;importjava.sql.SQLOutput;importjava.util.Scanner;importjava.util.Random;publicclassMoontest{publicstat......
  • HarmonyOS video 视频播放器
    源码import{display,window}from'@kit.ArkUI';@Entry@ComponentstructIndex{controller:VideoController=newVideoController()//video控制器@StatecurRate:PlaybackSpeed=PlaybackSpeed.Speed_Forward_1_00_X;//播放倍数@StatecurRateName......
  • HarmonyOS开发之Search组件
    在HarmonyOS应用开发中,Search组件提供了丰富的自定义选项,允许开发者根据应用的需求定制搜索栏的外观和行为。本文将通过几个具体的场景来介绍如何使用Search组件的不同属性来自定义搜索栏。场景一:自定义搜索图标为了使搜索栏更加符合应用的设计风格,可以通过searchIcon属性来自......
  • 第一章、HarmonyOS介绍简介
    1.前言欢迎来到鸿蒙应用开发系列教程的第一课,在本单元,你将学习HarmonyOS的基本概念,熟悉HarmonyOS核心技术理念、开发语言、UI框架开发和测试工具,了解应用的上架与分发能力。2.应用开发的机遇、挑战和趋势随着万物互联时代的开启,应用的设备底座将从几十亿手机扩展到数百亿的iot设......
  • 支付宝携手HarmonyOS SDK打造高效便捷的扫码支付体验
    背景在日常的购物转账、生活缴费等在线支付中,用户在正式拉起支付界面前,均需要至少经历一次"识别"+两次"寻找",即识别归属应用、寻找应用、寻找扫码入口,才能完成扫码、付款,每一步都带来不同程度的用户流失。如何将步骤繁琐的扫码支付做到最简化,是优化在线支付体验的关键。策略支付宝......
  • ArgoWorkflow教程(四)---Workflow & 日志归档
    上一篇我们分析了argo-workflow中的artifact,包括artifact-repository配置以及Workflow中如何使用artifact。本篇主要分析流水线GC以及归档,防止无限占用集群中etcd的空间。1.概述因为ArgoWorkflow是用CRD方式实现的,不需要外部存储服务也可以正常运行:运行记录......
  • OpenHarmony 明星开发板和应用招募启动,等你来!
    为助力企业和开发者快速找到好用的开发板和应用,推动OpenHarmony生态发展,现启动 “OpenHarmony明星开发板和应用招募”评选活动!本次活动旨在为OpenHarmony生态树立优秀标杆,活动将围绕OpenHarmony的开发板和应用展开,历经报名、初选、复选、公示四个阶段,最终评选出的优秀开发板......
  • 深入理解日志轮转:管理日志文件的最佳实践
    日志轮转(LogRotation)是管理日志文件的一种技术。它的主要目的是防止日志文件占用过多磁盘空间,同时保持系统的稳定性和日志的可用性。工作原理日志文件生成:系统或应用程序会生成日志文件,这些文件记录系统运行或应用程序操作的信息。触发条件:日志文件的大小或时间间隔达到预设的......
  • 鸿蒙开发中获取定位信息
    使用LocationButton位置控件,首次点击应用中的位置控件,系统将弹窗请求用户授权,如果用户点取消,弹窗消失,应用无授权,用户再次点击位置控件时将会重新弹窗;用户点击允许,弹窗消失,应用将被授予临时位置权限,此后点击该应用的位置控件将不会弹窗。精准定位的临时授权会持续到灭屏、应用切......