From a2ec23228093d0b33bd43a8591a1bcb08881c14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Android=20=E8=BD=AE=E5=AD=90=E5=93=A5?= Date: Sun, 28 Jan 2024 15:58:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=83=E9=99=90=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=E5=99=A8=E6=8E=A5=E5=8F=A3=E5=91=BD=E5=90=8D=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A1=86=E6=9E=B6=E5=86=85=E9=83=A8=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HelpDoc-en.md | 2 +- HelpDoc-zh.md | 8 +- README-en.md | 8 +- README.md | 20 +---- app/build.gradle | 6 +- app/src/main/AndroidManifest.xml | 8 +- .../hjq/permissions/demo/MainActivity.java | 68 ++++++++--------- .../demo/PermissionDescriptionConvert.java | 38 +++++++++ .../demo/PermissionInterceptor.java | 39 +++++++--- .../demo/PermissionNameConvert.java | 4 +- .../layout/permission_description_popup.xml | 5 +- .../main/res/values-zh/strings_permission.xml | 4 +- .../main/res/values/strings_permission.xml | 4 +- library/build.gradle | 4 +- .../hjq/permissions/AndroidManifestInfo.java | 1 + .../permissions/AndroidManifestParser.java | 72 +++++++++--------- ...ptor.java => OnPermissionInterceptor.java} | 5 +- .../hjq/permissions/PermissionFragment.java | 64 +++++++++------- .../permissions/PermissionPageFragment.java | 41 +++++++--- .../com/hjq/permissions/PermissionUtils.java | 39 ++++++---- .../hjq/permissions/StartActivityManager.java | 14 ++-- .../com/hjq/permissions/XXPermissions.java | 20 ++--- picture/zh/demo_request_single_permission.jpg | Bin 18073 -> 28463 bytes 23 files changed, 282 insertions(+), 192 deletions(-) create mode 100644 app/src/main/java/com/hjq/permissions/demo/PermissionDescriptionConvert.java rename library/src/main/java/com/hjq/permissions/{IPermissionInterceptor.java => OnPermissionInterceptor.java} (94%) diff --git a/HelpDoc-en.md b/HelpDoc-en.md index 4dae2d3..5081cad 100644 --- a/HelpDoc-en.md +++ b/HelpDoc-en.md @@ -123,7 +123,7 @@ XXPermissions.with(MainActivity.this) #### What should I do if the dialog box pops up before and after the permission application -* An interceptor interface is provided inside the framework. It is enough to implement the interface provided [ IPermissionInterceptor ](/library/src/main/java/com/hjq/permissions/IPermissionInterceptor.java) in the framework. For specific implementation, please refer to the [ PermissionInterceptor ](app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java) class provided in Demo. It is recommended to download the source code and read it, and then introduce the code into the project +* An interceptor interface is provided inside the framework. It is enough to implement the interface provided [OnPermissionInterceptor](/library/src/main/java/com/hjq/permissions/OnPermissionInterceptor.java) in the framework. For specific implementation, please refer to the [ PermissionInterceptor ](app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java) class provided in Demo. It is recommended to download the source code and read it, and then introduce the code into the project * The way to use interception is also very simple. There are two specific settings, one for local settings and the other for global settings. diff --git a/HelpDoc-zh.md b/HelpDoc-zh.md index 08bd013..12bb3e2 100644 --- a/HelpDoc-zh.md +++ b/HelpDoc-zh.md @@ -129,7 +129,7 @@ XXPermissions.with(MainActivity.this) #### 我想在申请前和申请后统一弹对话框该怎么处理 -* 框架内部有提供一个拦截器接口,通过实现框架中提供的 [IPermissionInterceptor](/library/src/main/java/com/hjq/permissions/IPermissionInterceptor.java) 接口即可,具体实现可参考 Demo 中提供的 [PermissionInterceptor](app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java) 类,建议下载源码后进行阅读,再将代码引入到项目中 +* 框架内部有提供一个拦截器接口,通过实现框架中提供的 [OnPermissionInterceptor](/library/src/main/java/com/hjq/permissions/OnPermissionInterceptor.java) 接口即可,具体实现可参考 Demo 中提供的 [PermissionInterceptor](app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java) 类,建议下载源码后进行阅读,再将代码引入到项目中 * 使用拦截的方式也很简单,具体有两种设置方式,一种针对局部设置,另外一种是全局设置 @@ -334,10 +334,10 @@ context.startActivity(intent); #### 如何应对国内某些应用商店在明确拒绝权限后 48 小时内不允许再次申请的问题 -* 首先这种属于业务逻辑的问题,框架本身是不会做这种事情的,但并非不能实现,这得益于框架良好的设计,框架内部提供了一个叫 IPermissionInterceptor 的拦截器类,当前有权限申请的时候,会走 requestPermissions 方法的回调,你可以重写这个方法的逻辑,先去判断要申请的权限是否在 48 小时内已经申请过了一次了,如果没有的话,就走权限申请的流程,如果有的话,那么就直接回调权限申请失败的方法。 +* 首先这种属于业务逻辑的问题,框架本身是不会做这种事情的,但并非不能实现,这得益于框架良好的设计,框架内部提供了一个叫 OnPermissionInterceptor 的拦截器类,当前有权限申请的时候,会走 requestPermissions 方法的回调,你可以重写这个方法的逻辑,先去判断要申请的权限是否在 48 小时内已经申请过了一次了,如果没有的话,就走权限申请的流程,如果有的话,那么就直接回调权限申请失败的方法。 ```java -public final class PermissionInterceptor implements IPermissionInterceptor { +public final class PermissionInterceptor implements OnPermissionInterceptor { private static final String SP_NAME_PERMISSION_REQUEST_TIME_RECORD = "permission_request_time_record"; @@ -359,7 +359,7 @@ public final class PermissionInterceptor implements IPermissionInterceptor { } sharedPreferences.edit().putLong(permissionKey, System.currentTimeMillis()).apply(); // 如果之前没有申请过权限,或者距离上次申请已经超过了 48 个小时,则进行申请权限 - IPermissionInterceptor.super.requestPermissions(activity, allPermissions, callback); + OnPermissionInterceptor.super.requestPermissions(activity, allPermissions, callback); } @Override diff --git a/README-en.md b/README-en.md index c7acec6..bbadfcd 100644 --- a/README-en.md +++ b/README-en.md @@ -6,7 +6,7 @@ * project address: [Github](https://github.com/getActivity/XXPermissions) -* [Click here to download demo apk directly](https://github.com/getActivity/XXPermissions/releases/download/18.5/XXPermissions.apk) +* [Click here to download demo apk directly](https://github.com/getActivity/XXPermissions/releases/download/18.6/XXPermissions.apk) ![](picture/en/demo_request_permission_activity.jpg) ![](picture/en/demo_request_single_permission.jpg) ![](picture/en/demo_request_group_permission.jpg) @@ -55,7 +55,7 @@ android { dependencies { // Permission request framework:https://github.com/getActivity/XXPermissions - implementation 'com.github.getActivity:XXPermissions:18.5' + implementation 'com.github.getActivity:XXPermissions:18.6' } ``` @@ -190,7 +190,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions, // Setting not to trigger error detection mechanism (global setting) XXPermissions.setCheckMode(false); // Set permission request interceptor (global setting) -XXPermissions.setInterceptor(new IPermissionInterceptor() {}); +XXPermissions.setInterceptor(new OnPermissionInterceptor() {}); ``` #### Framework proguard rules @@ -217,7 +217,7 @@ XXPermissions.setInterceptor(new IPermissionInterceptor() {}); | Adaptation details | [XXPermissions](https://github.com/getActivity/XXPermissions) | [AndPermission](https://github.com/yanzhenjie/AndPermission) | [PermissionX](https://github.com/guolindev/PermissionX) | [AndroidUtilCode-PermissionUtils](https://github.com/Blankj/AndroidUtilCode) | [PermissionsDispatcher](https://github.com/permissions-dispatcher/PermissionsDispatcher) | [RxPermissions](https://github.com/tbruyelle/RxPermissions) | [EasyPermissions](https://github.com/googlesamples/easypermissions) | |:-------------------------------------------------------------:| :----------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------: | -| Corresponding version | 18.5 | 2.0.3 | 1.7.1 | 1.31.0 | 4.9.2 | 0.12 | 3.0.0 | +| Corresponding version | 18.6 | 2.0.3 | 1.7.1 | 1.31.0 | 4.9.2 | 0.12 | 3.0.0 | | Number of issues | [![](https://img.shields.io/github/issues/getActivity/XXPermissions.svg)](https://github.com/getActivity/XXPermissions/issues) | [![](https://img.shields.io/github/issues/yanzhenjie/AndPermission.svg)](https://github.com/yanzhenjie/AndPermission/issues) | [![](https://img.shields.io/github/issues/guolindev/PermissionX.svg)](https://github.com/guolindev/PermissionX/issues) | [![](https://img.shields.io/github/issues/Blankj/AndroidUtilCode.svg)](https://github.com/Blankj/AndroidUtilCode/issues) | [![](https://img.shields.io/github/issues/permissions-dispatcher/PermissionsDispatcher.svg)](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues) | [![](https://img.shields.io/github/issues/tbruyelle/RxPermissions.svg)](https://github.com/tbruyelle/RxPermissions/issues) | [![](https://img.shields.io/github/issues/googlesamples/easypermissions.svg)](https://github.com/googlesamples/easypermissions/issues) | | Framework volume | 85 KB | 127 KB | 97 KB | 500 KB | 99 KB | 28 KB | 48 KB | | Framework Maintenance Status | **In maintenance** | stop maintenance | **In maintenance** | stop maintenance | stop maintenance | stop maintenance | stop maintenance | diff --git a/README.md b/README.md index 82e7b63..ce60131 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ * 博文地址:[一句代码搞定权限请求,从未如此简单](https://www.jianshu.com/p/c69ff8a445ed) -* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/XXPermissions/releases/download/18.5/XXPermissions.apk) +* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/XXPermissions/releases/download/18.6/XXPermissions.apk) ![](picture/zh/download_demo_apk_qr_code.png) @@ -61,7 +61,7 @@ android { dependencies { // 权限请求框架:https://github.com/getActivity/XXPermissions - implementation 'com.github.getActivity:XXPermissions:18.5' + implementation 'com.github.getActivity:XXPermissions:18.6' } ``` @@ -196,7 +196,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions, // 设置不触发错误检测机制(全局设置) XXPermissions.setCheckMode(false); // 设置权限申请拦截器(全局设置) -XXPermissions.setInterceptor(new IPermissionInterceptor() {}); +XXPermissions.setInterceptor(new OnPermissionInterceptor() {}); ``` #### 框架混淆规则 @@ -223,7 +223,7 @@ XXPermissions.setInterceptor(new IPermissionInterceptor() {}); | 适配细节 | [XXPermissions](https://github.com/getActivity/XXPermissions) | [AndPermission](https://github.com/yanzhenjie/AndPermission) | [PermissionX](https://github.com/guolindev/PermissionX) | [AndroidUtilCode-PermissionUtils](https://github.com/Blankj/AndroidUtilCode) | [PermissionsDispatcher](https://github.com/permissions-dispatcher/PermissionsDispatcher) | [RxPermissions](https://github.com/tbruyelle/RxPermissions) | [EasyPermissions](https://github.com/googlesamples/easypermissions) | | :--------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: | -| 对应版本 | 18.5 | 2.0.3 | 1.7.1 | 1.31.0 | 4.9.2 | 0.12 | 3.0.0 | +| 对应版本 | 18.6 | 2.0.3 | 1.7.1 | 1.31.0 | 4.9.2 | 0.12 | 3.0.0 | | issues 数 | [![](https://img.shields.io/github/issues/getActivity/XXPermissions.svg)](https://github.com/getActivity/XXPermissions/issues) | [![](https://img.shields.io/github/issues/yanzhenjie/AndPermission.svg)](https://github.com/yanzhenjie/AndPermission/issues) | [![](https://img.shields.io/github/issues/guolindev/PermissionX.svg)](https://github.com/guolindev/PermissionX/issues) | [![](https://img.shields.io/github/issues/Blankj/AndroidUtilCode.svg)](https://github.com/Blankj/AndroidUtilCode/issues) | [![](https://img.shields.io/github/issues/permissions-dispatcher/PermissionsDispatcher.svg)](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues) | [![](https://img.shields.io/github/issues/tbruyelle/RxPermissions.svg)](https://github.com/tbruyelle/RxPermissions/issues) | [![](https://img.shields.io/github/issues/googlesamples/easypermissions.svg)](https://github.com/googlesamples/easypermissions/issues) | | 框架体积 | 85 KB | 127 KB | 97 KB | 500 KB | 99 KB | 28 KB | 48 KB | | 框架维护状态 |**维护中**| 停止维护 | 停止维护 | 停止维护 | 停止维护 | 停止维护 | 停止维护 | @@ -438,18 +438,6 @@ XXPermissions.setInterceptor(new IPermissionInterceptor() {}); ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_ali.png) ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_wechat.png) -#### 广告区 - -* 我现在任腾讯云服务器推广大使,大家如果有购买服务器的需求,可以通过下面的链接购买 - -[![](https://upload-dianshi-1255598498.file.myqcloud.com/upload/nodir/345X200-9ae456f58874df499adf7c331c02cb0fed12b81d.jpg)](https://curl.qcloud.com/A6cYskvv) - -[【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中](https://curl.qcloud.com/A6cYskvv) - -[![](https://upload-dianshi-1255598498.file.myqcloud.com/345-200-b28f7dee9552f4241ea6a543f15a9798049701d4.jpg)](https://curl.qcloud.com/up4fQsdn) - -[【腾讯云】中小企业福利专场,多款刚需产品,满足企业通用场景需求](https://curl.qcloud.com/up4fQsdn) - ## License ```text diff --git a/app/build.gradle b/app/build.gradle index 5180b68..a77f3ec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.hjq.permissions.demo" minSdkVersion 16 targetSdkVersion 34 - versionCode 1805 - versionName "18.5" + versionCode 1806 + versionName "18.6" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -61,7 +61,7 @@ dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' // 吐司框架:https://github.com/getActivity/Toaster - implementation 'com.github.getActivity:Toaster:12.5' + implementation 'com.github.getActivity:Toaster:12.6' // 内存泄漏检测:https://github.com/square/leakcanary debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c7fc4e2..a6d315c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,9 +30,6 @@ - - - @@ -103,6 +100,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/com/hjq/permissions/demo/MainActivity.java b/app/src/main/java/com/hjq/permissions/demo/MainActivity.java index 760e042..e45eeda 100644 --- a/app/src/main/java/com/hjq/permissions/demo/MainActivity.java +++ b/app/src/main/java/com/hjq/permissions/demo/MainActivity.java @@ -77,19 +77,19 @@ public void onClick(View view) { if (viewId == R.id.btn_main_request_single_permission) { XXPermissions.with(this) - .permission(Permission.CAMERA) - .interceptor(new PermissionInterceptor()) - .request(new OnPermissionCallback() { - - @Override - public void onGranted(@NonNull List permissions, boolean allGranted) { - if (!allGranted) { - return; - } - toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + .permission(Permission.CAMERA) + .interceptor(new PermissionInterceptor()) + .request(new OnPermissionCallback() { + + @Override + public void onGranted(@NonNull List permissions, boolean allGranted) { + if (!allGranted) { + return; } - }); + toast(String.format(getString(R.string.demo_obtain_permission_success_hint), + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); + } + }); } else if (viewId == R.id.btn_main_request_group_permission) { @@ -105,7 +105,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -125,7 +125,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -143,7 +143,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -160,7 +160,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); addCountStepListener(); } }); @@ -190,7 +190,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); } @@ -219,7 +219,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); } @@ -252,7 +252,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); new Thread(new Runnable() { @Override public void run() { @@ -293,7 +293,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); } @@ -325,7 +325,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); } @@ -344,7 +344,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -361,7 +361,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -378,7 +378,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -395,7 +395,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -422,7 +422,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); } @@ -441,7 +441,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { toggleNotificationListenerService(); } @@ -461,7 +461,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -478,7 +478,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -495,7 +495,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -512,7 +512,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -529,7 +529,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -546,7 +546,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); } }); @@ -563,7 +563,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { return; } toast(String.format(getString(R.string.demo_obtain_permission_success_hint), - PermissionNameConvert.getPermissionString(MainActivity.this, permissions))); + PermissionNameConvert.getPermissionNames(MainActivity.this, permissions))); getAppList(); } }); diff --git a/app/src/main/java/com/hjq/permissions/demo/PermissionDescriptionConvert.java b/app/src/main/java/com/hjq/permissions/demo/PermissionDescriptionConvert.java new file mode 100644 index 0000000..8cb6a1d --- /dev/null +++ b/app/src/main/java/com/hjq/permissions/demo/PermissionDescriptionConvert.java @@ -0,0 +1,38 @@ +package com.hjq.permissions.demo; + +import android.content.Context; +import android.support.annotation.NonNull; +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/XXPermissions + * time : 2023/01/02 + * desc : 权限描述转换器 + */ +public final class PermissionDescriptionConvert { + + /** + * 获取权限描述 + */ + public static String getPermissionDescription(Context context, List permissions) { + StringBuilder stringBuilder = new StringBuilder(); + List permissionNames = PermissionNameConvert.permissionsToNames(context, permissions); + for (String permissionName : permissionNames) { + stringBuilder.append(permissionName) + .append(context.getString(R.string.common_permission_colon)) + .append(permissionsToDescription(context, permissionName)) + .append("\n"); + } + return stringBuilder.toString().trim(); + } + + /** + * 将权限名称列表转换成对应权限描述 + */ + @NonNull + public static String permissionsToDescription(Context context, String permissionName) { + // 请根据权限名称转换成对应权限说明 + return "用于 xxx 业务"; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java b/app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java index c23dbf0..f97fa6c 100644 --- a/app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java +++ b/app/src/main/java/com/hjq/permissions/demo/PermissionInterceptor.java @@ -19,14 +19,13 @@ import android.view.WindowManager; import android.widget.PopupWindow; import android.widget.TextView; -import com.hjq.permissions.IPermissionInterceptor; +import com.hjq.permissions.OnPermissionInterceptor; import com.hjq.permissions.OnPermissionCallback; import com.hjq.permissions.OnPermissionPageCallback; import com.hjq.permissions.Permission; import com.hjq.permissions.PermissionFragment; import com.hjq.permissions.XXPermissions; import com.hjq.toast.Toaster; -import java.util.ArrayList; import java.util.List; /** @@ -35,7 +34,7 @@ * time : 2021/01/04 * desc : 权限申请拦截器 */ -public final class PermissionInterceptor implements IPermissionInterceptor { +public final class PermissionInterceptor implements OnPermissionInterceptor { public static final Handler HANDLER = new Handler(Looper.getMainLooper()); @@ -45,11 +44,26 @@ public final class PermissionInterceptor implements IPermissionInterceptor { /** 权限申请说明 Popup */ private PopupWindow mPermissionPopup; + /** 权限说明文案 */ + @Nullable + private String mPermissionDescription; + + public PermissionInterceptor() { + this(null); + } + + public PermissionInterceptor(@Nullable String permissionDescription) { + mPermissionDescription = permissionDescription; + } + @Override public void launchPermissionRequest(@NonNull Activity activity, @NonNull List allPermissions, @Nullable OnPermissionCallback callback) { mRequestFlag = true; List deniedPermissions = XXPermissions.getDenied(activity, allPermissions); - String message = activity.getString(R.string.common_permission_message, PermissionNameConvert.getPermissionString(activity, deniedPermissions)); + + if (TextUtils.isEmpty(mPermissionDescription)) { + mPermissionDescription = generatePermissionDescription(activity, deniedPermissions); + } ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); int activityOrientation = activity.getResources().getConfiguration().orientation; @@ -73,7 +87,7 @@ public void launchPermissionRequest(@NonNull Activity activity, @NonNull List(allPermissions), this, callback); + PermissionFragment.launch(activity, allPermissions, this, callback); // 延迟 300 毫秒是为了避免出现 PopupWindow 显示然后立马消失的情况 // 因为框架没有办法在还没有申请权限的情况下,去判断权限是否永久拒绝了,必须要在发起权限申请之后 // 所以只能通过延迟显示 PopupWindow 来做这件事,如果 300 毫秒内权限申请没有结束,证明本次申请的权限没有永久拒绝 @@ -85,17 +99,17 @@ public void launchPermissionRequest(@NonNull Activity activity, @NonNull List= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) { return; } - showPopupWindow(activity, decorView, message); + showPopupWindow(activity, decorView, mPermissionDescription); }, 300); } else { // 注意:这里的 Dialog 只是示例,没有用 DialogFragment 来处理 Dialog 生命周期 new AlertDialog.Builder(activity) - .setTitle(R.string.common_permission_description) - .setMessage(message) + .setTitle(R.string.common_permission_description_title) + .setMessage(mPermissionDescription) .setCancelable(false) .setPositiveButton(R.string.common_permission_granted, (dialog, which) -> { dialog.dismiss(); - PermissionFragment.launch(activity, new ArrayList<>(allPermissions), + PermissionFragment.launch(activity, allPermissions, PermissionInterceptor.this, callback); }) .setNegativeButton(R.string.common_permission_denied, (dialog, which) -> { @@ -172,6 +186,13 @@ public void finishPermissionRequest(@NonNull Activity activity, @NonNull List permissions) { + return PermissionDescriptionConvert.getPermissionDescription(context, permissions); + } + private void showPopupWindow(Activity activity, ViewGroup decorView, String message) { if (mPermissionPopup == null) { View contentView = LayoutInflater.from(activity) diff --git a/app/src/main/java/com/hjq/permissions/demo/PermissionNameConvert.java b/app/src/main/java/com/hjq/permissions/demo/PermissionNameConvert.java index ad7646e..479a1c3 100644 --- a/app/src/main/java/com/hjq/permissions/demo/PermissionNameConvert.java +++ b/app/src/main/java/com/hjq/permissions/demo/PermissionNameConvert.java @@ -18,7 +18,7 @@ public final class PermissionNameConvert { /** * 获取权限名称 */ - public static String getPermissionString(Context context, List permissions) { + public static String getPermissionNames(Context context, List permissions) { return listToString(context, permissionsToNames(context, permissions)); } @@ -66,7 +66,7 @@ public static List permissionsToNames(Context context, List perm } case Permission.READ_MEDIA_IMAGES: case Permission.READ_MEDIA_VIDEO: - case Permission.READ_MEDIA_VISUAL_USER_SELECTED:{ + case Permission.READ_MEDIA_VISUAL_USER_SELECTED: { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { String hint = context.getString(R.string.common_permission_image_and_video); if (!permissionNames.contains(hint)) { diff --git a/app/src/main/res/layout/permission_description_popup.xml b/app/src/main/res/layout/permission_description_popup.xml index 9fc77cf..6a62ae9 100644 --- a/app/src/main/res/layout/permission_description_popup.xml +++ b/app/src/main/res/layout/permission_description_popup.xml @@ -20,7 +20,7 @@ android:layout_marginLeft="15dp" android:layout_marginTop="12dp" android:layout_marginRight="15dp" - android:text="@string/common_permission_description" + android:text="@string/common_permission_description_title" android:textColor="#333333" android:textSize="14sp" android:textStyle="bold" /> @@ -34,8 +34,9 @@ android:layout_marginRight="15dp" android:layout_marginBottom="12dp" android:textColor="#666666" + android:lineSpacingExtra="3dp" android:textSize="13sp" - tools:text="@string/common_permission_message" /> + tools:text="xxx" /> diff --git a/app/src/main/res/values-zh/strings_permission.xml b/app/src/main/res/values-zh/strings_permission.xml index 32add92..fe1629a 100644 --- a/app/src/main/res/values-zh/strings_permission.xml +++ b/app/src/main/res/values-zh/strings_permission.xml @@ -1,8 +1,7 @@ - 权限说明 - 使用此功能需要先授予%s + 权限说明 授予 取消 @@ -21,6 +20,7 @@ 权限 + 日历权限 相机权限 diff --git a/app/src/main/res/values/strings_permission.xml b/app/src/main/res/values/strings_permission.xml index d0d9542..5315877 100644 --- a/app/src/main/res/values/strings_permission.xml +++ b/app/src/main/res/values/strings_permission.xml @@ -1,8 +1,7 @@ - Permission description - %s needs to be granted to use this feature + Permission description Granted Denied @@ -21,6 +20,7 @@ permission , + : calendar permission camera permission diff --git a/library/build.gradle b/library/build.gradle index 5d8c31c..13a03c1 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -5,8 +5,8 @@ android { defaultConfig { minSdkVersion 14 - versionCode 1805 - versionName "18.5" + versionCode 1806 + versionName "18.6" } // 使用 JDK 1.8 diff --git a/library/src/main/java/com/hjq/permissions/AndroidManifestInfo.java b/library/src/main/java/com/hjq/permissions/AndroidManifestInfo.java index 9135d84..f3ac377 100644 --- a/library/src/main/java/com/hjq/permissions/AndroidManifestInfo.java +++ b/library/src/main/java/com/hjq/permissions/AndroidManifestInfo.java @@ -26,6 +26,7 @@ final class AndroidManifestInfo { final List permissionInfoList = new ArrayList<>(); /** Application 节点信息 */ + @Nullable ApplicationInfo applicationInfo; /** Activity 节点信息 */ diff --git a/library/src/main/java/com/hjq/permissions/AndroidManifestParser.java b/library/src/main/java/com/hjq/permissions/AndroidManifestParser.java index aad50cc..67b6a03 100644 --- a/library/src/main/java/com/hjq/permissions/AndroidManifestParser.java +++ b/library/src/main/java/com/hjq/permissions/AndroidManifestParser.java @@ -44,6 +44,8 @@ final class AndroidManifestParser { private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supportsPictureInPicture"; private static final String ATTR_PERMISSION = "permission"; + private AndroidManifestParser() {} + /** * 解析 apk 包中的清单文件 * @@ -53,54 +55,52 @@ final class AndroidManifestParser { @NonNull static AndroidManifestInfo parseAndroidManifest(@NonNull Context context, int apkCookie) throws IOException, XmlPullParserException { AndroidManifestInfo manifestInfo = new AndroidManifestInfo(); - XmlResourceParser parser = context.getAssets(). - openXmlResourceParser(apkCookie, ANDROID_MANIFEST_FILE_NAME); - do { - // 当前节点必须为标签头部 - if (parser.getEventType() != XmlResourceParser.START_TAG) { - continue; - } + try (XmlResourceParser parser = context.getAssets(). + openXmlResourceParser(apkCookie, ANDROID_MANIFEST_FILE_NAME)) { - String tagName = parser.getName(); + do { + // 当前节点必须为标签头部 + if (parser.getEventType() != XmlResourceParser.START_TAG) { + continue; + } - if (TextUtils.equals(TAG_MANIFEST, tagName)) { - manifestInfo.packageName = parser.getAttributeValue(null, ATTR_PACKAGE); - } + String tagName = parser.getName(); - if (TextUtils.equals(TAG_USES_SDK, tagName)) { - manifestInfo.usesSdkInfo = parseUsesSdkFromXml(parser); - } + if (TextUtils.equals(TAG_MANIFEST, tagName)) { + manifestInfo.packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + } - if (TextUtils.equals(TAG_USES_PERMISSION, tagName) || - TextUtils.equals(TAG_USES_PERMISSION_SDK_23, tagName) || - TextUtils.equals(TAG_USES_PERMISSION_SDK_M, tagName)) { - AndroidManifestInfo.PermissionInfo permissionInfo = parsePermissionFromXml(parser); - manifestInfo.permissionInfoList.add(permissionInfo); - } + if (TextUtils.equals(TAG_USES_SDK, tagName)) { + manifestInfo.usesSdkInfo = parseUsesSdkFromXml(parser); + } - if (TextUtils.equals(TAG_APPLICATION, tagName)) { - manifestInfo.applicationInfo = parseApplicationFromXml(parser); - } + if (TextUtils.equals(TAG_USES_PERMISSION, tagName) || + TextUtils.equals(TAG_USES_PERMISSION_SDK_23, tagName) || + TextUtils.equals(TAG_USES_PERMISSION_SDK_M, tagName)) { + manifestInfo.permissionInfoList.add(parsePermissionFromXml(parser)); + } - if (TextUtils.equals(TAG_ACTIVITY, tagName) || - TextUtils.equals(TAG_ACTIVITY_ALIAS, tagName)) { - AndroidManifestInfo.ActivityInfo activityInfo = parseActivityFromXml(parser); - manifestInfo.activityInfoList.add(activityInfo); - } + if (TextUtils.equals(TAG_APPLICATION, tagName)) { + manifestInfo.applicationInfo = parseApplicationFromXml(parser); + } - if (TextUtils.equals(TAG_SERVICE, tagName)) { - AndroidManifestInfo.ServiceInfo serviceInfo = parseServerFromXml(parser); - manifestInfo.serviceInfoList.add(serviceInfo); - } + if (TextUtils.equals(TAG_ACTIVITY, tagName) || + TextUtils.equals(TAG_ACTIVITY_ALIAS, tagName)) { + manifestInfo.activityInfoList.add(parseActivityFromXml(parser)); + } - } while (parser.next() != XmlResourceParser.END_DOCUMENT); + if (TextUtils.equals(TAG_SERVICE, tagName)) { + manifestInfo.serviceInfoList.add(parseServerFromXml(parser)); + } - parser.close(); + } while (parser.next() != XmlResourceParser.END_DOCUMENT); + } return manifestInfo; } + @NonNull private static AndroidManifestInfo.UsesSdkInfo parseUsesSdkFromXml(@NonNull XmlResourceParser parser) { AndroidManifestInfo.UsesSdkInfo usesSdkInfo = new AndroidManifestInfo.UsesSdkInfo(); usesSdkInfo.minSdkVersion = parser.getAttributeIntValue(ANDROID_NAMESPACE_URI, @@ -108,6 +108,7 @@ private static AndroidManifestInfo.UsesSdkInfo parseUsesSdkFromXml(@NonNull XmlR return usesSdkInfo; } + @NonNull private static AndroidManifestInfo.PermissionInfo parsePermissionFromXml(@NonNull XmlResourceParser parser) { AndroidManifestInfo.PermissionInfo permissionInfo = new AndroidManifestInfo.PermissionInfo(); permissionInfo.name = parser.getAttributeValue(ANDROID_NAMESPACE_URI, ATTR_NAME); @@ -118,6 +119,7 @@ private static AndroidManifestInfo.PermissionInfo parsePermissionFromXml(@NonNul return permissionInfo; } + @NonNull private static AndroidManifestInfo.ApplicationInfo parseApplicationFromXml(@NonNull XmlResourceParser parser) { AndroidManifestInfo.ApplicationInfo applicationInfo = new AndroidManifestInfo.ApplicationInfo(); applicationInfo.name = parser.getAttributeValue(ANDROID_NAMESPACE_URI, ATTR_NAME); @@ -126,6 +128,7 @@ private static AndroidManifestInfo.ApplicationInfo parseApplicationFromXml(@NonN return applicationInfo; } + @NonNull private static AndroidManifestInfo.ActivityInfo parseActivityFromXml(@NonNull XmlResourceParser parser) { AndroidManifestInfo.ActivityInfo activityInfo = new AndroidManifestInfo.ActivityInfo(); activityInfo.name = parser.getAttributeValue(ANDROID_NAMESPACE_URI, ATTR_NAME); @@ -134,6 +137,7 @@ private static AndroidManifestInfo.ActivityInfo parseActivityFromXml(@NonNull Xm return activityInfo; } + @NonNull private static AndroidManifestInfo.ServiceInfo parseServerFromXml(@NonNull XmlResourceParser parser) { AndroidManifestInfo.ServiceInfo serviceInfo = new AndroidManifestInfo.ServiceInfo(); serviceInfo.name = parser.getAttributeValue(ANDROID_NAMESPACE_URI, ATTR_NAME); diff --git a/library/src/main/java/com/hjq/permissions/IPermissionInterceptor.java b/library/src/main/java/com/hjq/permissions/OnPermissionInterceptor.java similarity index 94% rename from library/src/main/java/com/hjq/permissions/IPermissionInterceptor.java rename to library/src/main/java/com/hjq/permissions/OnPermissionInterceptor.java index 82ebc70..5fd2b41 100644 --- a/library/src/main/java/com/hjq/permissions/IPermissionInterceptor.java +++ b/library/src/main/java/com/hjq/permissions/OnPermissionInterceptor.java @@ -4,7 +4,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import java.util.ArrayList; import java.util.List; /** @@ -13,7 +12,7 @@ * time : 2020/12/26 * desc : 权限请求拦截器 */ -public interface IPermissionInterceptor { +public interface OnPermissionInterceptor { /** * 发起权限申请(可在此处先弹 Dialog 再申请权限,如果用户已经授予权限,则不会触发此回调) @@ -23,7 +22,7 @@ public interface IPermissionInterceptor { */ default void launchPermissionRequest(@NonNull Activity activity, @NonNull List allPermissions, @Nullable OnPermissionCallback callback) { - PermissionFragment.launch(activity, new ArrayList<>(allPermissions), this, callback); + PermissionFragment.launch(activity, allPermissions, this, callback); } /** diff --git a/library/src/main/java/com/hjq/permissions/PermissionFragment.java b/library/src/main/java/com/hjq/permissions/PermissionFragment.java index 6da06dd..15a37ac 100644 --- a/library/src/main/java/com/hjq/permissions/PermissionFragment.java +++ b/library/src/main/java/com/hjq/permissions/PermissionFragment.java @@ -3,6 +3,7 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.Fragment; +import android.app.FragmentManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -36,32 +37,38 @@ public final class PermissionFragment extends Fragment implements Runnable { /** * 开启权限申请 */ - public static void launch(@NonNull Activity activity, @NonNull ArrayList permissions, - @NonNull IPermissionInterceptor interceptor, @Nullable OnPermissionCallback callback) { + public static void launch(@NonNull Activity activity, @NonNull List permissions, + @NonNull OnPermissionInterceptor interceptor, @Nullable OnPermissionCallback callback) { PermissionFragment fragment = new PermissionFragment(); - Bundle bundle = new Bundle(); int requestCode; + Random random = new Random(); // 请求码随机生成,避免随机产生之前的请求码,必须进行循环判断 do { // 新版本的 Support 库限制请求码必须小于 65536 // 旧版本的 Support 库限制请求码必须小于 256 - requestCode = new Random().nextInt((int) Math.pow(2, 8)); + requestCode = random.nextInt((int) Math.pow(2, 8)); } while (REQUEST_CODE_ARRAY.contains(requestCode)); // 标记这个请求码已经被占用 REQUEST_CODE_ARRAY.add(requestCode); + + Bundle bundle = new Bundle(); bundle.putInt(REQUEST_CODE, requestCode); - bundle.putStringArrayList(REQUEST_PERMISSIONS, permissions); + if (permissions instanceof ArrayList) { + bundle.putStringArrayList(REQUEST_PERMISSIONS, (ArrayList) permissions); + } else { + bundle.putStringArrayList(REQUEST_PERMISSIONS, new ArrayList<>(permissions)); + } fragment.setArguments(bundle); // 设置保留实例,不会因为屏幕方向或配置变化而重新创建 fragment.setRetainInstance(true); // 设置权限申请标记 fragment.setRequestFlag(true); // 设置权限回调监听 - fragment.setCallBack(callback); + fragment.setOnPermissionCallback(callback); // 设置权限请求拦截器 - fragment.setInterceptor(interceptor); + fragment.setOnPermissionInterceptor(interceptor); // 绑定到 Activity 上面 - fragment.attachActivity(activity); + fragment.attachByActivity(activity); } /** 是否申请了特殊权限 */ @@ -79,7 +86,7 @@ public static void launch(@NonNull Activity activity, @NonNull ArrayList /** 权限请求拦截器 */ @Nullable - private IPermissionInterceptor mInterceptor; + private OnPermissionInterceptor mInterceptor; /** Activity 屏幕方向 */ private int mScreenOrientation; @@ -87,21 +94,29 @@ public static void launch(@NonNull Activity activity, @NonNull ArrayList /** * 绑定 Activity */ - public void attachActivity(@NonNull Activity activity) { - activity.getFragmentManager().beginTransaction().add(this, this.toString()).commitAllowingStateLoss(); + public void attachByActivity(@NonNull Activity activity) { + FragmentManager fragmentManager = activity.getFragmentManager(); + if (fragmentManager == null) { + return; + } + fragmentManager.beginTransaction().add(this, this.toString()).commitAllowingStateLoss(); } /** * 解绑 Activity */ - public void detachActivity(@NonNull Activity activity) { - activity.getFragmentManager().beginTransaction().remove(this).commitAllowingStateLoss(); + public void detachByActivity(@NonNull Activity activity) { + FragmentManager fragmentManager = activity.getFragmentManager(); + if (fragmentManager == null) { + return; + } + fragmentManager.beginTransaction().remove(this).commitAllowingStateLoss(); } /** * 设置权限监听回调监听 */ - public void setCallBack(@Nullable OnPermissionCallback callback) { + public void setOnPermissionCallback(@Nullable OnPermissionCallback callback) { mCallBack = callback; } @@ -115,7 +130,7 @@ public void setRequestFlag(boolean flag) { /** * 设置权限请求拦截器 */ - public void setInterceptor(@Nullable IPermissionInterceptor interceptor) { + public void setOnPermissionInterceptor(@Nullable OnPermissionInterceptor interceptor) { mInterceptor = interceptor; } @@ -162,7 +177,7 @@ public void onResume() { // 如果当前 Fragment 是通过系统重启应用触发的,则不进行权限申请 if (!mRequestFlag) { - detachActivity(getActivity()); + detachByActivity(getActivity()); return; } @@ -290,15 +305,15 @@ public void requestDangerousPermission() { /** * 拆分两次请求权限(有些情况下,需要先申请 A 权限,才能再申请 B 权限) */ - public void splitTwiceRequestPermission(@NonNull Activity activity, @NonNull ArrayList allPermissions, - @NonNull ArrayList firstPermissions, int requestCode) { + public void splitTwiceRequestPermission(@NonNull Activity activity, @NonNull List allPermissions, + @NonNull List firstPermissions, int requestCode) { ArrayList secondPermissions = new ArrayList<>(allPermissions); for (String permission : firstPermissions) { secondPermissions.remove(permission); } - PermissionFragment.launch(activity, firstPermissions, new IPermissionInterceptor() {}, new OnPermissionCallback() { + PermissionFragment.launch(activity, firstPermissions, new OnPermissionInterceptor() {}, new OnPermissionCallback() { @Override public void onGranted(@NonNull List permissions, boolean allGranted) { @@ -311,7 +326,7 @@ public void onGranted(@NonNull List permissions, boolean allGranted) { // 为什么延迟时间是 150 毫秒? 经过实践得出 100 还是有概率会出现失败,但是换成 150 试了很多次就都没有问题了 long delayMillis = AndroidVersion.isAndroid13() ? 150 : 0; PermissionUtils.postDelayed(() -> PermissionFragment.launch(activity, secondPermissions, - new IPermissionInterceptor() {}, new OnPermissionCallback() { + new OnPermissionInterceptor() {}, new OnPermissionCallback() { @Override public void onGranted(@NonNull List permissions, boolean allGranted) { @@ -360,10 +375,7 @@ public void onDenied(@NonNull List permissions, boolean doNotAskAgain) { @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { // Github issue 地址:https://github.com/getActivity/XXPermissions/issues/236 - if (permissions == null || grantResults == null) { - return; - } - if (permissions.length == 0 || grantResults.length == 0) { + if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) { return; } @@ -377,7 +389,7 @@ public void onRequestPermissionsResult(int requestCode, String[] permissions, in OnPermissionCallback callback = mCallBack; mCallBack = null; - IPermissionInterceptor interceptor = mInterceptor; + OnPermissionInterceptor interceptor = mInterceptor; mInterceptor = null; // 优化权限回调结果 @@ -389,7 +401,7 @@ public void onRequestPermissionsResult(int requestCode, String[] permissions, in // 释放对这个请求码的占用 REQUEST_CODE_ARRAY.remove((Integer) requestCode); // 将 Fragment 从 Activity 移除 - detachActivity(activity); + detachByActivity(activity); // 获取已授予的权限 List grantedPermissions = PermissionApi.getGrantedPermissions(allPermissions, grantResults); diff --git a/library/src/main/java/com/hjq/permissions/PermissionPageFragment.java b/library/src/main/java/com/hjq/permissions/PermissionPageFragment.java index 48cda05..8fbdf2a 100644 --- a/library/src/main/java/com/hjq/permissions/PermissionPageFragment.java +++ b/library/src/main/java/com/hjq/permissions/PermissionPageFragment.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.app.Fragment; +import android.app.FragmentManager; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; @@ -25,20 +26,24 @@ public final class PermissionPageFragment extends Fragment implements Runnable { /** * 开启权限申请 */ - public static void beginRequest(@NonNull Activity activity, @NonNull ArrayList permissions, + public static void launch(@NonNull Activity activity, @NonNull List permissions, @Nullable OnPermissionPageCallback callback) { PermissionPageFragment fragment = new PermissionPageFragment(); Bundle bundle = new Bundle(); - bundle.putStringArrayList(REQUEST_PERMISSIONS, permissions); + if (permissions instanceof ArrayList) { + bundle.putStringArrayList(REQUEST_PERMISSIONS, (ArrayList) permissions); + } else { + bundle.putStringArrayList(REQUEST_PERMISSIONS, new ArrayList<>(permissions)); + } fragment.setArguments(bundle); // 设置保留实例,不会因为屏幕方向或配置变化而重新创建 fragment.setRetainInstance(true); // 设置权限申请标记 fragment.setRequestFlag(true); // 设置权限回调监听 - fragment.setCallBack(callback); + fragment.setOnPermissionPageCallback(callback); // 绑定到 Activity 上面 - fragment.attachActivity(activity); + fragment.attachByActivity(activity); } /** 权限回调对象 */ @@ -54,21 +59,29 @@ public static void beginRequest(@NonNull Activity activity, @NonNull ArrayList allPermissions = arguments.getStringArrayList(REQUEST_PERMISSIONS); + if (allPermissions == null || allPermissions.isEmpty()) { + return; + } List grantedPermissions = PermissionApi.getGrantedPermissions(activity, allPermissions); if (grantedPermissions.size() == allPermissions.size()) { @@ -155,6 +172,6 @@ public void run() { callback.onDenied(); } - detachActivity(activity); + detachByActivity(activity); } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/permissions/PermissionUtils.java b/library/src/main/java/com/hjq/permissions/PermissionUtils.java index bc3bc16..9f62bd5 100644 --- a/library/src/main/java/com/hjq/permissions/PermissionUtils.java +++ b/library/src/main/java/com/hjq/permissions/PermissionUtils.java @@ -19,7 +19,9 @@ import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.text.TextUtils; +import android.view.Display; import android.view.Surface; +import android.view.WindowManager; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -93,7 +95,7 @@ static boolean checkOpNoThrow(Context context, String opName) { /** * 解决 Android 12 调用 shouldShowRequestPermissionRationale 出现内存泄漏的问题 * Android 12L 和 Android 13 版本经过测试不会出现这个问题,证明 Google 在新版本上已经修复了这个问题 - * 但是对于 Android 12 仍是一个历史遗留问题,这是我们所有应用开发者不得不面对的一个事情 + * 但是对于 Android 12 仍是一个历史遗留问题,这是我们所有 Android App 开发者不得不面对的一个事情 * * issues 地址:https://github.com/getActivity/XXPermissions/issues/133 */ @@ -130,15 +132,13 @@ static void postActivityResult(@NonNull List permissions, @NonNull Runna } else { delayMillis = 500; } - } else if (PhoneRomUtils.isMiui()) { - // 经过测试,发现小米 Android 11 及以上的版本,申请这个权限需要 1 秒钟才能判断到 + } else if (PhoneRomUtils.isMiui() && AndroidVersion.isAndroid11() && + PermissionUtils.containsPermission(permissions, Permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { + // 经过测试,发现小米 Android 11 及以上的版本,申请这个权限需要 1000 毫秒才能判断到(测试了 800 毫秒还不行) // 因为在 Android 10 的时候,这个特殊权限弹出的页面小米还是用谷歌原生的 // 然而在 Android 11 之后的,这个权限页面被小米改成了自己定制化的页面 // 测试了原生的模拟器和 vivo 云测并发现没有这个问题,所以断定这个 Bug 就是小米特有的 - if (AndroidVersion.isAndroid11() && - PermissionUtils.containsPermission(permissions, Permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - delayMillis = 1000; - } + delayMillis = 1000; } postDelayed(runnable, delayMillis); } @@ -174,9 +174,7 @@ static AndroidManifestInfo getAndroidManifestInfo(Context context) { androidManifestInfo.packageName)) { return null; } - } catch (IOException e) { - e.printStackTrace(); - } catch (XmlPullParserException e) { + } catch (IOException | XmlPullParserException e) { e.printStackTrace(); } @@ -344,9 +342,9 @@ static boolean isScopedStorage(@NonNull Context context) { try { String metaKey = "ScopedStorage"; Bundle metaData = context.getPackageManager().getApplicationInfo( - context.getPackageName(), PackageManager.GET_META_DATA).metaData; + context.getPackageName(), PackageManager.GET_META_DATA).metaData; if (metaData != null && metaData.containsKey(metaKey)) { - return Boolean.parseBoolean(String.valueOf(metaData.get(metaKey))); + return metaData.getBoolean(metaKey); } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); @@ -386,13 +384,22 @@ static void lockActivityOrientation(@NonNull Activity activity) { * 判断 Activity 是否反方向旋转了 */ static boolean isActivityReverse(@NonNull Activity activity) { - // 获取 Activity 旋转的角度 - int activityRotation; + Display display = null; if (AndroidVersion.isAndroid11()) { - activityRotation = activity.getDisplay().getRotation(); + display = activity.getDisplay(); } else { - activityRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + WindowManager windowManager = activity.getWindowManager(); + if (windowManager != null) { + display = windowManager.getDefaultDisplay(); + } } + + if (display == null) { + return false; + } + + // 获取 Activity 旋转的角度 + int activityRotation = display.getRotation(); switch (activityRotation) { case Surface.ROTATION_180: case Surface.ROTATION_270: diff --git a/library/src/main/java/com/hjq/permissions/StartActivityManager.java b/library/src/main/java/com/hjq/permissions/StartActivityManager.java index 4022183..9037e45 100644 --- a/library/src/main/java/com/hjq/permissions/StartActivityManager.java +++ b/library/src/main/java/com/hjq/permissions/StartActivityManager.java @@ -63,7 +63,7 @@ static boolean startActivity(@NonNull android.support.v4.app.Fragment fragment, return startActivity(new StartActivityDelegateSupportFragmentImpl(fragment), intent); } - static boolean startActivity(@NonNull IStartActivityDelegate delegate, @NonNull Intent intent) { + static boolean startActivity(@NonNull StartActivityDelegate delegate, @NonNull Intent intent) { try { delegate.startActivity(intent); return true; @@ -89,7 +89,7 @@ static boolean startActivityForResult(@NonNull android.support.v4.app.Fragment f return startActivityForResult(new StartActivityDelegateSupportFragmentImpl(fragment), intent, requestCode); } - static boolean startActivityForResult(@NonNull IStartActivityDelegate delegate, @NonNull Intent intent, int requestCode) { + static boolean startActivityForResult(@NonNull StartActivityDelegate delegate, @NonNull Intent intent, int requestCode) { try { delegate.startActivityForResult(intent, requestCode); return true; @@ -103,14 +103,14 @@ static boolean startActivityForResult(@NonNull IStartActivityDelegate delegate, } } - private interface IStartActivityDelegate { + private interface StartActivityDelegate { void startActivity(@NonNull Intent intent); void startActivityForResult(@NonNull Intent intent, int requestCode); } - private static class StartActivityDelegateContextImpl implements IStartActivityDelegate { + private static class StartActivityDelegateContextImpl implements StartActivityDelegate { private final Context mContext; @@ -134,7 +134,7 @@ public void startActivityForResult(@NonNull Intent intent, int requestCode) { } } - private static class StartActivityDelegateActivityImpl implements IStartActivityDelegate { + private static class StartActivityDelegateActivityImpl implements StartActivityDelegate { private final Activity mActivity; @@ -153,7 +153,7 @@ public void startActivityForResult(@NonNull Intent intent, int requestCode) { } } - private static class StartActivityDelegateFragmentImpl implements IStartActivityDelegate { + private static class StartActivityDelegateFragmentImpl implements StartActivityDelegate { private final Fragment mFragment; @@ -172,7 +172,7 @@ public void startActivityForResult(@NonNull Intent intent, int requestCode) { } } - private static class StartActivityDelegateSupportFragmentImpl implements IStartActivityDelegate { + private static class StartActivityDelegateSupportFragmentImpl implements StartActivityDelegate { private final android.support.v4.app.Fragment mFragment; diff --git a/library/src/main/java/com/hjq/permissions/XXPermissions.java b/library/src/main/java/com/hjq/permissions/XXPermissions.java index 44e5cae..cf827bd 100644 --- a/library/src/main/java/com/hjq/permissions/XXPermissions.java +++ b/library/src/main/java/com/hjq/permissions/XXPermissions.java @@ -22,7 +22,7 @@ public final class XXPermissions { public static final int REQUEST_CODE = 1024 + 1; /** 权限请求拦截器 */ - private static IPermissionInterceptor sInterceptor; + private static OnPermissionInterceptor sInterceptor; /** 当前是否为检查模式 */ private static Boolean sCheckMode; @@ -54,16 +54,16 @@ public static void setCheckMode(boolean checkMode) { /** * 设置全局权限请求拦截器 */ - public static void setInterceptor(IPermissionInterceptor interceptor) { + public static void setInterceptor(OnPermissionInterceptor interceptor) { sInterceptor = interceptor; } /** * 获取全局权限请求拦截器 */ - public static IPermissionInterceptor getInterceptor() { + public static OnPermissionInterceptor getInterceptor() { if (sInterceptor == null) { - sInterceptor = new IPermissionInterceptor() {}; + sInterceptor = new OnPermissionInterceptor() {}; } return sInterceptor; } @@ -78,7 +78,7 @@ public static IPermissionInterceptor getInterceptor() { /** 权限请求拦截器 */ @Nullable - private IPermissionInterceptor mInterceptor; + private OnPermissionInterceptor mInterceptor; /** 设置不检查 */ @Nullable @@ -130,7 +130,7 @@ public XXPermissions permission(@Nullable List permissions) { /** * 设置权限请求拦截器 */ - public XXPermissions interceptor(@Nullable IPermissionInterceptor interceptor) { + public XXPermissions interceptor(@Nullable OnPermissionInterceptor interceptor) { mInterceptor = interceptor; return this; } @@ -157,7 +157,7 @@ public void request(@Nullable OnPermissionCallback callback) { final Context context = mContext; - final IPermissionInterceptor interceptor = mInterceptor; + final OnPermissionInterceptor interceptor = mInterceptor; // 权限请求列表(为什么直接不用字段?因为框架要兼容新旧权限,在低版本下会自动添加旧权限申请,为了避免重复添加) final List permissions = new ArrayList<>(mPermissions); @@ -415,7 +415,7 @@ public static void startPermissionActivity(@NonNull Activity activity, StartActivityManager.startActivity(activity, PermissionIntentManager.getApplicationDetailsIntent(activity)); return; } - PermissionPageFragment.beginRequest(activity, (ArrayList) permissions, callback); + PermissionPageFragment.launch(activity, permissions, callback); } /* android.app.Fragment */ @@ -480,7 +480,7 @@ public static void startPermissionActivity(@NonNull Fragment fragment, StartActivityManager.startActivity(fragment, PermissionIntentManager.getApplicationDetailsIntent(activity)); return; } - PermissionPageFragment.beginRequest(activity, (ArrayList) permissions, callback); + PermissionPageFragment.launch(activity, permissions, callback); } /* android.support.v4.app.Fragment */ @@ -545,6 +545,6 @@ public static void startPermissionActivity(@NonNull android.support.v4.app.Fragm StartActivityManager.startActivity(fragment, PermissionIntentManager.getApplicationDetailsIntent(activity)); return; } - PermissionPageFragment.beginRequest(activity, (ArrayList) permissions, callback); + PermissionPageFragment.launch(activity, permissions, callback); } } \ No newline at end of file diff --git a/picture/zh/demo_request_single_permission.jpg b/picture/zh/demo_request_single_permission.jpg index c21b1ca69b0860630a48370285468a83aac42f72..5014ac423f40cf6bce662509796872b244217a78 100644 GIT binary patch literal 28463 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

rDHpB(w`U3DWDF0ef3J!c6#ONcz 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 UjZXSEv(;}$*Z=T|4gd200iTw@(*OVf