Commit b8509db6 by Maurits van Beusekom Committed by GitHub

Merge pull request #75 from BaseflowIT/features/check_service_status

 Added option to check service status
parents 33b0bad1 b3d010c7
......@@ -14,7 +14,7 @@ matrix:
- libstdc++6
- fonts-droid
before_script:
- git clone https://github.com/flutter/flutter.git $HOME/flutter
- git clone --single-branch --branch stable https://github.com/flutter/flutter.git $HOME/flutter
- export PATH=$HOME/flutter/bin:$HOME/flutter/bin/cache/dart-sdk/bin:$PATH
- flutter doctor
script:
......
## 2.2.0
* Added new method `checkServiceStatus` to allow users to check if the location services (on Android and iOS) and motion services (iOS only) are enabled;
* When checking permission status (using `checkPermissionStatus`) return `PermissionStatus.disabled` when permissions are granted or denied and the location services (on Android and iOS) or the motion services (iOS only) are disabled.
## 2.1.3
* Fixed bug on iOS where result of the `openAppSettings` call always returned `false`;
......
......@@ -22,7 +22,7 @@ To use this plugin, add `permission_handler` as a [dependency in your pubspec.ya
```yaml
dependencies:
permission_handler: '^2.1.3'
permission_handler: '^2.2.0'
```
> **NOTE:** There's a known issue with integrating plugins that use Swift into a Flutter project created with the Objective-C template. See issue [Flutter#16049](https://github.com/flutter/flutter/issues/16049) for help on integration.
......@@ -31,7 +31,7 @@ dependencies:
### Requesting permission
``` dart
```dart
import 'package:permission_handler/permission_handler.dart';
Map<PermissionGroup, PermissionStatus> permissions = await PermissionHandler().requestPermissions([PermissionGroup.contacts]);
......@@ -39,15 +39,25 @@ Map<PermissionGroup, PermissionStatus> permissions = await PermissionHandler().r
### Checking permission
``` dart
```dart
import 'package:permission_handler/permission_handler.dart';
PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.contacts);
```
### Checking service status
```dart
import 'package:permission_handler/permission_handler.dart';
ServiceStatus serviceStatus = await PermissionHandler().checkServiceStatus(PermissionGroup.location);
```
Checking the service status only makes sense for the `PermissionGroup.location` on Android and the `PermissionGroup.location`, `PermissionGroup.locationWhenInUser`, `PermissionGroup.locationAlways` or `PermissionGroup.sensors` on iOS. All other permission groups are not backed by a separate service and will always return `ServiceStatus.notApplicable`.
### Open app settings
``` dart
```dart
import 'package:permission_handler/permission_handler.dart';
bool isOpened = await PermissionHandler().openAppSettings();
......@@ -55,7 +65,7 @@ bool isOpened = await PermissionHandler().openAppSettings();
### Show a rationale for requesting permission (Android only)
``` dart
```dart
import 'package:permission_handler/permission_handler.dart';
bool isShown = await PermissionHandler().shouldShowRequestPermissionRationale(PermissionGroup.contacts);
......@@ -67,7 +77,7 @@ This will always return `false` on iOS.
Defines the permission groups for which permissions can be checked or requested.
``` dart
```dart
enum PermissionGroup {
/// The unknown permission only used for return type, never requested
unknown,
......@@ -138,7 +148,7 @@ enum PermissionGroup {
Defines the state of a permission group
``` dart
```dart
enum PermissionStatus {
/// Permission to access the requested feature is denied by the user.
denied,
......@@ -157,6 +167,27 @@ enum PermissionStatus {
}
```
### Overview of possible service statuses
Defines the state of the backing service for the supplied permission group
```dart
/// Defines the state of a service related to the permission group
enum ServiceStatus {
/// The unknown service status indicates the state of the service could not be determined.
unknown,
/// There is no service for the supplied permission group.
notApplicable,
/// The service for the supplied permission group is disabled.
disabled,
/// The service for the supplied permission group is enabled.
enabled
}
```
## Issues
Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/BaseflowIT/flutter-permission-handler/issues) page.
......
......@@ -12,6 +12,7 @@ import androidx.core.content.ContextCompat
import android.util.Log
import com.baseflow.permissionhandler.data.PermissionGroup
import com.baseflow.permissionhandler.data.PermissionStatus
import com.baseflow.permissionhandler.data.ServiceStatus
import com.baseflow.permissionhandler.utils.Codec
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
......@@ -99,7 +100,14 @@ class PermissionHandlerPlugin(private val registrar: Registrar, private var requ
call.method == "checkPermissionStatus" -> {
val permission = Codec.decodePermissionGroup(call.arguments)
val permissionStatus = checkPermissionStatus(permission)
handleSuccess(permissionStatus, result)
result?.success(Codec.encodePermissionStatus(permissionStatus))
}
call.method == "checkServiceStatus" -> {
val permission = Codec.decodePermissionGroup(call.arguments)
val serviceStatus = checkServiceStatus(permission)
result?.success(Codec.encodeServiceStatus(serviceStatus))
}
call.method == "requestPermissions" -> {
if (mResult != null) {
......@@ -168,6 +176,20 @@ class PermissionHandlerPlugin(private val registrar: Registrar, private var requ
return PermissionStatus.GRANTED
}
private fun checkServiceStatus(permission: PermissionGroup) : ServiceStatus {
val context: Context? = registrar.activity() ?: registrar.activeContext()
if (context == null) {
Log.d(mLogTag, "Unable to detect current Activity or App Context.")
return ServiceStatus.UNKNOWN
}
if (permission == PermissionGroup.LOCATION || permission == PermissionGroup.LOCATION_ALWAYS || permission == PermissionGroup.LOCATION_WHEN_IN_USE) {
return if(isLocationServiceEnabled(context)) ServiceStatus.ENABLED else ServiceStatus.DISABLED
}
return ServiceStatus.NOT_APPLICABLE
}
private fun shouldShowRequestPermissionRationale(permission: PermissionGroup) : Boolean {
val activity = registrar.activity()
if(activity == null)
......@@ -479,8 +501,4 @@ class PermissionHandlerPlugin(private val registrar: Registrar, private var requ
return !TextUtils.isEmpty(locationProviders)
}
}
private fun handleSuccess(permissionStatus: PermissionStatus, result: Result?) {
result?.success(Codec.encodePermissionStatus(permissionStatus))
}
}
package com.baseflow.permissionhandler.data
import com.google.gson.annotations.SerializedName
enum class ServiceStatus {
@SerializedName("unknown")
UNKNOWN,
@SerializedName("disabled")
DISABLED,
@SerializedName("enabled")
ENABLED,
@SerializedName("notApplicable")
NOT_APPLICABLE,
}
\ No newline at end of file
......@@ -2,6 +2,7 @@ package com.baseflow.permissionhandler.utils
import com.baseflow.permissionhandler.data.PermissionGroup
import com.baseflow.permissionhandler.data.PermissionStatus
import com.baseflow.permissionhandler.data.ServiceStatus
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
......@@ -29,6 +30,11 @@ class Codec {
}
@JvmStatic
fun encodeServiceStatus(serviceStatus: ServiceStatus) : String {
return gsonDecoder.toJson(serviceStatus)
}
@JvmStatic
fun encodePermissionRequestResult(permissionResults: Map<PermissionGroup, PermissionStatus>) : String {
val jsonString = gsonDecoder.toJson(permissionResults)
return jsonString
......
......@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
......@@ -38,6 +39,7 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
36F84C9A972D968241208A7F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
......@@ -81,6 +83,7 @@
children = (
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
......@@ -203,6 +206,7 @@
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
......@@ -218,7 +222,7 @@
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
"${PODS_ROOT}/../.symlinks/flutter/ios-release/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/permission_handler/permission_handler.framework",
);
name = "[CP] Embed Pods Frameworks";
......
......@@ -98,12 +98,28 @@ class _PermissionState extends State<PermissionWidget> {
_permissionStatus.toString(),
style: TextStyle(color: getPermissionColor()),
),
onTap: () async {
trailing: IconButton(
icon: const Icon(Icons.info),
onPressed: () {
checkServiceStatus(context, _permissionGroup);
}),
onTap: () {
requestPermission(_permissionGroup);
},
);
}
void checkServiceStatus(BuildContext context, PermissionGroup permission) {
PermissionHandler()
.checkServiceStatus(permission)
.then((ServiceStatus serviceStatus) {
final SnackBar snackBar =
SnackBar(content: Text(serviceStatus.toString()));
Scaffold.of(context).showSnackBar(snackBar);
});
}
void requestPermission(PermissionGroup permission) {
final List<PermissionGroup> permissions = <PermissionGroup>[permission];
final Future<Map<PermissionGroup, PermissionStatus>> requestFuture =
......
......@@ -22,6 +22,13 @@ class PermissionManager: NSObject {
result(Codec.encodePermissionStatus(permissionStatus: permissionStatus))
}
static func checkServiceStatus(permission: PermissionGroup, result: @escaping FlutterResult) {
let permissionStrategy = PermissionManager.createPermissionStrategy(permission: permission)
let serviceStatus = permissionStrategy.checkServiceStatus(permission: permission)
result(Codec.encodeServiceStatus(serviceStatus: serviceStatus))
}
func requestPermissions(permissions: [PermissionGroup], completion: @escaping PermissionRequestCompletion) {
var requestQueue = Set(permissions.map { $0 })
var permissionStatusResult: [PermissionGroup: PermissionStatus] = [:]
......
......@@ -25,6 +25,12 @@ public class SwiftPermissionHandlerPlugin: NSObject, FlutterPlugin {
PermissionManager.checkPermissionStatus(
permission: permission,
result: result)
} else if call.method == "checkServiceStatus" {
let permission: PermissionGroup = Codec.decodePermissionGroup(from: call.arguments)
PermissionManager.checkServiceStatus(
permission: permission,
result: result)
} else if call.method == "requestPermissions" {
if _methodResult != nil {
result(FlutterError(
......
//
// ServiceStatus.swift
// permission_handler
//
// Created by Maurits van Beusekom on 12/02/2019.
//
import Foundation
enum ServiceStatus : String, Codable {
case disabled = "disabled"
case enabled = "enabled"
case notApplicable = "notApplicable"
case unknown = "unknown"
}
......@@ -19,6 +19,10 @@ class AudioVideoPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
private static func getPermissionStatus(mediaType: AVMediaType) -> PermissionStatus {
let status: AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
......
......@@ -45,6 +45,10 @@ class ContactPermissionStrategy : NSObject, PermissionStrategy {
}
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let permissionStatus = checkPermissionStatus(permission: permission)
......
......@@ -36,6 +36,10 @@ class EventPermissionStrategy : NSObject, PermissionStrategy {
}
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let permissionStatus = checkPermissionStatus(permission: permission)
......
......@@ -23,13 +23,19 @@ class LocationPermissionStrategy : NSObject, PermissionStrategy, CLLocationManag
permission: permission,
authorizationStatus: authorizationStatus)
if permissionStatus == PermissionStatus.granted && !CLLocationManager.locationServicesEnabled() {
if (permissionStatus == PermissionStatus.granted || permissionStatus == PermissionStatus.denied) && !CLLocationManager.locationServicesEnabled() {
return PermissionStatus.disabled
}
return permissionStatus
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return CLLocationManager.locationServicesEnabled()
? ServiceStatus.enabled
: ServiceStatus.disabled
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let permissionStatus = checkPermissionStatus(permission: permission)
......
......@@ -24,6 +24,10 @@ class MediaLibraryPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) -> Void {
let status = checkPermissionStatus(permission: permission)
if status != PermissionStatus.unknown {
......
......@@ -11,5 +11,6 @@ typealias PermissionStatusHandler = (_ permissionStatus: PermissionStatus) -> Vo
protocol PermissionStrategy {
func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler)
}
......@@ -20,6 +20,10 @@ class PhotoPermissionStrategy : NSObject, PermissionStrategy {
return PhotoPermissionStrategy.determinePermissionStatus(authorizationStatus: status)
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let status = checkPermissionStatus(permission: permission)
......
......@@ -30,7 +30,7 @@ class SensorPermissionStrategy : NSObject, PermissionStrategy {
permissionStatus = PermissionStatus.unknown
}
if permissionStatus == PermissionStatus.granted && !CMMotionActivityManager.isActivityAvailable() {
if (permissionStatus == PermissionStatus.granted || permissionStatus == PermissionStatus.denied) && !CMMotionActivityManager.isActivityAvailable() {
return PermissionStatus.disabled
} else {
return permissionStatus
......@@ -40,6 +40,16 @@ class SensorPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
if #available(iOS 11.0, *) {
return CMMotionActivityManager.isActivityAvailable()
? ServiceStatus.enabled
: ServiceStatus.disabled
}
return ServiceStatus.unknown
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let status = checkPermissionStatus(permission: permission)
......
......@@ -24,6 +24,10 @@ class SpeechPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
let status = checkPermissionStatus(permission: permission)
......
......@@ -12,6 +12,10 @@ class UnknownPermissionStrategy : NSObject, PermissionStrategy {
return PermissionStatus.unknown
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.unknown
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
completionHandler(PermissionStatus.unknown)
}
......
......@@ -31,6 +31,11 @@ struct Codec {
return status
}
static func encodeServiceStatus(serviceStatus: ServiceStatus) -> String? {
let status = "\"" + serviceStatus.rawValue + "\""
return status
}
static func encodePermissionRequestResult(permissionStatusResult: [PermissionGroup: PermissionStatus]) -> String? {
let jsonDict = Dictionary(uniqueKeysWithValues:
permissionStatusResult.map {
......
......@@ -3,7 +3,7 @@
#
Pod::Spec.new do |s|
s.name = 'permission_handler'
s.version = '2.1.3'
s.version = '2.2.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.
......
......@@ -18,6 +18,21 @@ enum PermissionStatus {
unknown
}
/// Defines the state of a service related to the permission group
enum ServiceStatus {
/// The unknown service status indicates the state of the service could not be determined.
unknown,
/// There is no service for the supplied permission group.
notApplicable,
/// The service for the supplied permission group is disabled.
disabled,
/// The service for the supplied permission group is enabled.
enabled
}
/// Defines the permission groups for which permissions can be checked or requested.
enum PermissionGroup {
/// The unknown permission only used for return type, never requested
......
......@@ -29,6 +29,8 @@ class PermissionHandler {
final MethodChannel _methodChannel;
/// Check current permission status.
///
/// Returns a [Future] containing the current permission status for the supplied [PermissionGroup].
Future<PermissionStatus> checkPermissionStatus(
PermissionGroup permission) async {
......@@ -38,11 +40,22 @@ class PermissionHandler {
return Codec.decodePermissionStatus(status);
}
/// Check current service status.
///
/// Returns a [Future] containing the current service status for the supplied [PermissionGroup].
Future<ServiceStatus> checkServiceStatus(PermissionGroup permission) async {
final String status = await _methodChannel.invokeMethod(
'checkServiceStatus', Codec.encodePermissionGroup(permission));
return Codec.decodeServiceStatus(status);
}
/// Open the App settings page.
///
/// Returns [true] if the app settings page could be opened, otherwise [false] is returned.
Future<bool> openAppSettings() async {
final bool hasOpened = await _methodChannel.invokeMethod('openAppSettings');
return hasOpened;
}
......
......@@ -2,12 +2,19 @@ part of permission_handler;
class Codec {
static PermissionStatus decodePermissionStatus(String value) {
final dynamic permission = json.decode(value);
final String permission = json.decode(value);
return PermissionStatus.values.firstWhere(
(PermissionStatus e) => e.toString().split('.').last == permission);
}
static ServiceStatus decodeServiceStatus(String value) {
final String status = json.decode(value);
return ServiceStatus.values.firstWhere(
(ServiceStatus s) => s.toString().split('.').last == status);
}
static Map<PermissionGroup, PermissionStatus> decodePermissionRequestResult(
String value) {
final Map<String, dynamic> jsonObject = json.decode(value);
......
name: permission_handler
description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
version: 2.1.3
version: 2.2.0
author: Baseflow <hello@baseflow.com>
homepage: https://github.com/baseflowit/flutter-permission-handler
......
......@@ -20,7 +20,7 @@ unzip -qq gradle-4.1-bin.zip -d $HOME/gradle-4.1
export GRADLE_HOME=$HOME/gradle-4.1
export PATH=$GRADLE_HOME/bin:$PATH
gradle -v
git clone https://github.com/flutter/flutter.git $HOME/flutter
git clone --single-branch --branch stable https://github.com/flutter/flutter.git $HOME/flutter
export PATH=$HOME/flutter/bin:$HOME/flutter/bin/cache/dart-sdk/bin:$PATH
flutter doctor
......@@ -5,6 +5,6 @@ brew install ideviceinstaller
brew install ios-deploy
pod repo update
gem update cocoapods
git clone https://github.com/flutter/flutter.git $HOME/flutter
git clone --single-branch --branch stable https://github.com/flutter/flutter.git $HOME/flutter
export PATH=$HOME/flutter/bin:$HOME/flutter/bin/cache/dart-sdk/bin:$PATH
flutter doctor
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