用uni-app开发的app,上架华为被拒,问题如下:
您的应用在运行时,未见向用户告知权限申请的目的,向用户索取(电话、相机、存储)等权限,不符合华为应用市场审核标准。
测试步骤:任意招聘信息详情页-电话联系,申请电话权限;点击置顶推广-保存二维码到相册,申请存储权限;点击发布-任意服务-上传图片-拍摄/从相册选择,申请相机、存储权限;修改建议:APP在调用终端权限时,应同步告知用户申请该权限的目的。请排查应用内所有权限申请行为,确保均符合要求。
本文是分文pinia版本和vuex版本
20214.4.23 uniapp权限弹框视频
pinia权限管理仓库
import {
defineStore
} from 'pinia'
export const usePermission = defineStore('permission', {
state: () => ({
dialogView: null,
permissionListener: null,
list: [
{
name: "READ_CALENDAR",
title: "手机状态权限申请说明:",
content: "uni-app正在申请手机日历日历状态权限,允许或拒绝均不会获取任何隐私信息。",
}, {
name: "CALL_PHONE",
title: "拨打电话权限申请说明:",
content: "uni-app正在申请拨打电话权限,允许或拒绝均不会获取任何隐私信息。",
}, {
name: "CAMERA",
title: "读取存储权限申请说明:",
content: "uni-app正在申请摄像头权限,允许或拒绝均不会获取任何隐私信息。",
}, {
name: "READ_EXTERNAL_STORAGE",
title: "读取存储权限申请说明:",
content: "uni-app正在申请读取存储权限,允许或拒绝均不会获取任何隐私信息。",
}
]
}),
getters: {
},
actions: {
//监听权限申请
async requstPermission(permissionID) {
return new Promise((resolve, reject) => {
try {
// if (!uni.getSystemInfoSync().platform == 'android') return resolve(true)
/**
* @description plus.navigator.checkPermission 检查应用是否获取指定权限
* 有些权限检测不到 就继续下面的代码,比如相册权限就可以直接检测,就很方便,授权情况下不需要再走下面代码了
* checkPermission 返回参数
* @params undetermined 未确定
* @params authorized 授权
*/
let checkPermission = plus.navigator.checkPermission('android.permission.' + permissionID)
if (checkPermission == 'authorized') return resolve(true)
//判断是否自己在list里面配置了这个权限
let index = this.list.findIndex(item => item.name == permissionID)
if (index == -1) throw new Error('这个权限没有配置')
//唤起原生权限说明弹框
this.requstPermissionDialog(index)
//授权检测回调
plus.android.requestPermissions(
[
'android.permission.' + permissionID //单个权限
],
(resultObj) => {
this.permissionListener.stop();
console.log(resultObj, 'resultObj');
// 权限申请结果
/**
* @description resultObj.deniedAlways 永久拒绝授权
* 多个权限返回结果可能是{"granted":["android.permission.CAMERA"],"deniedPresent":[],"deniedAlways":["android.permission.READ_EXTERNAL_STORAGE"]}
* 这个情况就是我同时授权相册和相机,但是只允许了相机,没有授权相册
* 这个时候 可以通过deniedAlways 查看哪个权限被永久拒绝了,然后自行在设置弹框内容
* 所以可以自己判断细分一下,我下面的代码是先判断了是否有永久拒绝的权限,然后直接弹框提示用户去设置
*/
if (resultObj.deniedAlways && resultObj.deniedAlways.length > 0) {
uni.showModal({
title: '提示',
content: '操作权限已被拒绝,请手动前往设置',
confirmText: "立即设置",
success: (res) => {
if (res.confirm) {
this.gotoAppPermissionSetting()
} else {
resolve(false)
}
}
})
console.log('永久拒绝授权');
} else if (resultObj.deniedPresent && resultObj.deniedPresent.length > 0) {
resolve(false)
console.log('拒绝授权');
} else
if (resultObj.granted && resultObj.granted.length > 0) {
resolve(true)
console.log('授权成功');
}
},
(error) => {
reject(false)
console.log('申请权限错误:',error);
}
);
} catch (err) {
reject(false)
console.log(err);
}
})
},
//监听弹框
requstPermissionDialog(index) {
try {
if (!this.permissionListener) this.permissionListener = uni.createRequestPermissionListener()
const dialogData = this.list[index]
this.permissionListener.onConfirm((res) => {
this.dialogStyle(dialogData, true)
})
this.permissionListener.onComplete(async (res) => {
this.dialogStyle({}, false)
})
} catch (err) {
console.log('监听弹框错误', err);
}
},
//弹框样式
dialogStyle({ title = '', content = '' }, status) {
try {
if (!status) return this.dialogView.close()
const systemInfo = uni.getSystemInfoSync();
const statusBarHeight = systemInfo.statusBarHeight;
const navigationBarHeight = systemInfo.platform === 'android' ? 48 :
44;
const totalHeight = statusBarHeight + navigationBarHeight;
this.dialogView = new plus.nativeObj.View('per-modal', {
top: '0px',
left: '0px',
width: '100%',
backgroundColor: '#444',
//opacity: .5;
})
this.dialogView.drawRect({
color: '#fff',
radius: '5px'
}, {
top: totalHeight + 'px',
left: '5%',
width: '90%',
height: "100px",
})
this.dialogView.drawText(title, {
top: totalHeight + 5 + 'px',
left: "8%",
height: "30px"
}, {
align: "left",
color: "#000",
})
this.dialogView.drawText(content, {
top: totalHeight + 35 + 'px',
height: "60px",
left: "8%",
width: "84%"
}, {
whiteSpace: 'normal',
size: "14px",
align: "left",
color: "#656563"
})
this.dialogView.show()
} catch (e) {
console.log(e, '权限说明弹框样式错误');
}
},
//跳转到app权限设置页面
gotoAppPermissionSetting() {
if (!uni.getSystemInfoSync().platform == 'android') {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
// console.log(plus.device.vendor);
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
}
})
在vuex里面创建permissionToast模块
permissionToast.js
const state = {
dialogView: null,
permissionListener: null,
list: [{
name: "CAMERA",
title: "相机权限申请说明:",
content: "用于您拍摄照片,更新头像或者发送照片,允许或拒绝均不会获取任何隐私信息。",
},
{
name: "READ_EXTERNAL_STORAGE",
title: "读取外部存储权限申请说明:",
content: "用于读取您的设备存储中的文件,允许或拒绝均不会获取任何隐私信息。",
},
{
name: "RECORD_AUDIO",
title: "录音权限申请说明:",
content: "用于录制音频,例如语音消息,允许或拒绝均不会获取任何隐私信息。",
},
{
name: "ACCESS_FINE_LOCATION",
title: "位置权限申请说明:",
content: "用于连接蓝牙设备,例如通过蓝牙连接检测仪器,允许或拒绝均不会获取任何隐私信息。",
}
]
}
const actions = {
//清除弹框
asyncClearDialogView({
commit
}) {
commit('clearDialogView')
},
//权限获取
//监听权限申请
async requstPermission({
state,
dispatch,
commit
}, permissionID) {
return new Promise((resolve, reject) => {
try {
console.log('','当前申请的权限',permissionID);
// if (!uni.getSystemInfoSync().platform == 'android') return resolve(true)
/**
* @description plus.navigator.checkPermission 检查应用是否获取指定权限
* 有些权限检测不到 就继续下面的代码,比如相册权限就可以直接检测,就很方便,授权情况下不需要再走下面代码了
* checkPermission 返回参数
* @params undetermined 未确定
* @params authorized 授权
*/
let checkPermission = plus.navigator.checkPermission('android.permission.' +
permissionID)
console.log(checkPermission);
if (checkPermission == 'authorized') return resolve(true)
//判断是否自己在list里面配置了这个权限
let index = state.list.findIndex(item => item.name == permissionID)
console.log(index, 789789789798, permissionID);
if (index == -1) throw new Error('这个权限没有配置')
//唤起原生权限说明弹框
dispatch('requstPermissionDialog', index)
//授权检测回调
plus.android.requestPermissions(
[
'android.permission.' + permissionID //单个权限
// 'android.permission.CAMERA', 'android.permission.READ_EXTERNAL_STORAGE' //多个权限
],
async (resultObj) => {
console.log(resultObj, 'resultObj');
await dispatch('asyncClearDialogView')
// 权限申请结果
/**
* @description resultObj.deniedAlways 永久拒绝授权
* 多个权限返回结果可能是{"granted":["android.permission.CAMERA"],"deniedPresent":[],"deniedAlways":["android.permission.READ_EXTERNAL_STORAGE"]}
* 这个情况就是我同时授权相册和相机,但是只允许了相机,没有授权相册
* 这个时候 可以通过deniedAlways 查看哪个权限被永久拒绝了,然后自行在设置弹框内容
* 所以可以自己判断细分一下,我下面的代码是先判断了是否有永久拒绝的权限,然后直接弹框提示用户去设置
*/
if (resultObj.deniedAlways && resultObj.deniedAlways.length > 0) {
uni.showModal({
title: '提示',
content: '操作权限已被拒绝,请手动前往设置',
confirmText: "立即设置",
success: (res) => {
if (res.confirm) {
dispatch('gotoAppPermissionSetting')
} else {
resolve(false)
}
}
})
console.log('永久拒绝授权');
} else if (resultObj.deniedPresent && resultObj.deniedPresent.length > 0) {
resolve(false)
console.log('拒绝授权');
} else
if (resultObj.granted && resultObj.granted.length > 0) {
resolve(true)
console.log('授权成功');
}
},
(error) => {
reject(false)
console.log('申请权限错误:', error);
}
);
} catch (err) {
reject(false)
console.log(err);
}
})
},
//监听弹框
requstPermissionDialog({
state,
dispatch
}, index) {
try {
let permissionListener = state.permissionListener;
console.log(index,permissionListener);
if (!permissionListener) permissionListener = uni.createRequestPermissionListener()
const dialogData = state.list[index]
permissionListener.onConfirm((res) => {
dispatch('dialogStyle', {
...dialogData,
status: true
})
})
permissionListener.onComplete(async (res) => {
permissionListener.stop()
dispatch('dialogStyle', {
title: '',
content: '',
status: false
})
})
} catch (err) {
console.log('监听弹框错误', err);
}
},
//弹框样式
dialogStyle({
state,
commit
}, data) {
console.log(data);
const {
title,
content,
status
} = data
try {
console.log('弹框样式函数',status);
if(!status) {
commit('clearDialogView')
return
}
const systemInfo = uni.getSystemInfoSync();
const statusBarHeight = systemInfo.statusBarHeight;
const navigationBarHeight = systemInfo.platform === 'android' ? 48 :
44;
const totalHeight = statusBarHeight + navigationBarHeight;
commit('setDialogView', {
totalHeight,
title,
content
})
} catch (e) {
console.log(e, '权限说明弹框样式错误');
}
},
//跳转到app权限设置页面
gotoAppPermissionSetting() {
if (!uni.getSystemInfoSync().platform == 'android') {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
// console.log(plus.device.vendor);
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
}
const mutations = {
//设置弹框
setDialogView(state, {
totalHeight,
title,
content
}) {
state.dialogView = new plus.nativeObj.View('per-modal', {
top: '0px',
left: '0px',
width: '100%',
backgroundColor: '#444',
//opacity: .5;
})
state.dialogView.drawRect({
color: '#fff',
radius: '5px'
}, {
top: totalHeight + 'px',
left: '5%',
width: '90%',
height: "100px",
})
state.dialogView.drawText(title, {
top: totalHeight + 5 + 'px',
left: "8%",
height: "30px"
}, {
align: "left",
color: "#000",
})
state.dialogView.drawText(content, {
top: totalHeight + 35 + 'px',
height: "60px",
left: "8%",
width: "84%"
}, {
whiteSpace: 'normal',
size: "14px",
align: "left",
color: "#656563"
})
state.dialogView.show()
},
//清除弹框
clearDialogView(state) {
console.log('清除弹框样式');
state.dialogView && state.dialogView.close()
state.dialogView = null
}
}
export default {
namespaced: true,
state,
actions,
mutations
};
更多permissionID 值域清单权限 App权限判断和提示 - DCloud 插件市场