Uniapp关于华为应用市场上架需要同步告知使用权限的简单处理的解决方案

1,359 阅读5分钟

Uniapp关于华为应用市场上架需要同步告知使用权限的简单处理的解决方案

华为上架审核被拒绝,审核意见:

您的应用在允许时,向用户索取(相机,存储)等权限,未同步告知权限申请的目的,不符合华为应用市场审核标准。

修改建议:

请参考审核结果测试步骤,并自行全面排查应用内所有申请权限的场景,请在用户触发相关功能服务后,在申请权限的同时给用户同步告知目的,建议申请目的与“权限弹窗”需同屏展示。 (可参考附件截图合规示例,告知方式:不限于弹窗、蒙层、浮窗、或者自定义操作系统权限弹框等。)

解决方案

  1. 去网上找了uni-registerRequestPermissionTips插件,在使用这个插件后,调用了uni.chooseImage方法,在红米手机上点击相机没有反应,由于第三方UI库使用这个方法uni.chooseImage如果重新自定义组件比较耗时和麻烦:

    1. 点击拍摄没有反应: image-20240727095111804
  2. 去网上去找其它方法,发现了:

    1. 关于华为应用市场上架,申请权限未告知目的被驳回问题的简单处理方式这篇文章,但是这篇文章里面使用时需要在时调用一下permision.premissionCheck方法,但是在我们app应用的内部使用的第三方的组件库,也使用uni.chooseImage这个方法,不能很好处理第三方组件库权限问题,所以这个方法不能完全适用
    2. uni.createRequestPermissionListeneruniapp一个可以监听权限的方法,通过这个方法再结合上面画区域的提示的方法进行改造
  3. 通过将功能功能封装到一个工具函数中,然后在onLaunch生命周期直接调用:

    1. 封装工具函数

      1. 封装工具函数,console.log输出,监听打印的内容,得到权限

        1. HBuilder X开发调试查看输出的权限内容,知道权限内容后就可以对权限添加对应提示配置
        // @/utils/perListener.js 
        export function perListener() {
          // 只在 App 端使用
          // #ifdef APP-PLUS
          const permissionListener = uni.createRequestPermissionListener();
          permissionListener.onConfirm((e) => {
           console.log(e, '查看权限')
          });
          permissionListener.onComplete((e) => {
            
          });
          // #endif
        }
        

        查看权限

        ["android.permission.READ_MEDIA_IMAGES","android.permission.READ_MEDIA_VIDEO","android.permission.WRITE_EXTERNAL_STORAGE"] ,  查看权限
        

        2. 在App.vue里面使用

        <script>
          // App.vue
          import {perListener} from "@/utils/perListener.js"
          export default {
            onLaunch: function () {
                perListener()
            },
            onShow: function () {
              console.log("App Show");
            },
            onHide: function () {
              console.log("App Hide");
            },
            onExit: function() {
              unregisterRequestPermissionTipsListener(null)
            }
          };
        </script>
        ​
        <style>
          /*每个页面公共css */
        </style>
        
      2. 对权限进行映射与文本提示

        1. 监听到权限获取到的是一个字符串数组,可能在不同机型上获取到的权限列表是不相同,我这里只是选取一个公用的权限字符串进行映射,如果监听到的权限包含这个这个权限key就是访问这个权限进行提示
        // @/utils/perListener.js 
        const permissionMap = {
          Android: {
            "android.permission.WRITE_EXTERNAL_STORAGE": {
              title: "相机/相册权限说明",
              content:
                "便于您使用该功能上传您的照片/图片/视频及用于更换头像、发布产品/需求、下载、与客服沟通等场景中读取和写入相册和文件内容",
            },
            "android.permission.CAMERA": {
              title: "相机权限说明",
              content: "便于您使用该功能上传图片,用于与客服沟通等场景中发送拍摄图片",
            },
          },
          iOS: {},
        };
        
      3. 绘制文本说明,参考:关于华为应用市场上架,申请权限未告知目的被驳回问题的简单处理方式,这篇文章的showViewDesc函数进行修改

          // @/utils/perListener.js 
          export function createView() {
            return new plus.nativeObj.View('per-modal', {
              top: '0px',
              left: '0px',
              width: '100%',
              backgroundColor: 'rgba(0,0,0,0.2)',
              // opacity: '.9'
            })
          }
          
        export function showViewDesc(view, permission, plat = "android") {
        view.drawRect(
          {
            color: "#fff",
            radius: "5px",
          },
          {
            top: "30px",
            left: "5%",
            width: "90%",
            height: "100px",
          }
        );
        const info = permissionMap[plat][permission]
        if(info){
          view.drawRect(
            {
              color: '#fff',
              radius: '5px',
            },
            {
              top: '30px',
              left: '5%',
              width: '90%',
              height: '100px',
            },
          )
          view.drawText(
            info.title,
            {
              top: '40px',
              left: '8%',
              height: '30px',
            },
            {
              align: 'left',
              color: '#000',
            },
          )
          view.drawText(
            info.content,
            {
              top: '65px',
              height: '60px',
              left: '8%',
              width: '84%',
            },
            {
              whiteSpace: 'normal',
              size: '14px',
              align: 'left',
              color: '#656563',
            },
          )
        }
      }
      
      1. 全部代码

        // @/utils/perListener.js 
        const permissionMap = {
          Android: {
            "android.permission.WRITE_EXTERNAL_STORAGE": {
              title: "相机/相册权限说明",
              content:
                "便于您使用该功能上传您的照片/图片/视频及用于更换头像、发布产品/需求、下载、与客服沟通等场景中读取和写入相册和文件内容",
            },
            "android.permission.CAMERA": {
              title: "相机权限说明",
              content: "便于您使用该功能上传图片,用于与客服沟通等场景中发送拍摄图片",
            },
          },
          iOS: {},
        };
        ​
        export function createView() {
          return new plus.nativeObj.View("per-modal", {
            top: "0px",
            left: "0px",
            width: "100%",
            backgroundColor: "rgba(0,0,0,0.2)",
            // opacity: '.9'
          });
        }
        ​
        export function showViewDesc(view, permission, plat = "android") {
          view.drawRect(
            {
              color: "#fff",
              radius: "5px",
            },
            {
              top: "30px",
              left: "5%",
              width: "90%",
              height: "100px",
            }
          );
          const info = permissionMap[plat][permission]
          if(info){
            view.drawRect(
              {
                color: '#fff',
                radius: '5px',
              },
              {
                top: '30px',
                left: '5%',
                width: '90%',
                height: '100px',
              },
            )
            view.drawText(
              info.title,
              {
                top: '40px',
                left: '8%',
                height: '30px',
              },
              {
                align: 'left',
                color: '#000',
              },
            )
            view.drawText(
              info.content,
              {
                top: '65px',
                height: '60px',
                left: '8%',
                width: '84%',
              },
              {
                whiteSpace: 'normal',
                size: '14px',
                align: 'left',
                color: '#656563',
              },
            )
          }
        }
        ​
        ​
        export function perListener() {
          // 只在 App 端使用
          // #ifdef APP-PLUS
          const permissionListener = uni.createRequestPermissionListener();
          // 得到使用平台
          const plat = plus.os.name;
          const view = createView();
          permissionListener.onConfirm((e) => {
            const keys = Object.keys(permissionMap[plat]);
            for (const key of keys) {
              // 判断是否保护公共权限
              if (e.includes(key)) {
                showViewDesc(view, key, plat);
                view.show();
                break;
              }
            } 
           
          });
          permissionListener.onComplete((e) => {
            view.hide();
          });
          // #endif
        }
        
    2. App.vueonLaunch生命周期中调用:

      <script>
        // App.vue
        import {perListener} from "@/utils/perListener.js"
        export default {
          onLaunch: function () {
              perListener()
          },
          onShow: function () {
            console.log("App Show");
          },
          onHide: function () {
            console.log("App Hide");
          },
        };
      </script>
      ​
      <style>
        /*每个页面公共css */
      </style>
    

4 展示效果:

  1. 可以在开发调试或打包后查看效果:

    image-20240727095802698

    Snipaste_2024-07-27_10-03-26

最后尝试实现检查用户是否已经永久拒绝了权限,并引导用户开启权限,由于通过plus.android.requestPermissions检测权限是否已经拒绝,会重新回调uni.createRequestPermissionListener().onConfirm()uni.createRequestPermissionListener().onComplete()导致一直循环弹窗。

没有查询到直接判断权限是否拒绝而不需要直接请求的方法,uni.createRequestPermissionListener()中回调的方法也没有告诉权限的状态,所以没有实现没有实现引导用户开启权限。