Commit 921652cd by Jeroen Weener Committed by GitHub

Handle special permissions correctly when requesting multiple permissions (#1139)

* Handle special permissions for multirequest

* Update PI docs (#1133)

* Add background location section to faq (#1134)

* Open settings screen (#1138)

* Update Android version mentions (#1135)

* Update Android version references (#1136)

* Refactor launching of special permissions

* Handle special permissions for multirequest

* Refactor launching of special permissions
parent 9da1209b
## 10.3.6
* Fixes a bug where requesting multiple permissions would crash the app if at least one of the permissions was a [special permission](https://developer.android.com/guide/topics/permissions/overview#special).
## 10.3.5 ## 10.3.5
* Fixes a bug where `Permission.ScheduleExactAlarm` was not opening the settings * Fixes a bug where `Permission.ScheduleExactAlarm` was not opening the settings
......
...@@ -15,9 +15,9 @@ import android.os.PowerManager; ...@@ -15,9 +15,9 @@ import android.os.PowerManager;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.AlarmManagerCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
...@@ -36,22 +36,39 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -36,22 +36,39 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
@Nullable @Nullable
private Activity activity; private Activity activity;
/**
* The number of pending permission requests.
* <p>
* This number is set by {@link this#requestPermissions(List, Activity, RequestPermissionsSuccessCallback, ErrorCallback)}
* and then reduced when receiving results in {@link this#onActivityResult(int, int, Intent)}
* and {@link this#onRequestPermissionsResult(int, String[], int[])}.
*/
private int pendingRequestCount;
/**
* The results of resolved permission requests.
* <p>
* This map holds the results to resolved permission requests received through
* {@link this#onActivityResult(int, int, Intent)} and
* {@link this#onRequestPermissionsResult(int, String[], int[])}.
* It is (re)initialized when new permissions are requested through
* {@link this#requestPermissions(List, Activity, RequestPermissionsSuccessCallback, ErrorCallback)}.
*/
private Map<Integer, Integer> requestResults; private Map<Integer, Integer> requestResults;
@Override @Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) { public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS && if (requestCode != PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS &&
requestCode != PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE && requestCode != PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE &&
requestCode != PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW && requestCode != PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW &&
requestCode != PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES && requestCode != PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES &&
requestCode != PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY && requestCode != PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY &&
requestCode != PermissionConstants.PERMISSION_CODE_SCHEDULE_EXACT_ALARM) { requestCode != PermissionConstants.PERMISSION_CODE_SCHEDULE_EXACT_ALARM) {
return false; return false;
} }
int status = resultCode == Activity.RESULT_OK int status = resultCode == Activity.RESULT_OK
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
int permission; int permission;
...@@ -60,8 +77,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -60,8 +77,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
} else if (requestCode == PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE) { } else if (requestCode == PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
status = Environment.isExternalStorageManager() status = Environment.isExternalStorageManager()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
} else { } else {
return false; return false;
} }
...@@ -69,8 +86,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -69,8 +86,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
} else if (requestCode == PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW) { } else if (requestCode == PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
status = Settings.canDrawOverlays(activity) status = Settings.canDrawOverlays(activity)
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW; permission = PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW;
} else { } else {
return false; return false;
...@@ -78,8 +95,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -78,8 +95,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
} else if (requestCode == PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES) { } else if (requestCode == PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
status = activity.getPackageManager().canRequestPackageInstalls() status = activity.getPackageManager().canRequestPackageInstalls()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES; permission = PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES;
} else { } else {
return false; return false;
...@@ -88,8 +105,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -88,8 +105,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager notificationManager = (NotificationManager) activity.getSystemService(Application.NOTIFICATION_SERVICE); NotificationManager notificationManager = (NotificationManager) activity.getSystemService(Application.NOTIFICATION_SERVICE);
status = notificationManager.isNotificationPolicyAccessGranted() status = notificationManager.isNotificationPolicyAccessGranted()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY; permission = PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY;
} else { } else {
return false; return false;
...@@ -98,8 +115,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -98,8 +115,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
AlarmManager alarmManager = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE); AlarmManager alarmManager = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE);
status = alarmManager.canScheduleExactAlarms() status = alarmManager.canScheduleExactAlarms()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_SCHEDULE_EXACT_ALARM; permission = PermissionConstants.PERMISSION_GROUP_SCHEDULE_EXACT_ALARM;
} else { } else {
return false; return false;
...@@ -108,28 +125,36 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -108,28 +125,36 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
return false; return false;
} }
HashMap<Integer, Integer> results = new HashMap<>(); requestResults.put(permission, status);
results.put(permission, status); pendingRequestCount--;
successCallback.onSuccess(results);
// Post result if all requests have been handled.
if (pendingRequestCount == 0) {
this.successCallback.onSuccess(requestResults);
}
return true; return true;
} }
@Override @Override
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public boolean onRequestPermissionsResult(
int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode != PermissionConstants.PERMISSION_CODE) { if (requestCode != PermissionConstants.PERMISSION_CODE) {
ongoing = false; pendingRequestCount = 0;
return false; return false;
} }
if (requestResults == null) { if (requestResults == null) {
return false; return false;
} }
for (int i = 0; i < permissions.length; i++) { for (int i = 0; i < permissions.length; i++) {
final String permissionName = permissions[i]; final String permissionName = permissions[i];
@PermissionConstants.PermissionGroup final int permission = @PermissionConstants.PermissionGroup final int permission =
PermissionUtils.parseManifestName(permissionName); PermissionUtils.parseManifestName(permissionName);
if (permission == PermissionConstants.PERMISSION_GROUP_UNKNOWN) if (permission == PermissionConstants.PERMISSION_GROUP_UNKNOWN)
continue; continue;
...@@ -139,51 +164,55 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -139,51 +164,55 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
if (permission == PermissionConstants.PERMISSION_GROUP_MICROPHONE) { if (permission == PermissionConstants.PERMISSION_GROUP_MICROPHONE) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_MICROPHONE)) { if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_MICROPHONE)) {
requestResults.put( requestResults.put(
PermissionConstants.PERMISSION_GROUP_MICROPHONE, PermissionConstants.PERMISSION_GROUP_MICROPHONE,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result)); PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
} }
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_SPEECH)) { if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_SPEECH)) {
requestResults.put( requestResults.put(
PermissionConstants.PERMISSION_GROUP_SPEECH, PermissionConstants.PERMISSION_GROUP_SPEECH,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result)); PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
} }
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS) { } else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS) {
@PermissionConstants.PermissionStatus int permissionStatus = @PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result); PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) { if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus); requestResults.put(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
} }
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION) { } else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION) {
@PermissionConstants.PermissionStatus int permissionStatus = @PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result); PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) { if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put( requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS, PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS,
permissionStatus); permissionStatus);
} }
} }
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) { if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) {
requestResults.put( requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE, PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
permissionStatus); permissionStatus);
} }
requestResults.put(permission, permissionStatus); requestResults.put(permission, permissionStatus);
} else if (!requestResults.containsKey(permission)) { } else if (!requestResults.containsKey(permission)) {
requestResults.put( requestResults.put(
permission, permission,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result)); PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
} }
PermissionUtils.updatePermissionShouldShowStatus(this.activity, permission); PermissionUtils.updatePermissionShouldShowStatus(this.activity, permission);
} }
this.successCallback.onSuccess(requestResults); pendingRequestCount -= grantResults.length;
ongoing = false;
// Post result if all requests have been handled.
if (pendingRequestCount == 0) {
this.successCallback.onSuccess(requestResults);
}
return true; return true;
} }
...@@ -202,27 +231,53 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -202,27 +231,53 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
void onSuccess(boolean shouldShowRequestPermissionRationale); void onSuccess(boolean shouldShowRequestPermissionRationale);
} }
private boolean ongoing = false;
void checkPermissionStatus( void checkPermissionStatus(
@PermissionConstants.PermissionGroup int permission, @PermissionConstants.PermissionGroup int permission,
Context context, Context context,
CheckPermissionsSuccessCallback successCallback) { CheckPermissionsSuccessCallback successCallback) {
successCallback.onSuccess(determinePermissionStatus( successCallback.onSuccess(determinePermissionStatus(
permission, permission,
context)); context));
} }
/**
* Requests the user for the provided permissions.
* <p>
* This method will throw an error if it is called before all permission requests that were
* requested in a previous call have been resolved.
* <p>
* Android distinguishes between
* <a href="https://developer.android.com/guide/topics/permissions/overview#runtime">runtime permissions</a>
* and
* <a href="https://developer.android.com/guide/topics/permissions/overview#special">special permissions</a>.
* Runtime permissions give an app additional access to restricted data or let the app perform
* restricted actions that more substantially affect the system and other apps. These
* permissions present the user with a dialog where they can choose to grant or deny the
* permission. Special permissions guard access to system resources that are particularly
* sensitive or not directly related to user privacy. These permissions are requested by sending
* an {@link Intent} to the OS. The OS will open a special settings page where the user can
* grant the permission.
* Runtime permission request results will be reported through
* {@link this#onRequestPermissionsResult(int, String[], int[])}, while special permissions
* request results will be reported through {@link this#onActivityResult(int, int, Intent)}.
* When these methods receive request results, they check whether all permissions that were
* requested through this method were handled, and if so, return the result back to Dart.
*
* @param permissions the permissions that are requested.
* @param activity the activity.
* @param successCallback the callback for returning the permission results.
* @param errorCallback the callback to call in case of an error.
*/
void requestPermissions( void requestPermissions(
List<Integer> permissions, List<Integer> permissions,
Activity activity, Activity activity,
RequestPermissionsSuccessCallback successCallback, RequestPermissionsSuccessCallback successCallback,
ErrorCallback errorCallback) { ErrorCallback errorCallback) {
if (ongoing) { if (pendingRequestCount > 0) {
errorCallback.onError( errorCallback.onError(
"PermissionHandler.PermissionManager", "PermissionHandler.PermissionManager",
"A request for permissions is already running, please wait for it to finish before doing another request (note that you can request multiple permissions at the same time)."); "A request for permissions is already running, please wait for it to finish before doing another request (note that you can request multiple permissions at the same time).");
return; return;
} }
...@@ -230,14 +285,15 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -230,14 +285,15 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity."); Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity.");
errorCallback.onError( errorCallback.onError(
"PermissionHandler.PermissionManager", "PermissionHandler.PermissionManager",
"Unable to detect current Android Activity."); "Unable to detect current Android Activity.");
return; return;
} }
this.successCallback = successCallback; this.successCallback = successCallback;
this.activity = activity; this.activity = activity;
this.requestResults = new HashMap<>(); this.requestResults = new HashMap<>();
this.pendingRequestCount = 0; // sanity check
ArrayList<String> permissionsToRequest = new ArrayList<>(); ArrayList<String> permissionsToRequest = new ArrayList<>();
for (Integer permission : permissions) { for (Integer permission : permissions) {
...@@ -274,55 +330,56 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -274,55 +330,56 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
continue; continue;
} }
// Request special permissions.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
executeIntent( launchSpecialPermission(
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS); PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) {
executeIntent( launchSpecialPermission(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE); PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW) {
executeIntent( launchSpecialPermission(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW); PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && permission == PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && permission == PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES) {
executeIntent( launchSpecialPermission(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES); PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY) {
executeSimpleIntent( launchSpecialPermission(
Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS, Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS,
PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY); PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && permission == PermissionConstants.PERMISSION_GROUP_SCHEDULE_EXACT_ALARM) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && permission == PermissionConstants.PERMISSION_GROUP_SCHEDULE_EXACT_ALARM) {
executeIntent( launchSpecialPermission(
Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM, Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
PermissionConstants.PERMISSION_CODE_SCHEDULE_EXACT_ALARM); PermissionConstants.PERMISSION_CODE_SCHEDULE_EXACT_ALARM);
} else { } else {
permissionsToRequest.addAll(names); permissionsToRequest.addAll(names);
pendingRequestCount += names.size();
} }
} }
final String[] requestPermissions = permissionsToRequest.toArray(new String[0]); // Request runtime permissions.
if (permissionsToRequest.size() > 0) { if (permissionsToRequest.size() > 0) {
ongoing = true; final String[] requestPermissions = permissionsToRequest.toArray(new String[0]);
ActivityCompat.requestPermissions( ActivityCompat.requestPermissions(
activity, activity,
requestPermissions, requestPermissions,
PermissionConstants.PERMISSION_CODE); PermissionConstants.PERMISSION_CODE);
} else { }
ongoing = false;
if (requestResults.size() > 0) { // Post results immediately if no requests are pending.
successCallback.onSuccess(requestResults); if (pendingRequestCount == 0) {
} this.successCallback.onSuccess(requestResults);
} }
} }
@PermissionConstants.PermissionStatus @PermissionConstants.PermissionStatus
private int determinePermissionStatus( private int determinePermissionStatus(
@PermissionConstants.PermissionGroup int permission, @PermissionConstants.PermissionGroup int permission,
Context context) { Context context) {
if (permission == PermissionConstants.PERMISSION_GROUP_NOTIFICATION) { if (permission == PermissionConstants.PERMISSION_GROUP_NOTIFICATION) {
return checkNotificationPermissionStatus(context); return checkNotificationPermissionStatus(context);
...@@ -333,8 +390,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -333,8 +390,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
} }
if (permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_CONNECT if (permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_CONNECT
|| permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_SCAN || permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_SCAN
|| permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_ADVERTISE){ || permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_ADVERTISE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
return checkBluetoothPermissionStatus(context); return checkBluetoothPermissionStatus(context);
} }
...@@ -399,23 +456,23 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -399,23 +456,23 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
} }
return Environment.isExternalStorageManager() return Environment.isExternalStorageManager()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
} }
if (permission == PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW) { if (permission == PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Settings.canDrawOverlays(context) return Settings.canDrawOverlays(context)
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
} }
} }
if (permission == PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES) { if (permission == PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return context.getPackageManager().canRequestPackageInstalls() return context.getPackageManager().canRequestPackageInstalls()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
} }
} }
...@@ -423,8 +480,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -423,8 +480,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Application.NOTIFICATION_SERVICE); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Application.NOTIFICATION_SERVICE);
return notificationManager.isNotificationPolicyAccessGranted() return notificationManager.isNotificationPolicyAccessGranted()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
} }
} }
...@@ -432,8 +489,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -432,8 +489,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
return alarmManager.canScheduleExactAlarms() return alarmManager.canScheduleExactAlarms()
? PermissionConstants.PERMISSION_STATUS_GRANTED ? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED; : PermissionConstants.PERMISSION_STATUS_DENIED;
} else { } else {
return PermissionConstants.PERMISSION_STATUS_GRANTED; return PermissionConstants.PERMISSION_STATUS_GRANTED;
} }
...@@ -448,29 +505,37 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -448,29 +505,37 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
return PermissionConstants.PERMISSION_STATUS_GRANTED; return PermissionConstants.PERMISSION_STATUS_GRANTED;
} }
private void executeIntent(String action, int requestCode) { /**
String packageName = activity.getPackageName(); * Launches a request for a <a href="https://developer.android.com/training/permissions/requesting-special">special permission</a>.
Intent intent = new Intent(); * <p>
intent.setAction(action); * There is a special case for {@link Settings#ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS}. See
intent.setData(Uri.parse("package:" + packageName)); * <a href="https://github.com/Baseflow/flutter-permission-handler/pull/587#discussion_r649295489">this comment</a>
* for more details.
*
* @param permissionAction the action for launching the settings page for a particular permission.
* @param requestCode a request code to verify incoming results.
*/
private void launchSpecialPermission(String permissionAction, int requestCode) {
Intent intent = new Intent(permissionAction);
if (!permissionAction.equals(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)) {
String packageName = activity.getPackageName();
intent.setData(Uri.parse("package:" + packageName));
}
activity.startActivityForResult(intent, requestCode); activity.startActivityForResult(intent, requestCode);
} pendingRequestCount++;
private void executeSimpleIntent(String action, int requestCode) {
activity.startActivityForResult(new Intent(action), requestCode);
} }
void shouldShowRequestPermissionRationale( void shouldShowRequestPermissionRationale(
int permission, int permission,
Activity activity, Activity activity,
ShouldShowRequestPermissionRationaleSuccessCallback successCallback, ShouldShowRequestPermissionRationaleSuccessCallback successCallback,
ErrorCallback errorCallback) { ErrorCallback errorCallback) {
if (activity == null) { if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity."); Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity.");
errorCallback.onError( errorCallback.onError(
"PermissionHandler.PermissionManager", "PermissionHandler.PermissionManager",
"Unable to detect current Android Activity."); "Unable to detect current Android Activity.");
return; return;
} }
......
name: permission_handler_android name: permission_handler_android
description: Permission plugin for Flutter. This plugin provides the Android API to request and check permissions. description: Permission plugin for Flutter. This plugin provides the Android API to request and check permissions.
homepage: https://github.com/baseflow/flutter-permission-handler homepage: https://github.com/baseflow/flutter-permission-handler
version: 10.3.5 version: 10.3.6
environment: environment:
sdk: ">=2.15.0 <4.0.0" sdk: ">=2.15.0 <4.0.0"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment