Commit 05a0191c by Maurits van Beusekom

Merge branch 'develop'

parents 2612cf36 bf128819
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 06b979c4d5e1b499745422269f01a00341257058
channel: master
project_type: plugin
# Specify analysis options.
#
# Until there are meta linter rules, each desired lint must be explicitly enabled.
# See: https://github.com/dart-lang/linter/issues/288
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
#
# There are other similar analysis options files in the flutter repos,
# which should be kept in sync with this file:
#
# - analysis_options.yaml (this file)
# - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
#
# This file contains the analysis options used by Flutter tools, such as IntelliJ,
# Android Studio, and the `flutter analyze` command.
analyzer: analyzer:
strong-mode: strong-mode:
implicit-dynamic: false implicit-dynamic: false
errors: errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning missing_return: warning
# allow having TODOs in the code
todo: ignore todo: ignore
# Ignore analyzer hints for updating pubspecs when using Future or
# Stream and not importing dart:async
# Please see https://github.com/flutter/flutter/pull/24528 for details.
#sdk_version_async_exported_from_core: ignore
exclude:
- 'bin/cache/**'
# the following two are relative to the stocks example and the flutter package respectively
# see https://github.com/dart-lang/sdk/issues/28463
- 'lib/i18n/stock_messages_*.dart'
- 'lib/src/http/**'
linter: linter:
rules: rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types - always_declare_return_types
- always_put_control_body_on_new_line - always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters - always_require_non_null_named_parameters
- always_specify_types - always_specify_types
- annotate_overrides - annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_as - avoid_as
# - avoid_bool_literals_in_conditional_expressions # not yet tested
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
- avoid_classes_with_only_static_members - avoid_classes_with_only_static_members
# - avoid_double_and_int_checks # only useful when targeting JS runtime
- avoid_empty_else - avoid_empty_else
- avoid_field_initializers_in_const_classes - avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls - avoid_function_literals_in_foreach_calls
# - avoid_implementing_value_types # not yet tested
- avoid_init_to_null - avoid_init_to_null
# - avoid_js_rounded_ints # only useful when targeting JS runtime
- avoid_null_checks_in_equality_operators - avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
- avoid_relative_lib_imports - avoid_relative_lib_imports
- avoid_renaming_method_parameters - avoid_renaming_method_parameters
- avoid_return_types_on_setters - avoid_return_types_on_setters
# - avoid_returning_null # there are plenty of valid reasons to return null
# - avoid_returning_null_for_future # not yet tested
- avoid_returning_null_for_void - avoid_returning_null_for_void
# - avoid_returning_this # there are plenty of valid reasons to return this
# - avoid_setters_without_getters # not yet tested
# - avoid_shadowing_type_parameters # not yet tested
# - avoid_single_cascade_in_expression_statements # not yet tested
- avoid_slow_async_io - avoid_slow_async_io
- avoid_types_as_parameter_names - avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
- avoid_unused_constructor_parameters - avoid_unused_constructor_parameters
- avoid_void_async - avoid_void_async
- await_only_futures - await_only_futures
- camel_case_types - camel_case_types
- cancel_subscriptions - cancel_subscriptions
# - cascade_invocations # not yet tested
# - close_sinks # not reliable enough
# - comment_references # blocked on https://github.com/flutter/flutter/issues/20765
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally - control_flow_in_finally
# - curly_braces_in_flow_control_structures # not yet tested
- directives_ordering - directives_ordering
- empty_catches - empty_catches
- empty_constructor_bodies - empty_constructor_bodies
- empty_statements - empty_statements
# - file_names # not yet tested
- flutter_style_todos - flutter_style_todos
- hash_and_equals - hash_and_equals
- implementation_imports - implementation_imports
# - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
- iterable_contains_unrelated_type - iterable_contains_unrelated_type
# - join_return_with_assignment # not yet tested
- library_names - library_names
- library_prefixes - library_prefixes
# - lines_longer_than_80_chars # not yet tested
- list_remove_unrelated_type - list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
- no_adjacent_strings_in_list - no_adjacent_strings_in_list
- no_duplicate_case_values - no_duplicate_case_values
- non_constant_identifier_names - non_constant_identifier_names
# - null_closures # not yet tested
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields - overridden_fields
- package_api_docs - package_api_docs
- package_names - package_names
- package_prefixed_library_names - package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation - prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists - prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment - prefer_conditional_assignment
- prefer_const_constructors - prefer_const_constructors
- prefer_const_constructors_in_immutables - prefer_const_constructors_in_immutables
- prefer_const_declarations - prefer_const_declarations
- prefer_const_literals_to_create_immutables - prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains - prefer_contains
- prefer_equal_for_default_values - prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields - prefer_final_fields
- prefer_final_locals - prefer_final_locals
- prefer_foreach - prefer_foreach
# - prefer_function_declarations_over_variables # not yet tested
- prefer_generic_function_type_aliases - prefer_generic_function_type_aliases
- prefer_initializing_formals - prefer_initializing_formals
# - prefer_int_literals # not yet tested
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty - prefer_is_empty
- prefer_is_not_empty - prefer_is_not_empty
- prefer_iterable_whereType - prefer_iterable_whereType
# - prefer_mixin # https://github.com/dart-lang/language/issues/32
- prefer_single_quotes - prefer_single_quotes
- prefer_typing_uninitialized_variables - prefer_typing_uninitialized_variables
- prefer_void_to_null - prefer_void_to_null
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters - recursive_getters
- slash_for_doc_comments - slash_for_doc_comments
- sort_constructors_first - sort_constructors_first
- sort_pub_dependencies - sort_pub_dependencies
- sort_unnamed_constructors_first - sort_unnamed_constructors_first
- super_goes_last
- test_types_in_equals - test_types_in_equals
- throw_in_finally - throw_in_finally
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals - type_init_formals
# - unawaited_futures # too many false positives
# - unnecessary_await_in_return # not yet tested
- unnecessary_brace_in_string_interps - unnecessary_brace_in_string_interps
- unnecessary_const - unnecessary_const
- unnecessary_getters_setters - unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_new - unnecessary_new
- unnecessary_null_aware_assignments - unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators - unnecessary_null_in_if_null_operators
...@@ -170,10 +89,5 @@ linter: ...@@ -170,10 +89,5 @@ linter:
- unnecessary_statements - unnecessary_statements
- unnecessary_this - unnecessary_this
- unrelated_type_equality_checks - unrelated_type_equality_checks
# - use_function_type_syntax_for_parameters # not yet tested
- use_rethrow_when_possible - use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested - valid_regexps
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 \ No newline at end of file
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
# - void_checks # not yet tested
\ No newline at end of file
...@@ -2,15 +2,14 @@ group 'com.baseflow.permissionhandler' ...@@ -2,15 +2,14 @@ group 'com.baseflow.permissionhandler'
version '1.0-SNAPSHOT' version '1.0-SNAPSHOT'
buildscript { buildscript {
ext.kotlin_version = '1.3.0'
repositories { repositories {
google() google()
jcenter() jcenter()
maven { url 'https://maven.google.com' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.0' classpath 'com.android.tools.build:gradle:3.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
...@@ -22,14 +21,10 @@ rootProject.allprojects { ...@@ -22,14 +21,10 @@ rootProject.allprojects {
} }
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android { android {
compileSdkVersion 28 compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...@@ -40,8 +35,10 @@ android { ...@@ -40,8 +35,10 @@ android {
} }
dependencies { dependencies {
api 'androidx.core:core:1.0.1' implementation 'androidx.annotation:annotation:1.0.1'
implementation 'androidx.core:core:1.0.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.google.code.gson:gson:2.8.5"
} }
repositories {
google()
}
\ No newline at end of file
package com.baseflow.permissionhandler;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.IntDef;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;
public class PermissionHandlerPlugin implements MethodCallHandler {
private static final String LOG_TAG = "permissions_handler";
private static final int PERMISSION_CODE = 25;
//PERMISSION_GROUP
private static final int PERMISSION_GROUP_CALENDAR = 0;
private static final int PERMISSION_GROUP_CAMERA = 1;
private static final int PERMISSION_GROUP_CONTACTS = 2;
private static final int PERMISSION_GROUP_LOCATION = 3;
private static final int PERMISSION_GROUP_LOCATION_ALWAYS = 4;
private static final int PERMISSION_GROUP_LOCATION_WHEN_IN_USE = 5;
private static final int PERMISSION_GROUP_MEDIA_LIBRARY = 6;
private static final int PERMISSION_GROUP_MICROPHONE = 7;
private static final int PERMISSION_GROUP_PHONE = 8;
private static final int PERMISSION_GROUP_PHOTOS = 9;
private static final int PERMISSION_GROUP_REMINDERS = 10;
private static final int PERMISSION_GROUP_SENSORS = 11;
private static final int PERMISSION_GROUP_SMS = 12;
private static final int PERMISSION_GROUP_SPEECH = 13;
private static final int PERMISSION_GROUP_STORAGE = 14;
private static final int PERMISSION_GROUP_UNKNOWN = 15;
private PermissionHandlerPlugin(Registrar mRegistrar) {
this.mRegistrar = mRegistrar;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PERMISSION_GROUP_CALENDAR,
PERMISSION_GROUP_CAMERA,
PERMISSION_GROUP_CONTACTS,
PERMISSION_GROUP_LOCATION,
PERMISSION_GROUP_LOCATION_ALWAYS,
PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
PERMISSION_GROUP_MEDIA_LIBRARY,
PERMISSION_GROUP_MICROPHONE,
PERMISSION_GROUP_PHONE,
PERMISSION_GROUP_PHOTOS,
PERMISSION_GROUP_REMINDERS,
PERMISSION_GROUP_SENSORS,
PERMISSION_GROUP_SMS,
PERMISSION_GROUP_SPEECH,
PERMISSION_GROUP_STORAGE,
PERMISSION_GROUP_UNKNOWN,
})
private @interface PermissionGroup {
}
//PERMISSION_STATUS
private static final int PERMISSION_STATUS_DENIED = 0;
private static final int PERMISSION_STATUS_DISABLED = 1;
private static final int PERMISSION_STATUS_GRANTED = 2;
private static final int PERMISSION_STATUS_RESTRICTED = 3;
private static final int PERMISSION_STATUS_UNKNOWN = 4;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PERMISSION_STATUS_DENIED,
PERMISSION_STATUS_DISABLED,
PERMISSION_STATUS_GRANTED,
PERMISSION_STATUS_RESTRICTED,
PERMISSION_STATUS_UNKNOWN,
})
private @interface PermissionStatus {
}
//SERVICE_STATUS
private static final int SERVICE_STATUS_DISABLED = 0;
private static final int SERVICE_STATUS_ENABLED = 1;
private static final int SERVICE_STATUS_NOT_APPLICABLE = 2;
private static final int SERVICE_STATUS_UNKNOWN = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SERVICE_STATUS_DISABLED,
SERVICE_STATUS_ENABLED,
SERVICE_STATUS_NOT_APPLICABLE,
SERVICE_STATUS_UNKNOWN,
})
private @interface ServiceStatus {
}
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter.baseflow.com/permissions/methods");
final PermissionHandlerPlugin permissionHandlerPlugin = new PermissionHandlerPlugin(registrar);
channel.setMethodCallHandler(permissionHandlerPlugin);
registrar.addRequestPermissionsResultListener(new PluginRegistry.RequestPermissionsResultListener() {
@Override
public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults) {
if (id == PERMISSION_CODE) {
permissionHandlerPlugin.handlePermissionsRequest(permissions, grantResults);
return true;
} else {
return false;
}
}
});
}
@PermissionGroup
private static int parseManifestName(String permission) {
switch (permission) {
case Manifest.permission.READ_CALENDAR:
case Manifest.permission.WRITE_CALENDAR:
return PERMISSION_GROUP_CALENDAR;
case Manifest.permission.CAMERA:
return PERMISSION_GROUP_CAMERA;
case Manifest.permission.READ_CONTACTS:
case Manifest.permission.WRITE_CONTACTS:
case Manifest.permission.GET_ACCOUNTS:
return PERMISSION_GROUP_CONTACTS;
case Manifest.permission.ACCESS_COARSE_LOCATION:
case Manifest.permission.ACCESS_FINE_LOCATION:
return PERMISSION_GROUP_LOCATION;
case Manifest.permission.RECORD_AUDIO:
return PERMISSION_GROUP_MICROPHONE;
case Manifest.permission.READ_PHONE_STATE:
case Manifest.permission.CALL_PHONE:
case Manifest.permission.READ_CALL_LOG:
case Manifest.permission.WRITE_CALL_LOG:
case Manifest.permission.ADD_VOICEMAIL:
case Manifest.permission.USE_SIP:
case Manifest.permission.PROCESS_OUTGOING_CALLS:
return PERMISSION_GROUP_PHONE;
case Manifest.permission.BODY_SENSORS:
return PERMISSION_GROUP_SENSORS;
case Manifest.permission.SEND_SMS:
case Manifest.permission.RECEIVE_SMS:
case Manifest.permission.READ_SMS:
case Manifest.permission.RECEIVE_WAP_PUSH:
case Manifest.permission.RECEIVE_MMS:
return PERMISSION_GROUP_SMS;
case Manifest.permission.READ_EXTERNAL_STORAGE:
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
return PERMISSION_GROUP_STORAGE;
default:
return PERMISSION_GROUP_UNKNOWN;
}
}
private final Registrar mRegistrar;
private Result mResult;
private ArrayList<String> mRequestedPermissions;
@SuppressLint("UseSparseArrays")
private Map<Integer, Integer> mRequestResults = new HashMap<>();
@Override
public void onMethodCall(MethodCall call, Result result) {
switch (call.method) {
case "checkPermissionStatus": {
@PermissionGroup final int permission = (int) call.arguments;
@PermissionStatus final int permissionStatus = checkPermissionStatus(permission);
result.success(permissionStatus);
break;
}
case "checkServiceStatus": {
@PermissionGroup final int permission = (int) call.arguments;
@ServiceStatus final int serviceStatus = checkServiceStatus(permission);
result.success(serviceStatus);
break;
}
case "requestPermissions":
if (mResult != null) {
result.error(
"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);
}
mResult = result;
final List<Integer> permissions = call.arguments();
requestPermissions(permissions);
break;
case "shouldShowRequestPermissionRationale": {
@PermissionGroup final int permission = (int) call.arguments;
result.success(shouldShowRequestPermissionRationale(permission));
break;
}
case "openAppSettings":
boolean isOpen = openAppSettings();
result.success(isOpen);
break;
default:
result.notImplemented();
break;
}
}
@PermissionStatus
private int checkPermissionStatus(@PermissionGroup int permission) {
final List<String> names = getManifestNames(permission);
if (names == null) {
Log.d(LOG_TAG, "No android specific permissions needed for: $permission");
return PERMISSION_STATUS_GRANTED;
}
//if no permissions were found then there is an issue and permission is not set in Android manifest
if (names.size() == 0) {
Log.d(LOG_TAG, "No permissions found in manifest for: $permission");
return PERMISSION_STATUS_UNKNOWN;
}
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return PERMISSION_STATUS_UNKNOWN;
}
final boolean targetsMOrHigher = context.getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.M;
for (String name : names) {
if (targetsMOrHigher) {
final int permissionStatus = ContextCompat.checkSelfPermission(context, name);
if (permissionStatus == PackageManager.PERMISSION_DENIED) {
return PERMISSION_STATUS_DENIED;
} else if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PERMISSION_STATUS_UNKNOWN;
}
}
}
if (permission == PERMISSION_GROUP_LOCATION || permission == PERMISSION_GROUP_LOCATION_ALWAYS || permission == PERMISSION_GROUP_LOCATION_WHEN_IN_USE) {
if (!isLocationServiceEnabled(context)) {
return PERMISSION_STATUS_DISABLED;
}
}
return PERMISSION_STATUS_GRANTED;
}
@ServiceStatus
private int checkServiceStatus(int permission) {
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return SERVICE_STATUS_UNKNOWN;
}
if (permission == PERMISSION_GROUP_LOCATION || permission == PERMISSION_GROUP_LOCATION_ALWAYS || permission == PERMISSION_GROUP_LOCATION_WHEN_IN_USE) {
return isLocationServiceEnabled(context) ? SERVICE_STATUS_ENABLED : SERVICE_STATUS_DISABLED;
}
return SERVICE_STATUS_NOT_APPLICABLE;
}
private boolean shouldShowRequestPermissionRationale(int permission) {
Activity activity = mRegistrar.activity();
if (activity == null) {
Log.d(LOG_TAG, "Unable to detect current Activity.");
return false;
}
List<String> names = getManifestNames(permission);
// if isn't an android specific group then go ahead and return false;
if (names == null) {
Log.d(LOG_TAG, "No android specific permissions needed for: $permission");
return false;
}
if (names.isEmpty()) {
Log.d(LOG_TAG, "No permissions found in manifest for: $permission no need to show request rationale");
return false;
}
//noinspection LoopStatementThatDoesntLoop
for (String name : names) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, name);
}
return false;
}
private void requestPermissions(List<Integer> permissions) {
if (mRegistrar.activity() == null) {
Log.d(LOG_TAG, "Unable to detect current Activity.");
for (Integer permission : permissions) {
mRequestResults.put(permission, PERMISSION_STATUS_UNKNOWN);
}
processResult();
return;
}
ArrayList<String> permissionsToRequest = new ArrayList<>();
for (Integer permission : permissions) {
@PermissionStatus final int permissionStatus = checkPermissionStatus(permission);
if (permissionStatus != PERMISSION_STATUS_GRANTED) {
final List<String> names = getManifestNames(permission);
//check to see if we can find manifest names
//if we can't add as unknown and continue
if (names == null || names.isEmpty()) {
if (!mRequestResults.containsKey(permission)) {
mRequestResults.put(permission, PERMISSION_STATUS_UNKNOWN);
}
continue;
}
permissionsToRequest.addAll(names);
} else {
if (!mRequestResults.containsKey(permission)) {
mRequestResults.put(permission, PERMISSION_STATUS_GRANTED);
}
}
}
final String[] requestPermissions = permissionsToRequest.toArray(new String[0]);
if (permissionsToRequest.size() > 0) {
ActivityCompat.requestPermissions(mRegistrar.activity(), requestPermissions, PERMISSION_CODE);
} else if (mRequestResults.size() > 0) {
processResult();
}
}
private void handlePermissionsRequest(String[] permissions, int[] grantResults) {
if (mResult == null) {
return;
}
for (int i = 0; i < permissions.length; i++) {
@PermissionGroup final int permission = parseManifestName(permissions[i]);
if (permission == PERMISSION_GROUP_UNKNOWN)
continue;
if (permission == PERMISSION_GROUP_MICROPHONE) {
if (!mRequestResults.containsKey(PERMISSION_GROUP_MICROPHONE)) {
mRequestResults.put(PERMISSION_GROUP_MICROPHONE, toPermissionStatus(grantResults[i]));
}
if (!mRequestResults.containsKey(PERMISSION_GROUP_SPEECH)) {
mRequestResults.put(PERMISSION_GROUP_SPEECH, toPermissionStatus(grantResults[i]));
}
} else if (permission == PERMISSION_GROUP_LOCATION) {
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
final boolean isLocationServiceEnabled = context != null && isLocationServiceEnabled(context);
@PermissionStatus int permissionStatus = toPermissionStatus(grantResults[i]);
if (permissionStatus == PERMISSION_STATUS_GRANTED && !isLocationServiceEnabled) {
permissionStatus = PERMISSION_STATUS_DISABLED;
}
if (!mRequestResults.containsKey(PERMISSION_GROUP_LOCATION_ALWAYS)) {
mRequestResults.put(PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
}
if (!mRequestResults.containsKey(PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) {
mRequestResults.put(PERMISSION_GROUP_LOCATION_WHEN_IN_USE, permissionStatus);
}
mRequestResults.put(permission, permissionStatus);
} else if (!mRequestResults.containsKey(permission)) {
mRequestResults.put(permission, toPermissionStatus(grantResults[i]));
}
}
processResult();
}
@PermissionStatus
private int toPermissionStatus(int grantResult) {
return grantResult == PackageManager.PERMISSION_GRANTED ? PERMISSION_STATUS_GRANTED : PERMISSION_STATUS_DENIED;
}
private void processResult() {
mResult.success(mRequestResults);
mRequestResults.clear();
mResult = null;
}
private boolean openAppSettings() {
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return false;
}
try {
Intent settingsIntent = new Intent();
settingsIntent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
settingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
settingsIntent.setData(android.net.Uri.parse("package:" + context.getPackageName()));
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(settingsIntent);
return true;
} catch (Exception ex) {
return false;
}
}
private List<String> getManifestNames(@PermissionGroup int permission) {
final ArrayList<String> permissionNames = new ArrayList<>();
switch (permission) {
case PERMISSION_GROUP_CALENDAR:
if (hasPermissionInManifest(Manifest.permission.READ_CALENDAR))
permissionNames.add(Manifest.permission.READ_CALENDAR);
if (hasPermissionInManifest(Manifest.permission.WRITE_CALENDAR))
permissionNames.add(Manifest.permission.WRITE_CALENDAR);
break;
case PERMISSION_GROUP_CAMERA:
if (hasPermissionInManifest(Manifest.permission.CAMERA))
permissionNames.add(Manifest.permission.CAMERA);
break;
case PERMISSION_GROUP_CONTACTS:
if (hasPermissionInManifest(Manifest.permission.READ_CONTACTS))
permissionNames.add(Manifest.permission.READ_CONTACTS);
if (hasPermissionInManifest(Manifest.permission.WRITE_CONTACTS))
permissionNames.add(Manifest.permission.WRITE_CONTACTS);
if (hasPermissionInManifest(Manifest.permission.GET_ACCOUNTS))
permissionNames.add(Manifest.permission.GET_ACCOUNTS);
break;
case PERMISSION_GROUP_LOCATION_ALWAYS:
case PERMISSION_GROUP_LOCATION_WHEN_IN_USE:
case PERMISSION_GROUP_LOCATION:
if (hasPermissionInManifest(Manifest.permission.ACCESS_COARSE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_COARSE_LOCATION);
if (hasPermissionInManifest(Manifest.permission.ACCESS_FINE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_FINE_LOCATION);
break;
case PERMISSION_GROUP_SPEECH:
case PERMISSION_GROUP_MICROPHONE:
if (hasPermissionInManifest(Manifest.permission.RECORD_AUDIO))
permissionNames.add(Manifest.permission.RECORD_AUDIO);
break;
case PERMISSION_GROUP_PHONE:
if (hasPermissionInManifest(Manifest.permission.READ_PHONE_STATE))
permissionNames.add(Manifest.permission.READ_PHONE_STATE);
if (hasPermissionInManifest(Manifest.permission.CALL_PHONE))
permissionNames.add(Manifest.permission.CALL_PHONE);
if (hasPermissionInManifest(Manifest.permission.READ_CALL_LOG))
permissionNames.add(Manifest.permission.READ_CALL_LOG);
if (hasPermissionInManifest(Manifest.permission.WRITE_CALL_LOG))
permissionNames.add(Manifest.permission.WRITE_CALL_LOG);
if (hasPermissionInManifest(Manifest.permission.ADD_VOICEMAIL))
permissionNames.add(Manifest.permission.ADD_VOICEMAIL);
if (hasPermissionInManifest(Manifest.permission.USE_SIP))
permissionNames.add(Manifest.permission.USE_SIP);
if (hasPermissionInManifest(Manifest.permission.PROCESS_OUTGOING_CALLS))
permissionNames.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
break;
case PERMISSION_GROUP_SENSORS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
if (hasPermissionInManifest(Manifest.permission.BODY_SENSORS)) {
permissionNames.add(Manifest.permission.BODY_SENSORS);
}
}
break;
case PERMISSION_GROUP_SMS:
if (hasPermissionInManifest(Manifest.permission.SEND_SMS))
permissionNames.add(Manifest.permission.SEND_SMS);
if (hasPermissionInManifest(Manifest.permission.RECEIVE_SMS))
permissionNames.add(Manifest.permission.RECEIVE_SMS);
if (hasPermissionInManifest(Manifest.permission.READ_SMS))
permissionNames.add(Manifest.permission.READ_SMS);
if (hasPermissionInManifest(Manifest.permission.RECEIVE_WAP_PUSH))
permissionNames.add(Manifest.permission.RECEIVE_WAP_PUSH);
if (hasPermissionInManifest(Manifest.permission.RECEIVE_MMS))
permissionNames.add(Manifest.permission.RECEIVE_MMS);
break;
case PERMISSION_GROUP_STORAGE:
if (hasPermissionInManifest(Manifest.permission.READ_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (hasPermissionInManifest(Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
break;
case PERMISSION_GROUP_MEDIA_LIBRARY:
case PERMISSION_GROUP_PHOTOS:
case PERMISSION_GROUP_REMINDERS:
case PERMISSION_GROUP_UNKNOWN:
return null;
}
return permissionNames;
}
private boolean hasPermissionInManifest(String permission) {
try {
if (mRequestedPermissions != null) {
for (String r : mRequestedPermissions) {
if (r.equals(permission)) {
return true;
}
}
}
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return false;
}
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
if (info == null) {
Log.d(LOG_TAG, "Unable to get Package info, will not be able to determine permissions to request.");
return false;
}
mRequestedPermissions = new ArrayList<>(Arrays.asList(info.requestedPermissions));
for (String r : mRequestedPermissions) {
if (r.equals(permission)) {
return true;
}
}
} catch (Exception ex) {
Log.d(LOG_TAG, "Unable to check manifest for permission: ", ex);
}
return false;
}
@SuppressWarnings("deprecation")
private boolean isLocationServiceEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
final LocationManager locationManager = context.getSystemService(LocationManager.class);
if (locationManager == null) {
return false;
}
return locationManager.isLocationEnabled();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final int locationMode;
try {
locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
return false;
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
} else {
final String locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
}
}
package com.baseflow.permissionhandler
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.provider.Settings
import androidx.core.app.ActivityCompat
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
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.Registrar
import android.text.TextUtils
import android.provider.Settings.SettingNotFoundException
class PermissionHandlerPlugin(private val registrar: Registrar, private var requestedPermissions: MutableList<String>? = null) : MethodCallHandler {
private var mRequestResults = mutableMapOf<PermissionGroup, PermissionStatus>()
private var mResult: Result? = null
companion object {
const val permissionCode = 25
@JvmStatic
private val mLogTag = "permissions_handler"
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "flutter.baseflow.com/permissions/methods")
val instance = PermissionHandlerPlugin(registrar)
channel.setMethodCallHandler(instance)
registrar.addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener { id, permissions, grantResults ->
if (id == permissionCode) {
instance.handlePermissionsRequest(permissions, grantResults)
return@RequestPermissionsResultListener true
}
return@RequestPermissionsResultListener false
})
}
@JvmStatic
fun parseManifestName(permission: String): PermissionGroup {
when (permission) {
Manifest.permission.READ_CALENDAR,
Manifest.permission.WRITE_CALENDAR ->
return PermissionGroup.CALENDAR
Manifest.permission.CAMERA ->
return PermissionGroup.CAMERA
Manifest.permission.READ_CONTACTS,
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.GET_ACCOUNTS ->
return PermissionGroup.CONTACTS
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION ->
return PermissionGroup.LOCATION
Manifest.permission.RECORD_AUDIO ->
return PermissionGroup.MICROPHONE
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CALL_PHONE,
Manifest.permission.READ_CALL_LOG,
Manifest.permission.WRITE_CALL_LOG,
Manifest.permission.ADD_VOICEMAIL,
Manifest.permission.USE_SIP,
Manifest.permission.PROCESS_OUTGOING_CALLS ->
return PermissionGroup.PHONE
Manifest.permission.BODY_SENSORS ->
return PermissionGroup.SENSORS
Manifest.permission.SEND_SMS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_WAP_PUSH,
Manifest.permission.RECEIVE_MMS ->
return PermissionGroup.SMS
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE ->
return PermissionGroup.STORAGE
}
return PermissionGroup.UNKNOWN
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
when {
call.method == "checkPermissionStatus" -> {
val permission = Codec.decodePermissionGroup(call.arguments)
val permissionStatus = checkPermissionStatus(permission)
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) {
result.error(
"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)
}
mResult = result
val permissions = Codec.decodePermissionGroups(call.arguments)
requestPermissions(permissions)
}
call.method == "shouldShowRequestPermissionRationale" -> {
val permission = Codec.decodePermissionGroup(call.arguments)
result.success(shouldShowRequestPermissionRationale(permission))
}
call.method == "openAppSettings" -> {
val isOpen = openAppSettings()
result.success(isOpen)
}
else -> result.notImplemented()
}
}
private fun checkPermissionStatus(permission: PermissionGroup): PermissionStatus {
val names = getManifestNames(permission)
if (names == null) {
Log.d(mLogTag, "No android specific permissions needed for: $permission")
return PermissionStatus.GRANTED
}
//if no permissions were found then there is an issue and permission is not set in Android manifest
if (names.count() == 0) {
Log.d(mLogTag, "No permissions found in manifest for: $permission")
return PermissionStatus.UNKNOWN
}
val context: Context? = registrar.activity() ?: registrar.activeContext()
if (context == null) {
Log.d(mLogTag, "Unable to detect current Activity or App Context.")
return PermissionStatus.UNKNOWN
}
val targetsMOrHigher = context.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.M
for (name in names) {
if (targetsMOrHigher) {
val permissionStatus = ContextCompat.checkSelfPermission(context, name)
if (permissionStatus == PackageManager.PERMISSION_DENIED) {
return PermissionStatus.DENIED
} else if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PermissionStatus.UNKNOWN
}
}
}
if (permission == PermissionGroup.LOCATION || permission == PermissionGroup.LOCATION_ALWAYS || permission == PermissionGroup.LOCATION_WHEN_IN_USE) {
if (!isLocationServiceEnabled(context)) {
return PermissionStatus.DISABLED
}
}
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)
{
Log.d(mLogTag, "Unable to detect current Activity.")
return false
}
val names = getManifestNames(permission)
// if isn't an android specific group then go ahead and return false;
if (names == null)
{
Log.d(mLogTag, "No android specific permissions needed for: $permission")
return false
}
if (names.isEmpty())
{
Log.d(mLogTag,"No permissions found in manifest for: $permission no need to show request rationale")
return false
}
for(name in names)
{
return ActivityCompat.shouldShowRequestPermissionRationale(activity, name)
}
return false
}
private fun requestPermissions(permissions: Array<PermissionGroup>) {
if (registrar.activity() == null) {
Log.d(mLogTag, "Unable to detect current Activity.")
for (permission in permissions) {
mRequestResults[permission] = PermissionStatus.UNKNOWN
}
processResult()
return
}
val permissionsToRequest = mutableListOf<String>()
for (permission in permissions) {
val permissionStatus = checkPermissionStatus(permission)
if (permissionStatus != PermissionStatus.GRANTED) {
val names = getManifestNames(permission)
//check to see if we can find manifest names
//if we can't add as unknown and continue
if (names == null || names.isEmpty()) {
if (!mRequestResults.containsKey(permission)) {
mRequestResults[permission] = PermissionStatus.UNKNOWN
}
continue
}
names.let { permissionsToRequest.addAll(it) }
} else {
if (!mRequestResults.containsKey(permission)) {
mRequestResults[permission] = PermissionStatus.GRANTED
}
}
}
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) {
if (mResult == null) {
return
}
for (i in permissions.indices) {
val permission = parseManifestName(permissions[i])
if (permission == PermissionGroup.UNKNOWN)
continue
if (permission == PermissionGroup.MICROPHONE) {
if (!mRequestResults.containsKey(PermissionGroup.SPEECH)) {
mRequestResults[PermissionGroup.SPEECH] = grantResults[i].toPermissionStatus()
}
} else if (permission == PermissionGroup.LOCATION) {
val context: Context? = registrar.activity() ?: registrar.activeContext()
val isLocationServiceEnabled= if (context == null) false else isLocationServiceEnabled(context)
var permissionStatus = grantResults[i].toPermissionStatus()
if (permissionStatus == PermissionStatus.GRANTED && !isLocationServiceEnabled) {
permissionStatus = PermissionStatus.DISABLED
}
if (!mRequestResults.containsKey(PermissionGroup.LOCATION_ALWAYS)) {
mRequestResults[PermissionGroup.LOCATION_ALWAYS] = permissionStatus
}
if (!mRequestResults.containsKey(PermissionGroup.LOCATION_WHEN_IN_USE)) {
mRequestResults[PermissionGroup.LOCATION_WHEN_IN_USE] = permissionStatus
}
mRequestResults[permission] = permissionStatus
} else if (!mRequestResults.containsKey(permission)) {
mRequestResults[permission] = grantResults[i].toPermissionStatus()
}
}
processResult()
}
private fun Int.toPermissionStatus(): PermissionStatus {
return if (this == PackageManager.PERMISSION_GRANTED) PermissionStatus.GRANTED else PermissionStatus.DENIED
}
private fun processResult() {
mResult?.success(Codec.encodePermissionRequestResult(mRequestResults))
mRequestResults.clear()
mResult = null
}
private fun openAppSettings(): Boolean {
val context: Context? = registrar.activity() ?: registrar.activeContext()
if (context == null) {
Log.d(mLogTag, "Unable to detect current Activity or App Context.")
return false
}
return try {
val settingsIntent = Intent()
settingsIntent.action = android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
settingsIntent.addCategory(Intent.CATEGORY_DEFAULT)
settingsIntent.data = android.net.Uri.parse("package:" + context.packageName)
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
context.startActivity(settingsIntent)
true
} catch(ex: Exception) {
false
}
}
private fun getManifestNames(permission: PermissionGroup): List<String>? {
val permissionNames: MutableList<String> = mutableListOf()
when (permission) {
PermissionGroup.CALENDAR -> {
if (hasPermissionInManifest(Manifest.permission.READ_CALENDAR))
permissionNames.add(Manifest.permission.READ_CALENDAR)
if (hasPermissionInManifest(Manifest.permission.WRITE_CALENDAR))
permissionNames.add(Manifest.permission.WRITE_CALENDAR)
}
PermissionGroup.CAMERA -> {
if (hasPermissionInManifest(Manifest.permission.CAMERA))
permissionNames.add(Manifest.permission.CAMERA)
}
PermissionGroup.CONTACTS -> {
if (hasPermissionInManifest(Manifest.permission.READ_CONTACTS))
permissionNames.add(Manifest.permission.READ_CONTACTS)
if (hasPermissionInManifest(Manifest.permission.WRITE_CONTACTS))
permissionNames.add(Manifest.permission.WRITE_CONTACTS)
if (hasPermissionInManifest(Manifest.permission.GET_ACCOUNTS))
permissionNames.add(Manifest.permission.GET_ACCOUNTS)
}
PermissionGroup.LOCATION_ALWAYS,
PermissionGroup.LOCATION_WHEN_IN_USE,
PermissionGroup.LOCATION -> {
if (hasPermissionInManifest(Manifest.permission.ACCESS_COARSE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_COARSE_LOCATION)
if (hasPermissionInManifest(Manifest.permission.ACCESS_FINE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_FINE_LOCATION)
}
PermissionGroup.SPEECH,
PermissionGroup.MICROPHONE -> {
if (hasPermissionInManifest(Manifest.permission.RECORD_AUDIO))
permissionNames.add(Manifest.permission.RECORD_AUDIO)
}
PermissionGroup.PHONE -> {
if (hasPermissionInManifest(Manifest.permission.READ_PHONE_STATE))
permissionNames.add(Manifest.permission.READ_PHONE_STATE)
if (hasPermissionInManifest(Manifest.permission.CALL_PHONE))
permissionNames.add(Manifest.permission.CALL_PHONE)
if (hasPermissionInManifest(Manifest.permission.READ_CALL_LOG))
permissionNames.add(Manifest.permission.READ_CALL_LOG)
if (hasPermissionInManifest(Manifest.permission.WRITE_CALL_LOG))
permissionNames.add(Manifest.permission.WRITE_CALL_LOG)
if (hasPermissionInManifest(Manifest.permission.ADD_VOICEMAIL))
permissionNames.add(Manifest.permission.ADD_VOICEMAIL)
if (hasPermissionInManifest(Manifest.permission.USE_SIP))
permissionNames.add(Manifest.permission.USE_SIP)
if (hasPermissionInManifest(Manifest.permission.PROCESS_OUTGOING_CALLS))
permissionNames.add(Manifest.permission.PROCESS_OUTGOING_CALLS)
}
PermissionGroup.SENSORS -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
if (hasPermissionInManifest(Manifest.permission.BODY_SENSORS)) {
permissionNames.add(Manifest.permission.BODY_SENSORS)
}
}
}
PermissionGroup.SMS -> {
if (hasPermissionInManifest(Manifest.permission.SEND_SMS))
permissionNames.add(Manifest.permission.SEND_SMS)
if (hasPermissionInManifest(Manifest.permission.RECEIVE_SMS))
permissionNames.add(Manifest.permission.RECEIVE_SMS)
if (hasPermissionInManifest(Manifest.permission.READ_SMS))
permissionNames.add(Manifest.permission.READ_SMS)
if (hasPermissionInManifest(Manifest.permission.RECEIVE_WAP_PUSH))
permissionNames.add(Manifest.permission.RECEIVE_WAP_PUSH)
if (hasPermissionInManifest(Manifest.permission.RECEIVE_MMS))
permissionNames.add(Manifest.permission.RECEIVE_MMS)
}
PermissionGroup.STORAGE -> {
if (hasPermissionInManifest(Manifest.permission.READ_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.READ_EXTERNAL_STORAGE)
if (hasPermissionInManifest(Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
else -> return null
}
return permissionNames
}
private fun hasPermissionInManifest(permission: String): Boolean {
try {
requestedPermissions?.let {
return it.any { r -> r.equals(permission, true) }
}
val context: Context? = registrar.activity() ?: registrar.activeContext()
if (context == null) {
Log.d(mLogTag, "Unable to detect current Activity or App Context.")
return false
}
val info: PackageInfo? = context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
if (info == null) {
Log.d(mLogTag, "Unable to get Package info, will not be able to determine permissions to request.")
return false
}
requestedPermissions = info.requestedPermissions.toMutableList()
if (requestedPermissions == null) {
Log.d(mLogTag, "There are no requested permissions, please check to ensure you have marked permissions you want to request.")
return false
}
requestedPermissions?.let {
return it.any { r -> r.equals(permission, true) }
} ?: return false
} catch (ex: Exception) {
Log.d(mLogTag, "Unable to check manifest for permission: $ex")
}
return false
}
private fun isLocationServiceEnabled(context: Context): Boolean {
val locationMode: Int
val locationProviders: String
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
locationMode = Settings.Secure.getInt(context.contentResolver, Settings.Secure.LOCATION_MODE)
} catch (e: SettingNotFoundException) {
e.printStackTrace()
return false
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF
} else {
locationProviders = Settings.Secure.getString(context.contentResolver, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
return !TextUtils.isEmpty(locationProviders)
}
}
}
package com.baseflow.permissionhandler.data
import com.google.gson.annotations.SerializedName
enum class PermissionGroup {
@SerializedName("unknown")
UNKNOWN,
@SerializedName("calendar")
CALENDAR,
@SerializedName("camera")
CAMERA,
@SerializedName("contacts")
CONTACTS,
@SerializedName("location")
LOCATION,
@SerializedName("microphone")
MICROPHONE,
@SerializedName("phone")
PHONE,
@SerializedName("photos")
PHOTOS,
@SerializedName("reminders")
REMINDERS,
@SerializedName("sensors")
SENSORS,
@SerializedName("sms")
SMS,
@SerializedName("storage")
STORAGE,
@SerializedName("speech")
SPEECH,
@SerializedName("locationAlways")
LOCATION_ALWAYS,
@SerializedName("locationWhenInUse")
LOCATION_WHEN_IN_USE,
@SerializedName("mediaLibrary")
MEDIA_LIBRARY
}
\ No newline at end of file
package com.baseflow.permissionhandler.data
import com.google.gson.annotations.SerializedName
enum class PermissionStatus {
@SerializedName("unknown")
UNKNOWN,
@SerializedName("denied")
DENIED,
@SerializedName("disabled")
DISABLED,
@SerializedName("granted")
GRANTED,
}
\ No newline at end of file
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
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
class Codec {
companion object {
@JvmStatic
private val gsonDecoder : Gson = GsonBuilder().enableComplexMapKeySerialization().create()
@JvmStatic
fun decodePermissionGroup(arguments: Any) : PermissionGroup {
return Codec.gsonDecoder.fromJson(arguments.toString(), PermissionGroup::class.java)
}
@JvmStatic
fun decodePermissionGroups(arguments: Any) : Array<PermissionGroup> {
var permissionGroupsType = object: TypeToken<Array<PermissionGroup>>() {}.type
return Codec.gsonDecoder.fromJson(arguments.toString(), permissionGroupsType)
}
@JvmStatic
fun encodePermissionStatus(permissionStatus: PermissionStatus) : String {
return gsonDecoder.toJson(permissionStatus)
}
@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
}
}
}
\ No newline at end of file
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store .DS_Store
.dart_tool/ .atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.packages .packages
.pub-cache/
.pub/ .pub/
build/ build/
.flutter-plugins # Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
<component name="libraryTable"> <component name="libraryTable">
<library name="Dart SDK"> <library name="Dart SDK">
<CLASSES> <CLASSES>
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/async" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/collection" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/convert" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/core" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/developer" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/html" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/io" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/isolate" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/math" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/mirrors" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file:///Users/maurits/Developer/flutter/bin/cache/dart-sdk/lib/typed_data" /> <root url="file:///Users/long1eu/IDE/flutter/bin/cache/dart-sdk/lib/typed_data" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />
......
<component name="libraryTable"> <component name="libraryTable">
<library name="Flutter for Android"> <library name="Flutter for Android">
<CLASSES> <CLASSES>
<root url="jar:///Users/maurits/Developer/flutter/bin/cache/artifacts/engine/android-arm/flutter.jar!/" /> <root url="jar:///Users/long1eu/IDE/flutter/bin/cache/artifacts/engine/android-arm/flutter.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/permission_handler_example.iml" filepath="$PROJECT_DIR$/permission_handler_example.iml" /> <module fileurl="file://$PROJECT_DIR$/permission_handler_example.iml" filepath="$PROJECT_DIR$/permission_handler_example.iml" />
<module fileurl="file://$PROJECT_DIR$/permission_handler_example_android.iml" filepath="$PROJECT_DIR$/permission_handler_example_android.iml" /> <module fileurl="file://$PROJECT_DIR$/android/permission_handler_example_android.iml" filepath="$PROJECT_DIR$/android/permission_handler_example_android.iml" />
</modules> </modules>
</component> </component>
</project> </project>
...@@ -4,5 +4,7 @@ ...@@ -4,5 +4,7 @@
# This file should be version controlled and should not be manually edited. # This file should be version controlled and should not be manually edited.
version: version:
revision: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b revision: 06b979c4d5e1b499745422269f01a00341257058
channel: beta channel: master
project_type: app
...@@ -4,5 +4,13 @@ Demonstrates how to use the permission_handler plugin. ...@@ -4,5 +4,13 @@ Demonstrates how to use the permission_handler plugin.
## Getting Started ## Getting Started
For help getting started with Flutter, view our online This project is a starting point for a Flutter application.
[documentation](https://flutter.io/).
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
*.iml
*.class
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
GeneratedPluginRegistrant.java
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>app</name>
<comment>Project app created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>
connection.project.dir=..
eclipse.preferences.version=1
...@@ -11,29 +11,33 @@ if (flutterRoot == null) { ...@@ -11,29 +11,33 @@ if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
} }
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 28 compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions { lintOptions {
disable 'InvalidPackage' disable 'InvalidPackage'
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.baseflow.permissionhandlerexample" applicationId "com.example.permissionhandlerexample"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionCode 1 versionCode flutterVersionCode.toInteger()
versionName "1.0" versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
...@@ -47,11 +51,4 @@ android { ...@@ -47,11 +51,4 @@ android {
flutter { flutter {
source '../..' source '../..'
} }
\ No newline at end of file
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.permissionhandlerexample">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baseflow.permissionhandlerexample"> xmlns:tools="http://schemas.android.com/tools"
package="com.example.permissionhandlerexample">
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application <uses-permission android:name="android.permission.INTERNET" />
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_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_MMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" /> <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" /> <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CALENDAR" /> <uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" /> <uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" /> <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SENSORS" /> <uses-permission android:name="android.permission.SENSORS" />
<uses-permission android:name="android.permission.BODY_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.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application <application
android:name="io.flutter.app.FlutterApplication" android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher"
android:label="permission_handler_example" android:label="permission_handler_example"
android:icon="@mipmap/ic_launcher"> tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data <meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" /> android:value="true" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>
......
package com.example.permissionhandlerexample;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
package com.baseflow.permissionhandlerexample
import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity(): FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.permissionhandlerexample">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
buildscript { buildscript {
ext.kotlin_version = '1.3.0'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.3.0' classpath 'com.android.tools.build:gradle:3.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
......
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableJetifier=true
android.useAndroidX=true
\ No newline at end of file
#Wed Oct 10 08:31:26 CEST 2018 #Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
......
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/app.flx
/Flutter/app.zip
/Flutter/flutter_assets/
/Flutter/App.framework
/Flutter/Flutter.framework
/Flutter/Generated.xcconfig
/ServiceDefinitions.json
Pods/
.symlinks/
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"
FLUTTER_BUILD_MODE=debug
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=') def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file) file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path if !File.exists? file_abs_path
...@@ -27,8 +33,6 @@ def parse_KV_file(file, separator='=') ...@@ -27,8 +33,6 @@ def parse_KV_file(file, separator='=')
end end
target 'Runner' do target 'Runner' do
use_frameworks!
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines. # referring to absolute paths on developers' machines.
system('rm -rf .symlinks') system('rm -rf .symlinks')
...@@ -60,7 +64,6 @@ post_install do |installer| ...@@ -60,7 +64,6 @@ post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
target.build_configurations.each do |config| target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO' config.build_settings['ENABLE_BITCODE'] = 'NO'
config.build_settings['SWIFT_VERSION'] = '4.2'
end end
end end
end end
...@@ -8,17 +8,18 @@ ...@@ -8,17 +8,18 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 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 */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 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, ); }; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 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, ); }; }; 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 */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
F83E7534C5D827024000AB4C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F84C9A972D968241208A7F /* Pods_Runner.framework */; }; B7634AD7E1771AEDD2D1A5F7 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D132729FF349998420355A35 /* libPods-Runner.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
...@@ -39,21 +40,21 @@ ...@@ -39,21 +40,21 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; }; 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D132729FF349998420355A35 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -63,17 +64,17 @@ ...@@ -63,17 +64,17 @@
files = ( files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
F83E7534C5D827024000AB4C /* Pods_Runner.framework in Frameworks */, B7634AD7E1771AEDD2D1A5F7 /* libPods-Runner.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
1850378EF7B1AE3FA40E4F87 /* Frameworks */ = { 55A8F50F97AFD5E34636A6AC /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
36F84C9A972D968241208A7F /* Pods_Runner.framework */, D132729FF349998420355A35 /* libPods-Runner.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -83,7 +84,6 @@ ...@@ -83,7 +84,6 @@
children = ( children = (
3B80C3931E831B6300D905FE /* App.framework */, 3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */, 9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
...@@ -98,8 +98,8 @@ ...@@ -98,8 +98,8 @@
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
A2B39E4F789ABF9C57736776 /* Pods */, EDB9D82C0C7AC839E456BFF1 /* Pods */,
1850378EF7B1AE3FA40E4F87 /* Frameworks */, 55A8F50F97AFD5E34636A6AC /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
...@@ -114,6 +114,8 @@ ...@@ -114,6 +114,8 @@
97C146F01CF9000F007C117D /* Runner */ = { 97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
...@@ -121,8 +123,6 @@ ...@@ -121,8 +123,6 @@
97C146F11CF9000F007C117D /* Supporting Files */, 97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
); );
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -130,11 +130,12 @@ ...@@ -130,11 +130,12 @@
97C146F11CF9000F007C117D /* Supporting Files */ = { 97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146F21CF9000F007C117D /* main.m */,
); );
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
A2B39E4F789ABF9C57736776 /* Pods */ = { EDB9D82C0C7AC839E456BFF1 /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
); );
...@@ -148,14 +149,14 @@ ...@@ -148,14 +149,14 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
E8ED1A28A9A0730EDF0565B2 /* [CP] Check Pods Manifest.lock */, CD618854C236B3F0FBC77F28 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
259652FD16940B446BA7C6C6 /* [CP] Embed Pods Frameworks */, 8BA3EF5BA9C129C83EB3F474 /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
...@@ -172,12 +173,12 @@ ...@@ -172,12 +173,12 @@
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0940; LastUpgradeCheck = 0910;
ORGANIZATIONNAME = "The Chromium Authors"; ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0910; DevelopmentTeam = DKY2FBVP6L;
}; };
}; };
}; };
...@@ -206,7 +207,7 @@ ...@@ -206,7 +207,7 @@
files = ( files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
); );
...@@ -215,39 +216,41 @@ ...@@ -215,39 +216,41 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
259652FD16940B446BA7C6C6 /* [CP] Embed Pods Frameworks */ = { 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios-release/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/permission_handler/permission_handler.framework",
); );
name = "[CP] Embed Pods Frameworks"; name = "Thin Binary";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/permission_handler.framework",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
showEnvVarsInLog = 0;
}; };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 8BA3EF5BA9C129C83EB3F474 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = (
);
inputPaths = ( inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
); );
name = "Thin Binary";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
}; };
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
...@@ -263,16 +266,20 @@ ...@@ -263,16 +266,20 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
}; };
E8ED1A28A9A0730EDF0565B2 /* [CP] Check Pods Manifest.lock */ = { CD618854C236B3F0FBC77F28 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = (
);
inputPaths = ( inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock", "${PODS_ROOT}/Manifest.lock",
); );
name = "[CP] Check Pods Manifest.lock"; name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = ( outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
); );
...@@ -288,7 +295,8 @@ ...@@ -288,7 +295,8 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
...@@ -315,6 +323,78 @@ ...@@ -315,6 +323,78 @@
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = 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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = DKY2FBVP6L;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.permissionHandlerExample;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = { 97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
...@@ -329,14 +409,12 @@ ...@@ -329,14 +409,12 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
...@@ -367,7 +445,6 @@ ...@@ -367,7 +445,6 @@
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
...@@ -386,14 +463,12 @@ ...@@ -386,14 +463,12 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
...@@ -417,8 +492,6 @@ ...@@ -417,8 +492,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
...@@ -429,8 +502,8 @@ ...@@ -429,8 +502,8 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = DKY2FBVP6L;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
...@@ -442,12 +515,8 @@ ...@@ -442,12 +515,8 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.baseflow.permissionHandlerExample; PRODUCT_BUNDLE_IDENTIFIER = com.example.permissionHandlerExample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.2;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
...@@ -457,8 +526,8 @@ ...@@ -457,8 +526,8 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = DKY2FBVP6L;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
...@@ -470,11 +539,8 @@ ...@@ -470,11 +539,8 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.baseflow.permissionHandlerExample; PRODUCT_BUNDLE_IDENTIFIER = com.example.permissionHandlerExample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.2;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;
...@@ -487,6 +553,7 @@ ...@@ -487,6 +553,7 @@
buildConfigurations = ( buildConfigurations = (
97C147031CF9000F007C117D /* Debug */, 97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */, 97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
...@@ -496,6 +563,7 @@ ...@@ -496,6 +563,7 @@
buildConfigurations = ( buildConfigurations = (
97C147061CF9000F007C117D /* Debug */, 97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */, 97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0940" LastUpgradeVersion = "0910"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
</Testables> </Testables>
...@@ -45,6 +46,7 @@ ...@@ -45,6 +46,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
...@@ -65,7 +67,7 @@ ...@@ -65,7 +67,7 @@
</AdditionalOptions> </AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = "" savedToolIdentifier = ""
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
......
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
...@@ -2,10 +2,6 @@ ...@@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
...@@ -19,11 +15,11 @@ ...@@ -19,11 +15,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
...@@ -44,36 +40,36 @@ ...@@ -44,36 +40,36 @@
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string>Need location when in use</string> <string>Need location when in use</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Always and when in use!</string> <string>Always and when in use!</string>
<key>NSLocationUsageDescription</key> <key>NSLocationUsageDescription</key>
<string>Older devices need location.</string> <string>Older devices need location.</string>
<key>NSLocationAlwaysUsageDescription</key> <key>NSLocationAlwaysUsageDescription</key>
<string>Can I haz location always?</string> <string>Can I haz location always?</string>
<key>NSAppleMusicUsageDescription</key> <key>NSAppleMusicUsageDescription</key>
<string>Music!</string> <string>Music!</string>
<key>NSBluetoothPeripheralUsageDescription</key> <key>NSBluetoothPeripheralUsageDescription</key>
<string>bluetooth</string> <string>bluetooth</string>
<key>NSCalendarsUsageDescription</key> <key>NSCalendarsUsageDescription</key>
<string>Calendars</string> <string>Calendars</string>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>camera</string> <string>camera</string>
<key>NSContactsUsageDescription</key> <key>NSContactsUsageDescription</key>
<string>contacts</string> <string>contacts</string>
<key>kTCCServiceMediaLibrary</key> <key>kTCCServiceMediaLibrary</key>
<string>media</string> <string>media</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>microphone</string> <string>microphone</string>
<key>NSMotionUsageDescription</key> <key>NSMotionUsageDescription</key>
<string>motion</string> <string>motion</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>photos</string> <string>photos</string>
<key>NSRemindersUsageDescription</key> <key>NSRemindersUsageDescription</key>
<string>reminders</string> <string>reminders</string>
<key>NSSpeechRecognitionUsageDescription</key> <key>NSSpeechRecognitionUsageDescription</key>
<string>speech</string> <string>speech</string>
</dict> </dict>
</plist> </plist>
#import "GeneratedPluginRegistrant.h"
\ No newline at end of file
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
...@@ -9,40 +9,41 @@ class MyApp extends StatelessWidget { ...@@ -9,40 +9,41 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Plugin example app'), title: const Text('Plugin example app'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
icon: const Icon(Icons.settings), icon: const Icon(Icons.settings),
onPressed: () { onPressed: () {
PermissionHandler().openAppSettings().then((bool hasOpened) => PermissionHandler().openAppSettings().then((bool hasOpened) =>
debugPrint('App Settings opened: ' + hasOpened.toString())); debugPrint('App Settings opened: ' + hasOpened.toString()));
}, },
) )
], ],
),
body: Center(
child: 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) =>
PermissionWidget(permission))
.toList()),
),
), ),
body: Center( );
child: 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) =>
PermissionWidget(permission))
.toList()),
),
));
} }
} }
...@@ -120,16 +121,15 @@ class _PermissionState extends State<PermissionWidget> { ...@@ -120,16 +121,15 @@ class _PermissionState extends State<PermissionWidget> {
}); });
} }
void requestPermission(PermissionGroup permission) { Future<void> requestPermission(PermissionGroup permission) async {
final List<PermissionGroup> permissions = <PermissionGroup>[permission]; final List<PermissionGroup> permissions = <PermissionGroup>[permission];
final Future<Map<PermissionGroup, PermissionStatus>> requestFuture = final Map<PermissionGroup, PermissionStatus> permissionRequestResult =
PermissionHandler().requestPermissions(permissions); await PermissionHandler().requestPermissions(permissions);
requestFuture setState(() {
.then((Map<PermissionGroup, PermissionStatus> permissionRequestResult) { print(permissionRequestResult);
setState(() { _permissionStatus = permissionRequestResult[permission];
_permissionStatus = permissionRequestResult[permission]; print(_permissionStatus);
});
}); });
} }
} }
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/android/gen" />
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/android/gen" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/android/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/android/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/android/assets" />
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/android/libs" />
<option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/android/proguard_logs" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/android">
<sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/android/gen" isTestSource="false" generated="true" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Flutter for Android" level="project" />
</component>
</module>
name: permission_handler_example name: permission_handler_example
description: Demonstrates how to use the permission_handler plugin. description: Demonstrates how to use the permission_handler plugin.
environment:
sdk: ">=2.0.0 <3.0.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
dev_dependencies: dev_dependencies:
flutter_test:
sdk: flutter
permission_handler: permission_handler:
path: ../ path: ../
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter: flutter:
uses-material-design: true
# The following line ensures that the Material Icons font is \ No newline at end of file
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.io/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.io/custom-fonts/#from-packages
//
// Enums.h
// permission_handler
//
// Created by Razvan Lung on 15/02/2019.
//
typedef NS_ENUM(int, PermissionGroup) {
PermissionGroupCalendar = 0,
PermissionGroupCamera,
PermissionGroupContacts,
PermissionGroupLocation,
PermissionGroupLocationAlways,
PermissionGroupLocationWhenInUse,
PermissionGroupMediaLibrary,
PermissionGroupMicrophone,
PermissionGroupPhone,
PermissionGroupPhotos,
PermissionGroupReminders,
PermissionGroupSensors,
PermissionGroupSms,
PermissionGroupSpeech,
PermissionGroupStorage,
PermissionGroupUnknown,
};
typedef NS_ENUM(int, PermissionStatus) {
PermissionStatusDenied = 0,
PermissionStatusDisabled,
PermissionStatusGranted,
PermissionStatusRestricted,
PermissionStatusUnknown,
};
typedef NS_ENUM(int, ServiceStatus) {
ServiceStatusDisabled = 0,
ServiceStatusEnabled,
ServiceStatusNotApplicable,
ServiceStatusUnknown,
};
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import "PermissionManager.h"
@interface PermissionHandlerPlugin : NSObject <FlutterPlugin> @interface PermissionHandlerPlugin : NSObject<FlutterPlugin>
- (instancetype)initWithPermissionManager:(PermissionManager *)permissionManager;
@end @end
#import "PermissionHandlerPlugin.h" #import "PermissionHandlerPlugin.h"
#import <permission_handler/permission_handler-Swift.h>
@implementation PermissionHandlerPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar { @implementation PermissionHandlerPlugin {
[SwiftPermissionHandlerPlugin registerWithRegistrar:registrar]; PermissionManager *_Nonnull _permissionManager;
_Nullable FlutterResult _methodResult;
}
- (instancetype)initWithPermissionManager:(PermissionManager *)permissionManager {
self = [super init];
if (self) {
_permissionManager = permissionManager;
}
return self;
}
+ (void)registerWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
FlutterMethodChannel *channel = [FlutterMethodChannel
methodChannelWithName:@"flutter.baseflow.com/permissions/methods"
binaryMessenger:[registrar messenger]];
PermissionManager *permissionManager = [[PermissionManager alloc] initWithStrategyInstances];
PermissionHandlerPlugin *instance = [[PermissionHandlerPlugin alloc] initWithPermissionManager:permissionManager];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if ([@"checkPermissionStatus" isEqualToString:call.method]) {
PermissionGroup permission = [Codec decodePermissionGroupFrom:call.arguments];
[PermissionManager checkPermissionStatus:permission result:result];
} else if ([@"checkServiceStatus" isEqualToString:call.method]) {
PermissionGroup permission = [Codec decodePermissionGroupFrom:call.arguments];
[PermissionManager checkServiceStatus:permission result:result];
} else if ([@"requestPermissions" isEqualToString:call.method]) {
if (_methodResult != nil) {
result([FlutterError errorWithCode:@"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;
NSArray *permissions = [Codec decodePermissionGroupsFrom:call.arguments];
[_permissionManager
requestPermissions:permissions completion:^(NSDictionary *permissionRequestResults) {
if (self->_methodResult != nil) {
self->_methodResult(permissionRequestResults);
}
self->_methodResult = nil;
}];
} else if ([@"shouldShowRequestPermissionRationale" isEqualToString:call.method]) {
result(@false);
} else if ([@"openAppSettings" isEqualToString:call.method]) {
[PermissionManager openAppSettings:result];
} else {
result(FlutterMethodNotImplemented);
}
} }
@end @end
//
// PermissionManager.h
// permission_handler
//
// Created by Razvan Lung on 15/02/2019.
//
#import <Flutter/Flutter.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "AudioVideoPermissionStrategy.h"
#import "ContactPermissionStrategy.h"
#import "EventPermissionStrategy.h"
#import "LocationPermissionStrategy.h"
#import "MediaLibraryPermissionStrategy.h"
#import "PermissionStrategy.h"
#import "PhotoPermissionStrategy.h"
#import "SensorPermissionStrategy.h"
#import "SpeechPermissionStrategy.h"
#import "UnknownPermissionStrategy.h"
#import "Enums.h"
#import "Codec.h"
typedef void (^PermissionRequestCompletion)(NSDictionary *permissionRequestResults);
@interface PermissionManager : NSObject
- (instancetype)initWithStrategyInstances;
+ (void)checkPermissionStatus:(enum PermissionGroup)permission result:(FlutterResult)result;
+ (void)checkServiceStatus:(enum PermissionGroup)permission result:(FlutterResult)result;
+ (void)openAppSettings:(FlutterResult)result;
- (void)requestPermissions:(NSArray *)permissions completion:(PermissionRequestCompletion)completion;
@end
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "PermissionManager.h"
@implementation PermissionManager {
NSMutableArray <id <PermissionStrategy>> *_strategyInstances;
}
- (instancetype)initWithStrategyInstances {
self = [super init];
if (self) {
_strategyInstances = _strategyInstances = [[NSMutableArray alloc] init];
}
return self;
}
+ (void)checkPermissionStatus:(enum PermissionGroup)permission result:(FlutterResult)result {
id <PermissionStrategy> permissionStrategy = [PermissionManager createPermissionStrategy:permission];
PermissionStatus status = [permissionStrategy checkPermissionStatus:permission];
result([Codec encodePermissionStatus:status]);
}
+ (void)checkServiceStatus:(enum PermissionGroup)permission result:(FlutterResult)result {
id <PermissionStrategy> permissionStrategy = [PermissionManager createPermissionStrategy:permission];
ServiceStatus status = [permissionStrategy checkServiceStatus:permission];
result([Codec encodeServiceStatus:status]);
}
- (void)requestPermissions:(NSArray *)permissions completion:(PermissionRequestCompletion)completion {
NSMutableSet *requestQueue = [[NSMutableSet alloc] initWithArray:permissions];
NSMutableDictionary *permissionStatusResult = [[NSMutableDictionary alloc] init];
for (int i = 0; i < permissions.count; ++i) {
PermissionGroup value;
[permissions[i] getValue:&value];
PermissionGroup permission = value;
id <PermissionStrategy> permissionStrategy = [PermissionManager createPermissionStrategy:permission];
[_strategyInstances addObject:permissionStrategy];
[permissionStrategy requestPermission:permission completionHandler:^(PermissionStatus permissionStatus) {
permissionStatusResult[@(permission)] = @(permissionStatus);
[requestQueue removeObject:@(permission)];
[self->_strategyInstances removeObject:permissionStrategy];
if (requestQueue.count == 0) {
completion(permissionStatusResult);
return;
}
}];
}
}
+ (void)openAppSettings:(FlutterResult)result {
if (@available(iOS 10, *)) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]
options:[[NSDictionary alloc] init]
completionHandler:^(BOOL success) {
result([[NSNumber alloc] initWithBool:success]);
}];
} else if (@available(iOS 8.0, *)) {
BOOL success = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
result([[NSNumber alloc] initWithBool:success]);
} else {
result(@false);
}
}
+ (id)createPermissionStrategy:(PermissionGroup)permission {
switch (permission) {
case PermissionGroupCalendar:
return [EventPermissionStrategy new];
case PermissionGroupCamera:
return [AudioVideoPermissionStrategy new];
case PermissionGroupContacts:
return [ContactPermissionStrategy new];
case PermissionGroupLocation:
case PermissionGroupLocationAlways:
case PermissionGroupLocationWhenInUse:
return [[LocationPermissionStrategy alloc] initWithLocationManager];
case PermissionGroupMediaLibrary:
return [MediaLibraryPermissionStrategy new];
case PermissionGroupMicrophone:
return [AudioVideoPermissionStrategy new];
case PermissionGroupPhotos:
return [PhotoPermissionStrategy new];
case PermissionGroupReminders:
return [EventPermissionStrategy new];
case PermissionGroupSensors:
return [SensorPermissionStrategy new];
case PermissionGroupSpeech:
return [SpeechPermissionStrategy new];
default:
return [UnknownPermissionStrategy new];
}
}
@end
//
// PermissionManager.swift
// permission_handler
//
// Created by Maurits van Beusekom on 26/07/2018.
//
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)
let permissionStatus = permissionStrategy.checkPermissionStatus(permission: permission)
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] = [:]
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, *) {
guard let url = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(url) else {
result(false)
return
}
let optionsKeyDictionary = [UIApplication.OpenExternalURLOptionsKey(rawValue: "universalLinksOnly"): NSNumber(value: true)]
UIApplication.shared.open(url, options: optionsKeyDictionary, completionHandler: { (success) in result(success) });
return
} else {
let success = UIApplication.shared.openURL(URL.init(string: UIApplication.openSettingsURLString)!)
result(success)
}
}
result(false)
}
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()
default:
return UnknownPermissionStrategy()
}
}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}
import CoreLocation
import CoreMotion
import EventKit
import Flutter
import Foundation
import Photos
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())
let instance = SwiftPermissionHandlerPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "checkPermissionStatus" {
let permission: PermissionGroup = Codec.decodePermissionGroup(from: call.arguments)
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(
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 {
result(FlutterMethodNotImplemented)
}
}
}
//
// 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 phone = "phone"
case photos = "photos"
case reminders = "reminders"
case sensors = "sensors"
case sms = "sms"
case speech = "speech"
case storage = "storage"
}
//
// 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"
}
//
// 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"
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
@interface AudioVideoPermissionStrategy : NSObject <PermissionStrategy>
@end
\ No newline at end of file
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "AudioVideoPermissionStrategy.h"
@implementation AudioVideoPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
if (permission == PermissionGroupCamera) {
return [AudioVideoPermissionStrategy permissionStatus:AVMediaTypeVideo];
} else if (permission == PermissionGroupMicrophone) {
return [AudioVideoPermissionStrategy permissionStatus:AVMediaTypeAudio];
}
return PermissionStatusUnknown;
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusNotApplicable;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if (status != PermissionStatusUnknown) {
completionHandler(status);
return;
}
AVMediaType mediaType;
if (permission == PermissionGroupCamera) {
mediaType = AVMediaTypeVideo;
} else if (permission == PermissionGroupMicrophone) {
mediaType = AVMediaTypeAudio;
} else {
completionHandler(PermissionStatusUnknown);
return;
}
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
if (granted) {
completionHandler(PermissionStatusGranted);
} else {
completionHandler(PermissionStatusDenied);
}
}];
}
+ (PermissionStatus)permissionStatus:(AVMediaType const)mediaType {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
switch (status) {
case AVAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case AVAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case AVAuthorizationStatusDenied:
return PermissionStatusDenied;
case AVAuthorizationStatusAuthorized:
return PermissionStatusGranted;
}
return PermissionStatusUnknown;
}
@end
\ No newline at end of file
//
// 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 checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
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
}
}
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)
}
})
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <AddressBook/ABAddressBook.h>
#import <Contacts/Contacts.h>
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
@interface ContactPermissionStrategy : NSObject <PermissionStrategy>
@end
\ No newline at end of file
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "ContactPermissionStrategy.h"
@implementation ContactPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return [ContactPermissionStrategy permissionStatus];
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusNotApplicable;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if (status != PermissionStatusUnknown) {
completionHandler(status);
}
if (@available(iOS 9.0, *)) {
[ContactPermissionStrategy requestPermissionsFromContactStore:completionHandler];
} else {
[ContactPermissionStrategy requestPermissionsFromAddressBook:completionHandler];
}
}
+ (PermissionStatus)permissionStatus {
if (@available(iOS 9.0, *)) {
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
switch (status) {
case CNAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case CNAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case CNAuthorizationStatusDenied:
return PermissionStatusDenied;
case CNAuthorizationStatusAuthorized:
return PermissionStatusGranted;
}
} else {
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
switch (status) {
case kABAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case kABAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kABAuthorizationStatusDenied:
return PermissionStatusDenied;
case kABAuthorizationStatusAuthorized:
return PermissionStatusGranted;
}
}
return PermissionStatusUnknown;
}
+ (void)requestPermissionsFromContactStore:(PermissionStatusHandler)completionHandler API_AVAILABLE(ios(9)) {
CNContactStore *contactStore = [CNContactStore new];
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError *__nullable error) {
if (granted) {
completionHandler(PermissionStatusGranted);
} else {
completionHandler(PermissionStatusDenied);
}
}];
ABAddressBookRequestAccessWithCompletion(ABAddressBookCreate(), ^(bool granted, CFErrorRef error) {
if (granted) {
completionHandler(PermissionStatusGranted);
} else {
completionHandler(PermissionStatusDenied);
}
});
}
+ (void)requestPermissionsFromAddressBook:(PermissionStatusHandler)completionHandler {
ABAddressBookRequestAccessWithCompletion(ABAddressBookCreate(), ^(bool granted, CFErrorRef error) {
if (granted) {
completionHandler(PermissionStatusGranted);
} else {
completionHandler(PermissionStatusDenied);
}
});
}
@end
//
// ContactPermissions.swift
// permission_handler
//
// Created by Maurits van Beusekom on 26/07/2018.
//
import AddressBook
import Contacts
import Foundation
class ContactPermissionStrategy : NSObject, PermissionStrategy {
func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus {
return ContactPermissionStrategy.getPermissionStatus()
}
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 {
case ABAuthorizationStatus.authorized:
return PermissionStatus.granted
case ABAuthorizationStatus.denied:
return PermissionStatus.denied
case ABAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
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)
}
})
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <EventKit/EventKit.h>
#import "PermissionStrategy.h"
@interface EventPermissionStrategy : NSObject <PermissionStrategy>
@end
\ No newline at end of file
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "EventPermissionStrategy.h"
@implementation EventPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
if (permission == PermissionGroupCalendar) {
return [EventPermissionStrategy permissionStatus:EKEntityTypeEvent];
} else if (permission == PermissionGroupReminders) {
return [EventPermissionStrategy permissionStatus:EKEntityTypeReminder];
}
return PermissionStatusUnknown;
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusNotApplicable;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus permissionStatus = [self checkPermissionStatus:permission];
if (permissionStatus != PermissionStatusUnknown) {
completionHandler(permissionStatus);
return;
}
EKEntityType entityType;
if (permission == PermissionGroupCalendar) {
entityType = EKEntityTypeEvent;
} else if (permission == PermissionGroupReminders) {
entityType = EKEntityTypeReminder;
} else {
completionHandler(PermissionStatusUnknown);
return;
}
EKEventStore *eventStore = [[EKEventStore alloc] init];
[eventStore requestAccessToEntityType:entityType completion:^(BOOL granted, NSError *error) {
if (granted) {
completionHandler(PermissionStatusGranted);
} else {
completionHandler(PermissionStatusDenied);
}
}];
}
+ (PermissionStatus)permissionStatus:(EKEntityType)entityType {
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:entityType];
switch (status) {
case EKAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case EKAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case EKAuthorizationStatusDenied:
return PermissionStatusDenied;
case EKAuthorizationStatusAuthorized:
return PermissionStatusGranted;
}
return PermissionStatusUnknown;
}
@end
//
// 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
}
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
}
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
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)
}
}
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
@interface LocationPermissionStrategy : NSObject <PermissionStrategy, CLLocationManagerDelegate>
- (instancetype)initWithLocationManager;
@end
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "LocationPermissionStrategy.h"
@implementation LocationPermissionStrategy {
CLLocationManager *_locationManager;
PermissionStatusHandler _permissionStatusHandler;
PermissionGroup _requestedPermission;
}
- (instancetype)initWithLocationManager {
self = [super init];
if (self) {
_locationManager = [CLLocationManager new];
_locationManager.delegate = self;
}
return self;
}
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return [LocationPermissionStrategy permissionStatus:permission];
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return [CLLocationManager locationServicesEnabled] ? ServiceStatusEnabled : ServiceStatusDisabled;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse && permission == PermissionGroupLocationAlways) {
// don't do anything and continue requesting permissions
} else if (status != PermissionStatusUnknown) {
completionHandler(status);
}
_permissionStatusHandler = completionHandler;
_requestedPermission = permission;
if (permission == PermissionGroupLocation) {
if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] != nil) {
[_locationManager requestAlwaysAuthorization];
} else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil) {
[_locationManager requestWhenInUseAuthorization];
} else {
[[NSException exceptionWithName:NSInternalInconsistencyException 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 == PermissionGroupLocationAlways) {
if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] != nil) {
[_locationManager requestAlwaysAuthorization];
} else {
[[NSException exceptionWithName:NSInternalInconsistencyException 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 == PermissionGroupLocationWhenInUse) {
if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil) {
[_locationManager requestWhenInUseAuthorization];
} else {
[[NSException exceptionWithName:NSInternalInconsistencyException reason:@"To use location in iOS8 you need to define NSLocationWhenInUseUsageDescription in the app bundle's Info.plist file" userInfo:nil] raise];
}
}
}
// {WARNING}
// This is called when the location manager is first initialized and raises the following situations:
// 1. When we first request [PermissionGroupLocationWhenInUse] and then [PermissionGroupLocationAlways]
// this will be called when the [CLLocationManager] is first initialized with
// [kCLAuthorizationStatusAuthorizedWhenInUse]. As a consequence we send back the result to early.
// 2. When we first request [PermissionGroupLocationWhenInUse] and then [PermissionGroupLocationAlways]
// and the user doesn't allow for [kCLAuthorizationStatusAuthorizedAlways] this method is not called
// at all.
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusNotDetermined) {
return;
}
if (_permissionStatusHandler == nil || @(_requestedPermission) == nil) {
return;
}
PermissionStatus permissionStatus = [LocationPermissionStrategy
determinePermissionStatus:_requestedPermission authorizationStatus:status];
_permissionStatusHandler(permissionStatus);
}
+ (PermissionStatus)permissionStatus:(PermissionGroup)permission {
CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
PermissionStatus status = [LocationPermissionStrategy
determinePermissionStatus:permission authorizationStatus:authorizationStatus];
if ((status == PermissionStatusGranted || status == PermissionStatusDenied)
&& ![CLLocationManager locationServicesEnabled]) {
return PermissionStatusDisabled;
}
return status;
}
+ (PermissionStatus)determinePermissionStatus:(PermissionGroup)permission authorizationStatus:(CLAuthorizationStatus)authorizationStatus {
if (@available(iOS 8.0, *)) {
if (permission == PermissionGroupLocationAlways) {
switch (authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case kCLAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kCLAuthorizationStatusDenied:
case kCLAuthorizationStatusAuthorizedWhenInUse:
return PermissionStatusDenied;
case kCLAuthorizationStatusAuthorizedAlways:
return PermissionStatusGranted;
}
}
switch (authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case kCLAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kCLAuthorizationStatusDenied:
return PermissionStatusDenied;
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusAuthorizedWhenInUse:
return PermissionStatusGranted;
}
}
switch (authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case kCLAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kCLAuthorizationStatusDenied:
return PermissionStatusDenied;
case kCLAuthorizationStatusAuthorized:
return PermissionStatusGranted;
default:
return PermissionStatusUnknown;
}
}
@end
//
// LocationPermissions.swift
// permission_handler
//
// Created by Maurits van Beusekom on 26/07/2018.
//
import CoreLocation
import Foundation
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)
}
private static func getPermissionStatus(permission: PermissionGroup) -> PermissionStatus {
let authorizationStatus: CLAuthorizationStatus = CLLocationManager.authorizationStatus()
let permissionStatus: PermissionStatus = LocationPermissionStrategy.determinePermissionStatus(
permission: permission,
authorizationStatus: authorizationStatus)
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)
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 authorizationStatus {
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 authorizationStatus {
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 authorizationStatus {
case CLAuthorizationStatus.authorized:
return PermissionStatus.granted
case CLAuthorizationStatus.denied:
return PermissionStatus.denied
case CLAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#include "PermissionStrategy.h"
@interface MediaLibraryPermissionStrategy : NSObject <PermissionStrategy>
@end
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "MediaLibraryPermissionStrategy.h"
@implementation MediaLibraryPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return [MediaLibraryPermissionStrategy permissionStatus];
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusNotApplicable;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if (status != PermissionStatusUnknown) {
completionHandler(status);
return;
}
if (@available(iOS 9.3, *)) {
[MPMediaLibrary requestAuthorization:^(MPMediaLibraryAuthorizationStatus status) {
completionHandler([MediaLibraryPermissionStrategy determinePermissionStatus:status]);
}];
} else {
completionHandler(PermissionStatusUnknown);
return;
}
}
+ (PermissionStatus)permissionStatus {
if (@available(iOS 9.3, *)) {
MPMediaLibraryAuthorizationStatus status = [MPMediaLibrary authorizationStatus];
return [MediaLibraryPermissionStrategy determinePermissionStatus:status];
}
return PermissionStatusUnknown;
}
+ (PermissionStatus)determinePermissionStatus:(MPMediaLibraryAuthorizationStatus)authorizationStatus {
switch (authorizationStatus) {
case MPMediaLibraryAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case MPMediaLibraryAuthorizationStatusDenied:
return PermissionStatusDenied;
case MPMediaLibraryAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case MPMediaLibraryAuthorizationStatusAuthorized:
return PermissionStatusGranted;
}
return PermissionStatusUnknown;
}
@end
\ No newline at end of file
//
// 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()
}
private static func getPermissionStatus() -> PermissionStatus {
if #available(iOS 9.3, *) {
let status: MPMediaLibraryAuthorizationStatus = MPMediaLibrary.authorizationStatus()
return MediaLibraryPermissionStrategy.determinePermissionStatus(authorizationStatus: status)
}
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 {
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
}
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Enums.h"
typedef void (^PermissionStatusHandler)(PermissionStatus permissionStatus);
@protocol PermissionStrategy <NSObject>
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission;
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission;
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler;
@end
//
// Task.swift
// permission_handler
//
// Created by Maurits van Beusekom on 26/07/2018.
//
import Foundation
typealias PermissionStatusHandler = (_ permissionStatus: PermissionStatus) -> Void
protocol PermissionStrategy {
func checkPermissionStatus(permission: PermissionGroup) -> PermissionStatus
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler)
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
#import <Photos/Photos.h>
@interface PhotoPermissionStrategy : NSObject <PermissionStrategy>
@end
\ No newline at end of file
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "PhotoPermissionStrategy.h"
@implementation PhotoPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return [PhotoPermissionStrategy permissionStatus];
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusNotApplicable;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if (status != PermissionStatusUnknown) {
completionHandler(status);
return;
}
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus authorizationStatus) {
completionHandler([PhotoPermissionStrategy determinePermissionStatus:authorizationStatus]);
}];
}
+ (PermissionStatus)permissionStatus {
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
return [PhotoPermissionStrategy determinePermissionStatus:status];
}
+ (PermissionStatus)determinePermissionStatus:(PHAuthorizationStatus)authorizationStatus {
switch (authorizationStatus) {
case PHAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case PHAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case PHAuthorizationStatusDenied:
return PermissionStatusDenied;
case PHAuthorizationStatusAuthorized:
return PermissionStatusGranted;
}
return PermissionStatusUnknown;
}
@end
\ No newline at end of file
//
// 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()
}
private static func getPermissionStatus() -> PermissionStatus {
let status: PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
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)
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:
return PermissionStatus.denied
case PHAuthorizationStatus.restricted:
return PermissionStatus.restricted
default:
return PermissionStatus.unknown
}
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <CoreMotion/CoreMotion.h>
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
@interface SensorPermissionStrategy : NSObject <PermissionStrategy>
@end
\ No newline at end of file
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "SensorPermissionStrategy.h"
@implementation SensorPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return [SensorPermissionStrategy permissionStatus];
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
if (@available(iOS 11.0, *)) {
return [CMMotionActivityManager isActivityAvailable]
? ServiceStatusEnabled
: ServiceStatusDisabled;
}
return ServiceStatusUnknown;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if (status != PermissionStatusUnknown) {
completionHandler(status);
return;
}
if (@available(iOS 11.0, *)) {
CMMotionActivityManager *motionManager = [[CMMotionActivityManager alloc] init];
NSDate *today = [NSDate new];
[motionManager queryActivityStartingFromDate:today toDate:today toQueue:[NSOperationQueue mainQueue] withHandler:^(NSArray<CMMotionActivity *> *__nullable activities, NSError *__nullable error) {
if (error != nil && error.code == CMErrorMotionActivityNotAuthorized) {
completionHandler(PermissionStatusDenied);
} else {
completionHandler(PermissionStatusGranted);
}
}];
} else {
completionHandler(PermissionStatusUnknown);
}
}
+ (PermissionStatus)permissionStatus {
if (@available(iOS 11.0, *)) {
CMAuthorizationStatus status = [CMMotionActivityManager authorizationStatus];
PermissionStatus permissionStatus;
switch (status) {
case CMAuthorizationStatusNotDetermined:
permissionStatus = PermissionStatusUnknown;
break;
case CMAuthorizationStatusRestricted:
permissionStatus = PermissionStatusRestricted;
break;
case CMAuthorizationStatusDenied:
permissionStatus = PermissionStatusDenied;
break;
case CMAuthorizationStatusAuthorized:
permissionStatus = PermissionStatusGranted;
break;
default:
permissionStatus = PermissionStatusGranted;
}
if ((permissionStatus == PermissionStatusGranted || permissionStatus == PermissionStatusDenied) && ![CMMotionActivityManager isActivityAvailable]) {
return PermissionStatusDisabled;
} else {
return permissionStatus;
}
}
return PermissionStatusUnknown;
}
@end
//
// 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()
}
private static func getPermissionStatus() -> PermissionStatus {
if #available(iOS 11.0, *) {
let status: CMAuthorizationStatus = CMMotionActivityManager.authorizationStatus()
var permissionStatus: PermissionStatus
switch status {
case CMAuthorizationStatus.authorized:
permissionStatus = PermissionStatus.granted
case CMAuthorizationStatus.denied:
permissionStatus = PermissionStatus.denied
case CMAuthorizationStatus.restricted:
permissionStatus = PermissionStatus.restricted
default:
permissionStatus = PermissionStatus.unknown
}
if (permissionStatus == PermissionStatus.granted || permissionStatus == PermissionStatus.denied) && !CMMotionActivityManager.isActivityAvailable() {
return PermissionStatus.disabled
} else {
return permissionStatus
}
}
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)
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)
}
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
#import <Speech/Speech.h>
@interface SpeechPermissionStrategy : NSObject <PermissionStrategy>
@end
\ No newline at end of file
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "SpeechPermissionStrategy.h"
@implementation SpeechPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return [SpeechPermissionStrategy permissionStatus];
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusNotApplicable;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
PermissionStatus status = [self checkPermissionStatus:permission];
if (status != PermissionStatusUnknown) {
completionHandler(status);
return;
}
if (@available(iOS 10.0, *)) {
[SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus authorizationStatus) {
completionHandler([SpeechPermissionStrategy determinePermissionStatus:authorizationStatus]);
}];
} else {
completionHandler(PermissionStatusUnknown);
}
}
+ (PermissionStatus)permissionStatus {
if (@available(iOS 10.0, *)) {
SFSpeechRecognizerAuthorizationStatus status = [SFSpeechRecognizer authorizationStatus];
return [SpeechPermissionStrategy determinePermissionStatus:status];
}
return PermissionStatusUnknown;
}
+ (PermissionStatus)determinePermissionStatus:(SFSpeechRecognizerAuthorizationStatus)authorizationStatus {
switch (authorizationStatus) {
case SFSpeechRecognizerAuthorizationStatusNotDetermined:
return PermissionStatusUnknown;
case SFSpeechRecognizerAuthorizationStatusDenied:
return PermissionStatusDenied;
case SFSpeechRecognizerAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case SFSpeechRecognizerAuthorizationStatusAuthorized:
return PermissionStatusGranted;
}
return PermissionStatusUnknown;
}
@end
//
// 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()
}
private static func getPermissionStatus() -> PermissionStatus {
if #available(iOS 10.0, *) {
let status: SFSpeechRecognizerAuthorizationStatus = SFSpeechRecognizer.authorizationStatus()
return SpeechPermissionStrategy.determinePermissionStatus(authorizationStatus: status)
}
return PermissionStatus.unknown
}
func checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.notApplicable
}
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
}
}
}
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "PermissionStrategy.h"
@interface UnknownPermissionStrategy : NSObject <PermissionStrategy>
@end
\ No newline at end of file
//
// Created by Razvan Lung(long1eu) on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "UnknownPermissionStrategy.h"
@implementation UnknownPermissionStrategy
- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
return PermissionStatusUnknown;
}
- (ServiceStatus)checkServiceStatus:(PermissionGroup)permission {
return ServiceStatusUnknown;
}
- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler {
completionHandler(PermissionStatusUnknown);
}
@end
\ No newline at end of file
//
// 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 checkServiceStatus(permission: PermissionGroup) -> ServiceStatus {
return ServiceStatus.unknown
}
func requestPermission(permission: PermissionGroup, completionHandler: @escaping PermissionStatusHandler) {
completionHandler(PermissionStatus.unknown)
}
}
//
// Created by Razvan Lung on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Enums.h"
@interface Codec : NSObject
+ (PermissionGroup)decodePermissionGroupFrom:(NSNumber *_Nonnull)event;
+ (NSArray*_Nullable)decodePermissionGroupsFrom:(NSArray<NSNumber *> *_Nullable)event;
+ (NSNumber *_Nullable)encodePermissionStatus:(enum PermissionStatus)permissionStatus;
+ (NSNumber *_Nullable)encodeServiceStatus:(enum ServiceStatus)serviceStatus;
@end
//
// Created by Razvan Lung on 2019-02-15.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
//
#import "Codec.h"
@implementation Codec
+ (PermissionGroup)decodePermissionGroupFrom:(NSNumber *)event {
return (PermissionGroup) event.intValue;
}
+ (NSArray *_Nullable)decodePermissionGroupsFrom:(NSArray<NSNumber *> *)event {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (NSNumber *number in event) {
[result addObject:@([self decodePermissionGroupFrom:number])];
}
return [[NSArray alloc] initWithArray:result];
}
+ (NSNumber *_Nullable)encodePermissionStatus:(enum PermissionStatus)permissionStatus {
return [[NSNumber alloc] initWithInt:permissionStatus];
}
+ (NSNumber *_Nullable)encodeServiceStatus:(enum ServiceStatus)serviceStatus {
return [[NSNumber alloc] initWithInt:serviceStatus];
}
@end
//
// 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 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 encodeServiceStatus(serviceStatus: ServiceStatus) -> String? {
let status = "\"" + serviceStatus.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,10 +3,10 @@ ...@@ -3,10 +3,10 @@
# #
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'permission_handler' s.name = 'permission_handler'
s.version = '2.2.0' s.version = '3.0.0'
s.summary = 'Permission plugin for Flutter.' s.summary = 'Permission plugin for Flutter.'
s.description = <<-DESC s.description = <<-DESC
Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions. A new Flutter project.
DESC DESC
s.homepage = 'https://github.com/baseflowit/flutter-permission-handler' s.homepage = 'https://github.com/baseflowit/flutter-permission-handler'
s.license = { :file => '../LICENSE' } s.license = { :file => '../LICENSE' }
...@@ -15,8 +15,7 @@ Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Andro ...@@ -15,8 +15,7 @@ Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Andro
s.source_files = 'Classes/**/*' s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h' s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter' s.dependency 'Flutter'
s.swift_version = '4.2'
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.2' }
end end
library permission_handler; library permission_handler;
import 'dart:async'; export 'src/permission_enums.dart';
import 'dart:convert'; export 'src/permission_handler.dart';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
part 'package:permission_handler/permission_enums.dart';
part 'package:permission_handler/utils/codec.dart';
/// Provides a cross-platform (iOS, Android) API to request and check permissions.
class PermissionHandler {
factory PermissionHandler() {
if (_instance == null) {
const MethodChannel methodChannel =
MethodChannel('flutter.baseflow.com/permissions/methods');
_instance = PermissionHandler.private(methodChannel);
}
return _instance;
}
@visibleForTesting
PermissionHandler.private(this._methodChannel);
static PermissionHandler _instance;
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 {
final String status = await _methodChannel.invokeMethod(
'checkPermissionStatus', Codec.encodePermissionGroup(permission));
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;
}
/// Request the user for access to the supplied list of permissiongroups.
///
/// Returns a [Map] containing the status per requested permissiongroup.
Future<Map<PermissionGroup, PermissionStatus>> requestPermissions(
List<PermissionGroup> permissions) async {
final String jsonData = Codec.encodePermissionGroups(permissions);
final String status =
await _methodChannel.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
/// returns [false].
Future<bool> shouldShowRequestPermissionRationale(
PermissionGroup permission) async {
if (!Platform.isAndroid) {
return false;
}
final bool shouldShowRationale = await _methodChannel.invokeMethod(
'shouldShowRequestPermissionRationale',
Codec.encodePermissionGroup(permission));
return shouldShowRationale;
}
}
part of permission_handler;
/// Defines the state of a permission group /// Defines the state of a permission group
enum PermissionStatus { class PermissionStatus {
const PermissionStatus._(this.value);
final int value;
/// Permission to access the requested feature is denied by the user. /// Permission to access the requested feature is denied by the user.
denied, static const PermissionStatus denied = PermissionStatus._(0);
/// The feature is disabled (or not available) on the device. /// The feature is disabled (or not available) on the device.
disabled, static const PermissionStatus disabled = PermissionStatus._(1);
/// Permission to access the requested feature is granted by the user. /// Permission to access the requested feature is granted by the user.
granted, static const PermissionStatus granted = PermissionStatus._(2);
/// The user granted restricted access to the requested feature (only on iOS). /// The user granted restricted access to the requested feature (only on iOS).
restricted, static const PermissionStatus restricted = PermissionStatus._(3);
/// Permission is in an unknown state /// Permission is in an unknown state
unknown static const PermissionStatus unknown = PermissionStatus._(4);
static const List<PermissionStatus> values = <PermissionStatus>[
denied,
disabled,
granted,
restricted,
unknown,
];
static const List<String> _names = <String>[
'denied',
'disabled',
'granted',
'restricted',
'unknown',
];
@override
String toString() => 'PermissionStatus.${_names[value]}';
} }
/// Defines the state of a service related to the permission group /// Defines the state of a service related to the permission group
enum ServiceStatus { class ServiceStatus {
/// The unknown service status indicates the state of the service could not be determined. const ServiceStatus._(this.value);
unknown,
/// There is no service for the supplied permission group. final int value;
notApplicable,
/// The service for the supplied permission group is disabled. /// The service for the supplied permission group is disabled.
disabled, static const ServiceStatus disabled = ServiceStatus._(0);
/// The service for the supplied permission group is enabled. /// The service for the supplied permission group is enabled.
enabled static const ServiceStatus enabled = ServiceStatus._(1);
/// There is no service for the supplied permission group.
static const ServiceStatus notApplicable = ServiceStatus._(2);
/// The unknown service status indicates the state of the service could not be determined.
static const ServiceStatus unknown = ServiceStatus._(3);
static const List<ServiceStatus> values = <ServiceStatus>[
disabled,
enabled,
notApplicable,
unknown,
];
static const List<String> _names = <String>[
'disabled',
'enabled',
'notApplicable',
'unknown',
];
@override
String toString() => 'ServiceStatus.${_names[value]}';
} }
/// Defines the permission groups for which permissions can be checked or requested. /// Defines the permission groups for which permissions can be checked or requested.
enum PermissionGroup { class PermissionGroup {
/// The unknown permission only used for return type, never requested const PermissionGroup._(this.value);
unknown,
final int value;
/// Android: Calendar /// Android: Calendar
/// iOS: Calendar (Events) /// iOS: Calendar (Events)
calendar, static const PermissionGroup calendar = PermissionGroup._(0);
/// Android: Camera /// Android: Camera
/// iOS: Photos (Camera Roll and Camera) /// iOS: Photos (Camera Roll and Camera)
camera, static const PermissionGroup camera = PermissionGroup._(1);
/// Android: Contacts /// Android: Contacts
/// iOS: AddressBook /// iOS: AddressBook
contacts, static const PermissionGroup contacts = PermissionGroup._(2);
/// Android: Fine and Coarse Location /// Android: Fine and Coarse Location
/// iOS: CoreLocation (Always and WhenInUse) /// iOS: CoreLocation (Always and WhenInUse)
location, static const PermissionGroup location = PermissionGroup._(3);
/// Android: Fine and Coarse Location
/// iOS: CoreLocation - Always
static const PermissionGroup locationAlways = PermissionGroup._(4);
/// Android: Fine and Coarse Location
/// iOS: CoreLocation - WhenInUse
static const PermissionGroup locationWhenInUse = PermissionGroup._(5);
/// Android: None
/// iOS: MPMediaLibrary
static const PermissionGroup mediaLibrary = PermissionGroup._(6);
/// Android: Microphone /// Android: Microphone
/// iOS: Microphone /// iOS: Microphone
microphone, static const PermissionGroup microphone = PermissionGroup._(7);
/// Android: Phone /// Android: Phone
/// iOS: Nothing /// iOS: Nothing
phone, static const PermissionGroup phone = PermissionGroup._(8);
/// Android: Nothing /// Android: Nothing
/// iOS: Photos /// iOS: Photos
photos, static const PermissionGroup photos = PermissionGroup._(9);
/// Android: Nothing /// Android: Nothing
/// iOS: Reminders /// iOS: Reminders
reminders, static const PermissionGroup reminders = PermissionGroup._(10);
/// Android: Body Sensors /// Android: Body Sensors
/// iOS: CoreMotion /// iOS: CoreMotion
sensors, static const PermissionGroup sensors = PermissionGroup._(11);
/// Android: Sms /// Android: Sms
/// iOS: Nothing /// iOS: Nothing
sms, static const PermissionGroup sms = PermissionGroup._(12);
/// Android: External Storage
/// iOS: Nothing
storage,
/// Android: Microphone /// Android: Microphone
/// iOS: Speech /// iOS: Speech
speech, static const PermissionGroup speech = PermissionGroup._(13);
/// Android: Fine and Coarse Location
/// iOS: CoreLocation - Always
locationAlways,
/// Android: Fine and Coarse Location /// Android: External Storage
/// iOS: CoreLocation - WhenInUse /// iOS: Nothing
locationWhenInUse, static const PermissionGroup storage = PermissionGroup._(14);
/// Android: None /// The unknown permission only used for return type, never requested
/// iOS: MPMediaLibrary static const PermissionGroup unknown = PermissionGroup._(15);
mediaLibrary
static const List<PermissionGroup> values = <PermissionGroup>[
calendar,
camera,
contacts,
location,
locationAlways,
locationWhenInUse,
mediaLibrary,
microphone,
phone,
photos,
reminders,
sensors,
sms,
speech,
storage,
unknown,
];
static const List<String> _names = <String>[
'calendar',
'camera',
'contacts',
'location',
'locationAlways',
'locationWhenInUse',
'mediaLibrary',
'microphone',
'phone',
'photos',
'reminders',
'sensors',
'sms',
'speech',
'storage',
'unknown',
];
@override
String toString() => 'PermissionGroup.${_names[value]}';
} }
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'package:permission_handler/src/permission_enums.dart';
import 'package:permission_handler/src/utils/codec.dart';
/// Provides a cross-platform (iOS, Android) API to request and check permissions.
class PermissionHandler {
factory PermissionHandler() {
if (_instance == null) {
const MethodChannel methodChannel =
MethodChannel('flutter.baseflow.com/permissions/methods');
_instance = PermissionHandler.private(methodChannel);
}
return _instance;
}
@visibleForTesting
PermissionHandler.private(this._methodChannel);
static PermissionHandler _instance;
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 {
final int status = await _methodChannel.invokeMethod(
'checkPermissionStatus', permission.value);
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 int status = await _methodChannel.invokeMethod(
'checkServiceStatus', permission.value);
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;
}
/// Request the user for access to the supplied list of permissiongroups.
///
/// Returns a [Map] containing the status per requested permissiongroup.
Future<Map<PermissionGroup, PermissionStatus>> requestPermissions(
List<PermissionGroup> permissions) async {
final List<int> data = Codec.encodePermissionGroups(permissions);
final Map<dynamic, dynamic> status =
await _methodChannel.invokeMethod('requestPermissions', data);
return Codec.decodePermissionRequestResult(Map<int, int>.from(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
/// returns [false].
Future<bool> shouldShowRequestPermissionRationale(
PermissionGroup permission) async {
if (!Platform.isAndroid) {
return false;
}
final bool shouldShowRationale = await _methodChannel.invokeMethod(
'shouldShowRequestPermissionRationale', permission.value);
return shouldShowRationale;
}
}
import 'package:permission_handler/src/permission_enums.dart';
class Codec {
static PermissionStatus decodePermissionStatus(int value) {
return PermissionStatus.values[value];
}
static ServiceStatus decodeServiceStatus(int value) {
return ServiceStatus.values[value];
}
static Map<PermissionGroup, PermissionStatus> decodePermissionRequestResult(
Map<int, int> value) {
print('decodePermissionRequestResult called with: value:[$value]');
return value.map((int key, int value) =>
MapEntry<PermissionGroup, PermissionStatus>(
PermissionGroup.values[key], PermissionStatus.values[value]));
}
static List<int> encodePermissionGroups(List<PermissionGroup> permissions) {
return permissions.map((PermissionGroup it) => it.value).toList();
}
}
part of permission_handler;
class Codec {
static PermissionStatus decodePermissionStatus(String 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);
final Map<PermissionGroup, PermissionStatus> permissionResults =
<PermissionGroup, PermissionStatus>{};
jsonObject.forEach((String key, dynamic value) {
final PermissionGroup permissionGroup = PermissionGroup.values.firstWhere(
(PermissionGroup e) =>
e.toString().split('.').last == key.toString());
final PermissionStatus permissionStatus = PermissionStatus.values
.firstWhere((PermissionStatus 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<PermissionGroup> permissions) =>
json.encode(
permissions.map((PermissionGroup p) => _encodeEnum(p)).toList());
static String _encodeEnum(dynamic value) {
return value.toString().split('.').last;
}
}
...@@ -8,11 +8,11 @@ ...@@ -8,11 +8,11 @@
<excludeFolder url="file://$MODULE_DIR$/.idea" /> <excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" /> <excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" /> <excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" /> <excludeFolder url="file://$MODULE_DIR$/example/build" />
</content> </content>
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" /> <orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" /> <orderEntry type="library" name="Flutter Plugins" level="project" />
</component> </component>
......
name: permission_handler name: permission_handler
description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions. description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
version: 2.2.0 version: 3.0.0
author: Baseflow <hello@baseflow.com> authors:
- Baseflow <hello@baseflow.com>
- long1eu <home@long1.eu>
homepage: https://github.com/baseflowit/flutter-permission-handler homepage: https://github.com/baseflowit/flutter-permission-handler
environment:
sdk: ">=2.0.0 <3.0.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
meta: ^1.1.6
# For information on the generic Dart part of this file, see the meta: ^1.1.6
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter: flutter:
plugin: plugin:
androidPackage: com.baseflow.permissionhandler androidPackage: com.baseflow.permissionhandler
pluginClass: PermissionHandlerPlugin pluginClass: PermissionHandlerPlugin
\ No newline at end of file
environment:
sdk: ">=2.1.0-dev.5.0 <3.0.0"
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.io/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.io/custom-fonts/#from-packages
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