JokerCats

Android 权限帮助类
需求来源Android M(6.0)以后Google添加了动态权限的区分,将权限分为了普通权限和危险权限。当应用功...
扫描右侧二维码阅读全文
04
2019/12

Android 权限帮助类

需求来源

Android M(6.0)以后Google添加了动态权限的区分,将权限分为了普通权限和危险权限。当应用功能涉及到危险权限时需要动态向用户申请,且用户授权后方可使用。

普通权限

极少数给用户带来隐私信息风险的权限。该类权限可直接在 AndroidManifest 文件中注册,运行时系统自动给予。

危险权限

涉及到用户隐私信息的权限,例如:联系人,录音等等。该类权限不仅要在 AndroidManifest 文件中注册,还需使用代码动态向用户申请。需要注意的是:动态权限以划分,若用户授权其中的一个权限,则整组权限都将被授予。

工具类

import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;

/**
  * 检查 & 申请权限帮助类
  *
  * @author JokerCats
  */
public class PermissionHelper {

  private final int mRequestCode = 100;// 权限请求码
  private boolean mShowSystemSetting = true;// 若用户存在未授权情况,默认显示系统设置

  private PermissionHelper() {
  }

  private static PermissionHelper sHelper;

  public static PermissionHelper getInstance() {
      if (sHelper == null) {
          sHelper = new PermissionHelper();
      }
      return sHelper;
  }

  private PermissionResult mResult;

  /**
   * 引导用户进入系统设置的弹窗
   */
  private AlertDialog mDialog;

  /**
   * 检查权限
   *
   * @param ctx         上下文
   * @param permissions 待请求权限组
   * @param result      请求结果回调
   */
  public void checkPermissions(Activity ctx, String[] permissions, @NonNull PermissionResult result) {
      mResult = result;

      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
          mResult.passPermissions();// Android 6.0不需要动态申请权限
          return;
      }

      // 创建未授予权限数组
      ArrayList<String> unauthorizedList = new ArrayList<>();
      // 检查权限
      for (String permission : permissions) {
          if (ContextCompat.checkSelfPermission(ctx, permission) != PackageManager.PERMISSION_GRANTED) {
              unauthorizedList.add(permission);// 添加未授予权限
          }
      }

      if (unauthorizedList.size() > 0) {
          // 申请未授权权限
          ActivityCompat.requestPermissions(ctx, permissions, mRequestCode);
      } else {
          mResult.passPermissions();
      }

  }

  /**
   * 请求权限结果回调
   *
   * @param ctx          上下文
   * @param requestCode  请求码
   * @param permissions  待请求权限组
   * @param grantResults 授权标识数组(长度与待请求数组一致)数据 0 为允许权限;数据 -1 为禁止权限
   */
  public void onRequestPermissionResult(Activity ctx, int requestCode,
                                      @NonNull String[] permissions, @NonNull int[] grantResults) {
      // 授权结果假想值
      boolean allPass = true;
      if (requestCode == mRequestCode) {
          // 遍历标识数组,重置授权结果
          for (int result : grantResults) {
              if (result == PackageManager.PERMISSION_DENIED) {
                  allPass = false;
              }
          }
      }

      if (allPass) {
          mResult.passPermissions();
      } else {
          if (mShowSystemSetting) {
              showSettingDialog(ctx);
          } else {
              mResult.forbidPermissions();
          }
      }
  }

  /**
   * 当权限缺失时是否显示系统设置弹窗
   *
   * @param show 是否显示
   */
  public void showDialogWhenForbid(boolean show) {
      mShowSystemSetting = show;
  }

  /**
   * 显示系统设置弹窗
   *
   * @param ctx 上下文
   */
  private void showSettingDialog(Activity ctx) {
      if (mDialog == null) {
          mDialog = new AlertDialog.Builder(ctx)
                  .setMessage("权限已禁用,为避免功能缺失请到设置重新开启")
                  .setPositiveButton("设置", new DialogInterface.OnClickListener() {
                      @Override
                      public void onClick(DialogInterface dialog, int which) {
                          cancelSettingDialog();
                          Uri packageURI = Uri.parse("package:" + ctx.getPackageName());
                          Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
                          ctx.startActivity(intent);
                      }
                  })
                  .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                      @Override
                      public void onClick(DialogInterface dialog, int which) {
                          cancelSettingDialog();
                          mResult.forbidPermissions();
                      }
                  })
                  .setCancelable(false)
                  .create();
      }

      mDialog.show();
  }

  private void cancelSettingDialog() {
      if (mDialog != null) {
          mDialog.cancel();
          mDialog = null;
      }
  }

  public interface PermissionResult {
      // 通过的权限
      void passPermissions();

      // 禁止的权限
      void forbidPermissions();
  }

}

/*
  Danger permission:
  同组若一个权限被授予,则组内其他权限自动授予
  
  group:android.permission-group.CONTACTS(联系人)
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

  group:android.permission-group.PHONE(电话)
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

  group:android.permission-group.CALENDAR(日历)
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

  group:android.permission-group.CAMERA(相机)
  permission:android.permission.CAMERA

  group:android.permission-group.SENSORS(传感器)
  permission:android.permission.BODY_SENSORS

  group:android.permission-group.LOCATION(位置)
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

  group:android.permission-group.STORAGE(存储卡)
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

  group:android.permission-group.MICROPHONE(麦克风)
  permission:android.permission.RECORD_AUDIO

  group:android.permission-group.SMS(短信)
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS
  */

使用示例

private static String[] PERMISSIONS_STORAGE = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.CAMERA,
        Manifest.permission.RECORD_AUDIO
};

PermissionHelper.getInstance().checkPermissions(activity, PERMISSIONS_STORAGE, new PermissionHelper.PermissionResult() {
        @Override
        public void passPermissions() {
            Toast.makeText(activity, "权限已授权 为所欲为模式启动", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void forbidPermissions() {
            Toast.makeText(activity, "权限意识满分!!", Toast.LENGTH_SHORT).show();
        }
    });

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    PermissionHelper.getInstance().onRequestPermissionResult(this, requestCode, permissions, grantResults);
}

Tips

帮助类中列出了危险权限及对应功能,方便程序查找辨别。

想对权限这部分更加深入的同学可以移步 官方文档

当然很多优秀的开源框架也是不容错过的 RxPermissions (ฅ´ω`ฅ)

至此一个Android原生的权限帮助类就完成了,希望可以帮助到有需要的同学。

Last modification:December 4th, 2019 at 04:25 pm
本文采用 知识共享署名 4.0 国际许可协议进行许可
可自由转载、引用,但需署名作者且注明文章出处
If you think my article is useful to you, please feel free to appreciate
你也可以使用比特币或以太坊进行打赏,这样我会更开心ヾ(≧∇≦*)ゝ

Bitcoin Address: 1LqiT3NeBg9K33k7rFc3iZXrPyjECsVhNS

Ethereum Address: 0x3cd49bF3376E48ecdF2740b2868d0da682bF95ac

Leave a Comment

3 comments

  1. biuren

    <script>alert("baka大姐头");</script>

  2. biuren

    alert("baka姐姐");

  3. biu

    alert("baka姐姐");