Skip to content

Commit a540f84

Browse files
committed
新增支持画中画权限申请
修正身体活动权限在旧版本上面的判断逻辑 纠正框架文档上面的一处示例写法错误
1 parent 9ca8cc8 commit a540f84

File tree

18 files changed

+277
-37
lines changed

18 files changed

+277
-37
lines changed

.github/ISSUE_TEMPLATE/issue_template_bug.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ assignees: getActivity
2222

2323
* 出现问题的安卓版本【必填】:请填写出现问题的 Android 版本
2424

25+
* 问题信息的来源渠道【必填】:请填写问题的来源(例如:自己遇到的/Bugly 看到的/用户反馈等等)
26+
2527
#### 请回答
2628

2729
* 是部分机型还是所有机型都会出现【必答】:部分/全部(例如:某为,某 Android 版本会出现)

README.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
![](logo.png)
44

5-
* 项目地址:[Github](https://github.com/getActivity/XXPermissions)[码云](https://gitee.com/getActivity/XXPermissions)
5+
* 项目地址:[Github](https://github.com/getActivity/XXPermissions)
66

77
* 博文地址:[一句代码搞定权限请求,从未如此简单](https://www.jianshu.com/p/c69ff8a445ed)
88

9-
* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/XXPermissions/releases/download/16.0/XXPermissions.apk)
9+
* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/XXPermissions/releases/download/16.2/XXPermissions.apk)
1010

1111
![](picture/demo_code.png)
1212

@@ -20,6 +20,8 @@
2020

2121
![](picture/10.jpg) ![](picture/11.jpg) ![](picture/12.jpg)
2222

23+
![](picture/13.jpg) ![](picture/14.jpg) ![](picture/15.jpg)
24+
2325
#### 集成步骤
2426

2527
* 如果你的项目 Gradle 配置是在 `7.0 以下`,需要在 `build.gradle` 文件中加入
@@ -57,11 +59,11 @@ android {
5759
5860
dependencies {
5961
// 权限请求框架:https://github.com/getActivity/XXPermissions
60-
implementation 'com.github.getActivity:XXPermissions:16.0'
62+
implementation 'com.github.getActivity:XXPermissions:16.2'
6163
}
6264
```
6365

64-
#### AndroidX
66+
#### AndroidX 兼容
6567

6668
* 如果项目是基于 **AndroidX** 包,请在项目 `gradle.properties` 文件中加入
6769

@@ -148,7 +150,7 @@ XXPermissions.with(this)
148150
.request(object : OnPermissionCallback {
149151

150152
override fun onGranted(permissions: MutableList<String>, all: Boolean) {
151-
if (all) {
153+
if (!all) {
152154
toast("获取部分权限成功,但部分权限未正常授予")
153155
return
154156
}
@@ -205,19 +207,20 @@ XXPermissions.setInterceptor(new IPermissionInterceptor() {});
205207

206208
* 如果想知道回调中的某个权限是否被授权或者拒绝,可以调用 **List** 类中的 **contains(Permission.XXX)** 方法来判断这个集合中是否包含了这个权限。
207209

208-
### [其他常见疑问请点击此处查看](HelpDoc.md)
210+
## [其他常见疑问请点击此处查看](HelpDoc.md)
209211

210212
#### 同类权限请求框架之间的对比
211213

212214
| 适配细节 | [XXPermissions](https://github.com/getActivity/XXPermissions) | [AndPermission](https://github.com/yanzhenjie/AndPermission) | [PermissionX](https://github.com/guolindev/PermissionX) | [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) | [PermissionsDispatcher](https://github.com/permissions-dispatcher/PermissionsDispatcher) | [RxPermissions](https://github.com/tbruyelle/RxPermissions) | [EasyPermissions](https://github.com/googlesamples/easypermissions) |
213215
| :--------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: |
214-
| 对应版本 | 16.0 | 2.0.3 | 1.6.4 | 1.31.0 | 4.9.2 | 0.12 | 3.0.0 |
216+
| 对应版本 | 16.2 | 2.0.3 | 1.6.4 | 1.31.0 | 4.9.2 | 0.12 | 3.0.0 |
215217
| 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) |
216-
| 框架体积 | 51 KB | 127 KB | 90 KB | 500 KB | 99 KB | 28 KB | 48 KB |
217-
| 框架维护状态 |**维护中**| 停止维护 |**维护中**| 停止维护 |**维护中**| 停止维护 | 停止维护 |
218+
| 框架体积 | 52 KB | 127 KB | 90 KB | 500 KB | 99 KB | 28 KB | 48 KB |
219+
| 框架维护状态 |**维护中**| 停止维护 |**维护中**| 停止维护 | 停止维护 | 停止维护 | 停止维护 |
218220
| 闹钟提醒权限 ||||||||
219221
| 所有文件管理权限 ||||||||
220222
| 安装包权限 ||||||||
223+
| 画中画权限 ||||||||
221224
| 悬浮窗权限 ||||||||
222225
| 系统设置权限 ||||||||
223226
| 通知栏权限 ||||||||
@@ -256,7 +259,7 @@ XXPermissions.setInterceptor(new IPermissionInterceptor() {});
256259

257260
#### 后台申请权限场景介绍
258261

259-
* 当我们做耗时操作之后申请权限(例如在闪屏页获取隐私协议再申请权限),在网络请求的过程中将 Activity 返回桌面去(退到后台),然后会导致权限请求是在后台状态中进行,在这个时机上就可能会导致权限申请不正常,表现为不会显示授权对话框,处理不当的还会导致崩溃,例如 [RxPeremission/issues/249](https://github.com/tbruyelle/RxPermissions/issues/249)。原因在于框架中的 PermissionFragment 在 **commit / commitNow** 到 Activity 的时候会做一个检测,如果 Activity 的状态是不可见时则会抛出异常,而 **RxPeremission** 正是使用了 **commitNow** 才会导致崩溃 ,使用 **commitAllowingStateLoss / commitNowAllowingStateLoss** 则可以避开这个检测,虽然这样可以避免崩溃,但是会出现另外一个问题,系统提供的 **requestPermissions** API 在 Activity 不可见时调用也不会弹出授权对话框,**XXPermissions** 的解决方式是将 **requestPermissions** 时机从 **create** 转移到了 **resume**,因为 Activity 和 Fragment 的生命周期方法是捆绑在一起的,如果 Activity 是不可见的,那么就算创建了 Fragment 也只会调用 **onCreate** 方法,而不会去调用它的 **onResume** 方法,最后当 Activity 从后台返回到前台时,不仅会触发 **Activity.onResume** 方法,同时也会触发 **PermissionFragment** 的 **onResume** 方法,在这个方法申请权限就可以保证最终 **requestPermissions** 申请的时机是在 Activity **处于可见状态的情况**下。
262+
* 当我们做耗时操作之后申请权限(例如在闪屏页获取隐私协议再申请权限),在网络请求的过程中将 Activity 返回桌面去(退到后台),然后会导致权限请求是在后台状态中进行,在这个时机上就可能会导致权限申请不正常,表现为不会显示授权对话框,处理不当的还会导致崩溃,例如 [RxPeremission/issues/249](https://github.com/tbruyelle/RxPermissions/issues/249)。原因在于框架中的 PermissionFragment 在 **commit / commitNow** 到 Activity 的时候会做一个检测,如果 Activity 的状态是不可见时则会抛出异常,而 **RxPeremission** 正是使用了 **commitNow** 才会导致崩溃 ,使用 **commitAllowingStateLoss / commitNowAllowingStateLoss** 则可以避开这个检测,虽然这样可以避免崩溃,但是会出现另外一个问题,系统提供的 **requestPermissions** API 在 Activity 不可见时调用也不会弹出授权对话框,**XXPermissions** 的解决方式是将 **requestPermissions** 时机从 **create** 转移到了 **resume**,因为 Activity 和 Fragment 的生命周期方法是捆绑在一起的,如果 Activity 是不可见的,那么就算创建了 Fragment 也只会调用 **onCreate** 方法,而不会去调用它的 **onResume** 方法,最后当 Activity 从后台返回到前台时,不仅会触发 **Activity.onResume** 方法,同时也会触发 **PermissionFragment** 的 **onResume** 方法,在这个方法申请权限就可以保证最终 **requestPermissions** 调用的时机是在 Activity **处于可见状态的情况**下。
260263

261264
#### Android 12 内存泄漏问题修复介绍
262265

@@ -288,7 +291,7 @@ XXPermissions.setInterceptor(new IPermissionInterceptor() {});
288291

289292
* 幸好 Google 没有将 PackageManager.shouldShowRequestPermissionRationale 列入到反射黑名单中,否则这次想给 Google 擦屁股都没有办法了,要不然只能用修改系统源码实现的方式,但这种方式只能等谷歌在后续的 Android 版本上面修复了,不过庆幸的是,在 Android 12 L 的版本之后,这个问题被修复了,[具体的提交记录可以点击此处查看](https://cs.android.com/android/_/android/platform/frameworks/base/+/0d47a03bfa8f4ca54b883ff3c664cd4ea4a624d9:core/java/android/permission/PermissionUsageHelper.java;dlc=cec069482f80019c12f3c06c817d33fc5ad6151f),但是对于 Android 12 而言,这仍是一个历史遗留问题。
290293

291-
* 值得注意的是:XXPermissions 是目前同类框架第一款也是唯一一款修复这个问题的框架,另外针对这个问题,我还给谷歌的 [AndroidX](https://github.com/androidx/androidx/pull/435) 项目提供了解决方案
294+
* 值得注意的是:XXPermissions 是目前同类框架第一款也是唯一一款修复这个问题的框架,另外针对这个问题,我还给谷歌的 [AndroidX](https://github.com/androidx/androidx/pull/435) 项目无偿提供了解决方案,目前 Merge Request 已被合入主分支,我相信通过这一举措,将解决全球近 10 亿台 Android 12 设备出现的内存泄露问题
292295

293296
#### 错误检测机制介绍
294297

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ android {
77
applicationId "com.hjq.permissions.demo"
88
minSdkVersion 14
99
targetSdkVersion 33
10-
versionCode 1600
11-
versionName "16.0"
10+
versionCode 1620
11+
versionName "16.2"
1212
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
1313
}
1414

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
3737

3838
<uses-permission android:name="android.permission.BODY_SENSORS" />
39+
<uses-permission android:name="android.permission.BODY_SENSORS_BACKGROUND" />
40+
3941
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
4042

4143
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
@@ -55,8 +57,6 @@
5557

5658
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation" />
5759

58-
<uses-permission android:name="android.permission.BODY_SENSORS_BACKGROUND" />
59-
6060
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
6161
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
6262
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
@@ -72,7 +72,8 @@
7272
<activity
7373
android:name=".MainActivity"
7474
android:exported="true"
75-
android:launchMode="singleTop" >
75+
android:launchMode="singleTop"
76+
android:supportsPictureInPicture="true" >
7677
<intent-filter>
7778
<action android:name="android.intent.action.MAIN" />
7879
<action android:name="android.intent.action.VIEW" />

app/src/main/java/com/hjq/permissions/demo/MainActivity.java

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import android.content.Intent;
55
import android.content.pm.PackageManager;
66
import android.database.Cursor;
7+
import android.hardware.Sensor;
8+
import android.hardware.SensorEvent;
9+
import android.hardware.SensorEventListener;
10+
import android.hardware.SensorManager;
711
import android.location.Address;
812
import android.location.Geocoder;
913
import android.media.ExifInterface;
@@ -45,6 +49,7 @@ protected void onCreate(Bundle savedInstanceState) {
4549
findViewById(R.id.btn_main_request_group).setOnClickListener(this);
4650
findViewById(R.id.btn_main_request_location).setOnClickListener(this);
4751
findViewById(R.id.btn_main_request_sensors).setOnClickListener(this);
52+
findViewById(R.id.btn_main_request_activity_recognition).setOnClickListener(this);
4853
findViewById(R.id.btn_main_request_bluetooth).setOnClickListener(this);
4954
findViewById(R.id.btn_main_request_wifi).setOnClickListener(this);
5055
findViewById(R.id.btn_main_request_media_location).setOnClickListener(this);
@@ -60,6 +65,7 @@ protected void onCreate(Bundle savedInstanceState) {
6065
findViewById(R.id.btn_main_request_alarm).setOnClickListener(this);
6166
findViewById(R.id.btn_main_request_not_disturb).setOnClickListener(this);
6267
findViewById(R.id.btn_main_request_ignore_battery).setOnClickListener(this);
68+
findViewById(R.id.btn_main_request_picture_in_picture).setOnClickListener(this);
6369
findViewById(R.id.btn_main_request_open_vpn).setOnClickListener(this);
6470
findViewById(R.id.btn_main_app_details).setOnClickListener(this);
6571
}
@@ -136,6 +142,23 @@ public void onGranted(List<String> permissions, boolean all) {
136142
}
137143
});
138144

145+
} else if (viewId == R.id.btn_main_request_activity_recognition) {
146+
147+
XXPermissions.with(this)
148+
.permission(Permission.ACTIVITY_RECOGNITION)
149+
.interceptor(new PermissionInterceptor())
150+
.request(new OnPermissionCallback() {
151+
152+
@Override
153+
public void onGranted(List<String> permissions, boolean all) {
154+
if (!all) {
155+
return;
156+
}
157+
toast("获取" + PermissionNameConvert.getPermissionString(MainActivity.this, permissions) + "成功");
158+
addCountStepListener();
159+
}
160+
});
161+
139162
} else if (viewId == R.id.btn_main_request_bluetooth) {
140163

141164
long delayMillis = 0;
@@ -471,6 +494,22 @@ public void onGranted(List<String> permissions, boolean all) {
471494
}
472495
});
473496

497+
} else if (viewId == R.id.btn_main_request_picture_in_picture) {
498+
499+
XXPermissions.with(this)
500+
.permission(Permission.PICTURE_IN_PICTURE)
501+
.interceptor(new PermissionInterceptor())
502+
.request(new OnPermissionCallback() {
503+
504+
@Override
505+
public void onGranted(List<String> permissions, boolean all) {
506+
if (!all) {
507+
return;
508+
}
509+
toast("获取" + PermissionNameConvert.getPermissionString(MainActivity.this, permissions) + "成功");
510+
}
511+
});
512+
474513
} else if (viewId == R.id.btn_main_request_open_vpn) {
475514

476515
XXPermissions.with(this)
@@ -604,4 +643,50 @@ private String latLongToAddressString(float latitude, float longitude) {
604643
}
605644
return addressString;
606645
}
646+
647+
private final SensorEventListener mSensorEventListener = new SensorEventListener() {
648+
649+
@Override
650+
public void onSensorChanged(SensorEvent event) {
651+
Log.w("onSensorChanged", "event = " + event);
652+
switch (event.sensor.getType()) {
653+
case Sensor.TYPE_STEP_COUNTER:
654+
Log.w("XXPermissions", "开机以来当天总步数:" + event.values[0]);
655+
break;
656+
case Sensor.TYPE_STEP_DETECTOR:
657+
if (event.values[0] == 1) {
658+
Log.w("XXPermissions", "当前走了一步");
659+
}
660+
break;
661+
default:
662+
break;
663+
}
664+
}
665+
666+
@Override
667+
public void onAccuracyChanged(Sensor sensor, int accuracy) {
668+
Log.w("onAccuracyChanged", String.valueOf(accuracy));
669+
}
670+
};
671+
672+
/**
673+
* 添加步数监听
674+
*/
675+
private void addCountStepListener() {
676+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
677+
return;
678+
}
679+
SensorManager manager = (SensorManager) getSystemService(SENSOR_SERVICE);
680+
681+
Sensor stepSensor = manager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
682+
Sensor detectorSensor = manager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
683+
684+
if (stepSensor != null) {
685+
manager.registerListener(mSensorEventListener, stepSensor, SensorManager.SENSOR_DELAY_NORMAL);
686+
}
687+
688+
if (detectorSensor != null) {
689+
manager.registerListener(mSensorEventListener, detectorSensor, SensorManager.SENSOR_DELAY_NORMAL);
690+
}
691+
}
607692
}

app/src/main/java/com/hjq/permissions/demo/PermissionNameConvert.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public static String listToString(List<String> hints) {
5050
@NonNull
5151
public static List<String> permissionsToNames(Context context, List<String> permissions) {
5252
List<String> permissionNames = new ArrayList<>();
53+
if (context == null) {
54+
return permissionNames;
55+
}
5356
if (permissions == null) {
5457
return permissionNames;
5558
}
@@ -179,14 +182,17 @@ public static List<String> permissionsToNames(Context context, List<String> perm
179182
case Permission.WRITE_CALL_LOG:
180183
case Permission.PROCESS_OUTGOING_CALLS: {
181184
String hint = context.getString(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ?
182-
R.string.common_permission_call_log : R.string.common_permission_phone);
185+
R.string.common_permission_call_log :
186+
R.string.common_permission_phone);
183187
if (!permissionNames.contains(hint)) {
184188
permissionNames.add(hint);
185189
}
186190
break;
187191
}
188192
case Permission.ACTIVITY_RECOGNITION: {
189-
String hint = context.getString(R.string.common_permission_activity_recognition);
193+
String hint = context.getString(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ?
194+
R.string.common_permission_activity_recognition_30 :
195+
R.string.common_permission_activity_recognition_29);
190196
if (!permissionNames.contains(hint)) {
191197
permissionNames.add(hint);
192198
}
@@ -300,6 +306,13 @@ public static List<String> permissionsToNames(Context context, List<String> perm
300306
}
301307
break;
302308
}
309+
case Permission.PICTURE_IN_PICTURE: {
310+
String hint = context.getString(R.string.common_permission_picture_in_picture);
311+
if (!permissionNames.contains(hint)) {
312+
permissionNames.add(hint);
313+
}
314+
break;
315+
}
303316
default:
304317
break;
305318
}

0 commit comments

Comments
 (0)