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

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

安卓离线打包(本地打包)需要添加一些sdk,具体看这篇文章

https://ask.dcloud.net.cn/article/41380

测试部分,可能有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')  
    }
}

更多关于uni-app(vue2)项目【安卓权限说明顶部蒙层】全局监听权限,HBuilderX (4.0+) android 平台支持;上架华为应用市场审核所需的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于uni-app(vue2)项目【安卓权限说明顶部蒙层】全局监听权限,HBuilderX (4.0+) android 平台支持;上架华为应用市场审核所需的实战教程也可以访问 https://www.itying.com/category-93-b0.html


在uni-app(Vue2)项目中,如果你需要在安卓平台上实现全局监听权限请求,并在用户遇到权限请求时显示一个顶部蒙层说明,这可以通过结合HBuilderX(4.0+)提供的一些API和自定义组件来实现。以下是一个简要的代码示例,展示如何实现这一功能。

1. 创建顶部蒙层组件

首先,创建一个用于显示权限说明的顶部蒙层组件,比如PermissionOverlay.vue

<template>
  <view v-if="visible" class="overlay">
    <text>{{ message }}</text>
    <button @click="confirm">允许</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      visible: false,
      message: ''
    };
  },
  methods: {
    show(message) {
      this.message = message;
      this.visible = true;
    },
    confirm() {
      this.visible = false;
      // 这里可以添加处理权限请求的逻辑
    }
  }
};
</script>

<style>
.overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20px;
  color: white;
}
button {
  margin-top: 10px;
}
</style>

2. 在主应用中注册并使用该组件

在你的主应用入口文件(如App.vue)中注册并使用这个组件:

<template>
  <div id="app">
    <PermissionOverlay ref="permissionOverlay" />
    <router-view/>
  </div>
</template>

<script>
import PermissionOverlay from './components/PermissionOverlay.vue';

export default {
  components: {
    PermissionOverlay
  },
  mounted() {
    // 监听权限请求事件(这里以定位权限为例)
    plus.android.importClass('android.Manifest');
    plus.android.requestPermissions([plus.android.constants.Manifest.permission.ACCESS_FINE_LOCATION], (event) => {
      if (event.deniedAlways()) {
        this.$refs.permissionOverlay.show('为了提供更好的位置服务,请允许访问位置信息。');
      }
    });
  }
};
</script>

注意

  1. 权限请求逻辑:在实际应用中,权限请求的逻辑可能更加复杂,需要根据不同的权限类型和用户响应进行不同的处理。
  2. 适配华为应用市场:确保你的应用符合华为应用市场的审核要求,包括权限请求的合理性、用户隐私保护等。
  3. HBuilderX 版本:确保你使用的HBuilderX版本支持上述API调用,不同版本可能有细微差异。

这个示例展示了如何在uni-app中实现全局监听权限请求并在顶部显示蒙层说明的基本思路。你可以根据实际需求进行进一步的定制和优化。

回到顶部