蔡cai
蔡cai
  • 发布:2024-07-22 16:31
  • 更新:2024-08-12 14:09
  • 阅读:596

【安卓权限说明顶部蒙层】uniapp(vue2)项目全局监听权限,HBuilderX (4.0+) android 平台支持;上架华为应用市场审核所需

分类:uni-app


更新时间:2024-08-12 14:29
旧版时间:2024-08-12 14:06


测试部分,可能有bug
目前发现bug未解决
1、保存代码,代码热更新后,触发监听会报Error: [JS Framework] Failed to receiveTasks, instance (1) is not available.这个错误,重新编译正常

模拟器效果截图

permissionListener.js

/**  
 * @author cai  
 * @time 2024-08-12 14:29  
 * uni.createRequestPermissionListener()  
 * 文档地址:https://uniapp.dcloud.net.cn/api/system/create-request-permission-listener.html  
 * 注意:HBuilderX (4.0+) android 平台支持;HBuilderX 4.01 Vue2项目需要使用自定义基座测试监听权限申请的功能,标准基座暂不支持测试。  
 */  
// #ifndef APP  
export default null;  
// #endif   
// #ifdef APP  
const { osName } = uni.getSystemInfoSync();  
let permissionListener = null;  
// 是安卓平台,同时有uni.createRequestPermissionListener这个api  
if (osName === "android" && uni.createRequestPermissionListener) {  
    permissionListener = uni.createRequestPermissionListener();  
}  
let canRunListener = true;  // 是否可以执行所有监听方法  
let canStopListener = true; // 是否可以执行取消所有监听方法,避免唤起权限会触发App.vue的onHide生命周期  
const permissionEnums = {  
    "ACCESS_COARSE_LOCATION": {  
        name: "定位",  
        explain: "展示附近店铺、填写收货地址等相关功能"  
    },  
    "ACCESS_FINE_LOCATION": {  
        name: "定位",  
        explain: "展示附近店铺、填写收货地址等相关功能"  
    },  
    "READ_EXTERNAL_STORAGE": {  
        name: "存储",  
        explain: "上传图片、上传视频等相关功能"  
    },  
    "CAMERA": {  
        name: "相机",  
        explain: "扫二维码、拍摄图片等相关功能"  
    },  
    "WRITE_EXTERNAL_STORAGE": {  
        name: "存储",  
        explain: "把图片保存到相册等相关功能"  
    }  
}  

/**  
 * 权限说明文字  
 * @param {String} = permissionName  例如:ACCESS_COARSE_LOCATION  
 */  
const texts = (permissionName) => {  
    let title = "";  
    let content = "";  
    let permissionInfo = permissionEnums[permissionName] || null;  
    if (permissionInfo) {  
        const { name, explain } = permissionInfo;  
        title = `${name}权限使用说明`;  
        content = `将获取${name}权限,用于${explain}`;  
    } else {  
        title = "";  
        content = "";  
    }  
    return {  
        title,  
        content  
    }  
};  

/**  
 * 绘画顶部权限说明  
 * 文档地址:https://www.html5plus.org/doc/zh_cn/nativeobj.html  
 * @function drawView title标题,content描述使用说明  
 * @function hideView 隐藏顶部权限说明  
 */  
let view = null;  
const drawView = ({ title, content }) => {  
    console.log("drawView方法的参数值:", title, content);  
    if (view || !title || !content) return;     // 没有标题和内容则return出去  
    const { windowTop, windowWidth, statusBarHeight } = uni.getSystemInfoSync();  
    const topHeight = windowTop + statusBarHeight;  
    const distance = {  
        box: 10,    // 盒子距离视图两边的距离  
        text: 20    // 文字距离视图两边的距离  
    }  
    // 标题的相关样式  
    const titleStyle = {  
        size: 16,  
        height: 16,  
        top: `${topHeight + 22}`,  
        color: "#000",  
    }  
    // 内容的相关样式  
    const contentStyle = {  
        size: 14,  
        height: 0,  
        top: `${parseInt(titleStyle.top) + titleStyle.height + 6}`,  
        color: "#656563",  
    }  
    const contentLength = content.length;   // 权限说明内容文字长度  
    const contentWidth = windowWidth - distance.text * 2;   // 内容的宽度  
    const contentRowCount = Math.floor(contentWidth / contentStyle.size);   // 一行占几个文字  
    const contentRows = Math.ceil(contentLength / contentRowCount);     // 当前内容占几行  
    contentStyle.height = contentRows * (contentStyle.size + 4);    // 内容的高度  
    /**  
     * @description 计算盒子的高度  
     * 获取content到盒子顶部距离:parseInt(contentStyle.top) - topHeight - distance.box  
     * content的高度:contentStyle.height  
     * 获取content到盒子底部的距离:(distance.text - distance.box)  
     */  
    const boxHeight = (parseInt(contentStyle.top) - topHeight - distance.box) + contentStyle.height + (distance.text - distance.box);  
    view = new plus.nativeObj.View('per-modal', {  
        top: '0',  
        left: '0',  
        width: '100%',  
        backgroundColor: 'rgba(0,0,0,0.2)'  
    })  
    view.drawRect({  
        color: '#fff',  
        radius: '5px',  
    }, {  
        top: `${topHeight + distance.box}px`,  
        left: `${distance.box}px`,  
        right: `${distance.box}px`,  
        height: `${boxHeight}px`  
    })  
    view.drawText(title, {  
        top: `${titleStyle.top}px`,  
        left: `${distance.text}px`,  
        left: `${distance.text}px`,  
        height: `${titleStyle.height}px`  
    }, {  
        size: `${titleStyle.size}px`,  
        align: "left",  
        color: titleStyle.color,  
        weight: "bold"  
    })  
    view.drawText(content, {  
        top: `${contentStyle.top}px`,  
        left: `${distance.text}px`,  
        right: `${distance.text}px`,  
        height: `${contentStyle.height}px`,  
    }, {  
        size: `${contentStyle.size}px`,  
        lineSpacing: "2px",  
        align: "left",  
        color: contentStyle.color,  
        verticalAlign: "top",  
        whiteSpace: "normal"  
    })  
    let timer = setTimeout(() => {  
        view && view.show();  
        clearTimeout(timer);  
        timer = null;  
    }, 200)  
}  
// 关闭顶部权限说明  
const hideView = () => {  
    if (view) {  
        view.hide();  
        view = null;  
    }  
}  

// 监听权限方法  
const listenerFunc = () => {  
    stopFunc();     // 取消所有监听方法  
    if (canRunListener && permissionListener) {  
        let permissionName = "";    // 权限名称  
        let hasConfirm = false;     // 是否有权限弹窗(触发permissionListener.onConfirm这个回调)  
        canRunListener = false;  
        canStopListener = false;  
        // 监听申请系统权限  
        permissionListener.onRequest((e) => {  
            console.log("permissionListener.onRequest回调:", e);  
            if (Array.isArray(e) && e.length > 0) {  
                const stringToArray = e[0].split(".");  
                permissionName = stringToArray[stringToArray.length - 1];  
                console.log("权限名称:", permissionName);  
            }  
        });  
        // 监听弹出系统权限授权框  
        permissionListener.onConfirm((e) => {  
            console.log("permissionListener.onConfirm回调:", e);  
            hasConfirm = true;  
            if (permissionName) {  
                drawView(texts(permissionName));  
            }  
        });  
        // 监听权限申请完成  
        permissionListener.onComplete((e) => {  
            console.log("permissionListener.onComplete回调:", e);  
            // e.length === 0:权限列表无值,则不继续做相对逻辑  
            if (e.length === 0) return;  
            let name = "";      // 权限名称  
            let explain = "";   // 权限说明  
            if (permissionName && permissionEnums[permissionName]) {  
                name = permissionEnums[permissionName].name;  
                explain = permissionEnums[permissionName].explain;  
            }  
            const Manifest = plus.android.importClass("android.Manifest");  
            const MainActivity = plus.android.runtimeMainActivity();  
            const permissionStatus = MainActivity.checkSelfPermission(Manifest.permission[permissionName]);  
            console.log("当前权限状态:", permissionStatus);  
            /**  
             * @description 永久拒绝该权限,则引导用户前往设置页  
             * permissionStatus != 0:权限状态是拒绝  
             * !hasConfirm:没有permissionListener.onConfirm这个回调  
             */  
            if (permissionStatus != 0 && !hasConfirm && name && explain) {  
                uni.showModal({  
                    title: "温馨提示",  
                    content: `开启${name}权限后,才能${explain}`,  
                    showCancel: true,  
                    confirmText: "去设置",  
                    success: (res) => {  
                        if (res.confirm) {  
                            uni.openAppAuthorizeSetting();  
                        }  
                    }  
                })  
                return;  
            }  
            canStopListener = true;  
            hideView();  
        });  
    }  
}   

// 取消所有监听方法  
const stopFunc = () => {  
    if (canStopListener && permissionListener) {  
        console.log("执行permissionListener.stop()方法");  
        canRunListener = true;  
        hideView();  
        permissionListener.stop();  
    }  
}  

let exportObj = null;  
if (permissionListener) {  
    exportObj = {  
        listenerFunc,  
        stopFunc  
    };  
} else {  
    exportObj = null;  
}  

export default exportObj;  
// #endif

App.vue

    /**  
     * @author cai  
     * @time 2024-07-24 10:52  
     */  
    import permissionListener from  "@/utils/permissionListener.js";  
    export default {  
        onLaunch: function() {  
            console.log('App Launch')  
        },  
        onShow: function() {  
            permissionListener && permissionListener.listenerFunc();  
            console.log('App Show')   
        },  
        onHide: function() {   
            permissionListener && permissionListener.stopFunc();  
            console.log('App Hide')  
        }  
    }

↓↓↓ 各位大佬点点赞

4 关注 分享
武点点 9***@qq.com 1***@qq.com 无名之辈4133

要回复文章请先登录注册

蔡cai

蔡cai (作者)

回复 1***@qq.com :
已处理,已更新
2024-08-12 14:09
1***@qq.com

1***@qq.com

用这套代码真机运行到iOS会报《打包时未添加uni-createRequestPermissionListener,请参考http://ask.dcloud.net.cn/article/283》
2024-08-12 12:00
9***@qq.com

9***@qq.com

回复 蔡cai :
好,谢谢大佬
2024-08-10 16:42
蔡cai

蔡cai (作者)

回复 9***@qq.com :
兼容一下,可以试下
2024-08-09 15:38
9***@qq.com

9***@qq.com

回复 蔡cai :
好,谢谢大佬
2024-08-09 11:34
蔡cai

蔡cai (作者)

回复 9***@qq.com :
页面顶部权限说明是吧,我去试试能不能处理下
2024-08-09 11:32
蔡cai

蔡cai (作者)

回复 9***@qq.com :
可能备注有问题,永久拒绝权限后,再次获取权限才会唤起Modal;我这边试了是正常的
2024-08-09 11:30
蔡cai

蔡cai (作者)

回复 9***@qq.com :
我去试试
2024-08-09 11:27
9***@qq.com

9***@qq.com

回复 蔡cai :
其次,关于权限内容的提示文字,最多两行,文字多了就不显示了,无法动态高度。
最后总体还是不错,谢谢大佬解了燃眉之急,嘿嘿。(给跪了)
2024-08-09 11:26
9***@qq.com

9***@qq.com

回复 蔡cai :
// 权限列表有值,同时权限状态是拒绝,同时没有permissionListener.onConfirm这个回调,则前往设置页
这里的判断好像有问题,禁止授权后,再点击无法唤起引导Modal。
2024-08-09 11:23