From 605264044635d2cda6514796754eb1c531d0845e Mon Sep 17 00:00:00 2001 From: datang Date: Mon, 29 Aug 2022 15:22:28 +0800 Subject: [PATCH] new --- .dart_tool/package_config.json | 284 +++++++++++++ .dart_tool/package_config_subset | 185 +++++++++ .dart_tool/version | 1 + .idea/libraries/Dart_Packages.xml | 372 ++++++++++++++++++ .idea/libraries/Dart_SDK.xml | 27 ++ .idea/libraries/Flutter_Plugins.xml | 7 + .idea/modules.xml | 8 + ...ssion_handler_platform_interface-3.7.0.iml | 15 + .idea/vcs.xml | 6 + .packages | 52 +++ CHANGELOG.md | 79 ++++ LICENSE | 21 + analysis_options.yaml | 11 + ...permission_handler_platform_interface.dart | 11 + .../method_channel_permission_handler.dart | 88 +++++ lib/src/method_channel/utils/codec.dart | 25 ++ ...permission_handler_platform_interface.dart | 88 +++++ lib/src/permission_status.dart | 110 ++++++ lib/src/permissions.dart | 267 +++++++++++++ lib/src/service_status.dart | 66 ++++ pubspec.lock | 315 +++++++++++++++ pubspec.yaml | 22 ++ .../method_channel/method_channel_mock.dart | 34 ++ ...ethod_channel_permission_handler_test.dart | 202 ++++++++++ test/src/method_channel/utils/coded_test.dart | 36 ++ ...ssion_handler_platform_interface_test.dart | 103 +++++ test/src/permission_status_test.dart | 65 +++ test/src/permissions_test.dart | 81 ++++ test/src/service_status_test.dart | 51 +++ 29 files changed, 2632 insertions(+) create mode 100644 .dart_tool/package_config.json create mode 100644 .dart_tool/package_config_subset create mode 100644 .dart_tool/version create mode 100644 .idea/libraries/Dart_Packages.xml create mode 100644 .idea/libraries/Dart_SDK.xml create mode 100644 .idea/libraries/Flutter_Plugins.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/permission_handler_platform_interface-3.7.0.iml create mode 100644 .idea/vcs.xml create mode 100644 .packages create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 analysis_options.yaml create mode 100644 lib/permission_handler_platform_interface.dart create mode 100644 lib/src/method_channel/method_channel_permission_handler.dart create mode 100644 lib/src/method_channel/utils/codec.dart create mode 100644 lib/src/permission_handler_platform_interface.dart create mode 100644 lib/src/permission_status.dart create mode 100644 lib/src/permissions.dart create mode 100644 lib/src/service_status.dart create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 test/src/method_channel/method_channel_mock.dart create mode 100644 test/src/method_channel/method_channel_permission_handler_test.dart create mode 100644 test/src/method_channel/utils/coded_test.dart create mode 100644 test/src/permission_handler_platform_interface_test.dart create mode 100644 test/src/permission_status_test.dart create mode 100644 test/src/permissions_test.dart create mode 100644 test/src/service_status_test.dart diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json new file mode 100644 index 0000000..64d4119 --- /dev/null +++ b/.dart_tool/package_config.json @@ -0,0 +1,284 @@ +{ + "configVersion": 2, + "packages": [ + { + "name": "_fe_analyzer_shared", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/_fe_analyzer_shared-46.0.0", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "analyzer", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/analyzer-4.6.0", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "args", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/args-2.3.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "async", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/async-2.8.2", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "boolean_selector", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "build", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/build-2.3.0", + "packageUri": "lib/", + "languageVersion": "2.14" + }, + { + "name": "built_collection", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_collection-5.1.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "built_value", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_value-8.4.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "characters", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "charcode", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "clock", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "code_builder", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/code_builder-4.2.0", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "collection", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "convert", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/convert-3.0.2", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "crypto", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2", + "packageUri": "lib/", + "languageVersion": "2.14" + }, + { + "name": "dart_style", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/dart_style-2.2.3", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "fake_async", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "file", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/file-6.1.4", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "fixnum", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fixnum-1.0.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "flutter", + "rootUri": "file:///Users/datang/fvm/versions/3.0.0/packages/flutter", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "flutter_lints", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "flutter_test", + "rootUri": "file:///Users/datang/fvm/versions/3.0.0/packages/flutter_test", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "glob", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/glob-2.1.0", + "packageUri": "lib/", + "languageVersion": "2.15" + }, + { + "name": "lints", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "logging", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/logging-1.0.2", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "matcher", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "material_color_utilities", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4", + "packageUri": "lib/", + "languageVersion": "2.13" + }, + { + "name": "meta", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "mockito", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/mockito-5.3.0", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "package_config", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/package_config-2.1.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "path", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/path-1.8.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "plugin_platform_interface", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "pub_semver", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/pub_semver-2.1.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "sky_engine", + "rootUri": "file:///Users/datang/fvm/versions/3.0.0/bin/cache/pkg/sky_engine", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "source_gen", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_gen-1.2.2", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "source_span", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2", + "packageUri": "lib/", + "languageVersion": "2.14" + }, + { + "name": "stack_trace", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "stream_channel", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "string_scanner", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "term_glyph", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "test_api", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "typed_data", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "vector_math", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2", + "packageUri": "lib/", + "languageVersion": "2.14" + }, + { + "name": "watcher", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/watcher-1.0.1", + "packageUri": "lib/", + "languageVersion": "2.14" + }, + { + "name": "yaml", + "rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, + { + "name": "permission_handler_platform_interface", + "rootUri": "../", + "packageUri": "lib/", + "languageVersion": "2.14" + } + ], + "generated": "2022-08-29T07:21:04.113908Z", + "generator": "pub", + "generatorVersion": "2.17.0" +} diff --git a/.dart_tool/package_config_subset b/.dart_tool/package_config_subset new file mode 100644 index 0000000..1cd32ac --- /dev/null +++ b/.dart_tool/package_config_subset @@ -0,0 +1,185 @@ +_fe_analyzer_shared +2.17 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/_fe_analyzer_shared-46.0.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/_fe_analyzer_shared-46.0.0/lib/ +analyzer +2.17 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/analyzer-4.6.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/analyzer-4.6.0/lib/ +args +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/lib/ +async +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/lib/ +boolean_selector +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/ +build +2.14 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/build-2.3.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/build-2.3.0/lib/ +built_collection +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_collection-5.1.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_collection-5.1.1/lib/ +built_value +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_value-8.4.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_value-8.4.1/lib/ +characters +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib/ +charcode +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/ +clock +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/ +code_builder +2.17 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/code_builder-4.2.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/code_builder-4.2.0/lib/ +collection +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib/ +convert +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/convert-3.0.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/convert-3.0.2/lib/ +crypto +2.14 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/lib/ +dart_style +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/dart_style-2.2.3/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/dart_style-2.2.3/lib/ +fake_async +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0/lib/ +file +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/file-6.1.4/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/file-6.1.4/lib/ +fixnum +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fixnum-1.0.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fixnum-1.0.1/lib/ +flutter_lints +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4/lib/ +glob +2.15 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/glob-2.1.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/glob-2.1.0/lib/ +lints +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/lib/ +logging +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/logging-1.0.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/logging-1.0.2/lib/ +matcher +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/lib/ +material_color_utilities +2.13 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4/lib/ +meta +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib/ +mockito +2.17 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/mockito-5.3.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/mockito-5.3.0/lib/ +package_config +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/package_config-2.1.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/package_config-2.1.0/lib/ +path +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/path-1.8.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/path-1.8.1/lib/ +plugin_platform_interface +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2/lib/ +pub_semver +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/pub_semver-2.1.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/pub_semver-2.1.1/lib/ +source_gen +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_gen-1.2.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_gen-1.2.2/lib/ +source_span +2.14 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2/lib/ +stack_trace +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/ +stream_channel +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/ +string_scanner +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/ +term_glyph +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/ +test_api +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9/lib/ +typed_data +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1/lib/ +vector_math +2.14 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib/ +watcher +2.14 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/watcher-1.0.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/watcher-1.0.1/lib/ +yaml +2.12 +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/ +file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/lib/ +permission_handler_platform_interface +2.14 +file:///Users/datang/Desktop/permission_handler_platform_interface-3.7.0/ +file:///Users/datang/Desktop/permission_handler_platform_interface-3.7.0/lib/ +sky_engine +2.12 +file:///Users/datang/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/ +file:///Users/datang/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/lib/ +flutter +2.12 +file:///Users/datang/fvm/versions/3.0.0/packages/flutter/ +file:///Users/datang/fvm/versions/3.0.0/packages/flutter/lib/ +flutter_test +2.17 +file:///Users/datang/fvm/versions/3.0.0/packages/flutter_test/ +file:///Users/datang/fvm/versions/3.0.0/packages/flutter_test/lib/ +2 diff --git a/.dart_tool/version b/.dart_tool/version new file mode 100644 index 0000000..56fea8a --- /dev/null +++ b/.dart_tool/version @@ -0,0 +1 @@ +3.0.0 \ No newline at end of file diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml new file mode 100644 index 0000000..81b0d42 --- /dev/null +++ b/.idea/libraries/Dart_Packages.xml @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml new file mode 100644 index 0000000..3dad229 --- /dev/null +++ b/.idea/libraries/Dart_SDK.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml new file mode 100644 index 0000000..b0f6971 --- /dev/null +++ b/.idea/libraries/Flutter_Plugins.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..0fbf5da --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/permission_handler_platform_interface-3.7.0.iml b/.idea/permission_handler_platform_interface-3.7.0.iml new file mode 100644 index 0000000..a7a886f --- /dev/null +++ b/.idea/permission_handler_platform_interface-3.7.0.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.packages b/.packages new file mode 100644 index 0000000..c6a8eb2 --- /dev/null +++ b/.packages @@ -0,0 +1,52 @@ +# This file is deprecated. Tools should instead consume +# `.dart_tool/package_config.json`. +# +# For more info see: https://dart.dev/go/dot-packages-deprecation +# +# Generated by pub on 2022-08-29 15:21:04.100866. +_fe_analyzer_shared:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/_fe_analyzer_shared-46.0.0/lib/ +analyzer:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/analyzer-4.6.0/lib/ +args:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/lib/ +async:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/lib/ +boolean_selector:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/ +build:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/build-2.3.0/lib/ +built_collection:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_collection-5.1.1/lib/ +built_value:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/built_value-8.4.1/lib/ +characters:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib/ +charcode:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/ +clock:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/ +code_builder:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/code_builder-4.2.0/lib/ +collection:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib/ +convert:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/convert-3.0.2/lib/ +crypto:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/lib/ +dart_style:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/dart_style-2.2.3/lib/ +fake_async:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0/lib/ +file:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/file-6.1.4/lib/ +fixnum:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/fixnum-1.0.1/lib/ +flutter:file:///Users/datang/fvm/versions/3.0.0/packages/flutter/lib/ +flutter_lints:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4/lib/ +flutter_test:file:///Users/datang/fvm/versions/3.0.0/packages/flutter_test/lib/ +glob:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/glob-2.1.0/lib/ +lints:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/lib/ +logging:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/logging-1.0.2/lib/ +matcher:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/lib/ +material_color_utilities:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4/lib/ +meta:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib/ +mockito:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/mockito-5.3.0/lib/ +package_config:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/package_config-2.1.0/lib/ +path:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/path-1.8.1/lib/ +plugin_platform_interface:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2/lib/ +pub_semver:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/pub_semver-2.1.1/lib/ +sky_engine:file:///Users/datang/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/lib/ +source_gen:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_gen-1.2.2/lib/ +source_span:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2/lib/ +stack_trace:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/ +stream_channel:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/ +string_scanner:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/ +term_glyph:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/ +test_api:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9/lib/ +typed_data:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1/lib/ +vector_math:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib/ +watcher:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/watcher-1.0.1/lib/ +yaml:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/lib/ +permission_handler_platform_interface:lib/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1a40faa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,79 @@ +## 3.7.0 + +* Added support for the new Android 12 Bluetooth permissions: BLUETOOTH_SCAN, BLUETOOTH_ADVERTISE and BLUETOOTH_CONNECT. + +## 3.6.2 + +* Updated the MethodChannelMock due to breaking changes in the platform channel test interface. + +## 3.6.1 + +* Updated `meta` dependency to version `^1.3.0`. +* Updated documentation for the `locationAlways` permission + +## 3.6.0 + +* Add support for iOS Critical alerts and Android Access Notification Policy. + +## 3.5.1 + +* Updated API documentation for the `PermissionStatus.permanentlyDenied` status. + +## 3.5.0 + +* Added support for app tracking transparency permission. + +## 3.4.0 + +* Added support request install packages permission. + +## 3.3.0 + +* Added support for system alert window permission. + +## 3.2.0 + +* Added support for manage external storage permission available on Android 10 and up. + +## 3.1.3 + +* Implemented equality operator for `Permission` class; +* Reverted services status check for notification permission. Turns out implementation does not fit with idea's of permission_handler plugin. + +## 3.1.2 + +* Allow checking serviceStatus for notification permission. + +## 3.1.1 + +* Fixed conversion issue where `PermissionStatus.denied` was not translated to the correct index. +* Added unit-tests to guard API against breaking changes. + +## 3.1.0 + +* Added support for bluetooth permissions. + +## 3.0.0+1 + +* **BREAKING**: Removed PermissionStatus.undetermined. This is now replaced by PermissionStatus.denied. + +## 3.0.0 + +* Migrated to null safety. + +## 2.0.2 + +* Added support for the limited photos permission available on iOS 14 and up. + +## 2.0.1 + +* Update `platform_interface 1.0.2` +* Fix bug which allows requesting is the device has phone capabilities. + +## 2.0.0 + +- **BREAKING**: Created a much more intuitive API using Dart's new extension methods ([#230](https://github.com/Baseflow/flutter-permission-handler/issues/230)). Big thank you to [@marcelgarus](https://github.com/marcelgarus) for the idea and doing all the grunt work. + +## 1.0.0 + +- Initial open-source release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd6192f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Baseflow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..2e693b7 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,11 @@ + +include: package:flutter_lints/flutter.yaml + +analyzer: + exclude: + # Ignore generated files + - '**/*.g.dart' + - 'lib/src/generated/*.dart' +linter: + rules: + - public_member_api_docs \ No newline at end of file diff --git a/lib/permission_handler_platform_interface.dart b/lib/permission_handler_platform_interface.dart new file mode 100644 index 0000000..4f44f61 --- /dev/null +++ b/lib/permission_handler_platform_interface.dart @@ -0,0 +1,11 @@ +library permission_handler_platform_interface; + +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'src/method_channel/method_channel_permission_handler.dart'; + +part 'src/permission_handler_platform_interface.dart'; +part 'src/permission_status.dart'; +part 'src/permissions.dart'; +part 'src/service_status.dart'; diff --git a/lib/src/method_channel/method_channel_permission_handler.dart b/lib/src/method_channel/method_channel_permission_handler.dart new file mode 100644 index 0000000..2443cb8 --- /dev/null +++ b/lib/src/method_channel/method_channel_permission_handler.dart @@ -0,0 +1,88 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; + +import '../../permission_handler_platform_interface.dart'; +import 'utils/codec.dart'; + +const MethodChannel _methodChannel = + MethodChannel('flutter.baseflow.com/permissions/methods'); + +/// An implementation of [PermissionHandlerPlatform] that uses [MethodChannel]s. +class MethodChannelPermissionHandler extends PermissionHandlerPlatform { + /// Checks the current status of the given [Permission]. + @override + Future checkPermissionStatus(Permission permission) async { + final status = await _methodChannel.invokeMethod( + 'checkPermissionStatus', permission.value); + + return decodePermissionStatus(status); + } + + /// Checks the current status of the service associated with the given + /// [Permission]. + /// + /// Notes about specific permissions: + /// - **[Permission.phone]** + /// - Android: + /// - The method will return [ServiceStatus.notApplicable] when: + /// - the device lacks the TELEPHONY feature + /// - TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE + /// - when no Intents can be resolved to handle the `tel:` scheme + /// - The method will return [ServiceStatus.disabled] when: + /// - the SIM card is missing + /// - iOS: + /// - The method will return [ServiceStatus.notApplicable] when: + /// - the native code can not find a handler for the `tel:` scheme + /// - The method will return [ServiceStatus.disabled] when: + /// - the mobile network code (MNC) is either 0 or 65535. See + /// https://stackoverflow.com/a/11595365 for details + /// - **PLEASE NOTE that this is still not a perfect indication** of the + /// device's capability to place & connect phone calls as it also depends + /// on the network condition. + @override + Future checkServiceStatus(Permission permission) async { + final status = await _methodChannel.invokeMethod( + 'checkServiceStatus', permission.value); + + return decodeServiceStatus(status); + } + + /// Opens the app settings page. + /// + /// Returns [true] if the app settings page could be opened, otherwise + /// [false]. + @override + Future openAppSettings() async { + final wasOpened = await _methodChannel.invokeMethod('openAppSettings'); + + return wasOpened ?? false; + } + + /// Requests the user for access to the supplied list of [Permission]s, if + /// they have not already been granted before. + /// + /// Returns a [Map] containing the status per requested [Permission]. + @override + Future> requestPermissions( + List permissions) async { + final data = encodePermissions(permissions); + final status = + await _methodChannel.invokeMethod('requestPermissions', data); + + return decodePermissionRequestResult(Map.from(status)); + } + + /// Checks if you should show a rationale for requesting permission. + /// + /// This method is only implemented on Android, calling this on iOS always + /// returns [false]. + @override + Future shouldShowRequestPermissionRationale( + Permission permission) async { + final shouldShowRationale = await _methodChannel.invokeMethod( + 'shouldShowRequestPermissionRationale', permission.value); + + return shouldShowRationale ?? false; + } +} diff --git a/lib/src/method_channel/utils/codec.dart b/lib/src/method_channel/utils/codec.dart new file mode 100644 index 0000000..dc5db07 --- /dev/null +++ b/lib/src/method_channel/utils/codec.dart @@ -0,0 +1,25 @@ +import '../../../permission_handler_platform_interface.dart'; + +/// Converts the given [value] into a [PermissionStatus] instance. +PermissionStatus decodePermissionStatus(int value) { + return PermissionStatusValue.statusByValue(value); +} + +/// Converts the given [value] into a [ServiceStatus] instance. +ServiceStatus decodeServiceStatus(int value) { + return ServiceStatusValue.statusByValue(value); +} + +/// Converts the given [Map] of [int]s into a [Map] with [Permission]s as +/// keys and their respective [PermissionStatus] as value. +Map decodePermissionRequestResult( + Map value) { + return value.map((key, value) => MapEntry( + Permission.byValue(key), PermissionStatusValue.statusByValue(value))); +} + +/// Converts the given [List] of [Permission]s into a [List] of [int]s which +/// can be sent on the Flutter method channel. +List encodePermissions(List permissions) { + return permissions.map((it) => it.value).toList(); +} diff --git a/lib/src/permission_handler_platform_interface.dart b/lib/src/permission_handler_platform_interface.dart new file mode 100644 index 0000000..65d965b --- /dev/null +++ b/lib/src/permission_handler_platform_interface.dart @@ -0,0 +1,88 @@ +part of permission_handler_platform_interface; + +/// The interface that implementations of `permission_handler` must implement. +/// +/// Platform implementations should extend this class rather than implement it +/// as `permission_handler` does not consider newly added methods to be +/// breaking changes. Extending this class (using `extends`) ensures that the +/// subclass will get the default implementation, while platform +/// implementations that `implements` this interface will be broken by newly +/// added [PermissionHandlerPlatform] methods. +abstract class PermissionHandlerPlatform extends PlatformInterface { + /// Constructs a PermissionHandlerPlatform. + PermissionHandlerPlatform() : super(token: _token); + + static final Object _token = Object(); + + static PermissionHandlerPlatform _instance = MethodChannelPermissionHandler(); + + /// The default instance of [PermissionHandlerPlatform] to use. + /// + /// Defaults to [MethodChannelPermissionHandler]. + static PermissionHandlerPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own + /// platform-specific class that extends + /// [PermissionHandlerPlatform] when they register themselves. + static set instance(PermissionHandlerPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Checks the current status of the given [Permission]. + Future checkPermissionStatus(Permission permission) { + throw UnimplementedError( + 'checkPermissionStatus() has not been implemented.'); + } + + /// Checks the current status of the service associated with the given + /// [Permission]. + /// + /// Notes about specific permissions: + /// - **[Permission.phone]** + /// - Android: + /// - The method will return [ServiceStatus.notApplicable] when: + /// - the device lacks the TELEPHONY feature + /// - TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE + /// - when no Intents can be resolved to handle the `tel:` scheme + /// - The method will return [ServiceStatus.disabled] when: + /// - the SIM card is missing + /// - iOS: + /// - The method will return [ServiceStatus.notApplicable] when: + /// - the native code can not find a handler for the `tel:` scheme + /// - The method will return [ServiceStatus.disabled] when: + /// - the mobile network code (MNC) is either 0 or 65535. See + /// https://stackoverflow.com/a/11595365 for details + /// - **PLEASE NOTE that this is still not a perfect indication** of the + /// device's capability to place & connect phone calls as it also depends + /// on the network condition. + Future checkServiceStatus(Permission permission) { + throw UnimplementedError('checkServiceStatus() has not been implemented.'); + } + + /// Opens the app settings page. + /// + /// Returns [true] if the app settings page could be opened, otherwise + /// [false]. + Future openAppSettings() { + throw UnimplementedError('openAppSettings() has not been implemented.'); + } + + /// Requests the user for access to the supplied list of [Permission]s, if + /// they have not already been granted before. + /// + /// Returns a [Map] containing the status per requested [Permission]. + Future> requestPermissions( + List permissions) { + throw UnimplementedError('requestPermissions() has not been implemented.'); + } + + /// Checks if you should show a rationale for requesting permission. + /// + /// This method is only implemented on Android, calling this on iOS always + /// returns [false]. + Future shouldShowRequestPermissionRationale(Permission permission) { + throw UnimplementedError( + 'shouldShowRequestPermissionRationale() has not been implemented.'); + } +} diff --git a/lib/src/permission_status.dart b/lib/src/permission_status.dart new file mode 100644 index 0000000..af7aa3e --- /dev/null +++ b/lib/src/permission_status.dart @@ -0,0 +1,110 @@ +part of permission_handler_platform_interface; + +/// Defines the state of a [Permission]. +enum PermissionStatus { + /// The user denied access to the requested feature. + denied, + + /// The user granted access to the requested feature. + granted, + + /// The OS denied access to the requested feature. The user cannot change + /// this app's status, possibly due to active restrictions such as parental + /// controls being in place. + /// *Only supported on iOS.* + restricted, + + ///User has authorized this application for limited access. + /// *Only supported on iOS (iOS14+).* + limited, + + /// Permission to the requested feature is permanently denied, the permission + /// dialog will not be shown when requesting this permission. The user may + /// still change the permission status in the settings. + /// *Only supported on Android.* + permanentlyDenied, +} + +/// Conversion extension methods for the [PermissionStatus] type. +extension PermissionStatusValue on PermissionStatus { + /// Converts the [PermissionStatus] value into an integer. + int get value { + switch (this) { + case PermissionStatus.denied: + return 0; + case PermissionStatus.granted: + return 1; + case PermissionStatus.restricted: + return 2; + case PermissionStatus.limited: + return 3; + case PermissionStatus.permanentlyDenied: + return 4; + default: + throw UnimplementedError(); + } + } + + /// Converts the supplied integer value into a [PermissionStatus] enum. + static PermissionStatus statusByValue(int value) { + return [ + PermissionStatus.denied, + PermissionStatus.granted, + PermissionStatus.restricted, + PermissionStatus.limited, + PermissionStatus.permanentlyDenied, + ][value]; + } +} + +/// Utility getter extensions for the [PermissionStatus] type. +extension PermissionStatusGetters on PermissionStatus { + /// If the user denied access to the requested feature. + bool get isDenied => this == PermissionStatus.denied; + + /// If the user granted access to the requested feature. + bool get isGranted => this == PermissionStatus.granted; + + /// If the OS denied access to the requested feature. The user cannot change + /// this app's status, possibly due to active restrictions such as parental + /// controls being in place. + /// *Only supported on iOS.* + bool get isRestricted => this == PermissionStatus.restricted; + + /// If the user denied access to the requested feature and selected to never + /// again show a request for this permission. The user may still change the + /// permission status in the settings. + /// *Only supported on Android.* + /// + /// WARNING: This can only be determined AFTER requesting this permission. + /// Therefore make a `request` call first. + bool get isPermanentlyDenied => this == PermissionStatus.permanentlyDenied; + + /// Indicates that permission for limited use of the resource is granted. + bool get isLimited => this == PermissionStatus.limited; +} + +/// Utility getter extensions for the `Future` type. +extension FuturePermissionStatusGetters on Future { + /// If the user granted access to the requested feature. + Future get isGranted async => (await this).isGranted; + + /// If the user denied access to the requested feature. + Future get isDenied async => (await this).isDenied; + + /// If the OS denied access to the requested feature. The user cannot change + /// this app's status, possibly due to active restrictions such as parental + /// controls being in place. + /// *Only supported on iOS.* + Future get isRestricted async => (await this).isRestricted; + + /// If the user denied access to the requested feature and selected to never + /// again show a request for this permission. The user may still change the + /// permission status in the settings. + /// *Only supported on Android.* + Future get isPermanentlyDenied async => + (await this).isPermanentlyDenied; + + /// Indicates that permission for limited use of the resource is granted. + Future get isLimited async => (await this).isLimited; +} diff --git a/lib/src/permissions.dart b/lib/src/permissions.dart new file mode 100644 index 0000000..7d27710 --- /dev/null +++ b/lib/src/permissions.dart @@ -0,0 +1,267 @@ +part of permission_handler_platform_interface; + +/// A special kind of permission used to access a service. Additionally to the +/// actions that normal [Permission]s have, you can also query the status of +/// the related service. +class PermissionWithService extends Permission { + const PermissionWithService._(int value) : super._(value); + + /// Creates a [PermissionWithService] instance. + /// + /// This constructor is marked public for testing purposes only. + @visibleForTesting + const PermissionWithService.private(int value) : super._(value); +} + +/// Defines the permissions which can be checked and requested. +@immutable +class Permission { + const Permission._(this.value); + + /// Creates a [Permission] using the supplied integer value. + factory Permission.byValue(int value) => values[value]; + + /// Integer representation of the [Permission]. + final int value; + + /// Android: Calendar + /// iOS: Calendar (Events) + static const calendar = Permission._(0); + + /// Android: Camera + /// iOS: Photos (Camera Roll and Camera) + static const camera = Permission._(1); + + /// Android: Contacts + /// iOS: AddressBook + static const contacts = Permission._(2); + + /// Android: Fine and Coarse Location + /// iOS: CoreLocation (Always and WhenInUse) + static const location = PermissionWithService._(3); + + /// Android: + /// When running on Android < Q: Fine and Coarse Location + /// When running on Android Q and above: Background Location Permission + /// iOS: CoreLocation - Always + /// When requesting this permission the user needs to grant permission + /// for the `locationWhenInUse` permission first, clicking on + /// the `Àllow While Using App` option on the popup. + /// After allowing the permission the user can request the `locationAlways` + /// permission and can click on the `Change To Always Allow` option. + static const locationAlways = PermissionWithService._(4); + + /// Android: Fine and Coarse Location + /// iOS: CoreLocation - WhenInUse + static const locationWhenInUse = PermissionWithService._(5); + + /// Android: None + /// iOS: MPMediaLibrary + static const mediaLibrary = Permission._(6); + + /// Android: Microphone + /// iOS: Microphone + static const microphone = Permission._(7); + + /// Android: Phone + /// iOS: Nothing + static const phone = PermissionWithService._(8); + + /// Android: Nothing + /// iOS: Photos + /// iOS 14+ read & write access level + static const photos = Permission._(9); + + /// Android: Nothing + /// iOS: Photos + /// iOS 14+ read & write access level + static const photosAddOnly = Permission._(10); + + /// Android: Nothing + /// iOS: Reminders + static const reminders = Permission._(11); + + /// Android: Body Sensors + /// iOS: CoreMotion + static const sensors = Permission._(12); + + /// Android: Sms + /// iOS: Nothing + static const sms = Permission._(13); + + /// Android: Microphone + /// iOS: Speech + static const speech = Permission._(14); + + /// Android: External Storage + /// iOS: Access to folders like `Documents` or `Downloads`. Implicitly + /// granted. + static const storage = Permission._(15); + + /// Android: Ignore Battery Optimizations + static const ignoreBatteryOptimizations = Permission._(16); + + /// Android: Notification + /// iOS: Notification + static const notification = Permission._(17); + + /// Android: Allows an application to access any geographic locations + /// persisted in the user's shared collection. + static const accessMediaLocation = Permission._(18); + + /// When running on Android Q and above: Activity Recognition + /// When running on Android < Q: Nothing + /// iOS: Nothing + static const activityRecognition = Permission._(19); + + /// The unknown only used for return type, never requested + static const unknown = Permission._(20); + + /// iOS 13 and above: The authorization state of Core Bluetooth manager. + /// When running < iOS 13 or Android this is always allowed. + static const bluetooth = Permission._(21); + + /// Android: Allows an application a broad access to external storage in + /// scoped storage. + /// iOS: Nothing + /// + /// You should request the Manage External Storage permission only when + /// your app cannot effectively make use of the more privacy-friendly APIs. + /// For more information: https://developer.android.com/training/data-storage/manage-all-files + /// + /// When the privacy-friendly APIs (i.e. [Storage Access Framework](https://developer.android.com/guide/topics/providers/document-provider) + /// or the [MediaStore](https://developer.android.com/training/data-storage/shared/media) APIs) is all your app needs the + /// [PermissionGroup.storage] are the only permissions you need to request. + /// + /// If the usage of the Manage External Storage permission is needed, + /// you have to fill out the Permission Declaration Form upon submitting + /// your app to the Google Play Store. More details can be found here: + /// https://support.google.com/googleplay/android-developer/answer/9214102#zippy= + static const manageExternalStorage = Permission._(22); + + ///Android: Allows an app to create windows shown on top of all other apps + ///iOS: Nothing + static const systemAlertWindow = Permission._(23); + + ///Android: Allows an app to request installing packages. + ///iOS: Nothing + static const requestInstallPackages = Permission._(24); + + ///Android: Nothing + ///iOS: Allows user to accept that your app collects data about end users and + ///shares it with other companies for purposes of tracking across apps and + ///websites. + static const appTrackingTransparency = Permission._(25); + + ///Android: Nothing + ///iOS: Notifications that override your ringer + static const criticalAlerts = Permission._(26); + + ///Android: Allows the user to access the notification policy of the phone. + /// EX: Allows app to turn on and off do-not-disturb. + ///iOS: Nothing + static const accessNotificationPolicy = Permission._(27); + + ///Android: Allows the user to look for Bluetooth devices + ///(e.g. BLE peripherals). + ///iOS: Nothing + static const bluetoothScan = Permission._(28); + + ///Android: Allows the user to make this device discoverable to other + ///Bluetooth devices. + ///iOS: Nothing + static const bluetoothAdvertise = Permission._(29); + + ///Android: Allows the user to connect with already paired Bluetooth devices. + ///iOS: Nothing + static const bluetoothConnect = Permission._(30); + static const callLog = Permission._(31); + + /// Returns a list of all possible [PermissionGroup] values. + static const List values = [ + calendar, + camera, + contacts, + location, + locationAlways, + locationWhenInUse, + mediaLibrary, + microphone, + phone, + photos, + photosAddOnly, + reminders, + sensors, + sms, + speech, + storage, + ignoreBatteryOptimizations, + notification, + accessMediaLocation, + activityRecognition, + unknown, + bluetooth, + manageExternalStorage, + systemAlertWindow, + requestInstallPackages, + appTrackingTransparency, + criticalAlerts, + accessNotificationPolicy, + bluetoothScan, + bluetoothAdvertise, + bluetoothConnect, + callLog, + ]; + + static const List _names = [ + 'calendar', + 'camera', + 'contacts', + 'location', + 'locationAlways', + 'locationWhenInUse', + 'mediaLibrary', + 'microphone', + 'phone', + 'photos', + 'photosAddOnly', + 'reminders', + 'sensors', + 'sms', + 'speech', + 'storage', + 'ignoreBatteryOptimizations', + 'notification', + 'access_media_location', + 'activity_recognition', + 'unknown', + 'bluetooth', + 'manageExternalStorage', + 'systemAlertWindow', + 'requestInstallPackages', + 'appTrackingTransparency', + 'criticalAlerts', + 'accessNotificationPolicy', + 'bluetoothScan', + 'bluetoothAdvertise', + 'bluetoothConnect', + 'callLog', + ]; + + @override + String toString() => 'Permission.${_names[value]}'; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is Permission && other.value == value; + } + + @override + int get hashCode => value.hashCode; +} diff --git a/lib/src/service_status.dart b/lib/src/service_status.dart new file mode 100644 index 0000000..b8db14f --- /dev/null +++ b/lib/src/service_status.dart @@ -0,0 +1,66 @@ +part of permission_handler_platform_interface; + +/// Defines the different states a service can be in. +enum ServiceStatus { + /// The service for the permission is disabled. + disabled, + + /// The service for the permission is enabled. + enabled, + + /// The permission does not have an associated service on the current + /// platform. + notApplicable, +} + +/// Conversion extension methods for the [ServiceStatus] type. +extension ServiceStatusValue on ServiceStatus { + /// Converts the [ServiceStatus] value into an integer. + int get value { + switch (this) { + case ServiceStatus.disabled: + return 0; + case ServiceStatus.enabled: + return 1; + case ServiceStatus.notApplicable: + return 2; + default: + throw UnimplementedError(); + } + } + + /// Converts the supplied integer value into a [ServiceStatus] enum. + static ServiceStatus statusByValue(int value) { + return [ + ServiceStatus.disabled, + ServiceStatus.enabled, + ServiceStatus.notApplicable, + ][value]; + } +} + +/// Utility getter extensions for the [ServiceStatus] type. +extension ServiceStatusGetters on ServiceStatus { + /// If the service for the permission is disabled. + bool get isDisabled => this == ServiceStatus.disabled; + + /// If the service for the permission is enabled. + bool get isEnabled => this == ServiceStatus.enabled; + + /// If the permission does not have an associated service on the current + /// platform. + bool get isNotApplicable => this == ServiceStatus.notApplicable; +} + +/// Utility getter extensions for the `Future` type. +extension FutureServiceStatusGetters on Future { + /// If the service for the permission is disabled. + Future get isDisabled async => (await this).isDisabled; + + /// If the service for the permission is enabled. + Future get isEnabled async => (await this).isEnabled; + + /// If the permission does not have an associated service on the current + /// platform. + Future get isNotApplicable async => (await this).isNotApplicable; +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..d028bd5 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,315 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "46.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.4.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" + meta: + dependency: "direct main" + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + mockito: + dependency: "direct dev" + description: + name: mockito + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + plugin_platform_interface: + dependency: "direct main" + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.9" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.17.0 <3.0.0" + flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..de1b104 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,22 @@ +name: permission_handler_platform_interface +description: A common platform interface for the permission_handler plugin. +homepage: https://github.com/baseflow/flutter-permission-handler/tree/master/permission_handler_platform_interface +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 3.7.0 + +dependencies: + flutter: + sdk: flutter + meta: ^1.3.0 + plugin_platform_interface: ^2.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 + flutter_lints: ^1.0.4 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" diff --git a/test/src/method_channel/method_channel_mock.dart b/test/src/method_channel/method_channel_mock.dart new file mode 100644 index 0000000..ad85720 --- /dev/null +++ b/test/src/method_channel/method_channel_mock.dart @@ -0,0 +1,34 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class MethodChannelMock { + final MethodChannel methodChannel; + final String method; + final dynamic result; + final Duration delay; + + MethodChannelMock({ + required String channelName, + required this.method, + this.result, + this.delay = Duration.zero, + }) : methodChannel = MethodChannel(channelName) { + TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + .setMockMethodCallHandler(methodChannel, _handler); + } + + Future _handler(MethodCall methodCall) async { + if (methodCall.method != method) { + throw MissingPluginException('No implementation found for method ' + '$method on channel ${methodChannel.name}'); + } + + return Future.delayed(delay, () { + if (result is Exception) { + throw result; + } + + return Future.value(result); + }); + } +} diff --git a/test/src/method_channel/method_channel_permission_handler_test.dart b/test/src/method_channel/method_channel_permission_handler_test.dart new file mode 100644 index 0000000..1269c3a --- /dev/null +++ b/test/src/method_channel/method_channel_permission_handler_test.dart @@ -0,0 +1,202 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; +import 'package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart'; +import 'method_channel_mock.dart'; + +List get mockPermissions => List.of( + {Permission.calendar, Permission.camera, Permission.contacts}); + +Map get mockPermissionMap => {}; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('checkPermissionStatus: When checking for permission', () { + test('Should receive granted if user wants access to the requested feature', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.denied.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.calendar); + + expect(permissionStatus, PermissionStatus.denied); + }); + + test('Should receive denied if user denied access to the requested feature', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.denied.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.calendar); + + expect(permissionStatus, PermissionStatus.denied); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Should receive restricted if OS denied rights for to the requested feature', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.restricted.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.calendar); + + expect(permissionStatus, PermissionStatus.restricted); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Should receive limited if user has authorized this application for limited access', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.limited.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.calendar); + + expect(permissionStatus, PermissionStatus.limited); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Should receive permanentlyDenied if user denied access and selected to never show a request for this permission again', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.permanentlyDenied.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.calendar); + + expect(permissionStatus, PermissionStatus.permanentlyDenied); + }); + }); + + group('checkServiceStatus: When checking for service', () { + // ignore: lines_longer_than_80_chars + test( + 'Should receive disabled if the service for the permission is disabled', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkServiceStatus', + result: ServiceStatus.disabled.value, + ); + + final serviceStatus = await MethodChannelPermissionHandler() + .checkServiceStatus(Permission.calendar); + + expect(serviceStatus, ServiceStatus.disabled); + }); + + test('Should receive enabled if the service for the permission is enabled', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkServiceStatus', + result: ServiceStatus.enabled.value, + ); + + final serviceStatus = await MethodChannelPermissionHandler() + .checkServiceStatus(Permission.calendar); + + expect(serviceStatus, ServiceStatus.enabled); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Should receive notApplicable if the permission does not have an associated service on the current platform', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkServiceStatus', + result: ServiceStatus.notApplicable.value, + ); + + final serviceStatus = await MethodChannelPermissionHandler() + .checkServiceStatus(Permission.calendar); + + expect(serviceStatus, ServiceStatus.notApplicable); + }); + }); + + group('openAppSettings: When opening the App settings', () { + test('Should receive true if the page can be opened', () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'openAppSettings', + result: true, + ); + + final hasOpenedAppSettings = + await MethodChannelPermissionHandler().openAppSettings(); + + expect(hasOpenedAppSettings, true); + }); + + test('Should receive false if an error occurred', () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'openAppSettings', + result: false, + ); + + final hasOpenedAppSettings = + await MethodChannelPermissionHandler().openAppSettings(); + + expect(hasOpenedAppSettings, false); + }); + }); + + group('requestPermissions: When requesting for permission', () { + // ignore: lines_longer_than_80_chars + test('returns a Map with all the PermissionStatus of the given permissions', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'requestPermissions', + result: mockPermissionMap, + ); + + final result = await MethodChannelPermissionHandler() + .requestPermissions(mockPermissions); + + expect(result, isA>()); + }); + }); + + group('shouldShowRequestPermissionRationale:', () { + test( + // ignore: lines_longer_than_80_chars + 'should return true when you should show a rationale for requesting permission.', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'shouldShowRequestPermissionRationale', + result: true, + ); + + final shouldShowRationale = await MethodChannelPermissionHandler() + .shouldShowRequestPermissionRationale(mockPermissions.first); + + expect(shouldShowRationale, true); + }); + }); +} diff --git a/test/src/method_channel/utils/coded_test.dart b/test/src/method_channel/utils/coded_test.dart new file mode 100644 index 0000000..1742b9f --- /dev/null +++ b/test/src/method_channel/utils/coded_test.dart @@ -0,0 +1,36 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; +import 'package:permission_handler_platform_interface/src/method_channel/utils/codec.dart'; + +void main() { + group('Codec', () { + test('decodePermissionStatus should return a PermissionStatus', () { + expect(decodePermissionStatus(0), PermissionStatus.denied); + }); + + test('decodeServiceStatus should a corresponding ServiceStatus', () { + expect(decodeServiceStatus(0), ServiceStatus.disabled); + }); + + test( + 'decodePermissionRequestResult should convert a map' + 'to map', () { + var value = { + 1: 1, + }; + + var permissionMap = decodePermissionRequestResult(value); + + expect(permissionMap.keys.first, isA()); + expect(permissionMap.values.first, isA()); + }); + + test('encodePermissions should return a list of integers', () { + var permissions = [Permission.accessMediaLocation]; + + var integers = encodePermissions(permissions); + + expect(integers.first, isA()); + }); + }); +} diff --git a/test/src/permission_handler_platform_interface_test.dart b/test/src/permission_handler_platform_interface_test.dart new file mode 100644 index 0000000..574b34e --- /dev/null +++ b/test/src/permission_handler_platform_interface_test.dart @@ -0,0 +1,103 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; +import 'package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$PermissionHandlerPlatform', () { + test('$MethodChannelPermissionHandler is the default instance', () { + expect(PermissionHandlerPlatform.instance, + isA()); + }); + + test('Cannot be implemented with `implements`', () { + expect(() { + PermissionHandlerPlatform.instance = + ImplementsPermissionHandlerPlatform(); + }, throwsNoSuchMethodError); + }); + + test('Can be extended with `extend`', () { + PermissionHandlerPlatform.instance = ExtendsPermissionHandlerPlatform(); + }); + + test('Can be mocked with `implements`', () { + final mock = MockPermissionHandlerPlatform(); + PermissionHandlerPlatform.instance = mock; + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of checkPermissionStatus should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + + expect(() { + permissionHandlerPlatform + .checkPermissionStatus(Permission.accessMediaLocation); + }, throwsUnimplementedError); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of checkServiceStatus should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + + expect(() { + permissionHandlerPlatform + .checkServiceStatus(Permission.accessMediaLocation); + }, throwsUnimplementedError); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of openAppSettings should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + + expect( + permissionHandlerPlatform.openAppSettings, throwsUnimplementedError); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of requestPermissions should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + var permission = [Permission.accessMediaLocation]; + + expect(() { + permissionHandlerPlatform.requestPermissions(permission); + }, throwsUnimplementedError); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of shouldShowRequestPermissionRationale should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + expect(() { + permissionHandlerPlatform.shouldShowRequestPermissionRationale( + Permission.accessMediaLocation); + }, throwsUnimplementedError); + }); + }); +} + +class ImplementsPermissionHandlerPlatform implements PermissionHandlerPlatform { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class ExtendsPermissionHandlerPlatform extends PermissionHandlerPlatform {} + +class MockPermissionHandlerPlatform extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PermissionHandlerPlatform {} diff --git a/test/src/permission_status_test.dart b/test/src/permission_status_test.dart new file mode 100644 index 0000000..09e3569 --- /dev/null +++ b/test/src/permission_status_test.dart @@ -0,0 +1,65 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; + +void main() { + group('PermissionSatus', () { + test('PermissionStatus should contain 5 options', () { + const values = PermissionStatus.values; + + expect(values.length, 5); + }); + + test('PermissionStatus enum should have items in correct index', () { + const values = PermissionStatus.values; + + expect(values[0], PermissionStatus.denied); + expect(values[1], PermissionStatus.granted); + expect(values[2], PermissionStatus.restricted); + expect(values[3], PermissionStatus.limited); + expect(values[4], PermissionStatus.permanentlyDenied); + }); + }); + + group('PermissionStatusValue', () { + test('PermissonStatusValue returns right integer', () { + expect(PermissionStatus.denied.value, 0); + expect(PermissionStatus.granted.value, 1); + expect(PermissionStatus.restricted.value, 2); + expect(PermissionStatus.limited.value, 3); + expect(PermissionStatus.permanentlyDenied.value, 4); + }); + + test( + // ignore: lines_longer_than_80_chars + 'statusByValue should return right index int that corresponds with the right PermissionStatus', + () { + expect(PermissionStatusValue.statusByValue(0), PermissionStatus.denied); + expect(PermissionStatusValue.statusByValue(1), PermissionStatus.granted); + expect( + PermissionStatusValue.statusByValue(2), PermissionStatus.restricted); + expect(PermissionStatusValue.statusByValue(3), PermissionStatus.limited); + expect(PermissionStatusValue.statusByValue(4), + PermissionStatus.permanentlyDenied); + }); + }); + + group('PermissionStatusGetters', () { + test('Getters should return true if statement is met', () { + expect(PermissionStatus.denied.isDenied, true); + expect(PermissionStatus.granted.isGranted, true); + expect(PermissionStatus.restricted.isRestricted, true); + expect(PermissionStatus.limited.isLimited, true); + expect(PermissionStatus.permanentlyDenied.isPermanentlyDenied, true); + }); + + test('Getters should return false if statement is not met', () { + expect(PermissionStatus.denied.isGranted, false); + expect(PermissionStatus.granted.isDenied, false); + expect(PermissionStatus.restricted.isDenied, false); + expect(PermissionStatus.limited.isDenied, false); + expect(PermissionStatus.permanentlyDenied.isDenied, false); + }); + }); + + test('test', () {}); +} diff --git a/test/src/permissions_test.dart b/test/src/permissions_test.dart new file mode 100644 index 0000000..919146f --- /dev/null +++ b/test/src/permissions_test.dart @@ -0,0 +1,81 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; + +void main() { + test('Permission has the right amount of possible PermissionGroup values', + () { + const values = Permission.values; + + expect(values.length, 31); + }); + + test('check if byValue returns corresponding PermissionGroup value', () { + const values = Permission.values; + + for (var i = 0; i < values.length; i++) { + expect(values[i], Permission.byValue(i)); + } + }); + + test('check if toString method returns the corresponding name', () { + const PermissionWithService permissionWithService = + PermissionWithService.private(0); + var permissionName = permissionWithService.toString(); + expect(permissionName, 'Permission.calendar'); + }); + + test( + // ignore: lines_longer_than_80_chars + 'equality operator should return true for two instances with the same values', + () { + // Arrange + final firstPermission = Permission.byValue(1); + final secondPermission = Permission.byValue(1); + + // Act & Assert + expect( + firstPermission == secondPermission, + true, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'equality operator should return false for two instances with different values', + () { + // Arrange + final firstPermission = Permission.byValue(1); + final secondPermission = Permission.byValue(2); + + // Act & Assert + expect( + firstPermission == secondPermission, + false, + ); + }); + + test('hashCode should be the same for two instances with the same values', + () { + // Arrange + final firstPermission = Permission.byValue(1); + final secondPermission = Permission.byValue(1); + + // Act & Assert + expect( + firstPermission.hashCode, + secondPermission.hashCode, + ); + }); + + test('hashCode should not match for two instances with different values', () { + // Arrange + final firstPermission = Permission.byValue(1); + final secondPermission = Permission.byValue(2); + + // Act & Assert + expect( + firstPermission.hashCode == secondPermission.hashCode, + false, + ); + }); +} diff --git a/test/src/service_status_test.dart b/test/src/service_status_test.dart new file mode 100644 index 0000000..b5db024 --- /dev/null +++ b/test/src/service_status_test.dart @@ -0,0 +1,51 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; + +void main() { + group('ServiceStatus', () { + test('ServiceStatus should contain 3 options', () { + const values = ServiceStatus.values; + + expect(values.length, 3); + }); + + test('ServiceStatus enum should have items in correct index', () { + const values = ServiceStatus.values; + + expect(values[0], ServiceStatus.disabled); + expect(values[1], ServiceStatus.enabled); + expect(values[2], ServiceStatus.notApplicable); + }); + }); + + group('ServiceStatusValue', () { + test('ServiceStatusValue returns right integer', () { + expect(ServiceStatus.disabled.value, 0); + expect(ServiceStatus.enabled.value, 1); + expect(ServiceStatus.notApplicable.value, 2); + }); + + test( + // ignore: lines_longer_than_80_chars + 'statusByValue should return right index int that corresponds with the right PermissionStatus', + () { + expect(ServiceStatusValue.statusByValue(0), ServiceStatus.disabled); + expect(ServiceStatusValue.statusByValue(1), ServiceStatus.enabled); + expect(ServiceStatusValue.statusByValue(2), ServiceStatus.notApplicable); + }); + }); + + group('ServiceStatusGetters', () { + test('Getters should return true if statement is met', () { + expect(ServiceStatus.disabled.isDisabled, true); + expect(ServiceStatus.enabled.isEnabled, true); + expect(ServiceStatus.notApplicable.isNotApplicable, true); + }); + + test('Getters should return false if statement is not met', () { + expect(ServiceStatus.disabled.isEnabled, false); + expect(ServiceStatus.enabled.isDisabled, false); + expect(ServiceStatus.notApplicable.isDisabled, false); + }); + }); +}