Commit 6e786350 by Marcel Garus

implement discussed changes

parent 4a48af08
......@@ -65,7 +65,7 @@ class _PermissionState extends State<PermissionWidget> {
_PermissionState(this._permission);
final Permission _permission;
PermissionStatus _permissionStatus = PermissionStatus.unknown;
PermissionStatus _permissionStatus = PermissionStatus.undetermined;
@override
void initState() {
......
......@@ -21,89 +21,78 @@ extension PermissionActions on Permission {
///
/// This is only implemented on Android, calling this on iOS always returns
/// [false].
Future<bool> get shouldShowRequestRationale {
Future<bool> get shouldShowRequestRationale async {
if (!Platform.isAndroid) {
return Future.value(false);
return false;
}
return _handler.shouldShowRequestPermissionRationale(this);
}
/// Request the user for access to this permission.
/// Request the user for access to this [Permission], if access hasn't already
/// been grant access before.
///
/// Returns the new [PermissionStatus].
Future<PermissionStatus> request() async {
return (await [this].request())[this];
}
/// Request the user for access to this permission if it [isDenied].
///
/// Returns the new [PermissionStatus].
Future<PermissionStatus> requestIfDenied() async {
final status = await this.status;
if (status.isDenied) {
return request();
}
return status;
}
}
/// Shortcuts for checking the [status] of a permission.
/// Shortcuts for checking the [status] of a [Permission].
extension PermissionCheckShortcuts on Permission {
/// If the user denied access to the requested feature.
Future<bool> get isDenied => status.isDenied;
/// If this permission was never requested before.
Future<bool> get isUndetermined => status.isUndetermined;
/// If the user granted access to the requested feature.
/// If the user granted this permission.
Future<bool> get isGranted => status.isGranted;
/// If the OS denied access to the requested feature. The user cannot change
/// this app's status, possibly due to active restrictions such as parental
/// controls being in place.
/// If the user denied this permission.
Future<bool> get isDenied => status.isDenied;
/// If the OS denied this permission. The user cannot change the status,
/// possibly due to active restrictions such as parental controls being in
/// place.
/// *Only supported on iOS.*
Future<bool> get isRestricted => status.isRestricted;
/// If the permission is in an unknown state.
Future<bool> get isUnknown => status.isUnknown;
/// If the user denied access to the requested feature and selected to never
/// again show a request for this permission.
/// If the user denied this permission and selected to never again show a
/// request for it. The user may still change the permission's status in the
/// device settings.
/// *Only supported on Android.*
Future<bool> get isNeverAskedAgain => status.isNeverAskAgain;
Future<bool> get isPermanentlyDenied => status.isPermanentlyDenied;
}
/// Actions that apply only to permissions that have an associated service.
extension ServicePermissionActions on ServicePermission {
/// The current status of the service associated with this permission.
extension ServicePermissionActions on PermissionWithService {
/// Checks the current status of the service associated with this permission.
///
/// Notes about specific permissions:
/// - **[Permission.phone]**
/// - Android:
/// - The method will return [ServiceStatus.notApplicable] when:
/// 1. the device lacks the TELEPHONY feature
/// 2. TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE
/// 3. when no Intents can be resolved to handle the `tel:` scheme
/// - the device lacks the TELEPHONY feature
/// - TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE
/// - when no Intents can be resolved to handle the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// 1. the SIM card is missing
/// - the SIM card is missing
/// - iOS:
/// - The method will return [ServiceStatus.notApplicable] when:
/// 1. the native code can not find a handler for the `tel:` scheme
/// - the native code can not find a handler for the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// 1. the mobile network code (MNC) is either 0 or 65535. See
/// - the mobile network code (MNC) is either 0 or 65535. See
/// https://stackoverflow.com/a/11595365 for details
/// - **PLEASE NOTE that this is still not a perfect indication** of the
/// devices' capability to place & connect phone calls
/// as it also depends on the network condition.
/// device's capability to place & connect phone calls as it also depends
/// on the network condition.
Future<ServiceStatus> get serviceStatus => _handler.checkServiceStatus(this);
}
/// Actions that can be taken on a [List] of [Permission]s.
extension PermissionListActions on List<Permission> {
/// Request the user for access to this list of permissions.
/// Requests the user for access to these permissions, if they haven't already
/// been granted before.
///
/// Returns a [Map] containing the [PermissionStatus] per requested
/// [Permission].
Future<Map<Permission, PermissionStatus>> request() {
return _handler.requestPermissions(this);
}
/// Returns a [Map] containing the status per requested [Permission].
Future<Map<Permission, PermissionStatus>> request() =>
_handler.requestPermissions(this);
}
......@@ -8,12 +8,9 @@ import 'utils/codec.dart';
const MethodChannel _methodChannel =
MethodChannel('flutter.baseflow.com/permissions/methods');
/// An implementation of [PermissionHandlerPlatform] that uses method channels.
/// An implementation of [PermissionHandlerPlatform] that uses [MethodChannel]s.
class MethodChannelPermissionHandler extends PermissionHandlerPlatform {
/// Check current permission status.
///
/// Returns a [Future] containing the current permission status for the
/// supplied [Permission].
/// Checks the current status of the given [Permission].
Future<PermissionStatus> checkPermissionStatus(Permission permission) async {
final status = await _methodChannel.invokeMethod(
'checkPermissionStatus', permission.value);
......@@ -21,29 +18,27 @@ class MethodChannelPermissionHandler extends PermissionHandlerPlatform {
return Codec.decodePermissionStatus(status);
}
/// Check current service status.
///
/// Returns a [Future] containing the current service status for the supplied
/// Checks the current status of the service associated with the given
/// [Permission].
///
/// Notes about specific permissions:
/// - **[Permission.phone]**
/// - Android:
/// - The method will return [ServiceStatus.notApplicable] when:
/// 1. the device lacks the TELEPHONY feature
/// 1. TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE
/// 1. when no Intents can be resolved to handle the `tel:` scheme
/// - the device lacks the TELEPHONY feature
/// - TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE
/// - when no Intents can be resolved to handle the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// 1. the SIM card is missing
/// - the SIM card is missing
/// - iOS:
/// - The method will return [ServiceStatus.notApplicable] when:
/// 1. the native code can not find a handler for the `tel:` scheme
/// - the native code can not find a handler for the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// 1. the mobile network code (MNC) is either 0 or 65535. See
/// - the mobile network code (MNC) is either 0 or 65535. See
/// https://stackoverflow.com/a/11595365 for details
/// - **PLEASE NOTE that this is still not a perfect indication** of the
/// devices' capability to place & connect phone calls
/// as it also depends on the network condition.
/// device's capability to place & connect phone calls as it also depends
/// on the network condition.
Future<ServiceStatus> checkServiceStatus(Permission permission) async {
final status = await _methodChannel.invokeMethod(
'checkServiceStatus', permission.value);
......@@ -51,19 +46,18 @@ class MethodChannelPermissionHandler extends PermissionHandlerPlatform {
return Codec.decodeServiceStatus(status);
}
/// Open the App settings page.
/// Opens the app settings page.
///
/// Returns [true] if the app settings page could be opened,
/// otherwise [false] is returned.
/// Returns [true] if the app settings page could be opened, otherwise [false].
Future<bool> openAppSettings() async {
final hasOpened = await _methodChannel.invokeMethod('openAppSettings');
return hasOpened;
final wasOpened = await _methodChannel.invokeMethod('openAppSettings');
return wasOpened;
}
/// Request the user for access to the supplied list of Permissions.
/// Requests the user for access to the supplied list of [Permission]s, if
/// they have not already been granted before.
///
/// Returns a [Map] containing the status per requested Permission.
/// Returns a [Map] containing the status per requested [Permission].
Future<Map<Permission, PermissionStatus>> requestPermissions(
List<Permission> permissions) async {
final data = Codec.encodePermissions(permissions);
......@@ -73,7 +67,7 @@ class MethodChannelPermissionHandler extends PermissionHandlerPlatform {
return Codec.decodePermissionRequestResult(Map<int, int>.from(status));
}
/// Request to see if you should show a rationale for requesting permission.
/// Checks if you should show a rationale for requesting permission.
///
/// This method is only implemented on Android, calling this on iOS always
/// returns [false].
......
import '../../../permission_handler_platform_interface.dart';
/// Provides utility methods for encoding messages that are send on the Flutter
/// Provides utility methods for encoding messages that are sent on the Flutter
/// message channel.
class Codec {
/// Converts the supplied integer value into a [PermissionStatus] instance.
/// Converts the given [value] into a [PermissionStatus] instance.
static PermissionStatus decodePermissionStatus(int value) {
return PermissionStatusValue.statusByValue(value);
}
/// Converts the supplied integer value into a [ServiceStatus] instance.
/// Converts the given [value] into a [ServiceStatus] instance.
static ServiceStatus decodeServiceStatus(int value) {
return ServiceStatusValue.statusByValue(value);
}
/// Converts the supplied [Map] of integers into a [Map] of [Permission] key
/// and [PermissionStatus] value instances.
/// Converts the given [Map] of [int]s into a [Map] with [Permission]s as
/// keys and their respective [PermissionStatus] as value.
static Map<Permission, PermissionStatus> decodePermissionRequestResult(
Map<int, int> value) {
return value.map((key, value) => MapEntry<Permission, PermissionStatus>(
Permission.byValue(key), PermissionStatusValue.statusByValue(value)));
}
/// Converts the supplied [List] containing [Permission] instances into
/// a [List] containing integers which can be used to send on the Flutter
/// method channel.
/// Converts the given [List] of [Permission]s into a [List] of [int]s which
/// can be sent on the Flutter method channel.
static List<int> encodePermissions(List<Permission> permissions) {
return permissions.map((it) => it.value).toList();
}
......
part of permission_handler_platform_interface;
/// The interface that implementations of permission_handler must implement.
/// The interface that implementations of `permission_handler` must implement.
///
/// Platform implementations should extend this class rather than implement it
/// as `permission_handler` does not consider newly added methods to be
......@@ -28,59 +28,54 @@ abstract class PermissionHandlerPlatform extends PlatformInterface {
_instance = instance;
}
/// Check current permission status.
///
/// Returns a [Future] containing the current permission status for the
/// supplied [PermissionGroup].
/// Checks the current status of the given [Permission].
Future<PermissionStatus> checkPermissionStatus(Permission permission) {
throw UnimplementedError(
'checkPermissionStatus() has not been implemented.');
}
/// Check current service status.
///
/// Returns a [Future] containing the current service status for the supplied
/// Checks the current status of the service associated with the given
/// [Permission].
///
/// Notes about specific [Permission]s:
/// Notes about specific permissions:
/// - **[Permission.phone]**
/// - Android:
/// - The method will return [ServiceStatus.notApplicable] when:
/// 1. the device lacks the TELEPHONY feature
/// 1. TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE
/// 1. when no Intents can be resolved to handle the `tel:` scheme
/// - the device lacks the TELEPHONY feature
/// - TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE
/// - when no Intents can be resolved to handle the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// 1. the SIM card is missing
/// - the SIM card is missing
/// - iOS:
/// - The method will return [ServiceStatus.notApplicable] when:
/// 1. the native code can not find a handler for the `tel:` scheme
/// - the native code can not find a handler for the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// 1. the mobile network code (MNC) is either 0 or 65535. See
/// - the mobile network code (MNC) is either 0 or 65535. See
/// https://stackoverflow.com/a/11595365 for details
/// - **PLEASE NOTE that this is still not a perfect indication** of the
/// devices' capability to place & connect phone calls
/// as it also depends on the network condition.
/// device's capability to place & connect phone calls as it also depends
/// on the network condition.
Future<ServiceStatus> checkServiceStatus(Permission permission) {
throw UnimplementedError('checkServiceStatus() has not been implemented.');
}
/// Open the App settings page.
/// Opens the app settings page.
///
/// Returns [true] if the app settings page could be opened,
/// otherwise [false] is returned.
/// Returns [true] if the app settings page could be opened, otherwise [false].
Future<bool> openAppSettings() {
throw UnimplementedError('openAppSettings() has not been implemented.');
}
/// Request the user for access to the supplied list of permissiongroups.
/// Requests the user for access to the supplied list of [Permission]s, if
/// they have not already been granted before.
///
/// Returns a [Map] containing the status per requested permissiongroup.
/// Returns a [Map] containing the status per requested [Permission].
Future<Map<Permission, PermissionStatus>> requestPermissions(
List<Permission> permissions) {
throw UnimplementedError('requestPermissions() has not been implemented.');
}
/// Request to see if you should show a rationale for requesting permission.
/// Checks if you should show a rationale for requesting permission.
///
/// This method is only implemented on Android, calling this on iOS always
/// returns [false].
......
......@@ -2,25 +2,26 @@ part of permission_handler_platform_interface;
/// Defines the state of a [Permission].
enum PermissionStatus {
/// The user denied access to the requested feature.
denied,
/// The permission wasn't requested yet.
undetermined,
/// The user granted access to the requested feature.
granted,
/// The user denied access to the requested feature.
denied,
/// The OS denied access to the requested feature. The user cannot change
/// this app's status, possibly due to active restrictions such as parental
/// controls being in place.
/// *Only supported on iOS.*
restricted,
/// The permission is in an unknown state.
unknown,
/// The user denied access to the requested feature and selected to never
/// again show a request for this permission.
/// again show a request for this permission. The user may still change the
/// permission status in the settings.
/// *Only supported on Android.*
neverAskAgain,
permanentlyDenied,
}
extension PermissionStatusValue on PermissionStatus {
......@@ -32,9 +33,9 @@ extension PermissionStatusValue on PermissionStatus {
return 1;
case PermissionStatus.restricted:
return 2;
case PermissionStatus.unknown:
case PermissionStatus.undetermined:
return 3;
case PermissionStatus.neverAskAgain:
case PermissionStatus.permanentlyDenied:
return 4;
default:
throw UnimplementedError();
......@@ -46,52 +47,55 @@ extension PermissionStatusValue on PermissionStatus {
PermissionStatus.denied,
PermissionStatus.granted,
PermissionStatus.restricted,
PermissionStatus.unknown,
PermissionStatus.neverAskAgain,
PermissionStatus.undetermined,
PermissionStatus.permanentlyDenied,
][value];
}
}
extension PermissionStatusGetters on PermissionStatus {
/// If the user denied access to the requested feature.
bool get isDenied => this == PermissionStatus.denied;
/// If the permission was never requested before.
bool get isUndetermined => this == PermissionStatus.undetermined;
/// If the user granted access to the requested feature.
bool get isGranted => this == PermissionStatus.granted;
/// If the user denied access to the requested feature.
bool get isDenied => this == PermissionStatus.denied;
/// If the OS denied access to the requested feature. The user cannot change
/// this app's status, possibly due to active restrictions such as parental
/// controls being in place.
/// *Only supported on iOS.*
bool get isRestricted => this == PermissionStatus.restricted;
/// If the permission is in an unknown state.
bool get isUnknown => this == PermissionStatus.unknown;
/// If the user denied access to the requested feature and selected to never
/// again show a request for this permission.
/// again show a request for this permission. The user may still change the
/// permission status in the settings.
/// *Only supported on Android.*
bool get isNeverAskAgain => this == PermissionStatus.neverAskAgain;
bool get isPermanentlyDenied => this == PermissionStatus.permanentlyDenied;
}
extension FuturePermissionStatusGetters on Future<PermissionStatus> {
/// If the user denied access to the requested feature.
Future<bool> get isDenied async => (await this).isDenied;
/// If the permission was never requested before.
Future<bool> get isUndetermined async => (await this).isUndetermined;
/// If the user granted access to the requested feature.
Future<bool> get isGranted async => (await this).isGranted;
/// If the user denied access to the requested feature.
Future<bool> get isDenied async => (await this).isDenied;
/// If the OS denied access to the requested feature. The user cannot change
/// this app's status, possibly due to active restrictions such as parental
/// controls being in place.
/// *Only supported on iOS.*
Future<bool> get isRestricted async => (await this).isRestricted;
/// If the permission is in an unknown state.
Future<bool> get isUnknown async => (await this).isUnknown;
/// If the user denied access to the requested feature and selected to never
/// again show a request for this permission.
/// again show a request for this permission. The user may still change the
/// permission status in the settings.
/// *Only supported on Android.*
Future<bool> get isNeverAskAgain async => (await this).isNeverAskAgain;
Future<bool> get isPermanentlyDenied async =>
(await this).isPermanentlyDenied;
}
part of permission_handler_platform_interface;
/// A special kind of permission used to access a service. Additionally to
/// normal [Permission]s, you can also query the status of the related service.
class ServicePermission extends Permission {
const ServicePermission._(int value) : super._(value);
/// A special kind of permission used to access a service. Additionally to the
/// actions that normal [Permission]s have, you can also query the status of
/// the related service.
class PermissionWithService extends Permission {
const PermissionWithService._(int value) : super._(value);
}
/// Defines the permissions which can be checked and requested.
......@@ -28,17 +29,17 @@ class Permission {
/// Android: Fine and Coarse Location
/// iOS: CoreLocation (Always and WhenInUse)
static const location = ServicePermission._(3);
static const location = PermissionWithService._(3);
/// Android:
/// When running on Android < Q: Fine and Coarse Location
/// When running on Android Q and above: Background Location Permission
/// iOS: CoreLocation - Always
static const locationAlways = ServicePermission._(4);
static const locationAlways = PermissionWithService._(4);
/// Android: Fine and Coarse Location
/// iOS: CoreLocation - WhenInUse
static const locationWhenInUse = ServicePermission._(5);
static const locationWhenInUse = PermissionWithService._(5);
/// Android: None
/// iOS: MPMediaLibrary
......@@ -144,5 +145,5 @@ class Permission {
];
@override
String toString() => 'PermissionGroup.${_names[value]}';
String toString() => 'Permission.${_names[value]}';
}
......@@ -7,11 +7,9 @@ enum ServiceStatus {
/// The service for the permission is enabled.
enabled,
/// The permission does not have an associated service.
/// The permission does not have an associated service on the current
/// platform.
notApplicable,
/// The state of the service could not be determined.
unknown,
}
extension ServiceStatusValue on ServiceStatus {
......@@ -23,8 +21,6 @@ extension ServiceStatusValue on ServiceStatus {
return 1;
case ServiceStatus.notApplicable:
return 2;
case ServiceStatus.unknown:
return 3;
default:
throw UnimplementedError();
}
......@@ -35,7 +31,6 @@ extension ServiceStatusValue on ServiceStatus {
ServiceStatus.disabled,
ServiceStatus.enabled,
ServiceStatus.notApplicable,
ServiceStatus.unknown,
][value];
}
}
......@@ -47,11 +42,9 @@ extension ServiceStatusGetters on ServiceStatus {
/// If the service for the permission is enabled.
bool get isEnabled => this == ServiceStatus.enabled;
/// If the permission does not have an associated service.
/// If the permission does not have an associated service on the current
/// platform.
bool get isNotApplicable => this == ServiceStatus.notApplicable;
/// If the state of the service could not be determined.
bool get isUnknown => this == ServiceStatus.unknown;
}
extension FutureServiceStatusGetters on Future<ServiceStatus> {
......@@ -61,9 +54,7 @@ extension FutureServiceStatusGetters on Future<ServiceStatus> {
/// If the service for the permission is enabled.
Future<bool> get isEnabled async => (await this).isEnabled;
/// If the permission does not have an associated service.
/// If the permission does not have an associated service on the current
/// platform.
Future<bool> get isNotApplicable async => (await this).isNotApplicable;
/// If the state of the service could not be determined.
Future<bool> get isUnknown async => (await this).isUnknown;
}
......@@ -18,5 +18,5 @@ dev_dependencies:
effective_dart: ^1.2.1
environment:
sdk: ">=2.0.0-dev.28.0 <3.0.0"
flutter: ">=1.9.1+hotfix.4 <2.0.0"
\ No newline at end of file
sdk: ">=2.6.0 <3.0.0"
flutter: ">=1.9.1+hotfix.4 <2.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