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 ## 8.0.1
* Updated API documentation for the `PermissionStatus.permanentlyDenied` status. * Updated API documentation for the `PermissionStatus.permanentlyDenied` status.
......
...@@ -110,7 +110,10 @@ You must list permission you want to use in your application : ...@@ -110,7 +110,10 @@ You must list permission you want to use in your application :
# 'PERMISSION_BLUETOOTH=1', # 'PERMISSION_BLUETOOTH=1',
## dart: PermissionGroup.appTrackingTransparency ## dart: PermissionGroup.appTrackingTransparency
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1' # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
## dart: PermissionGroup.criticalAlerts
# 'PERMISSION_CRITICAL_ALERTS=1'
] ]
end end
...@@ -140,6 +143,7 @@ You must list permission you want to use in your application : ...@@ -140,6 +143,7 @@ You must list permission you want to use in your application :
| PermissionGroup.sensors | NSMotionUsageDescription | PERMISSION_SENSORS | | PermissionGroup.sensors | NSMotionUsageDescription | PERMISSION_SENSORS |
| PermissionGroup.bluetooth | NSBluetoothAlwaysUsageDescription, NSBluetoothPeripheralUsageDescription | PERMISSION_BLUETOOTH | | PermissionGroup.bluetooth | NSBluetoothAlwaysUsageDescription, NSBluetoothPeripheralUsageDescription | PERMISSION_BLUETOOTH |
| PermissionGroup.appTrackingTransparency | NSUserTrackingUsageDescription | PERMISSION_APP_TRACKING_TRANSPARENCY | | PermissionGroup.appTrackingTransparency | NSUserTrackingUsageDescription | PERMISSION_APP_TRACKING_TRANSPARENCY |
| PermissionGroup.criticalAlerts | PermissionGroupCriticalAlerts | PERMISSION_CRITICAL_ALERTS |
4. Clean & Rebuild 4. Clean & Rebuild
</details> </details>
...@@ -219,6 +223,7 @@ The following permissions will show no dialog, but will open the corresponding s ...@@ -219,6 +223,7 @@ The following permissions will show no dialog, but will open the corresponding s
- manageExternalStorage - manageExternalStorage
- systemAlertWindow - systemAlertWindow
- requestInstallPackages - requestInstallPackages
- accessNotificationPolicy
## Issues ## Issues
......
...@@ -12,6 +12,7 @@ final class PermissionConstants { ...@@ -12,6 +12,7 @@ final class PermissionConstants {
static final int PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE = 210; static final int PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE = 210;
static final int PERMISSION_CODE_SYSTEM_ALERT_WINDOW = 211; static final int PERMISSION_CODE_SYSTEM_ALERT_WINDOW = 211;
static final int PERMISSION_CODE_REQUEST_INSTALL_PACKAGES = 212; static final int PERMISSION_CODE_REQUEST_INSTALL_PACKAGES = 212;
static final int PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY = 213;
//PERMISSION_GROUP //PERMISSION_GROUP
static final int PERMISSION_GROUP_CALENDAR = 0; static final int PERMISSION_GROUP_CALENDAR = 0;
...@@ -39,6 +40,9 @@ final class PermissionConstants { ...@@ -39,6 +40,9 @@ final class PermissionConstants {
static final int PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE = 22; static final int PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE = 22;
static final int PERMISSION_GROUP_SYSTEM_ALERT_WINDOW = 23; static final int PERMISSION_GROUP_SYSTEM_ALERT_WINDOW = 23;
static final int PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES = 24; 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) @Retention(RetentionPolicy.SOURCE)
@IntDef({ @IntDef({
...@@ -65,7 +69,8 @@ final class PermissionConstants { ...@@ -65,7 +69,8 @@ final class PermissionConstants {
PERMISSION_GROUP_BLUETOOTH, PERMISSION_GROUP_BLUETOOTH,
PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE, PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE,
PERMISSION_GROUP_SYSTEM_ALERT_WINDOW, PERMISSION_GROUP_SYSTEM_ALERT_WINDOW,
PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES,
PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY
}) })
@interface PermissionGroup { @interface PermissionGroup {
} }
......
package com.baseflow.permissionhandler; package com.baseflow.permissionhandler;
import android.app.Activity; import android.app.Activity;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
...@@ -38,7 +41,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -38,7 +41,8 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
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) {
return false; return false;
} }
...@@ -73,6 +77,16 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -73,6 +77,16 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
} else { } else {
return false; 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 { } else {
return false; return false;
} }
...@@ -255,6 +269,10 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -255,6 +269,10 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
executeIntent( executeIntent(
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) {
executeSimpleIntent(
Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS,
PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY);
} else { } else {
permissionsToRequest.addAll(names); permissionsToRequest.addAll(names);
} }
...@@ -298,7 +316,7 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -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 no permissions were found then there is an issue and permission is not set in Android manifest
if (names.size() == 0) { 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 // 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. // 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, ...@@ -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); final int permissionStatus = ContextCompat.checkSelfPermission(context, name);
if (permissionStatus != PackageManager.PERMISSION_GRANTED) { if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PermissionConstants.PERMISSION_STATUS_DENIED; return PermissionConstants.PERMISSION_STATUS_DENIED;
...@@ -382,6 +409,10 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener, ...@@ -382,6 +409,10 @@ final class PermissionManager implements PluginRegistry.ActivityResultListener,
activity.startActivityForResult(intent, requestCode); activity.startActivityForResult(intent, requestCode);
} }
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,
......
...@@ -67,6 +67,8 @@ public class PermissionUtils { ...@@ -67,6 +67,8 @@ public class PermissionUtils {
return PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW; return PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW;
case Manifest.permission.REQUEST_INSTALL_PACKAGES: case Manifest.permission.REQUEST_INSTALL_PACKAGES:
return PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES; return PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES;
case Manifest.permission.ACCESS_NOTIFICATION_POLICY:
return PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY;
default: default:
return PermissionConstants.PERMISSION_GROUP_UNKNOWN; return PermissionConstants.PERMISSION_GROUP_UNKNOWN;
} }
...@@ -239,7 +241,12 @@ public class PermissionUtils { ...@@ -239,7 +241,12 @@ public class PermissionUtils {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.REQUEST_INSTALL_PACKAGES )) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.REQUEST_INSTALL_PACKAGES ))
permissionNames.add(Manifest.permission.REQUEST_INSTALL_PACKAGES); permissionNames.add(Manifest.permission.REQUEST_INSTALL_PACKAGES);
break; 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_NOTIFICATION:
case PermissionConstants.PERMISSION_GROUP_MEDIA_LIBRARY: case PermissionConstants.PERMISSION_GROUP_MEDIA_LIBRARY:
case PermissionConstants.PERMISSION_GROUP_PHOTOS: case PermissionConstants.PERMISSION_GROUP_PHOTOS:
......
...@@ -72,6 +72,9 @@ ...@@ -72,6 +72,9 @@
<!-- Permissions options for the `request install packages` group --> <!-- Permissions options for the `request install packages` group -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <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 <application
android:name="io.flutter.app.FlutterApplication" android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
......
...@@ -46,4 +46,7 @@ ...@@ -46,4 +46,7 @@
<!-- Permissions options for the `sensors` group --> <!-- Permissions options for the `sensors` group -->
<uses-permission android:name="android.permission.BODY_SENSORS" /> <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> </manifest>
...@@ -82,7 +82,10 @@ post_install do |installer| ...@@ -82,7 +82,10 @@ post_install do |installer|
'PERMISSION_BLUETOOTH=1', 'PERMISSION_BLUETOOTH=1',
## dart: PermissionGroup.appTrackingTransparency ## dart: PermissionGroup.appTrackingTransparency
'PERMISSION_APP_TRACKING_TRANSPARENCY=1' 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
## dart: PermissionGroup.criticalAlerts
'PERMISSION_CRITICAL_ALERTS=1',
] ]
end end
......
...@@ -44,14 +44,16 @@ class _PermissionHandlerWidgetState extends State<PermissionHandlerWidget> { ...@@ -44,14 +44,16 @@ class _PermissionHandlerWidgetState extends State<PermissionHandlerWidget> {
permission != Permission.activityRecognition && permission != Permission.activityRecognition &&
permission != Permission.manageExternalStorage && permission != Permission.manageExternalStorage &&
permission != Permission.systemAlertWindow && permission != Permission.systemAlertWindow &&
permission != Permission.requestInstallPackages; permission != Permission.requestInstallPackages &&
permission != Permission.accessNotificationPolicy;
} else { } else {
return permission != Permission.unknown && return permission != Permission.unknown &&
permission != Permission.mediaLibrary && permission != Permission.mediaLibrary &&
permission != Permission.photos && permission != Permission.photos &&
permission != Permission.photosAddOnly && permission != Permission.photosAddOnly &&
permission != Permission.reminders && permission != Permission.reminders &&
permission != Permission.appTrackingTransparency; permission != Permission.appTrackingTransparency &&
permission != Permission.criticalAlerts;
} }
}) })
.map((permission) => PermissionWidget(permission)) .map((permission) => PermissionWidget(permission))
......
...@@ -102,6 +102,14 @@ ...@@ -102,6 +102,14 @@
#define PERMISSION_APP_TRACKING_TRANSPARENCY 0 #define PERMISSION_APP_TRACKING_TRANSPARENCY 0
#endif #endif
// ios: PermissionGroupCriticalAlerts
// Info.plist: UNAuthorizationOptionCriticalAlert
// dart: PermissionGroup.criticalAlerts
#ifndef PERMISSION_CRITICAL_ALERTS
#define PERMISSION_CRITICAL_ALERTS 0
#endif
typedef NS_ENUM(int, PermissionGroup) { typedef NS_ENUM(int, PermissionGroup) {
PermissionGroupCalendar = 0, PermissionGroupCalendar = 0,
PermissionGroupCamera, PermissionGroupCamera,
...@@ -128,7 +136,9 @@ typedef NS_ENUM(int, PermissionGroup) { ...@@ -128,7 +136,9 @@ typedef NS_ENUM(int, PermissionGroup) {
PermissionGroupManageExternalStorage, PermissionGroupManageExternalStorage,
PermissionGroupSystemAlertWindow, PermissionGroupSystemAlertWindow,
PermissionGroupRequestInstallPackages, PermissionGroupRequestInstallPackages,
PermissionGroupAppTrackingTransparency PermissionGroupAppTrackingTransparency,
PermissionGroupCriticalAlerts,
PermissionGroupAccessNotificationPolicy,
}; };
typedef NS_ENUM(int, PermissionStatus) { typedef NS_ENUM(int, PermissionStatus) {
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#import "StoragePermissionStrategy.h" #import "StoragePermissionStrategy.h"
#import "UnknownPermissionStrategy.h" #import "UnknownPermissionStrategy.h"
#import "NotificationPermissionStrategy.h" #import "NotificationPermissionStrategy.h"
#import "CriticalAlertsPermissionStrategy.h"
#import "PermissionHandlerEnums.h" #import "PermissionHandlerEnums.h"
#import "Codec.h" #import "Codec.h"
......
...@@ -127,6 +127,8 @@ ...@@ -127,6 +127,8 @@
return [BluetoothPermissionStrategy new]; return [BluetoothPermissionStrategy new];
case PermissionGroupAppTrackingTransparency: case PermissionGroupAppTrackingTransparency:
return [AppTrackingTransparencyPermissionStrategy new]; return [AppTrackingTransparencyPermissionStrategy new];
case PermissionGroupCriticalAlerts:
return [CriticalAlertsPermissionStrategy new];
default: default:
return [UnknownPermissionStrategy new]; 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 name: permission_handler
description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions. 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 homepage: https://github.com/baseflowit/flutter-permission-handler
flutter: flutter:
...@@ -16,7 +16,7 @@ dependencies: ...@@ -16,7 +16,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
meta: ^1.3.0 meta: ^1.3.0
permission_handler_platform_interface: ^3.5.1 permission_handler_platform_interface: ^3.6.0
dev_dependencies: dev_dependencies:
flutter_test: 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