Commit 1bead2d6 by Maurits van Beusekom

Production release 1.0.0

parents 2fbc01a2 2d0802b7
## 0.0.1
## 1.0.0
* TODO: Describe initial release.
* Initial release.
......@@ -2,7 +2,7 @@ group 'com.baseflow.permissionhandler'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.1.51'
ext.kotlin_version = '1.2.51'
repositories {
google()
jcenter()
......
......@@ -99,7 +99,7 @@ class PermissionHandlerPlugin(private val registrar: Registrar, private var requ
call.method == "requestPermissions" -> {
if (mResult != null) {
result.error(
"ERROR_ALREADY_REQUESTED_PERMISSIONS",
"ERROR_ALREADY_REQUESTING_PERMISSIONS",
"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).",
null)
}
......@@ -220,10 +220,14 @@ class PermissionHandlerPlugin(private val registrar: Registrar, private var requ
}
}
ActivityCompat.requestPermissions(
registrar.activity(),
permissionsToRequest.toTypedArray(),
permissionCode)
if (permissionsToRequest.count() > 0) {
ActivityCompat.requestPermissions(
registrar.activity(),
permissionsToRequest.toTypedArray(),
permissionCode)
} else if (mRequestResults.count() > 0) {
processResult()
}
}
private fun handlePermissionsRequest(permissions: Array<String>, grantResults: IntArray) {
......
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$/../../android" />
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="5">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/android.iml" filepath="$PROJECT_DIR$/android.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/../../android/permission_handler.iml" filepath="$PROJECT_DIR$/../../android/permission_handler.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
......@@ -6,8 +6,28 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SENSORS" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
......
......@@ -444,7 +444,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
VERSIONING_SYSTEM = "apple-generic";
};
......@@ -471,7 +471,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.baseflow.permissionHandlerExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
VERSIONING_SYSTEM = "apple-generic";
};
......
......@@ -46,7 +46,13 @@
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Can I haz location?</string>
<string>Need location when in use</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Always and when in use!</string>
<key>NSLocationUsageDescription</key>
<string>Older devices need location.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can I haz location always?</string>
<key>NSAppleMusicUsageDescription</key>
<string>Music!</string>
<key>NSBluetoothPeripheralUsageDescription</key>
......
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_enums.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
class MyApp extends StatelessWidget {
@override
_MyAppState createState() => new _MyAppState();
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
PermissionHandler.openAppSettings();
},
)
],
),
body: new Center(
child: new ListView(
children: PermissionGroup.values
.where((PermissionGroup permission) {
if (Platform.isIOS) {
return permission != PermissionGroup.unknown &&
permission != PermissionGroup.phone &&
permission != PermissionGroup.sms &&
permission != PermissionGroup.storage;
} else {
return permission != PermissionGroup.unknown &&
permission != PermissionGroup.mediaLibrary &&
permission != PermissionGroup.photos &&
permission != PermissionGroup.reminders;
}
})
.map((PermissionGroup permission) =>
new PermissionWidget(permission))
.toList()),
),
));
}
}
class PermissionWidget extends StatefulWidget {
final PermissionGroup _permissionGroup;
const PermissionWidget(this._permissionGroup);
@override
_PermissionState createState() => _PermissionState(_permissionGroup);
}
class _MyAppState extends State<MyApp> {
String _permissionStatus = 'Unknown';
class _PermissionState extends State<PermissionWidget> {
final PermissionGroup _permissionGroup;
PermissionStatus _permissionStatus = PermissionStatus.unknown;
_PermissionState(this._permissionGroup);
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
PermissionStatus permissionStatus;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
permissionStatus = await PermissionHandler
.checkPermissionStatus(PermissionGroup.calendar);
/*
if (permissionStatus != PermissionStatus.granted) {
final bool shouldShowRationale = await PermissionHandler
.shouldShowRequestPermissionRationale(PermissionGroup.calendar);
if (shouldShowRationale) {
final Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler.requestPermissions(
<PermissionGroup>[PermissionGroup.calendar]);
if (permissions.containsKey(PermissionGroup.calendar)) {
permissionStatus = permissions[PermissionGroup.calendar];
}
}
}
*/
} on PlatformException {
permissionStatus = PermissionStatus.unknown;
}
_listenForPermissionStatus();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) {
return;
}
void _listenForPermissionStatus() async {
final PermissionStatus status =
await PermissionHandler.checkPermissionStatus(_permissionGroup);
setState(() {
_permissionStatus = permissionStatus.toString();
_permissionStatus = status;
});
}
Color getPermissionColor() {
switch (_permissionStatus) {
case PermissionStatus.denied:
return Colors.red;
case PermissionStatus.granted:
return Colors.green;
default:
return Colors.grey;
}
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: new Center(
child: new Column(
children: <Widget>[
new Text('Running on: $_permissionStatus\n'),
new RaisedButton(
child: const Text('Open settings'),
onPressed: () async =>
await PermissionHandler.openAppSettings(),
),
],
),
),
return new ListTile(
title: new Text(_permissionGroup.toString()),
subtitle: new Text(
_permissionStatus.toString(),
style: new TextStyle(color: getPermissionColor()),
),
onTap: () async {
requestPermission(_permissionGroup);
},
);
}
void requestPermission(PermissionGroup permission) async {
final List<PermissionGroup> permissions = <PermissionGroup>[permission];
final Map<PermissionGroup, PermissionStatus> permissionRequestResult =
await PermissionHandler.requestPermissions(permissions);
setState(() {
_permissionStatus = permissionRequestResult[permission];
});
}
}
......@@ -8,8 +8,12 @@
import Flutter
import Foundation
import UIKit
import Swift
typealias PermissionRequestCompletion = (_ permissionRequestResults: [PermissionGroup:PermissionStatus]) -> ()
class PermissionManager: NSObject {
private var _strategyInstances: [ObjectIdentifier: PermissionStrategy] = [:]
static func checkPermissionStatus(permission: PermissionGroup, result: @escaping FlutterResult) {
let permissionStrategy = PermissionManager.createPermissionStrategy(permission: permission)
......@@ -18,6 +22,28 @@ class PermissionManager: NSObject {
result(Codec.encodePermissionStatus(permissionStatus: permissionStatus))
}
func requestPermissions(permissions: [PermissionGroup], completion: @escaping PermissionRequestCompletion) {
var requestQueue = Set(permissions.map { $0 })
var permissionStatusResult: [PermissionGroup: PermissionStatus] = [:]
for permission in permissions {
let permissionStrategy = PermissionManager.createPermissionStrategy(permission: permission)
let identifier = ObjectIdentifier(permissionStrategy as AnyObject)
_strategyInstances[identifier] = permissionStrategy
permissionStrategy.requestPermission(permission: permission) { (permissionStatus: PermissionStatus) in
permissionStatusResult[permission] = permissionStatus
requestQueue.remove(permission)
self._strategyInstances.removeValue(forKey: ObjectIdentifier(permissionStrategy as AnyObject))
if requestQueue.count == 0 {
completion(permissionStatusResult)
return
}
}
}
}
static func openAppSettings(result: @escaping FlutterResult) {
if #available(iOS 8.0, *) {
if #available(iOS 10, *) {
......@@ -58,6 +84,8 @@ class PermissionManager: NSObject {
return SensorPermissionStrategy()
case PermissionGroup.speech:
return SpeechPermissionStrategy()
default:
return UnknownPermissionStrategy()
}
}
}
......@@ -9,6 +9,8 @@ import UIKit
public class SwiftPermissionHandlerPlugin: NSObject, FlutterPlugin {
private static let METHOD_CHANNEL_NAME = "flutter.baseflow.com/permissions/methods";
private let _permissionManager: PermissionManager = PermissionManager()
private var _methodResult: FlutterResult?
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: METHOD_CHANNEL_NAME, binaryMessenger: registrar.messenger())
......@@ -18,9 +20,33 @@ public class SwiftPermissionHandlerPlugin: NSObject, FlutterPlugin {
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "checkPermissionStatus" {
let permission: PermissionGroup = Codec.decodePermissionGroup(from: call.arguments)
PermissionManager.checkPermissionStatus(
permission: Codec.decodePermissionGroup(from: call.arguments),
permission: permission,
result: result)
} else if call.method == "requestPermissions" {
if _methodResult != nil {
result(FlutterError(
code: "ERROR_ALREADY_REQUESTING_PERMISSIONS",
message: "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).",
details: nil))
}
_methodResult = result
let permissions: [PermissionGroup] = Codec.decodePermissionGroups(from: call.arguments)
_permissionManager.requestPermissions(permissions: permissions) {
(permissionRequestResults: [PermissionGroup:PermissionStatus]) in
if self._methodResult != nil {
self._methodResult!(Codec.encodePermissionRequestResult(permissionStatusResult: permissionRequestResults))
}
self._methodResult = nil
}
} else if call.method == "shouldShowRequestPermissionRationale" {
result(false)
} else if call.method == "openAppSettings" {
PermissionManager.openAppSettings(result: result)
} else {
......
......@@ -16,8 +16,11 @@ enum PermissionGroup : String, Codable {
case locationWhenInUse = "locationWhenInUse"
case mediaLibrary = "mediaLibrary"
case microphone = "microphone"
case phone = "phone"
case photos = "photos"
case reminders = "reminders"
case sensors = "sensors"
case sms = "sms"
case speech = "speech"
case storage = "storage"
}
......@@ -19,11 +19,6 @@ class AudioVideoPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus(mediaType: AVMediaType) -> PermissionStatus {
let status: AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
......@@ -38,4 +33,33 @@ class AudioVideoPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let permissionStatus = checkPermissionStatus(permission: permission)
if permissionStatus != PermissionStatus.unknown {
completionHandler(permissionStatus)
return
}
var mediaType: AVMediaType
if permission == PermissionGroup.camera {
mediaType = AVMediaType.video
} else if permission == PermissionGroup.microphone {
mediaType = AVMediaType.audio
} else {
completionHandler(PermissionStatus.unknown)
return
}
AVCaptureDevice.requestAccess(for: mediaType, completionHandler: {
(granted: Bool) in
if granted {
completionHandler(PermissionStatus.granted)
} else {
completionHandler(PermissionStatus.denied)
}
})
}
}
......@@ -6,6 +6,7 @@
//
import AddressBook
import Contacts
import Foundation
class ContactPermissionStrategy : NSObject, PermissionStrategy {
......@@ -14,12 +15,22 @@ class ContactPermissionStrategy : NSObject, PermissionStrategy {
return ContactPermissionStrategy.getPermissionStatus()
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus() -> PermissionStatus {
if #available(iOS 9.0, *) {
let status: CNAuthorizationStatus = CNContactStore.authorizationStatus(for: .contacts)
switch status {
case CNAuthorizationStatus.authorized:
return PermissionStatus.granted
case CNAuthorizationStatus.denied:
return PermissionStatus.denied
case CNAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
}
let status: ABAuthorizationStatus = ABAddressBookGetAuthorizationStatus()
switch status {
......@@ -33,4 +44,44 @@ class ContactPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let permissionStatus = checkPermissionStatus(permission: permission)
if permissionStatus != PermissionStatus.unknown {
completionHandler(permissionStatus)
return
}
if #available(iOS 9.0, *) {
ContactPermissionStrategy.requestPermissionsFromContactStore(completionHandler: completionHandler)
} else {
ContactPermissionStrategy.requestPermissionsFromAddressBook(completionHandler: completionHandler)
}
}
@available(iOS 9.0, *)
private static func requestPermissionsFromContactStore(completionHandler: @escaping PermissionStatusHandler) {
let contactStore = CNContactStore.init()
contactStore.requestAccess(for: .contacts, completionHandler: {
(authorized: Bool, error: Error?) in
if authorized {
completionHandler(PermissionStatus.granted)
} else {
completionHandler(PermissionStatus.denied)
}
})
}
private static func requestPermissionsFromAddressBook(completionHandler: @escaping PermissionStatusHandler) {
ABAddressBookRequestAccessWithCompletion(ABAddressBookCreate() as ABAddressBook, {
(granted: Bool, error: CFError?) in
if granted {
completionHandler(PermissionStatus.granted)
} else {
completionHandler(PermissionStatus.denied)
}
})
}
}
......@@ -20,11 +20,6 @@ class EventPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus(entityType: EKEntityType) -> PermissionStatus {
let status: EKAuthorizationStatus = EKEventStore.authorizationStatus(for: entityType)
......@@ -40,4 +35,33 @@ class EventPermissionStrategy : NSObject, PermissionStrategy {
}
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let permissionStatus = checkPermissionStatus(permission: permission)
if permissionStatus != PermissionStatus.unknown {
completionHandler(permissionStatus)
return
}
var entityType: EKEntityType
if permission == PermissionGroup.calendar {
entityType = EKEntityType.event
} else if permission == PermissionGroup.reminders {
entityType = EKEntityType.reminder
} else {
completionHandler(PermissionStatus.unknown)
return
}
let eventStore: EKEventStore = EKEventStore.init()
eventStore.requestAccess(to: entityType) { (granted: Bool, error: Error?) in
if granted {
completionHandler(PermissionStatus.granted)
} else {
completionHandler(PermissionStatus.denied)
}
}
}
}
......@@ -8,17 +8,15 @@
import CoreLocation
import Foundation
class LocationPermissionStrategy : NSObject, PermissionStrategy {
class LocationPermissionStrategy : NSObject, PermissionStrategy, CLLocationManagerDelegate {
private var _locationManager: CLLocationManager? = nil
private var _permissionStatusHandler: PermissionStatusHandler? = nil
private var _requestedPermission: PermissionGroup? = nil
func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus {
return LocationPermissionStrategy.getPermissionStatus(permission: permission)
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus(permission: PermissionGroup) -> PermissionStatus {
if !CLLocationManager.locationServicesEnabled() {
return PermissionStatus.disabled
......@@ -26,9 +24,69 @@ class LocationPermissionStrategy : NSObject, PermissionStrategy {
let status: CLAuthorizationStatus = CLLocationManager.authorizationStatus()
return LocationPermissionStrategy.determinePermissionStatus(
permission: permission,
authorizationStatus: status)
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let permissionStatus = checkPermissionStatus(permission: permission)
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse && permission == PermissionGroup.locationAlways {
// don't do anything and continue requesting permissions
} else if permissionStatus != PermissionStatus.unknown {
completionHandler(permissionStatus)
return
}
_permissionStatusHandler = completionHandler
_requestedPermission = permission
if(_locationManager == nil) {
_locationManager = CLLocationManager()
_locationManager!.delegate = self
}
if(permission == PermissionGroup.location) {
if (Bundle.main.object(forInfoDictionaryKey: "NSLocationAlwaysUsageDescription") != nil) {
_locationManager!.requestAlwaysAuthorization()
} else if (Bundle.main.object(forInfoDictionaryKey: "NSLocationWhenInUseUsageDescription") != nil) {
_locationManager!.requestWhenInUseAuthorization();
} else {
NSException(name: NSExceptionName.internalInconsistencyException, reason:"To use location in iOS8 you need to define either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in the app bundle's Info.plist file", userInfo: nil).raise()
}
} else if permission == PermissionGroup.locationAlways {
if (Bundle.main.object(forInfoDictionaryKey: "NSLocationAlwaysUsageDescription") != nil) {
_locationManager!.requestAlwaysAuthorization();
} else {
NSException(name: NSExceptionName.internalInconsistencyException, reason:"To use location in iOS8 you need to define NSLocationAlwaysUsageDescription in the app bundle's Info.plist file", userInfo: nil).raise()
}
} else if permission == PermissionGroup.locationWhenInUse {
if (Bundle.main.object(forInfoDictionaryKey: "NSLocationWhenInUseUsageDescription") != nil) {
_locationManager!.requestWhenInUseAuthorization();
} else {
NSException(name: NSExceptionName.internalInconsistencyException, reason:"To use location in iOS8 you need to define NSLocationWhenInUseUsageDescription in the app bundle's Info.plist file", userInfo: nil).raise()
}
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == CLAuthorizationStatus.notDetermined {
return
}
guard let completionHandler = _permissionStatusHandler else { return }
completionHandler(
LocationPermissionStrategy.determinePermissionStatus(
permission: _requestedPermission!,
authorizationStatus: status))
}
private static func determinePermissionStatus(permission: PermissionGroup, authorizationStatus: CLAuthorizationStatus) -> PermissionStatus {
if #available(iOS 8.0, *) {
if permission == PermissionGroup.locationAlways {
switch status {
switch authorizationStatus {
case CLAuthorizationStatus.authorizedAlways:
return PermissionStatus.granted
case CLAuthorizationStatus.authorizedWhenInUse,
......@@ -41,7 +99,7 @@ class LocationPermissionStrategy : NSObject, PermissionStrategy {
}
}
switch status {
switch authorizationStatus {
case CLAuthorizationStatus.authorizedAlways,
CLAuthorizationStatus.authorizedWhenInUse:
return PermissionStatus.granted
......@@ -54,7 +112,7 @@ class LocationPermissionStrategy : NSObject, PermissionStrategy {
}
}
switch status {
switch authorizationStatus {
case CLAuthorizationStatus.authorized:
return PermissionStatus.granted
case CLAuthorizationStatus.denied:
......
......@@ -14,27 +14,45 @@ class MediaLibraryPermissionStrategy : NSObject, PermissionStrategy {
return MediaLibraryPermissionStrategy.getPermissionStatus()
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus() -> PermissionStatus {
if #available(iOS 9.3, *) {
let status: MPMediaLibraryAuthorizationStatus = MPMediaLibrary.authorizationStatus()
switch status {
case MPMediaLibraryAuthorizationStatus.authorized:
return PermissionStatus.granted
case MPMediaLibraryAuthorizationStatus.denied:
return PermissionStatus.denied
case MPMediaLibraryAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
return MediaLibraryPermissionStrategy.determinePermissionStatus(authorizationStatus: status)
}
return PermissionStatus.unknown
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) -> Void {
let status = checkPermissionStatus(permission: permission)
if status != PermissionStatus.unknown {
completionHandler(status)
return
}
if #available(iOS 9.3, *) {
MPMediaLibrary.requestAuthorization { (status: MPMediaLibraryAuthorizationStatus) in
completionHandler(
MediaLibraryPermissionStrategy.determinePermissionStatus(authorizationStatus: status))
}
} else {
completionHandler(PermissionStatus.unknown)
return
}
}
@available(iOS 9.3, *)
private static func determinePermissionStatus(authorizationStatus: MPMediaLibraryAuthorizationStatus) -> PermissionStatus {
switch authorizationStatus {
case MPMediaLibraryAuthorizationStatus.authorized:
return PermissionStatus.granted
case MPMediaLibraryAuthorizationStatus.denied:
return PermissionStatus.denied
case MPMediaLibraryAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
}
}
......@@ -7,7 +7,9 @@
import Foundation
typealias PermissionStatusHandler = (_ permissionStatus: PermissionStatus) -> Void
protocol PermissionStrategy {
func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus
func requestPermission(permission: PermissionGroup) -> PermissionStatus
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler)
}
......@@ -14,15 +14,28 @@ class PhotoPermissionStrategy : NSObject, PermissionStrategy {
return PhotoPermissionStrategy.getPermissionStatus()
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus() -> PermissionStatus {
let status: PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch status {
return PhotoPermissionStrategy.determinePermissionStatus(authorizationStatus: status)
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let status = checkPermissionStatus(permission: permission)
if status != PermissionStatus.unknown {
completionHandler(status)
return
}
PHPhotoLibrary.requestAuthorization { (authorizationStatus: PHAuthorizationStatus) in
completionHandler(
PhotoPermissionStrategy.determinePermissionStatus(authorizationStatus: authorizationStatus))
}
}
private static func determinePermissionStatus(authorizationStatus: PHAuthorizationStatus) -> PermissionStatus {
switch authorizationStatus {
case PHAuthorizationStatus.authorized:
return PermissionStatus.granted
case PHAuthorizationStatus.denied:
......
......@@ -14,11 +14,6 @@ class SensorPermissionStrategy : NSObject, PermissionStrategy {
return SensorPermissionStrategy.getPermissionStatus()
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus() -> PermissionStatus {
if !CMMotionActivityManager.isActivityAvailable() {
return PermissionStatus.disabled
......@@ -41,4 +36,25 @@ class SensorPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let status = checkPermissionStatus(permission: permission)
if status != PermissionStatus.unknown {
completionHandler(status)
return
}
if #available(iOS 11.0, *) {
let motionManager = CMMotionActivityManager.init()
motionManager.startActivityUpdates(to: OperationQueue.main) { (_) in
motionManager.stopActivityUpdates()
completionHandler(.granted)
}
} else {
completionHandler(.unknown)
}
}
}
......@@ -14,27 +14,46 @@ class SpeechPermissionStrategy : NSObject, PermissionStrategy {
return SpeechPermissionStrategy.getPermissionStatus()
}
func requestPermission(permission: PermissionGroup) -> PermissionStatus {
// TODO: Add implementation
return PermissionStatus.unknown
}
private static func getPermissionStatus() -> PermissionStatus {
if #available(iOS 10.0, *) {
let status: SFSpeechRecognizerAuthorizationStatus = SFSpeechRecognizer.authorizationStatus()
switch status {
case SFSpeechRecognizerAuthorizationStatus.authorized:
return PermissionStatus.granted
case SFSpeechRecognizerAuthorizationStatus.denied:
return PermissionStatus.denied
case SFSpeechRecognizerAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
return SpeechPermissionStrategy.determinePermissionStatus(authorizationStatus: status)
}
return PermissionStatus.unknown
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let status = checkPermissionStatus(permission: permission)
if status != PermissionStatus.unknown {
completionHandler(status)
return
}
if #available(iOS 10.0, *) {
SFSpeechRecognizer.requestAuthorization { (authorizationStatus: SFSpeechRecognizerAuthorizationStatus) in
completionHandler(
SpeechPermissionStrategy.determinePermissionStatus(authorizationStatus: authorizationStatus))
return
}
} else {
completionHandler(PermissionStatus.unknown)
}
}
@available(iOS 10.0, *)
private static func determinePermissionStatus(authorizationStatus: SFSpeechRecognizerAuthorizationStatus) -> PermissionStatus {
switch authorizationStatus {
case SFSpeechRecognizerAuthorizationStatus.authorized:
return PermissionStatus.granted
case SFSpeechRecognizerAuthorizationStatus.denied:
return PermissionStatus.denied
case SFSpeechRecognizerAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
}
}
//
// UnknownPermissionStrategy.swift
// permission_handler
//
// Created by Maurits van Beusekom on 07/08/2018.
//
import Foundation
class UnknownPermissionStrategy : NSObject, PermissionStrategy {
func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus {
return PermissionStatus.unknown
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
completionHandler(PermissionStatus.unknown)
}
}
......@@ -19,8 +19,28 @@ struct Codec {
return PermissionGroup(rawValue: permissionString)!
}
static func decodePermissionGroups(from arguments: Any?) -> [PermissionGroup] {
let data = (arguments as! String).data(using: .utf8)
let permissions = try! jsonDecoder.decode([PermissionGroup].self, from: data!)
return permissions
}
static func encodePermissionStatus(permissionStatus: PermissionStatus) -> String? {
let status = "\"" + permissionStatus.rawValue + "\""
return status
}
static func encodePermissionRequestResult(permissionStatusResult: [PermissionGroup: PermissionStatus]) -> String? {
let jsonDict = Dictionary(uniqueKeysWithValues:
permissionStatusResult.map {
(key: PermissionGroup, value: PermissionStatus) in (key.rawValue, value.rawValue)
})
let jsonData = try! JSONSerialization.data(withJSONObject: jsonDict, options: [])
let jsonString = String(data: jsonData, encoding: .utf8)!
return jsonString
}
}
......@@ -3,7 +3,7 @@
#
Pod::Spec.new do |s|
s.name = 'permission_handler'
s.version = '0.0.1'
s.version = '1.0.0'
s.summary = 'Permission plugin for Flutter.'
s.description = <<-DESC
Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
......
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_enums.dart';
......@@ -44,6 +45,11 @@ class PermissionHandler {
/// returns [false].
static Future<bool> shouldShowRequestPermissionRationale(
PermissionGroup permission) async {
if (!Platform.isAndroid) {
return false;
}
final bool shouldShowRationale = await _channel.invokeMethod(
'shouldShowRequestPermissionRationale',
Codec.encodePermissionGroup(permission));
......
......@@ -12,11 +12,11 @@ class Codec {
static Map<PermissionGroup, PermissionStatus> decodePermissionRequestResult(
dynamic value) {
final dynamic jsonObject = json.decode(value.toString());
final Map<String, dynamic> jsonObject = json.decode(value.toString());
final Map<PermissionGroup, PermissionStatus> permissionResults =
<PermissionGroup, PermissionStatus>{};
jsonObject.forEach((PermissionGroup key, PermissionStatus value) {
jsonObject.forEach((String key, dynamic value) {
final PermissionGroup permissionGroup = PermissionGroup.values.firstWhere(
(PermissionGroup e) =>
e.toString().split('.').last == key.toString());
......
name: permission_handler
description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
version: 0.0.1
version: 1.0.0
author: Baseflow <hello@baseflow.com>
homepage: https://github.com/baseflowit/flutter-permission-handler
......@@ -17,6 +17,9 @@ flutter:
androidPackage: com.baseflow.permissionhandler
pluginClass: PermissionHandlerPlugin
environment:
sdk: ">=2.0.0-dev.58.0 <3.0.0"
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
......
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