From 6874f12c1252ff058f166b0e632d5fc48a3b0dc7 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 24 Jul 2018 05:44:07 +0200 Subject: [PATCH 1/5] Format Dart code --- example/lib/main.dart | 20 +++++++++------- lib/permission_enums.dart | 47 +++++++++++++++++++++++++------------ lib/permission_handler.dart | 34 +++++++++++++-------------- lib/utils/codec.dart | 18 ++++++++------ 4 files changed, 71 insertions(+), 48 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index ae62c89d5..bb33b650a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -27,14 +27,17 @@ class _MyAppState extends State { // Platform messages may fail, so we use a try/catch PlatformException. try { - permissionStatus = await PermissionHandler.checkPermissionStatus(PermissionGroup.calendar); + permissionStatus = await PermissionHandler + .checkPermissionStatus(PermissionGroup.calendar); - if(permissionStatus != PermissionStatus.granted){ - final shouldShowRationale = await PermissionHandler.shouldShowRequestPermissionRationale(PermissionGroup.calendar); - - if(shouldShowRationale) { - var permissions = await PermissionHandler.requestPermissions([PermissionGroup.calendar]); - if(permissions.containsKey(PermissionGroup.calendar)) { + if (permissionStatus != PermissionStatus.granted) { + final shouldShowRationale = await PermissionHandler + .shouldShowRequestPermissionRationale(PermissionGroup.calendar); + + if (shouldShowRationale) { + var permissions = await PermissionHandler + .requestPermissions([PermissionGroup.calendar]); + if (permissions.containsKey(PermissionGroup.calendar)) { permissionStatus = permissions[PermissionGroup.calendar]; } } @@ -66,7 +69,8 @@ class _MyAppState extends State { new Text('Running on: $_permissionStatus\n'), new RaisedButton( child: new Text("Open settings"), - onPressed: () async => await PermissionHandler.openAppSettings(), + onPressed: () async => + await PermissionHandler.openAppSettings(), ), ], ), diff --git a/lib/permission_enums.dart b/lib/permission_enums.dart index 4945c477a..27b2488dd 100644 --- a/lib/permission_enums.dart +++ b/lib/permission_enums.dart @@ -1,66 +1,83 @@ /// Defines the state of a permission group -enum PermissionStatus -{ - /// Permission to access the requested feature is denied by the user. - denied, - /// The feature is disabled (or not available) on the device. - disabled, - /// Permission to access the requested feature is granted by the user. - granted, - /// The user granted restricted access to the requested feature (only on iOS). - restricted, - /// Permission is in an unknown state - unknown +enum PermissionStatus { + /// Permission to access the requested feature is denied by the user. + denied, + + /// The feature is disabled (or not available) on the device. + disabled, + + /// Permission to access the requested feature is granted by the user. + granted, + + /// The user granted restricted access to the requested feature (only on iOS). + restricted, + + /// Permission is in an unknown state + unknown } /// Defines the permission groups for which permissions can be checked or requested. -enum PermissionGroup -{ +enum PermissionGroup { /// The unknown permission only used for return type, never requested unknown, + /// Android: Calendar /// iOS: Calendar (Events) calendar, + /// Android: Camera /// iOS: Photos (Camera Roll and Camera) camera, + /// Android: Contacts /// iOS: AddressBook contacts, + /// Android: Fine and Coarse Location /// iOS: CoreLocation (Always and WhenInUse) location, + /// Android: Microphone /// iOS: Microphone microphone, + /// Android: Phone /// iOS: Nothing phone, + /// Android: Nothing /// iOS: Photos photos, + /// Android: Nothing /// iOS: Reminders reminders, + /// Android: Body Sensors /// iOS: CoreMotion sensors, + /// Android: Sms /// iOS: Nothing sms, + /// Android: External Storage /// iOS: Nothing storage, + /// Android: Microphone /// iOS: Speech speech, + /// Android: Fine and Coarse Location /// iOS: CoreLocation - Always locationAlways, + /// Android: Fine and Coarse Location /// iOS: CoreLocation - WhenInUse locationWhenInUse, + /// Android: None /// iOS: MPMediaLibrary mediaLibrary -} \ No newline at end of file +} diff --git a/lib/permission_handler.dart b/lib/permission_handler.dart index e8f92dfa4..ef8746dc1 100644 --- a/lib/permission_handler.dart +++ b/lib/permission_handler.dart @@ -10,39 +10,37 @@ class PermissionHandler { const MethodChannel('flutter.baseflow.com/permissions/methods'); /// Returns a [Future] containing the current permission status for the supplied [PermissionGroup]. - static Future checkPermissionStatus(PermissionGroup permission) async { + static Future checkPermissionStatus( + PermissionGroup permission) async { final status = await _channel.invokeMethod( - 'checkPermissionStatus', - Codec.encodePermissionGroup(permission)); - + 'checkPermissionStatus', Codec.encodePermissionGroup(permission)); + return Codec.decodePermissionStatus(status); } /// Open the App settings page. - /// + /// /// Returns [true] if the app settings page could be opened, otherwise [false] is returned. static Future openAppSettings() async => - await _channel.invokeMethod("openAppSettings"); + await _channel.invokeMethod("openAppSettings"); /// Request the user for access to the supplied list of permissiongroups. - /// + /// /// Returns a [Map] containing the status per requested permissiongroup. - static Future> requestPermissions(List permissions) async { + static Future> requestPermissions( + List permissions) async { final jsonData = Codec.encodePermissionGroups(permissions); - final status = await _channel.invokeMethod( - 'requestPermissions', - jsonData); - + final status = await _channel.invokeMethod('requestPermissions', jsonData); + return Codec.decodePermissionRequestResult(status); } /// Request to see if you should show a rationale for requesting permission. /// - /// This method is only implemented on Android, calling this on iOS always + /// This method is only implemented on Android, calling this on iOS always /// returns [false]. - static Future shouldShowRequestPermissionRationale(PermissionGroup permission) async => - await _channel.invokeMethod( - 'shouldShowRequestPermissionRationale', - Codec.encodePermissionGroup(permission)); - + static Future shouldShowRequestPermissionRationale( + PermissionGroup permission) async => + await _channel.invokeMethod('shouldShowRequestPermissionRationale', + Codec.encodePermissionGroup(permission)); } diff --git a/lib/utils/codec.dart b/lib/utils/codec.dart index d91745d9e..86e5a46a1 100644 --- a/lib/utils/codec.dart +++ b/lib/utils/codec.dart @@ -6,30 +6,34 @@ class Codec { static PermissionStatus decodePermissionStatus(dynamic value) { final permission = json.decode(value); - return PermissionStatus.values.firstWhere((e) => e.toString().split('.').last == permission); + return PermissionStatus.values + .firstWhere((e) => e.toString().split('.').last == permission); } - static Map decodePermissionRequestResult(dynamic value) { + static Map decodePermissionRequestResult( + dynamic value) { final jsonObject = json.decode(value); final permissionResults = Map(); jsonObject.forEach((key, value) { - final permissionGroup = PermissionGroup.values.firstWhere((e) => e.toString().split('.').last == key.toString()); - final permissionStatus = PermissionStatus.values.firstWhere((e) => e.toString().split('.').last == value.toString()); + final permissionGroup = PermissionGroup.values + .firstWhere((e) => e.toString().split('.').last == key.toString()); + final permissionStatus = PermissionStatus.values + .firstWhere((e) => e.toString().split('.').last == value.toString()); permissionResults[permissionGroup] = permissionStatus; }); return permissionResults; } - + static String encodePermissionGroup(PermissionGroup permissionGroup) => json.encode(_encodeEnum(permissionGroup)); static String encodePermissionGroups(List permissions) => - json.encode(permissions.map((p) => _encodeEnum(p)).toList()); + json.encode(permissions.map((p) => _encodeEnum(p)).toList()); static String _encodeEnum(dynamic value) { return value.toString().split('.').last; } -} \ No newline at end of file +} From 6aefd3318cd08188017057ef8a60b7bb10c1ce10 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 26 Jul 2018 19:26:11 +0300 Subject: [PATCH 2/5] Implementation to check permissions on iOS --- example/ios/Flutter/Debug.xcconfig | 2 + example/ios/Runner.xcodeproj/project.pbxproj | 10 +-- .../xcshareddata/xcschemes/Runner.xcscheme | 4 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 +++ example/ios/Runner/Info.plist | 28 ++++++++ example/lib/main.dart | 4 +- ios/Classes/PermissionManager.swift | 45 ++++++++++++ .../SwiftPermissionHandlerPlugin.swift | 30 +++++--- ios/Classes/data/PermissionGroup.swift | 23 +++++++ ios/Classes/data/PermissionStatus.swift | 16 +++++ .../AudioVideoPermissionStrategy.swift | 41 +++++++++++ .../ContactPermissionStrategy.swift | 36 ++++++++++ .../strategies/EventPermissionStrategy.swift | 43 ++++++++++++ .../LocationPermissionStrategy.swift | 68 +++++++++++++++++++ .../MediaLibraryPermissionStrategy.swift | 40 +++++++++++ .../strategies/PermissionStrategy.swift | 13 ++++ .../strategies/PhotoPermissionStrategy.swift | 36 ++++++++++ .../strategies/SensorPermissionStrategy.swift | 44 ++++++++++++ .../strategies/SpeechPermissionStrategy.swift | 40 +++++++++++ ios/Classes/utils/Codec.swift | 25 +++++++ 20 files changed, 539 insertions(+), 17 deletions(-) create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Classes/PermissionManager.swift create mode 100644 ios/Classes/data/PermissionGroup.swift create mode 100644 ios/Classes/data/PermissionStatus.swift create mode 100644 ios/Classes/strategies/AudioVideoPermissionStrategy.swift create mode 100644 ios/Classes/strategies/ContactPermissionStrategy.swift create mode 100644 ios/Classes/strategies/EventPermissionStrategy.swift create mode 100644 ios/Classes/strategies/LocationPermissionStrategy.swift create mode 100644 ios/Classes/strategies/MediaLibraryPermissionStrategy.swift create mode 100644 ios/Classes/strategies/PermissionStrategy.swift create mode 100644 ios/Classes/strategies/PhotoPermissionStrategy.swift create mode 100644 ios/Classes/strategies/SensorPermissionStrategy.swift create mode 100644 ios/Classes/strategies/SpeechPermissionStrategy.swift create mode 100644 ios/Classes/utils/Codec.swift diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index e8efba114..49dc19fc4 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1,4 @@ #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" + +FLUTTER_BUILD_MODE=debug diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 2bbcc85f6..38b524805 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,8 +15,6 @@ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; - 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -174,7 +172,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0910; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -207,9 +205,7 @@ buildActionMask = 2147483647; files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, @@ -333,12 +329,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -387,12 +385,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1263ac84b..4bafc4584 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 45830864e..2c214e83a 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,6 +2,10 @@ + UIRequiredDeviceCapabilities + + arm64 + CFBundleDevelopmentRegion en CFBundleExecutable @@ -41,5 +45,29 @@ UIViewControllerBasedStatusBarAppearance + NSLocationWhenInUseUsageDescription + Can I haz location? + NSAppleMusicUsageDescription + Music! + NSBluetoothPeripheralUsageDescription + bluetooth + NSCalendarsUsageDescription + Calendars + NSCameraUsageDescription + camera + NSContactsUsageDescription + contacts + kTCCServiceMediaLibrary + media + NSMicrophoneUsageDescription + microphone + NSMotionUsageDescription + motion + NSPhotoLibraryUsageDescription + photos + NSRemindersUsageDescription + reminders + NSSpeechRecognitionUsageDescription + speech diff --git a/example/lib/main.dart b/example/lib/main.dart index bb33b650a..5d0cbeb3a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -28,8 +28,9 @@ class _MyAppState extends State { // Platform messages may fail, so we use a try/catch PlatformException. try { permissionStatus = await PermissionHandler - .checkPermissionStatus(PermissionGroup.calendar); + .checkPermissionStatus(PermissionGroup.locationWhenInUse); +/* if (permissionStatus != PermissionStatus.granted) { final shouldShowRationale = await PermissionHandler .shouldShowRequestPermissionRationale(PermissionGroup.calendar); @@ -42,6 +43,7 @@ class _MyAppState extends State { } } } +*/ } on PlatformException { permissionStatus = PermissionStatus.unknown; } diff --git a/ios/Classes/PermissionManager.swift b/ios/Classes/PermissionManager.swift new file mode 100644 index 000000000..3e5008e8d --- /dev/null +++ b/ios/Classes/PermissionManager.swift @@ -0,0 +1,45 @@ +// +// PermissionManager.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import Foundation + +class PermissionManager: NSObject { + + static func checkPermissionStatus(permission: PermissionGroup, result: @escaping FlutterResult) { + let permissionStrategy = PermissionManager.createPermissionStrategy(permission: permission) + let permissionStatus = permissionStrategy.checkPermissionStatus(permission: permission) + + result(Codec.encodePermissionStatus(permissionStatus: permissionStatus)) + } + + private static func createPermissionStrategy(permission: PermissionGroup) -> PermissionStrategy { + switch permission { + case PermissionGroup.calendar: + return EventPermissionStrategy() + case PermissionGroup.camera: + return AudioVideoPermissionStrategy() + case PermissionGroup.contacts: + return ContactPermissionStrategy() + case PermissionGroup.location, + PermissionGroup.locationAlways, + PermissionGroup.locationWhenInUse: + return LocationPermissionStrategy() + case PermissionGroup.mediaLibrary: + return MediaLibraryPermissionStrategy() + case PermissionGroup.microphone: + return AudioVideoPermissionStrategy() + case PermissionGroup.photos: + return PhotoPermissionStrategy() + case PermissionGroup.reminders: + return EventPermissionStrategy() + case PermissionGroup.sensors: + return SensorPermissionStrategy() + case PermissionGroup.speech: + return SpeechPermissionStrategy() + } + } +} diff --git a/ios/Classes/SwiftPermissionHandlerPlugin.swift b/ios/Classes/SwiftPermissionHandlerPlugin.swift index 8c00a4a00..d7dbe56af 100644 --- a/ios/Classes/SwiftPermissionHandlerPlugin.swift +++ b/ios/Classes/SwiftPermissionHandlerPlugin.swift @@ -1,14 +1,28 @@ + +import CoreLocation +import CoreMotion +import EventKit import Flutter +import Foundation +import Photos import UIKit public class SwiftPermissionHandlerPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "permission_handler", binaryMessenger: registrar.messenger()) - let instance = SwiftPermissionHandlerPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } + private static let METHOD_CHANNEL_NAME = "flutter.baseflow.com/permissions/methods"; + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: METHOD_CHANNEL_NAME, binaryMessenger: registrar.messenger()) + let instance = SwiftPermissionHandlerPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) - } + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + if call.method == "checkPermissionStatus" { + PermissionManager.checkPermissionStatus( + permission: Codec.decodePermissionGroup(from: call.arguments), + result: result) + } else { + result(FlutterMethodNotImplemented) + } + } } diff --git a/ios/Classes/data/PermissionGroup.swift b/ios/Classes/data/PermissionGroup.swift new file mode 100644 index 000000000..206225e6f --- /dev/null +++ b/ios/Classes/data/PermissionGroup.swift @@ -0,0 +1,23 @@ +// +// PermissionGroup.swift +// permission_handler +// +// Created by Maurits van Beusekom on 25/07/2018. +// + +import Foundation + +enum PermissionGroup : String, Codable { + case calendar = "calendar" + case camera = "camera" + case contacts = "contacts" + case location = "location" + case locationAlways = "locationAlways" + case locationWhenInUse = "locationWhenInUse" + case mediaLibrary = "mediaLibrary" + case microphone = "microphone" + case photos = "photos" + case reminders = "reminders" + case sensors = "sensors" + case speech = "speech" +} diff --git a/ios/Classes/data/PermissionStatus.swift b/ios/Classes/data/PermissionStatus.swift new file mode 100644 index 000000000..ba98bcaf0 --- /dev/null +++ b/ios/Classes/data/PermissionStatus.swift @@ -0,0 +1,16 @@ +// +// PermissionStatus.swift +// permission_handler +// +// Created by Maurits van Beusekom on 25/07/2018. +// + +import Foundation + +enum PermissionStatus : String, Codable { + case denied = "denied" + case disabled = "disabled" + case granted = "granted" + case restricted = "restricted" + case unknown = "unknown" +} diff --git a/ios/Classes/strategies/AudioVideoPermissionStrategy.swift b/ios/Classes/strategies/AudioVideoPermissionStrategy.swift new file mode 100644 index 000000000..6030ff065 --- /dev/null +++ b/ios/Classes/strategies/AudioVideoPermissionStrategy.swift @@ -0,0 +1,41 @@ +// +// AudioVideoPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// +import AVFoundation +import Foundation + +class AudioVideoPermissionStrategy : NSObject, PermissionStrategy { + + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus { + if permission == PermissionGroup.camera { + return AudioVideoPermissionStrategy.getPermissionStatus(mediaType: AVMediaType.video) + } else if permission == PermissionGroup.microphone { + return AudioVideoPermissionStrategy.getPermissionStatus(mediaType: AVMediaType.audio) + } + + 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) + + switch status { + case AVAuthorizationStatus.authorized: + return PermissionStatus.granted + case AVAuthorizationStatus.denied: + return PermissionStatus.denied + case AVAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + } + } +} diff --git a/ios/Classes/strategies/ContactPermissionStrategy.swift b/ios/Classes/strategies/ContactPermissionStrategy.swift new file mode 100644 index 000000000..fb55754bb --- /dev/null +++ b/ios/Classes/strategies/ContactPermissionStrategy.swift @@ -0,0 +1,36 @@ +// +// ContactPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import AddressBook +import Foundation + +class ContactPermissionStrategy : NSObject, PermissionStrategy { + + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus { + return ContactPermissionStrategy.getPermissionStatus() + } + + func requestPermission(permission: PermissionGroup) -> PermissionStatus { + // TODO: Add implementation + return PermissionStatus.unknown + } + + private static func getPermissionStatus() -> PermissionStatus { + let status: ABAuthorizationStatus = ABAddressBookGetAuthorizationStatus() + + switch status { + case ABAuthorizationStatus.authorized: + return PermissionStatus.granted + case ABAuthorizationStatus.denied: + return PermissionStatus.denied + case ABAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + } + } +} diff --git a/ios/Classes/strategies/EventPermissionStrategy.swift b/ios/Classes/strategies/EventPermissionStrategy.swift new file mode 100644 index 000000000..262b2b13c --- /dev/null +++ b/ios/Classes/strategies/EventPermissionStrategy.swift @@ -0,0 +1,43 @@ +// +// EventPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import EventKit +import Foundation + +class EventPermissionStrategy : NSObject, PermissionStrategy { + + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus { + if permission == PermissionGroup.calendar { + return EventPermissionStrategy.getPermissionStatus(entityType: EKEntityType.event) + } else if permission == PermissionGroup.reminders { + return EventPermissionStrategy.getPermissionStatus(entityType: EKEntityType.reminder) + } + + 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) + + switch status { + case EKAuthorizationStatus.authorized: + return PermissionStatus.granted + case EKAuthorizationStatus.denied: + return PermissionStatus.denied + case EKAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + + } + } +} diff --git a/ios/Classes/strategies/LocationPermissionStrategy.swift b/ios/Classes/strategies/LocationPermissionStrategy.swift new file mode 100644 index 000000000..644b639fc --- /dev/null +++ b/ios/Classes/strategies/LocationPermissionStrategy.swift @@ -0,0 +1,68 @@ +// +// LocationPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import CoreLocation +import Foundation + +class LocationPermissionStrategy : NSObject, PermissionStrategy { + + 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 + } + + let status: CLAuthorizationStatus = CLLocationManager.authorizationStatus() + + if #available(iOS 8.0, *) { + if permission == PermissionGroup.locationAlways { + switch status { + case CLAuthorizationStatus.authorizedAlways: + return PermissionStatus.granted + case CLAuthorizationStatus.authorizedWhenInUse, + CLAuthorizationStatus.denied: + return PermissionStatus.denied + case CLAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + } + } + + switch status { + case CLAuthorizationStatus.authorizedAlways, + CLAuthorizationStatus.authorizedWhenInUse: + return PermissionStatus.granted + case CLAuthorizationStatus.denied: + return PermissionStatus.denied + case CLAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + } + } + + switch status { + case CLAuthorizationStatus.authorized: + return PermissionStatus.granted + case CLAuthorizationStatus.denied: + return PermissionStatus.denied + case CLAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + } + } +} diff --git a/ios/Classes/strategies/MediaLibraryPermissionStrategy.swift b/ios/Classes/strategies/MediaLibraryPermissionStrategy.swift new file mode 100644 index 000000000..06ae25022 --- /dev/null +++ b/ios/Classes/strategies/MediaLibraryPermissionStrategy.swift @@ -0,0 +1,40 @@ +// +// MediaLibraryPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import Foundation +import MediaPlayer + +class MediaLibraryPermissionStrategy : NSObject, PermissionStrategy { + + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus { + 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 PermissionStatus.unknown + } +} diff --git a/ios/Classes/strategies/PermissionStrategy.swift b/ios/Classes/strategies/PermissionStrategy.swift new file mode 100644 index 000000000..ba80da72e --- /dev/null +++ b/ios/Classes/strategies/PermissionStrategy.swift @@ -0,0 +1,13 @@ +// +// Task.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import Foundation + +protocol PermissionStrategy { + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus + func requestPermission(permission: PermissionGroup) -> PermissionStatus +} diff --git a/ios/Classes/strategies/PhotoPermissionStrategy.swift b/ios/Classes/strategies/PhotoPermissionStrategy.swift new file mode 100644 index 000000000..209b9fc4a --- /dev/null +++ b/ios/Classes/strategies/PhotoPermissionStrategy.swift @@ -0,0 +1,36 @@ +// +// PhotoPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import Foundation +import Photos + +class PhotoPermissionStrategy : NSObject, PermissionStrategy { + + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus { + 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 { + case PHAuthorizationStatus.authorized: + return PermissionStatus.granted + case PHAuthorizationStatus.denied: + return PermissionStatus.denied + case PHAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + } + } +} diff --git a/ios/Classes/strategies/SensorPermissionStrategy.swift b/ios/Classes/strategies/SensorPermissionStrategy.swift new file mode 100644 index 000000000..32c6ecfb6 --- /dev/null +++ b/ios/Classes/strategies/SensorPermissionStrategy.swift @@ -0,0 +1,44 @@ +// +// SensorPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import CoreMotion +import Foundation + +class SensorPermissionStrategy : NSObject, PermissionStrategy { + + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus { + 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 + } + + if #available(iOS 11.0, *) { + let status: CMAuthorizationStatus = CMMotionActivityManager.authorizationStatus() + + switch status { + case CMAuthorizationStatus.authorized: + return PermissionStatus.granted + case CMAuthorizationStatus.denied: + return PermissionStatus.denied + case CMAuthorizationStatus.restricted: + return PermissionStatus.restricted + default: + return PermissionStatus.unknown + } + } + + return PermissionStatus.unknown + } +} diff --git a/ios/Classes/strategies/SpeechPermissionStrategy.swift b/ios/Classes/strategies/SpeechPermissionStrategy.swift new file mode 100644 index 000000000..45f438b8b --- /dev/null +++ b/ios/Classes/strategies/SpeechPermissionStrategy.swift @@ -0,0 +1,40 @@ +// +// SpeechPermissions.swift +// permission_handler +// +// Created by Maurits van Beusekom on 26/07/2018. +// + +import Foundation +import Speech + +class SpeechPermissionStrategy : NSObject, PermissionStrategy { + + func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus { + 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 PermissionStatus.unknown + } +} diff --git a/ios/Classes/utils/Codec.swift b/ios/Classes/utils/Codec.swift new file mode 100644 index 000000000..b244feca5 --- /dev/null +++ b/ios/Classes/utils/Codec.swift @@ -0,0 +1,25 @@ +// +// Codec.swift +// permission_handler +// +// Created by Maurits van Beusekom on 25/07/2018. +// + +import Foundation + +struct Codec { + private static let jsonDecoder = JSONDecoder() + private static let jsonEncoder = JSONEncoder() + + static func decodePermissionGroup(from arguments: Any?) -> PermissionGroup { + var permissionString: String = arguments as! String + permissionString.removeFirst() + permissionString.removeLast() + + return PermissionGroup(rawValue: permissionString)! + } + + static func encodePermissionStatus(permissionStatus: PermissionStatus) -> String { + return permissionStatus.rawValue + } +} From f5b2004263a00274935a4e258f248a0a0bc19be2 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 26 Jul 2018 19:41:50 +0300 Subject: [PATCH 3/5] Fix merge conflict --- example/lib/main.dart | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 27b649185..169df706c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -28,20 +28,9 @@ class _MyAppState extends State { // Platform messages may fail, so we use a try/catch PlatformException. try { permissionStatus = await PermissionHandler -<<<<<<< HEAD - .checkPermissionStatus(PermissionGroup.locationWhenInUse); - -/* - if (permissionStatus != PermissionStatus.granted) { - final shouldShowRationale = await PermissionHandler - .shouldShowRequestPermissionRationale(PermissionGroup.calendar); - - if (shouldShowRationale) { - var permissions = await PermissionHandler - .requestPermissions([PermissionGroup.calendar]); -======= .checkPermissionStatus(PermissionGroup.calendar); +/* if (permissionStatus != PermissionStatus.granted) { final bool shouldShowRationale = await PermissionHandler .shouldShowRequestPermissionRationale(PermissionGroup.calendar); @@ -50,7 +39,7 @@ class _MyAppState extends State { final Map permissions = await PermissionHandler.requestPermissions( [PermissionGroup.calendar]); ->>>>>>> develop + if (permissions.containsKey(PermissionGroup.calendar)) { permissionStatus = permissions[PermissionGroup.calendar]; } @@ -85,11 +74,7 @@ class _MyAppState extends State { children: [ new Text('Running on: $_permissionStatus\n'), new RaisedButton( -<<<<<<< HEAD - child: new Text("Open settings"), -======= child: const Text('Open settings'), ->>>>>>> develop onPressed: () async => await PermissionHandler.openAppSettings(), ), From 0184bc2af66de560212f39fbe06c19d846efc09b Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 27 Jul 2018 08:16:27 +0300 Subject: [PATCH 4/5] fix serialization of PermissionStatus enum --- example/ios/Runner.xcodeproj/project.pbxproj | 2 +- ios/Classes/utils/Codec.swift | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 3d947a262..38b524805 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -222,7 +222,7 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../.symlinks/flutter/ios-release/Flutter.framework", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", "${BUILT_PRODUCTS_DIR}/permission_handler/permission_handler.framework", ); name = "[CP] Embed Pods Frameworks"; diff --git a/ios/Classes/utils/Codec.swift b/ios/Classes/utils/Codec.swift index b244feca5..01f93ae0e 100644 --- a/ios/Classes/utils/Codec.swift +++ b/ios/Classes/utils/Codec.swift @@ -19,7 +19,8 @@ struct Codec { return PermissionGroup(rawValue: permissionString)! } - static func encodePermissionStatus(permissionStatus: PermissionStatus) -> String { - return permissionStatus.rawValue + static func encodePermissionStatus(permissionStatus: PermissionStatus) -> String? { + let status = "\"" + permissionStatus.rawValue + "\"" + return status } } From 09f34a70dcc04f7b444bbbb5659bfaca7f22c41a Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 27 Jul 2018 08:44:14 +0300 Subject: [PATCH 5/5] Open App settings from ios --- ios/Classes/PermissionManager.swift | 18 ++++++++++++++++++ ios/Classes/SwiftPermissionHandlerPlugin.swift | 2 ++ 2 files changed, 20 insertions(+) diff --git a/ios/Classes/PermissionManager.swift b/ios/Classes/PermissionManager.swift index 3e5008e8d..817fd4bcf 100644 --- a/ios/Classes/PermissionManager.swift +++ b/ios/Classes/PermissionManager.swift @@ -5,7 +5,9 @@ // Created by Maurits van Beusekom on 26/07/2018. // +import Flutter import Foundation +import UIKit class PermissionManager: NSObject { @@ -16,6 +18,22 @@ class PermissionManager: NSObject { result(Codec.encodePermissionStatus(permissionStatus: permissionStatus)) } + static func openAppSettings(result: @escaping FlutterResult) { + if #available(iOS 8.0, *) { + if #available(iOS 10, *) { + UIApplication.shared.open(URL.init(string: UIApplicationOpenSettingsURLString)!, options: [:], + completionHandler: { + (success) in result(success) + }) + } else { + let success = UIApplication.shared.openURL(URL.init(string: UIApplicationOpenSettingsURLString)!) + result(success) + } + } + + result(false) + } + private static func createPermissionStrategy(permission: PermissionGroup) -> PermissionStrategy { switch permission { case PermissionGroup.calendar: diff --git a/ios/Classes/SwiftPermissionHandlerPlugin.swift b/ios/Classes/SwiftPermissionHandlerPlugin.swift index d7dbe56af..1efc5e0a5 100644 --- a/ios/Classes/SwiftPermissionHandlerPlugin.swift +++ b/ios/Classes/SwiftPermissionHandlerPlugin.swift @@ -21,6 +21,8 @@ public class SwiftPermissionHandlerPlugin: NSObject, FlutterPlugin { PermissionManager.checkPermissionStatus( permission: Codec.decodePermissionGroup(from: call.arguments), result: result) + } else if call.method == "openAppSettings" { + PermissionManager.openAppSettings(result: result) } else { result(FlutterMethodNotImplemented) }