Commit 72d3dde2 by Maurits van Beusekom Committed by GitHub

Merge pull request #587 from InvictusApps/master

Added iOS Critical Alerts Permission Strategy + Android Access Notification Policy
parents ea1097ad b7083aa3
## 8.1.0
* Added support for iOS 12+ Critical Alerts permission requesting.
* NOTE: This requires applying to Apple and recieving a special entitlement from them inorder to work. See [this article](https://medium.com/@shashidharyamsani/implementing-ios-critical-alerts-7d82b4bb5026) for an explination on how to use Critical Alerts.
* Added support for Android M+ Access Notification Policy permission requesting (ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS).
* Note: This opens a general page in settings, not specific to the package.
## 8.0.1
* Updated API documentation for the `PermissionStatus.permanentlyDenied` status.
......
......@@ -110,7 +110,10 @@ You must list permission you want to use in your application :
# 'PERMISSION_BLUETOOTH=1',
## dart: PermissionGroup.appTrackingTransparency
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1'
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
## dart: PermissionGroup.criticalAlerts
# 'PERMISSION_CRITICAL_ALERTS=1'
]
end
......@@ -140,6 +143,7 @@ You must list permission you want to use in your application :
| PermissionGroup.sensors | NSMotionUsageDescription | PERMISSION_SENSORS |
| PermissionGroup.bluetooth | NSBluetoothAlwaysUsageDescription, NSBluetoothPeripheralUsageDescription | PERMISSION_BLUETOOTH |
| PermissionGroup.appTrackingTransparency | NSUserTrackingUsageDescription | PERMISSION_APP_TRACKING_TRANSPARENCY |
| PermissionGroup.criticalAlerts | PermissionGroupCriticalAlerts | PERMISSION_CRITICAL_ALERTS |
4. Clean & Rebuild
</details>
......@@ -219,6 +223,7 @@ The following permissions will show no dialog, but will open the corresponding s
- manageExternalStorage
- systemAlertWindow
- requestInstallPackages
- accessNotificationPolicy
## Issues
......
......@@ -12,6 +12,7 @@ final class PermissionConstants {
static final int PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE = 210;
static final int PERMISSION_CODE_SYSTEM_ALERT_WINDOW = 211;
static final int PERMISSION_CODE_REQUEST_INSTALL_PACKAGES = 212;
static final int PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY = 213;
//PERMISSION_GROUP
static final int PERMISSION_GROUP_CALENDAR = 0;
......@@ -39,6 +40,9 @@ final class PermissionConstants {
static final int PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE = 22;
static final int PERMISSION_GROUP_SYSTEM_ALERT_WINDOW = 23;
static final int PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES = 24;
static final int PERMISSION_GROUP_APP_TRACK_TRANSPARENCY = 25;
static final int PERMISSION_GROUP_CRITICAL_ALERTS = 26;
static final int PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY = 27;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
......@@ -65,7 +69,8 @@ final class PermissionConstants {
PERMISSION_GROUP_BLUETOOTH,
PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE,
PERMISSION_GROUP_SYSTEM_ALERT_WINDOW,
PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES
PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES,
PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY
})
@interface PermissionGroup {
}
......
package com.baseflow.permissionhandler;
import android.app.Activity;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
......@@ -38,7 +41,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
if (requestCode != PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS &&
requestCode != PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE &&
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) {
return false;
}
......@@ -73,6 +77,16 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
} else {
return false;
}
} else if (requestCode == PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager notificationManager = (NotificationManager) activity.getSystemService(Application.NOTIFICATION_SERVICE);
status = notificationManager.isNotificationPolicyAccessGranted()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY;
} else {
return false;
}
} else {
return false;
}
......@@ -255,6 +269,10 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
executeIntent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY) {
executeSimpleIntent(
Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS,
PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY);
} else {
permissionsToRequest.addAll(names);
}
......@@ -298,7 +316,7 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
//if no permissions were found then there is an issue and permission is not set in Android manifest
if (names.size() == 0) {
Log.d(PermissionConstants.LOG_TAG, "No permissions found in manifest for: " + permission);
Log.d(PermissionConstants.LOG_TAG, "No permissions found in manifest for: " + names + permission);
// On Android below M, the android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS flag in AndroidManifest.xml
// may be ignored and not visible to the App as it's a new permission setting as a whole.
......@@ -365,6 +383,15 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
}
}
if (permission == PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Application.NOTIFICATION_SERVICE);
return notificationManager.isNotificationPolicyAccessGranted()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
}
final int permissionStatus = ContextCompat.checkSelfPermission(context, name);
if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PermissionConstants.PERMISSION_STATUS_DENIED;
......@@ -382,6 +409,10 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
activity.startActivityForResult(intent, requestCode);
}
private void executeSimpleIntent(String action, int requestCode) {
activity.startActivityForResult(new Intent(action), requestCode);
}
void shouldShowRequestPermissionRationale(
int permission,
Activity activity,
......
......@@ -67,6 +67,8 @@ public class PermissionUtils {
return PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW;
case Manifest.permission.REQUEST_INSTALL_PACKAGES:
return PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES;
case Manifest.permission.ACCESS_NOTIFICATION_POLICY:
return PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY;
default:
return PermissionConstants.PERMISSION_GROUP_UNKNOWN;
}
......@@ -239,7 +241,12 @@ public class PermissionUtils {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.REQUEST_INSTALL_PACKAGES ))
permissionNames.add(Manifest.permission.REQUEST_INSTALL_PACKAGES);
break;
case PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY:
// The REQUEST_NOTIFICATION_POLICY permission is introduced in Android M, meaning we should
// not handle permissions on pre Android M devices.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_NOTIFICATION_POLICY ))
permissionNames.add(Manifest.permission.ACCESS_NOTIFICATION_POLICY);
break;
case PermissionConstants.PERMISSION_GROUP_NOTIFICATION:
case PermissionConstants.PERMISSION_GROUP_MEDIA_LIBRARY:
case PermissionConstants.PERMISSION_GROUP_PHOTOS:
......
......@@ -72,6 +72,9 @@
<!-- Permissions options for the `request install packages` group -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- Permissions options for the `access notification policy` group -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
<application
android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher"
......
......@@ -46,4 +46,7 @@
<!-- Permissions options for the `sensors` group -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- Permissions options for the `access notification policy` group -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
</manifest>
......@@ -82,7 +82,10 @@ post_install do |installer|
'PERMISSION_BLUETOOTH=1',
## dart: PermissionGroup.appTrackingTransparency
'PERMISSION_APP_TRACKING_TRANSPARENCY=1'
'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
## dart: PermissionGroup.criticalAlerts
'PERMISSION_CRITICAL_ALERTS=1',
]
end
......
......@@ -44,14 +44,16 @@ class _PermissionHandlerWidgetState extends State<PermissionHandlerWidget> {
permission != Permission.activityRecognition &&
permission != Permission.manageExternalStorage &&
permission != Permission.systemAlertWindow &&
permission != Permission.requestInstallPackages;
permission != Permission.requestInstallPackages &&
permission != Permission.accessNotificationPolicy;
} else {
return permission != Permission.unknown &&
permission != Permission.mediaLibrary &&
permission != Permission.photos &&
permission != Permission.photosAddOnly &&
permission != Permission.reminders &&
permission != Permission.appTrackingTransparency;
permission != Permission.appTrackingTransparency &&
permission != Permission.criticalAlerts;
}
})
.map((permission) => PermissionWidget(permission))
......
......@@ -102,6 +102,14 @@
#define PERMISSION_APP_TRACKING_TRANSPARENCY 0
#endif
// ios: PermissionGroupCriticalAlerts
// Info.plist: UNAuthorizationOptionCriticalAlert
// dart: PermissionGroup.criticalAlerts
#ifndef PERMISSION_CRITICAL_ALERTS
#define PERMISSION_CRITICAL_ALERTS 0
#endif
typedef NS_ENUM(int, PermissionGroup) {
PermissionGroupCalendar = 0,
PermissionGroupCamera,
......@@ -128,7 +136,9 @@ typedef NS_ENUM(int, PermissionGroup) {
PermissionGroupManageExternalStorage,
PermissionGroupSystemAlertWindow,
PermissionGroupRequestInstallPackages,
PermissionGroupAppTrackingTransparency
PermissionGroupAppTrackingTransparency,
PermissionGroupCriticalAlerts,
PermissionGroupAccessNotificationPolicy,
};
typedef NS_ENUM(int, PermissionStatus) {
......
......@@ -24,6 +24,7 @@
#import "StoragePermissionStrategy.h"
#import "UnknownPermissionStrategy.h"
#import "NotificationPermissionStrategy.h"
#import "CriticalAlertsPermissionStrategy.h"
#import "PermissionHandlerEnums.h"
#import "Codec.h"
......
......@@ -127,6 +127,8 @@
return [BluetoothPermissionStrategy new];
case PermissionGroupAppTrackingTransparency:
return [AppTrackingTransparencyPermissionStrategy new];
case PermissionGroupCriticalAlerts:
return [CriticalAlertsPermissionStrategy new];
default:
return [UnknownPermissionStrategy new];
}
......
//
// CriticalAlertsPermissionStrategy.h
// permission_handler
//
// Created by Neal Soni on 2021/6/8.
//
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
#if PERMISSION_CRITICAL_ALERTS
#import <UserNotifications/UserNotifications.h>
@interface CriticalAlertsPermissionStrategy : NSObject <PermissionStrategy>
@end
#else
#import "UnknownPermissionStrategy.h"
@interface CriticalAlertsPermissionStrategy : UnknownPermissionStrategy
@end
#endif
//
// CriticalAlertsPermissionStrategy.m
// permission_handler
//
// Created by Neal Soni on 2021/6/8.
//
#import "CriticalAlertsPermissionStrategy.h"
#if PERMISSION_CRITICAL_ALERTS
@implementation CriticalAlertsPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return [CriticalAlertsPermissionStrategy permissionStatus];
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusNotApplicable;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if (status != PermissionStatusDenied) {
completionHandler(status);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if(@available(iOS 12.0, *)) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNAuthorizationOptions authorizationOptions = 0;
authorizationOptions += UNAuthorizationOptionCriticalAlert;
[center requestAuthorizationWithOptions:(authorizationOptions) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (error != nil || !granted) {
completionHandler(PermissionStatusPermanentlyDenied);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
completionHandler(PermissionStatusGranted);
});
}];
} else {
completionHandler(PermissionStatusPermanentlyDenied);
}
});
}
+ (PermissionStatus)permissionStatus {
__block PermissionStatus permissionStatus = PermissionStatusGranted;
if (@available(iOS 12 , *)) {
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.criticalAlertSetting == UNAuthorizationStatusDenied) {
permissionStatus = PermissionStatusPermanentlyDenied;
} else if (settings.criticalAlertSetting == UNAuthorizationStatusNotDetermined) {
permissionStatus = PermissionStatusDenied;
}
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
} else {
permissionStatus = PermissionStatusPermanentlyDenied;
}
return permissionStatus;
}
@end
#else
@implementation CriticalAlertsPermissionStrategy
@end
#endif
name: permission_handler
description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
version: 8.0.1
version: 8.1.0
homepage: https://github.com/baseflowit/flutter-permission-handler
flutter:
......@@ -16,7 +16,7 @@ dependencies:
flutter:
sdk: flutter
meta: ^1.3.0
permission_handler_platform_interface: ^3.5.1
permission_handler_platform_interface: ^3.6.0
dev_dependencies:
flutter_test:
......
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