Android 小程序 API 权限扩展配置

小程序的某些特殊 API,如定位、相机、相册等,通常会提示用户授权,待用户允许后方可执行API。

小程序容器允许针对 API 调用进行如下扩展:

  1. 自定义文案提示,接入方可控制文案以及展示样式。

  2. 允许接入方读写权限配置。

说明

此扩展配置仅在后台已开启 小程序权限控制 时才可用。

权限配置

对于需要用户授权使用的 API,都必须配置一个权限 key。多个 API 可对应同一个 key,比如选择图片和扫码可对应一个相机的 key。

小程序已有默认配置的 key 以及对应的 API,详见下表:

权限

key

API

相机

camera

scan, chooseImage, chooseVideo

相册

album

saveImage, saveVideosToPhotosAlbum, shareTokenImageSilent

位置

location

getLocation, getCurrentLocation

容器读取接入方传入的如下权限配置类来处理 API 调用权限:

 public static class PermissionConfig {
    public String action;   // API 名称
    public String key;      // 权限 key
    public String desc;     // API 调用展示的文案,文案中可加入 %s 占位符,容器会自动填入小程序的名称

    public PermissionConfig() {
    }
}
说明

actionchooseImagechooseVideo 时,接入方配置的 key 是不生效的,容器有特殊逻辑处理这两个 API,但文案依然是可配置的。

加载配置

加载权限配置,需使用容器提供的 TinyAppPermissionExternProvider 接口。接口类如下:

package com.alipay.mobile.nebula.provider;

import android.content.Context;

import java.util.List;

public abstract class TinyAppPermissionExternProvider {

    public interface PermissionCheckCallback {
        void accept();

        void deny();
    }

    public abstract List<PermissionConfig> loadPermissionCheckConfig();

    public abstract void showPermissionDialog(Context context, String appId, PermissionConfig config, PermissionCheckCallback callback);

    public abstract boolean shouldHandlePermissionDialog();
}

loadPermissionCheckConfig 方法负责将权限配置加载至容器。

自定义展示

自定义展示授权信息并允许用户进行操作确认。

  1. 首先需使 shouldHandlePermissionDialog 方法返回 true

  2. 随后容器会调用 showPermissionDialog 方法,接入方可在此处展示自定义样式。

  3. 当用户接受或者拒绝调用,需要调用 PermissionCheckCallback 接口的相应方法。

代码示例如下:

package com.mpaas.demo.nebula;

import android.content.Context;

import com.alipay.mobile.antui.dialog.AUNoticeDialog;
import com.alipay.mobile.nebula.provider.TinyAppPermissionExternProvider;

import java.util.ArrayList;
import java.util.List;

public class TinyExternalPermissionCheckProvider extends TinyAppPermissionExternProvider {

    private PermissionConfig create(String action, String key, String desc) {
        PermissionConfig config = new PermissionConfig();
        config.action = action;
        config.key = key;
        config.desc = desc;
        return config;
    }

    private List<PermissionConfig> permissionConfigs = new ArrayList<>();

    public TinyExternalPermissionCheckProvider() {
        permissionConfigs.add(create("saveFile", "file", "%s想使用您的文件存储"));
        permissionConfigs.add(create("getFileInfo", "file", "%s想使用您的文件存储"));
    }

    @Override
    public List<PermissionConfig> loadPermissionCheckConfig() {
        return permissionConfigs;
    }

    @Override
    public void showPermissionDialog(Context context, String action, PermissionConfig permissionConfig, final PermissionCheckCallback permissionCheckCallback) {
        AUNoticeDialog dialog = new AUNoticeDialog(context, "授权提醒", permissionConfig.desc, "接受", "拒绝");
        dialog.setPositiveListener(new AUNoticeDialog.OnClickPositiveListener() {
            @Override
            public void onClick() {
                permissionCheckCallback.accept();
            }
        });
        dialog.setNegativeListener(new AUNoticeDialog.OnClickNegativeListener() {
            @Override
            public void onClick() {
                permissionCheckCallback.deny();
            }
        });
        dialog.show();
    }

    @Override
    public boolean shouldHandlePermissionDialog() {
        return true;
    }
}

读写配置

读取配置可调用如下方法:

MPTinyHelper.getInstance().getMiniProgramSetting(appId)
说明

  • 小程序配置是以应用和用户两个维度存储的,因此要确保应用已经调用 MPLogger.setUserId 方法。

  • 当 API 从未被调用的情况下,是获取不到该 API 对应的 key 值的授权状态的。

写入配置可调用如下方法:

MPTinyHelper.getInstance().updateMiniProgramSetting(appId, key, isAllowed);

代码示例如下:

package com.mpaas.demo.nebula;

import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;

import com.alipay.mobile.antui.basic.AUSearchBar;
import com.alipay.mobile.antui.tablelist.AUSwitchListItem;
import com.alipay.mobile.framework.app.ui.BaseFragmentActivity;
import com.alipay.mobile.nebula.util.H5Utils;
import com.mpaas.nebula.adapter.api.MPTinyHelper;

import java.util.Map;

public class PermissionDisplayActivity extends BaseFragmentActivity {

    private ViewGroup mScrollView;

    private AUSearchBar mSearchInputBox;

    private Map<String, Boolean> permissions;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission);
        mScrollView = (ViewGroup) findViewById(R.id.scrollview);
        mSearchInputBox = (AUSearchBar) findViewById(R.id.search);
        mSearchInputBox.getSearchButton().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mScrollView.removeAllViews();
                final String appId = mSearchInputBox.getSearchEditView().getText().toString();
                permissions = MPTinyHelper.getInstance().getMiniProgramSetting(appId);
                for (Map.Entry<String, Boolean> entry : permissions.entrySet()) {
                    AUSwitchListItem item = new AUSwitchListItem(PermissionDisplayActivity.this);
                    final String key = entry.getKey();
                    item.setLeftText(key);
                    item.getCompoundSwitch().setChecked(entry.getValue());
                    item.getCompoundSwitch().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                        @Override
                        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                            MPTinyHelper.getInstance().updateMiniProgramSetting(appId, key, isChecked);
                        }
                    });
                    mScrollView.addView(item, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, H5Utils.dip2px(PermissionDisplayActivity.this, 48)));
                }
            }
        });
    }
}