戴余标 3 years ago
parent 35a13f0b3b
commit b9cfe590e6

@ -0,0 +1,80 @@
{
"configVersion": 2,
"packages": [
{
"name": "characters",
"rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "collection",
"rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0",
"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": "lints",
"rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1",
"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": "permission_handler_platform_interface",
"rootUri": "file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-3.7.0",
"packageUri": "lib/",
"languageVersion": "2.14"
},
{
"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": "sky_engine",
"rootUri": "file:///Users/datang/fvm/versions/3.0.0/bin/cache/pkg/sky_engine",
"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": "permission_handler_android",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "2.15"
}
],
"generated": "2022-07-29T09:47:41.527304Z",
"generator": "pub",
"generatorVersion": "2.17.0"
}

@ -0,0 +1,49 @@
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/
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/
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/
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/
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/
permission_handler_platform_interface
2.14
file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-3.7.0/
file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-3.7.0/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/
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/
permission_handler_android
2.15
file:///Users/datang/Downloads/flutter-permission-handler-master/permission_handler_android/
file:///Users/datang/Downloads/flutter-permission-handler-master/permission_handler_android/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/
2

@ -0,0 +1,300 @@
<component name="libraryTable">
<library name="Dart Packages" type="DartPackagesLibraryType">
<properties>
<option name="packageNameToDirsMap">
<entry key="async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/lib" />
</list>
</value>
</entry>
<entry key="baseflow_plugin_template">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/git/baseflow_plugin_template-0695ef43c098d75efffe5fd6212390b853f05bf7//lib" />
</list>
</value>
</entry>
<entry key="boolean_selector">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="characters">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="charcode">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
</list>
</value>
</entry>
<entry key="clock">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="collection">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
</list>
</value>
</entry>
<entry key="fake_async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0/lib" />
</list>
</value>
</entry>
<entry key="flutter">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/3.0.0/packages/flutter/lib" />
</list>
</value>
</entry>
<entry key="flutter_lints">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="flutter_test">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/3.0.0/packages/flutter_test/lib" />
</list>
</value>
</entry>
<entry key="flutter_web_plugins">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/3.0.0/packages/flutter_web_plugins/lib" />
</list>
</value>
</entry>
<entry key="js">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.4/lib" />
</list>
</value>
</entry>
<entry key="lints">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/lib" />
</list>
</value>
</entry>
<entry key="matcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/lib" />
</list>
</value>
</entry>
<entry key="material_color_utilities">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4/lib" />
</list>
</value>
</entry>
<entry key="meta">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib" />
</list>
</value>
</entry>
<entry key="path">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.8.1/lib" />
</list>
</value>
</entry>
<entry key="permission_handler_platform_interface">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-3.7.0/lib" />
</list>
</value>
</entry>
<entry key="plugin_platform_interface">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2/lib" />
</list>
</value>
</entry>
<entry key="sky_engine">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/lib" />
</list>
</value>
</entry>
<entry key="source_span">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2/lib" />
</list>
</value>
</entry>
<entry key="stack_trace">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
</list>
</value>
</entry>
<entry key="stream_channel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="string_scanner">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="term_glyph">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="test_api">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9/lib" />
</list>
</value>
</entry>
<entry key="url_launcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.1.5/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_android">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_android-6.0.17/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_ios">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_ios-6.0.17/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_linux">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-3.0.1/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_macos">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-3.0.1/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_platform_interface">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_web">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-2.0.12/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_windows">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-3.0.1/lib" />
</list>
</value>
</entry>
<entry key="vector_math">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
</list>
</value>
</entry>
</option>
</properties>
<CLASSES>
<root url="file://$USER_HOME$/.pub-cache/git/baseflow_plugin_template-0695ef43c098d75efffe5fd6212390b853f05bf7//lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.8.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-3.7.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.1.5/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_android-6.0.17/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_ios-6.0.17/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-3.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-3.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-2.0.12/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-3.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/lib" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/packages/flutter/lib" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/packages/flutter_test/lib" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/packages/flutter_web_plugins/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,27 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/async" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/cli" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/collection" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/convert" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/core" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/developer" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/ffi" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/html" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/io" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/isolate" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/js" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/js_util" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/math" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/svg" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/typed_data" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/web_audio" />
<root url="file://$USER_HOME$/fvm/default/bin/cache/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,7 @@
<component name="libraryTable">
<library name="Flutter Plugins" type="FlutterPluginsLibraryType">
<CLASSES />
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/permission_handler_android.iml" filepath="$PROJECT_DIR$/.idea/permission_handler_android.iml" />
</modules>
</component>
</project>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -0,0 +1,18 @@
# 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-07-29 17:47:41.517101.
characters:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib/
collection:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/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/
lints:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/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/
permission_handler_platform_interface:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-3.7.0/lib/
plugin_platform_interface:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2/lib/
sky_engine:file:///Users/datang/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/lib/
vector_math:file:///Users/datang/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib/
permission_handler_android:lib/

@ -0,0 +1,8 @@
# Below is a list of people and organizations that have contributed
# to the Flutter project. Names should be added to the list like so:
#
# Name/Organization <email address>
Baseflow <hello@baseflow.com>
Maurits van Beusekom <maurits@baseflow.com>

@ -0,0 +1,12 @@
## 10.0.0
* __BREAKING CHANGE__: Updated Android `compileSdkVersion` to `33` to handle the new `POST_NOTIFICATIONS` permission.
> When updating to version 10.0.0 make sure to update the `android/app/build.gradle` file and set the `compileSdkVersion` to `33`.
## 9.0.2+1
* Undoes PR [#765](https://github.com/baseflow/flutter-permission-handler/pull/765) which by mistake requests write_external_storage permission based on the target SDK instead of the actual SDK of the Android device.
## 9.0.2
* Moves Android implementation into its own package.

@ -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.

@ -0,0 +1,6 @@
<?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-11/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

@ -0,0 +1,13 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.1.1))
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true

@ -0,0 +1,40 @@
group 'com.baseflow.permissionhandler'
version '1.0'
def args = ["-Xlint:deprecation","-Xlint:unchecked","-Werror"]
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
project.getTasks().withType(JavaCompile){
options.compilerArgs.addAll(args)
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 33
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 16
}
}

@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

@ -0,0 +1 @@
rootProject.name = 'permission_handler'

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baseflow.permissionhandler">
</manifest>

@ -0,0 +1,39 @@
package com.baseflow.permissionhandler;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
final class AppSettingsManager {
@FunctionalInterface
interface OpenAppSettingsSuccessCallback {
void onSuccess(boolean appSettingsOpenedSuccessfully);
}
void openAppSettings(
Context context,
OpenAppSettingsSuccessCallback successCallback,
ErrorCallback errorCallback) {
if(context == null) {
Log.d(PermissionConstants.LOG_TAG, "Context cannot be null.");
errorCallback.onError("PermissionHandler.AppSettingsManager", "Android context cannot be null.");
return;
}
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);
successCallback.onSuccess(true);
} catch (Exception ex) {
successCallback.onSuccess(false);
}
}
}

@ -0,0 +1,6 @@
package com.baseflow.permissionhandler;
@FunctionalInterface
interface ErrorCallback {
void onError(String errorCode, String errorDescription);
}

@ -0,0 +1,104 @@
package com.baseflow.permissionhandler;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import java.util.List;
final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
private final Context applicationContext;
private final AppSettingsManager appSettingsManager;
private final PermissionManager permissionManager;
private final ServiceManager serviceManager;
MethodCallHandlerImpl(
Context applicationContext,
AppSettingsManager appSettingsManager,
PermissionManager permissionManager,
ServiceManager serviceManager) {
this.applicationContext = applicationContext;
this.appSettingsManager = appSettingsManager;
this.permissionManager = permissionManager;
this.serviceManager = serviceManager;
}
@Nullable
private Activity activity;
public void setActivity(@Nullable Activity activity) {
this.activity = activity;
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
{
switch (call.method) {
case "checkServiceStatus": {
@PermissionConstants.PermissionGroup final int permission = Integer.parseInt(call.arguments.toString());
serviceManager.checkServiceStatus(
permission,
applicationContext,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
break;
}
case "checkPermissionStatus": {
@PermissionConstants.PermissionGroup final int permission = Integer.parseInt(call.arguments.toString());
permissionManager.checkPermissionStatus(
permission,
applicationContext,
result::success);
break;
}
case "requestPermissions":
final List<Integer> permissions = call.arguments();
permissionManager.requestPermissions(
permissions,
activity,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
break;
case "shouldShowRequestPermissionRationale": {
@PermissionConstants.PermissionGroup final int permission = Integer.parseInt(call.arguments.toString());
permissionManager.shouldShowRequestPermissionRationale(
permission,
activity,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
break;
}
case "openAppSettings":
appSettingsManager.openAppSettings(
applicationContext,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
break;
default:
result.notImplemented();
break;
}
}
}

@ -0,0 +1,118 @@
package com.baseflow.permissionhandler;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
final class PermissionConstants {
static final String LOG_TAG = "permissions_handler";
static final int PERMISSION_CODE = 24;
static final int PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS = 209;
static final int PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE = 210;
static final int PERMISSION_CODE_SYSTEM_ALERT_WINDOW = 211;
static final int PERMISSION_CODE_REQUEST_INSTALL_PACKAGES = 212;
static final int PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY = 213;
//PERMISSION_GROUP
static final int PERMISSION_GROUP_CALENDAR = 0;
static final int PERMISSION_GROUP_CAMERA = 1;
static final int PERMISSION_GROUP_CONTACTS = 2;
static final int PERMISSION_GROUP_LOCATION = 3;
static final int PERMISSION_GROUP_LOCATION_ALWAYS = 4;
static final int PERMISSION_GROUP_LOCATION_WHEN_IN_USE = 5;
static final int PERMISSION_GROUP_MEDIA_LIBRARY = 6;
static final int PERMISSION_GROUP_MICROPHONE = 7;
static final int PERMISSION_GROUP_PHONE = 8;
static final int PERMISSION_GROUP_PHOTOS = 9;
static final int PERMISSION_GROUP_PHOTOS_ADD_ONLY = 10;
static final int PERMISSION_GROUP_REMINDERS = 11;
static final int PERMISSION_GROUP_SENSORS = 12;
static final int PERMISSION_GROUP_SMS = 13;
static final int PERMISSION_GROUP_SPEECH = 14;
static final int PERMISSION_GROUP_STORAGE = 15;
static final int PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS = 16;
static final int PERMISSION_GROUP_NOTIFICATION = 17;
static final int PERMISSION_GROUP_ACCESS_MEDIA_LOCATION = 18;
static final int PERMISSION_GROUP_ACTIVITY_RECOGNITION = 19;
static final int PERMISSION_GROUP_UNKNOWN = 20;
static final int PERMISSION_GROUP_BLUETOOTH = 21;
static final int PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE = 22;
static final int PERMISSION_GROUP_SYSTEM_ALERT_WINDOW = 23;
static final int PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES = 24;
static final int PERMISSION_GROUP_APP_TRACK_TRANSPARENCY = 25;
static final int PERMISSION_GROUP_CRITICAL_ALERTS = 26;
static final int PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY = 27;
static final int PERMISSION_GROUP_BLUETOOTH_SCAN = 28;
static final int PERMISSION_GROUP_BLUETOOTH_ADVERTISE = 29;
static final int PERMISSION_GROUP_BLUETOOTH_CONNECT = 30;
static final int PERMISSION_GROUP_CALL_LOG = 31;
@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_IGNORE_BATTERY_OPTIMIZATIONS,
PERMISSION_GROUP_NOTIFICATION,
PERMISSION_GROUP_ACCESS_MEDIA_LOCATION,
PERMISSION_GROUP_ACTIVITY_RECOGNITION,
PERMISSION_GROUP_UNKNOWN,
PERMISSION_GROUP_BLUETOOTH,
PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE,
PERMISSION_GROUP_SYSTEM_ALERT_WINDOW,
PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES,
PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY,
PERMISSION_GROUP_BLUETOOTH_SCAN,
PERMISSION_GROUP_BLUETOOTH_ADVERTISE,
PERMISSION_GROUP_BLUETOOTH_CONNECT,
PERMISSION_GROUP_CALL_LOG
})
@interface PermissionGroup {
}
//PERMISSION_STATUS
static final int PERMISSION_STATUS_DENIED = 0;
static final int PERMISSION_STATUS_GRANTED = 1;
static final int PERMISSION_STATUS_RESTRICTED = 2;
static final int PERMISSION_STATUS_LIMITED = 3;
static final int PERMISSION_STATUS_NEVER_ASK_AGAIN = 4;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PERMISSION_STATUS_DENIED,
PERMISSION_STATUS_GRANTED,
PERMISSION_STATUS_RESTRICTED,
PERMISSION_STATUS_LIMITED,
PERMISSION_STATUS_NEVER_ASK_AGAIN
})
@interface PermissionStatus {
}
//SERVICE_STATUS
static final int SERVICE_STATUS_DISABLED = 0;
static final int SERVICE_STATUS_ENABLED = 1;
static final int SERVICE_STATUS_NOT_APPLICABLE = 2;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SERVICE_STATUS_DISABLED,
SERVICE_STATUS_ENABLED,
SERVICE_STATUS_NOT_APPLICABLE
})
@interface ServiceStatus {
}
}

@ -0,0 +1,154 @@
package com.baseflow.permissionhandler;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
/**
* Platform implementation of the permission_handler Flutter plugin.
*
* <p>Instantiate this in an add to app scenario to gracefully handle activity and context changes.
* See {@code com.example.permissionhandlerexample.MainActivity} for an example.
*
* <p>Call {@link #registerWith(io.flutter.plugin.common.PluginRegistry.Registrar)} to register an
* implementation of this that uses the stable {@code io.flutter.plugin.common} package.
*/
public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAware {
private final PermissionManager permissionManager;
private MethodChannel methodChannel;
@SuppressWarnings("deprecation")
@Nullable private io.flutter.plugin.common.PluginRegistry.Registrar pluginRegistrar;
@Nullable private ActivityPluginBinding pluginBinding;
@Nullable
private MethodCallHandlerImpl methodCallHandler;
public PermissionHandlerPlugin() {
this.permissionManager = new PermissionManager();
}
/**
* Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
* package.
*
* <p>Calling this automatically initializes the plugin. However plugins initialized this way
* won't react to changes in activity or context, unlike {@link PermissionHandlerPlugin}.
*/
@SuppressWarnings("deprecation")
public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
final PermissionHandlerPlugin plugin = new PermissionHandlerPlugin();
plugin.pluginRegistrar = registrar;
plugin.registerListeners();
plugin.startListening(registrar.context(), registrar.messenger());
if (registrar.activeContext() instanceof Activity) {
plugin.startListeningToActivity(
registrar.activity()
);
}
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
startListening(
binding.getApplicationContext(),
binding.getBinaryMessenger()
);
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
stopListening();
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
startListeningToActivity(
binding.getActivity()
);
this.pluginBinding = binding;
registerListeners();
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
onAttachedToActivity(binding);
}
@Override
public void onDetachedFromActivity() {
stopListeningToActivity();
deregisterListeners();
}
@Override
public void onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity();
}
private void startListening(Context applicationContext, BinaryMessenger messenger) {
methodChannel = new MethodChannel(
messenger,
"flutter.baseflow.com/permissions/methods");
methodCallHandler = new MethodCallHandlerImpl(
applicationContext,
new AppSettingsManager(),
this.permissionManager,
new ServiceManager()
);
methodChannel.setMethodCallHandler(methodCallHandler);
}
private void stopListening() {
methodChannel.setMethodCallHandler(null);
methodChannel = null;
methodCallHandler = null;
}
private void startListeningToActivity(
Activity activity
) {
if (methodCallHandler != null) {
methodCallHandler.setActivity(activity);
}
}
private void stopListeningToActivity() {
if (methodCallHandler != null) {
methodCallHandler.setActivity(null);
}
}
private void registerListeners() {
if (this.pluginRegistrar != null) {
this.pluginRegistrar.addActivityResultListener(this.permissionManager);
this.pluginRegistrar.addRequestPermissionsResultListener(this.permissionManager);
} else if (pluginBinding != null) {
this.pluginBinding.addActivityResultListener(this.permissionManager);
this.pluginBinding.addRequestPermissionsResultListener(this.permissionManager);
}
}
private void deregisterListeners() {
if (this.pluginBinding != null) {
this.pluginBinding.removeActivityResultListener(this.permissionManager);
this.pluginBinding.removeRequestPermissionsResultListener(this.permissionManager);
}
}
}

@ -0,0 +1,497 @@
package com.baseflow.permissionhandler;
import android.Manifest;
import android.app.Activity;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
final class PermissionManager implements PluginRegistry.ActivityResultListener, PluginRegistry.RequestPermissionsResultListener {
@Nullable
private RequestPermissionsSuccessCallback successCallback;
@Nullable
private Activity activity;
private Map<Integer, Integer> requestResults;
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS &&
requestCode != PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE &&
requestCode != PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW &&
requestCode != PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES &&
requestCode != PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY) {
return false;
}
int status = resultCode == Activity.RESULT_OK
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
int permission;
if (requestCode == PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS) {
permission = PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS;
} else if (requestCode == PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
status = Environment.isExternalStorageManager()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
} else {
return false;
}
permission = PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE;
} else if (requestCode == PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
status = Settings.canDrawOverlays(activity)
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW;
} else {
return false;
}
} else if (requestCode == PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
status = activity.getPackageManager().canRequestPackageInstalls()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES;
} else {
return false;
}
} else if (requestCode == PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager notificationManager = (NotificationManager) activity.getSystemService(Application.NOTIFICATION_SERVICE);
status = notificationManager.isNotificationPolicyAccessGranted()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY;
} else {
return false;
}
} else {
return false;
}
HashMap<Integer, Integer> results = new HashMap<>();
results.put(permission, status);
successCallback.onSuccess(results);
return true;
}
@Override
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode != PermissionConstants.PERMISSION_CODE) {
ongoing = false;
return false;
}
if (requestResults == null) {
return false;
}
for (int i = 0; i < permissions.length; i++) {
final String permissionName = permissions[i];
@PermissionConstants.PermissionGroup final int permission =
PermissionUtils.parseManifestName(permissionName);
if (permission == PermissionConstants.PERMISSION_GROUP_UNKNOWN)
continue;
final int result = grantResults[i];
if (permission == PermissionConstants.PERMISSION_GROUP_MICROPHONE) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_MICROPHONE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_MICROPHONE,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_SPEECH)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_SPEECH,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_CALL_LOG)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_CALL_LOG,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS,
permissionStatus);
}
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
permissionStatus);
}
requestResults.put(permission, permissionStatus);
} else if (!requestResults.containsKey(permission)) {
requestResults.put(
permission,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
PermissionUtils.updatePermissionShouldShowStatus(this.activity, permission);
}
this.successCallback.onSuccess(requestResults);
ongoing = false;
return true;
}
@FunctionalInterface
interface RequestPermissionsSuccessCallback {
void onSuccess(Map<Integer, Integer> results);
}
@FunctionalInterface
interface CheckPermissionsSuccessCallback {
void onSuccess(@PermissionConstants.PermissionStatus int permissionStatus);
}
@FunctionalInterface
interface ShouldShowRequestPermissionRationaleSuccessCallback {
void onSuccess(boolean shouldShowRequestPermissionRationale);
}
private boolean ongoing = false;
void checkPermissionStatus(
@PermissionConstants.PermissionGroup int permission,
Context context,
CheckPermissionsSuccessCallback successCallback) {
successCallback.onSuccess(determinePermissionStatus(
permission,
context));
}
void requestPermissions(
List<Integer> permissions,
Activity activity,
RequestPermissionsSuccessCallback successCallback,
ErrorCallback errorCallback) {
if (ongoing) {
errorCallback.onError(
"PermissionHandler.PermissionManager",
"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).");
return;
}
if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity.");
errorCallback.onError(
"PermissionHandler.PermissionManager",
"Unable to detect current Android Activity.");
return;
}
this.successCallback = successCallback;
this.activity = activity;
this.requestResults = new HashMap<>();
ArrayList<String> permissionsToRequest = new ArrayList<>();
for (Integer permission : permissions) {
@PermissionConstants.PermissionStatus final int permissionStatus = determinePermissionStatus(permission, activity);
if (permissionStatus == PermissionConstants.PERMISSION_STATUS_GRANTED) {
if (!requestResults.containsKey(permission)) {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_GRANTED);
}
continue;
}
final List<String> names = PermissionUtils.getManifestNames(activity, 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 (!requestResults.containsKey(permission)) {
// On Android below M, the android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS flag in AndroidManifest.xml
// may be ignored and not visible to the App as it's a new permission setting as a whole.
if (permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_RESTRICTED);
} else {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_DENIED);
}
// On Android below R, the android.permission.MANAGE_EXTERNAL_STORAGE flag in AndroidManifest.xml
// may be ignored and not visible to the App as it's a new permission setting as a whole.
if (permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_RESTRICTED);
} else {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_DENIED);
}
}
continue;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
executeIntent(
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) {
executeIntent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW) {
executeIntent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
PermissionConstants.PERMISSION_CODE_SYSTEM_ALERT_WINDOW);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && permission == PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES) {
executeIntent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
PermissionConstants.PERMISSION_CODE_REQUEST_INSTALL_PACKAGES);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY) {
executeSimpleIntent(
Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS,
PermissionConstants.PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY);
} else {
permissionsToRequest.addAll(names);
}
}
final String[] requestPermissions = permissionsToRequest.toArray(new String[0]);
if (permissionsToRequest.size() > 0) {
ongoing = true;
ActivityCompat.requestPermissions(
activity,
requestPermissions,
PermissionConstants.PERMISSION_CODE);
} else {
ongoing = false;
if (requestResults.size() > 0) {
successCallback.onSuccess(requestResults);
}
}
}
@PermissionConstants.PermissionStatus
private int determinePermissionStatus(
@PermissionConstants.PermissionGroup int permission,
Context context) {
if (permission == PermissionConstants.PERMISSION_GROUP_NOTIFICATION) {
return checkNotificationPermissionStatus(context);
}
if (permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH) {
return checkBluetoothPermissionStatus(context);
}
if (permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_CONNECT
|| permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_SCAN
|| permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH_ADVERTISE){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
return checkBluetoothPermissionStatus(context);
}
}
final List<String> names = PermissionUtils.getManifestNames(context, permission);
if (names == null) {
Log.d(PermissionConstants.LOG_TAG, "No android specific permissions needed for: " + permission);
return PermissionConstants.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(PermissionConstants.LOG_TAG, "No permissions found in manifest for: " + names + permission);
// On Android below M, the android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS flag in AndroidManifest.xml
// may be ignored and not visible to the App as it's a new permission setting as a whole.
if (permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
}
// On Android below R, the android.permission.MANAGE_EXTERNAL_STORAGE flag in AndroidManifest.xml
// may be ignored and not visible to the App as it's a new permission setting as a whole.
if (permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
}
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
final boolean targetsMOrHigher = context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M;
for (String name : names) {
// Only handle them if the client app actually targets a API level greater than M.
if (targetsMOrHigher) {
if (permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
// PowerManager.isIgnoringBatteryOptimizations has been included in Android M first.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (pm != null && pm.isIgnoringBatteryOptimizations(packageName)) {
return PermissionConstants.PERMISSION_STATUS_GRANTED;
} else {
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
} else {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
}
if (permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
return Environment.isExternalStorageManager()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
if (permission == PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Settings.canDrawOverlays(context)
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
}
if (permission == PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return context.getPackageManager().canRequestPackageInstalls()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
}
if (permission == PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Application.NOTIFICATION_SERVICE);
return notificationManager.isNotificationPolicyAccessGranted()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
}
final int permissionStatus = ContextCompat.checkSelfPermission(context, name);
if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
}
}
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
private void executeIntent(String action, int requestCode) {
String packageName = activity.getPackageName();
Intent intent = new Intent();
intent.setAction(action);
intent.setData(Uri.parse("package:" + packageName));
activity.startActivityForResult(intent, requestCode);
}
private void executeSimpleIntent(String action, int requestCode) {
activity.startActivityForResult(new Intent(action), requestCode);
}
void shouldShowRequestPermissionRationale(
int permission,
Activity activity,
ShouldShowRequestPermissionRationaleSuccessCallback successCallback,
ErrorCallback errorCallback) {
if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity.");
errorCallback.onError(
"PermissionHandler.PermissionManager",
"Unable to detect current Android Activity.");
return;
}
List<String> names = PermissionUtils.getManifestNames(activity, permission);
// if isn't an android specific group then go ahead and return false;
if (names == null) {
Log.d(PermissionConstants.LOG_TAG, "No android specific permissions needed for: " + permission);
successCallback.onSuccess(false);
return;
}
if (names.isEmpty()) {
Log.d(PermissionConstants.LOG_TAG, "No permissions found in manifest for: " + permission + " no need to show request rationale");
successCallback.onSuccess(false);
return;
}
successCallback.onSuccess(ActivityCompat.shouldShowRequestPermissionRationale(activity, names.get(0)));
}
private int checkNotificationPermissionStatus(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
boolean isGranted = manager.areNotificationsEnabled();
if (isGranted) {
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
return context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
private int checkBluetoothPermissionStatus(Context context) {
List<String> names = PermissionUtils.getManifestNames(context, PermissionConstants.PERMISSION_GROUP_BLUETOOTH);
boolean missingInManifest = names == null || names.isEmpty();
if (missingInManifest) {
Log.d(PermissionConstants.LOG_TAG, "Bluetooth permission missing in manifest");
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
}

@ -0,0 +1,407 @@
package com.baseflow.permissionhandler;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PermissionUtils {
@PermissionConstants.PermissionGroup
static int parseManifestName(String permission) {
switch (permission) {
case Manifest.permission.READ_CALENDAR:
case Manifest.permission.WRITE_CALENDAR:
return PermissionConstants.PERMISSION_GROUP_CALENDAR;
case Manifest.permission.CAMERA:
return PermissionConstants.PERMISSION_GROUP_CAMERA;
case Manifest.permission.READ_CONTACTS:
case Manifest.permission.WRITE_CONTACTS:
case Manifest.permission.GET_ACCOUNTS:
return PermissionConstants.PERMISSION_GROUP_CONTACTS;
case Manifest.permission.ACCESS_BACKGROUND_LOCATION:
return PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS;
case Manifest.permission.ACCESS_COARSE_LOCATION:
case Manifest.permission.ACCESS_FINE_LOCATION:
return PermissionConstants.PERMISSION_GROUP_LOCATION;
case Manifest.permission.RECORD_AUDIO:
return PermissionConstants.PERMISSION_GROUP_MICROPHONE;
case Manifest.permission.READ_PHONE_STATE:
case Manifest.permission.READ_PHONE_NUMBERS:
case Manifest.permission.CALL_PHONE:
case Manifest.permission.ADD_VOICEMAIL:
case Manifest.permission.USE_SIP:
case Manifest.permission.BIND_CALL_REDIRECTION_SERVICE:
return PermissionConstants.PERMISSION_GROUP_PHONE;
case Manifest.permission.BODY_SENSORS:
return PermissionConstants.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 PermissionConstants.PERMISSION_GROUP_SMS;
case Manifest.permission.READ_EXTERNAL_STORAGE:
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
return PermissionConstants.PERMISSION_GROUP_STORAGE;
case Manifest.permission.ACCESS_MEDIA_LOCATION:
return PermissionConstants.PERMISSION_GROUP_ACCESS_MEDIA_LOCATION;
case Manifest.permission.ACTIVITY_RECOGNITION:
return PermissionConstants.PERMISSION_GROUP_ACTIVITY_RECOGNITION;
case Manifest.permission.MANAGE_EXTERNAL_STORAGE:
return PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE;
case Manifest.permission.SYSTEM_ALERT_WINDOW:
return PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW;
case Manifest.permission.REQUEST_INSTALL_PACKAGES:
return PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES;
case Manifest.permission.ACCESS_NOTIFICATION_POLICY:
return PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY;
case Manifest.permission.BLUETOOTH_SCAN:
return PermissionConstants.PERMISSION_GROUP_BLUETOOTH_SCAN;
case Manifest.permission.BLUETOOTH_ADVERTISE:
return PermissionConstants.PERMISSION_GROUP_BLUETOOTH_ADVERTISE;
case Manifest.permission.BLUETOOTH_CONNECT:
return PermissionConstants.PERMISSION_GROUP_BLUETOOTH_CONNECT;
case Manifest.permission.POST_NOTIFICATIONS:
return PermissionConstants.PERMISSION_GROUP_NOTIFICATION;
case Manifest.permission.READ_CALL_LOG:
case Manifest.permission.WRITE_CALL_LOG:
return PermissionConstants.PERMISSION_GROUP_CALL_LOG;
default:
return PermissionConstants.PERMISSION_GROUP_UNKNOWN;
}
}
static List<String> getManifestNames(Context context, @PermissionConstants.PermissionGroup int permission) {
final ArrayList<String> permissionNames = new ArrayList<>();
switch (permission) {
case PermissionConstants.PERMISSION_GROUP_CALENDAR:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_CALENDAR))
permissionNames.add(Manifest.permission.READ_CALENDAR);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CALENDAR))
permissionNames.add(Manifest.permission.WRITE_CALENDAR);
break;
case PermissionConstants.PERMISSION_GROUP_CAMERA:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.CAMERA))
permissionNames.add(Manifest.permission.CAMERA);
break;
case PermissionConstants.PERMISSION_GROUP_CONTACTS:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_CONTACTS))
permissionNames.add(Manifest.permission.READ_CONTACTS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CONTACTS))
permissionNames.add(Manifest.permission.WRITE_CONTACTS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.GET_ACCOUNTS))
permissionNames.add(Manifest.permission.GET_ACCOUNTS);
break;
case PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS:
// Note that the LOCATION_ALWAYS will deliberately fallthrough to the LOCATION
// case on pre Android Q devices. The ACCESS_BACKGROUND_LOCATION permission was only
// introduced in Android Q, before it should be treated as the ACCESS_COARSE_LOCATION or
// ACCESS_FINE_LOCATION.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_BACKGROUND_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
break;
}
case PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE:
case PermissionConstants.PERMISSION_GROUP_LOCATION:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_COARSE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_COARSE_LOCATION);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_FINE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_FINE_LOCATION);
break;
case PermissionConstants.PERMISSION_GROUP_SPEECH:
case PermissionConstants.PERMISSION_GROUP_MICROPHONE:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECORD_AUDIO))
permissionNames.add(Manifest.permission.RECORD_AUDIO);
break;
case PermissionConstants.PERMISSION_GROUP_PHONE:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_PHONE_STATE))
permissionNames.add(Manifest.permission.READ_PHONE_STATE);
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_PHONE_NUMBERS)) {
permissionNames.add(Manifest.permission.READ_PHONE_NUMBERS);
}
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.CALL_PHONE))
permissionNames.add(Manifest.permission.CALL_PHONE);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ADD_VOICEMAIL))
permissionNames.add(Manifest.permission.ADD_VOICEMAIL);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.USE_SIP))
permissionNames.add(Manifest.permission.USE_SIP);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && hasPermissionInManifest(context, permissionNames, Manifest.permission.BIND_CALL_REDIRECTION_SERVICE))
permissionNames.add(Manifest.permission.BIND_CALL_REDIRECTION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasPermissionInManifest(context, permissionNames, Manifest.permission.ANSWER_PHONE_CALLS))
permissionNames.add(Manifest.permission.ANSWER_PHONE_CALLS);
break;
case PermissionConstants.PERMISSION_GROUP_CALL_LOG:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_CALL_LOG))
permissionNames.add(Manifest.permission.READ_CALL_LOG);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CALL_LOG))
permissionNames.add(Manifest.permission.WRITE_CALL_LOG);
break;
case PermissionConstants.PERMISSION_GROUP_SENSORS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.BODY_SENSORS)) {
permissionNames.add(Manifest.permission.BODY_SENSORS);
}
}
break;
case PermissionConstants.PERMISSION_GROUP_SMS:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.SEND_SMS))
permissionNames.add(Manifest.permission.SEND_SMS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECEIVE_SMS))
permissionNames.add(Manifest.permission.RECEIVE_SMS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_SMS))
permissionNames.add(Manifest.permission.READ_SMS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECEIVE_WAP_PUSH))
permissionNames.add(Manifest.permission.RECEIVE_WAP_PUSH);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECEIVE_MMS))
permissionNames.add(Manifest.permission.RECEIVE_MMS);
break;
case PermissionConstants.PERMISSION_GROUP_STORAGE:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q && Environment.isExternalStorageLegacy())) {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
break;
}
break;
case PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))
permissionNames.add(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
break;
case PermissionConstants.PERMISSION_GROUP_ACCESS_MEDIA_LOCATION:
// The ACCESS_MEDIA_LOCATION permission is introduced in Android Q, meaning we should
// not handle permissions on pre Android Q devices.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
return null;
if(hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_MEDIA_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
break;
case PermissionConstants.PERMISSION_GROUP_ACTIVITY_RECOGNITION:
// The ACTIVITY_RECOGNITION permission is introduced in Android Q, meaning we should
// not handle permissions on pre Android Q devices.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
return null;
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACTIVITY_RECOGNITION))
permissionNames.add(Manifest.permission.ACTIVITY_RECOGNITION);
break;
case PermissionConstants.PERMISSION_GROUP_BLUETOOTH:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.BLUETOOTH))
permissionNames.add(Manifest.permission.BLUETOOTH);
break;
case PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE:
// The MANAGE_EXTERNAL_STORAGE permission is introduced in Android R, meaning we should
// not handle permissions on pre Android R devices.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasPermissionInManifest(context, permissionNames, Manifest.permission.MANAGE_EXTERNAL_STORAGE ))
permissionNames.add(Manifest.permission.MANAGE_EXTERNAL_STORAGE);
break;
case PermissionConstants.PERMISSION_GROUP_SYSTEM_ALERT_WINDOW:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.SYSTEM_ALERT_WINDOW ))
permissionNames.add(Manifest.permission.SYSTEM_ALERT_WINDOW);
break;
case PermissionConstants.PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES:
// The REQUEST_INSTALL_PACKAGES permission is introduced in Android M, meaning we should
// not handle permissions on pre Android M devices.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.REQUEST_INSTALL_PACKAGES ))
permissionNames.add(Manifest.permission.REQUEST_INSTALL_PACKAGES);
break;
case PermissionConstants.PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY:
// The REQUEST_NOTIFICATION_POLICY permission is introduced in Android M, meaning we should
// not handle permissions on pre Android M devices.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_NOTIFICATION_POLICY ))
permissionNames.add(Manifest.permission.ACCESS_NOTIFICATION_POLICY);
break;
case PermissionConstants.PERMISSION_GROUP_BLUETOOTH_SCAN: {
// The BLUETOOTH_SCAN permission is introduced in Android S, meaning we should
// not handle permissions on pre Android S devices.
String result = determineBluetoothPermission(context, Manifest.permission.BLUETOOTH_SCAN);
if (result != null) {
permissionNames.add(result);
}
break;
}
case PermissionConstants.PERMISSION_GROUP_BLUETOOTH_ADVERTISE: {
// The BLUETOOTH_ADVERTISE permission is introduced in Android S, meaning we should
// not handle permissions on pre Android S devices.
String result = determineBluetoothPermission(context, Manifest.permission.BLUETOOTH_ADVERTISE);
if (result != null) {
permissionNames.add(result);
}
break;
}
case PermissionConstants.PERMISSION_GROUP_BLUETOOTH_CONNECT: {
// The BLUETOOTH_CONNECT permission is introduced in Android S, meaning we should
// not handle permissions on pre Android S devices.
String result = determineBluetoothPermission(context, Manifest.permission.BLUETOOTH_CONNECT);
if (result != null) {
permissionNames.add(result);
}
break;
}
case PermissionConstants.PERMISSION_GROUP_NOTIFICATION:
// The POST_NOTIFICATIONS permission is introduced in Android 13, meaning we should
// not handle permissions on pre Android 13 devices.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && hasPermissionInManifest(context, permissionNames, Manifest.permission.POST_NOTIFICATIONS ))
permissionNames.add(Manifest.permission.POST_NOTIFICATIONS);
break;
case PermissionConstants.PERMISSION_GROUP_MEDIA_LIBRARY:
case PermissionConstants.PERMISSION_GROUP_PHOTOS:
case PermissionConstants.PERMISSION_GROUP_REMINDERS:
case PermissionConstants.PERMISSION_GROUP_UNKNOWN:
return null;
}
return permissionNames;
}
private static boolean hasPermissionInManifest(Context context, ArrayList<String> confirmedPermissions, String permission) {
try {
if (confirmedPermissions != null) {
for (String r : confirmedPermissions) {
if (r.equals(permission)) {
return true;
}
}
}
if (context == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity or App Context.");
return false;
}
PackageInfo info = getPackageInfo(context);
if (info == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to get Package info, will not be able to determine permissions to request.");
return false;
}
confirmedPermissions = new ArrayList<>(Arrays.asList(info.requestedPermissions));
for (String r : confirmedPermissions) {
if (r.equals(permission)) {
return true;
}
}
} catch (Exception ex) {
Log.d(PermissionConstants.LOG_TAG, "Unable to check manifest for permission: ", ex);
}
return false;
}
@PermissionConstants.PermissionStatus
static int toPermissionStatus(final Activity activity, final String permissionName, int grantResult) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && PermissionUtils.isNeverAskAgainSelected(activity, permissionName)
? PermissionConstants.PERMISSION_STATUS_NEVER_ASK_AGAIN
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
static void updatePermissionShouldShowStatus(final Activity activity, @PermissionConstants.PermissionGroup int permission) {
if (activity == null) {
return;
}
List<String> names = getManifestNames(activity, permission);
if (names == null || names.isEmpty()) {
return;
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
static boolean isNeverAskAgainSelected(final Activity activity, final String name) {
if (activity == null) {
return false;
}
final boolean shouldShowRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, name);
return !shouldShowRequestPermissionRationale;
}
private static String determineBluetoothPermission(Context context, String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && hasPermissionInManifest(context, null, permission )) {
return permission;
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if(hasPermissionInManifest(context, null, Manifest.permission.ACCESS_FINE_LOCATION)){
return Manifest.permission.ACCESS_FINE_LOCATION;
} else if (hasPermissionInManifest(context, null, Manifest.permission.ACCESS_COARSE_LOCATION)){
return Manifest.permission.ACCESS_COARSE_LOCATION;
}
return null;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && hasPermissionInManifest(context, null, Manifest.permission.ACCESS_FINE_LOCATION)) {
return Manifest.permission.ACCESS_FINE_LOCATION;
}
return null;
}
// Suppress deprecation warnings since its purpose is to support to be backwards compatible with
// pre TIRAMISU versions of Android
@SuppressWarnings("deprecation")
private static PackageInfo getPackageInfo(Context context) throws PackageManager.NameNotFoundException {
final PackageManager pm = context.getPackageManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return pm.getPackageInfo(context.getPackageName(), PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS));
} else {
return pm.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
}
}
}

@ -0,0 +1,177 @@
package com.baseflow.permissionhandler;
import static android.content.Context.BLUETOOTH_SERVICE;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import java.util.List;
final class ServiceManager {
@FunctionalInterface
interface SuccessCallback {
void onSuccess(@PermissionConstants.ServiceStatus int serviceStatus);
}
void checkServiceStatus(
int permission,
Context context,
SuccessCallback successCallback,
ErrorCallback errorCallback) {
if(context == null) {
Log.d(PermissionConstants.LOG_TAG, "Context cannot be null.");
errorCallback.onError("PermissionHandler.ServiceManager", "Android context cannot be null.");
return;
}
if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION ||
permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS ||
permission == PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE) {
final int serviceStatus = isLocationServiceEnabled(context)
? PermissionConstants.SERVICE_STATUS_ENABLED
: PermissionConstants.SERVICE_STATUS_DISABLED;
successCallback.onSuccess(serviceStatus);
return;
}
if(permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH){
final int serviceStatus = isBluetoothServiceEnabled(context)
? PermissionConstants.SERVICE_STATUS_ENABLED
: PermissionConstants.SERVICE_STATUS_DISABLED;
successCallback.onSuccess(serviceStatus);
}
if (permission == PermissionConstants.PERMISSION_GROUP_PHONE) {
PackageManager pm = context.getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
return;
}
TelephonyManager telephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager == null || telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
return;
}
List<ResolveInfo> callAppsList = getCallAppsList(pm);
if (callAppsList.isEmpty()) {
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
return;
}
if (telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) {
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_DISABLED);
return;
}
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_ENABLED);
return;
}
if (permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
final int serviceStatus = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
? PermissionConstants.SERVICE_STATUS_ENABLED
: PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
successCallback.onSuccess(serviceStatus);
return;
}
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
}
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) {
return isLocationServiceEnabledKitKat(context);
} else {
return isLocationServiceEnablePreKitKat(context);
}
}
// Suppress deprecation warnings since its purpose is to support to be backwards compatible with
// pre Pie versions of Android.
@SuppressWarnings("deprecation")
private static boolean isLocationServiceEnabledKitKat(Context context)
{
if (Build.VERSION.SDK_INT < VERSION_CODES.KITKAT) {
return false;
}
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;
}
// Suppress deprecation warnings since its purpose is to support to be backwards compatible with
// pre KitKat versions of Android.
@SuppressWarnings("deprecation")
private static boolean isLocationServiceEnablePreKitKat(Context context)
{
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT)
return false;
final String locationProviders = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
// Suppress deprecation warnings since its purpose is to support to be backwards compatible with
// pre S versions of Android
@SuppressWarnings("deprecation")
private boolean isBluetoothServiceEnabled(Context context) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
return BluetoothAdapter.getDefaultAdapter().isEnabled();
}
BluetoothManager manager = (BluetoothManager) context.getSystemService(BLUETOOTH_SERVICE);
final BluetoothAdapter adapter = manager.getAdapter();
return adapter.isEnabled();
}
// Suppress deprecation warnings since its purpose is to support to be backwards compatible with
// pre TIRAMISU versions of Android
@SuppressWarnings("deprecation")
private List<ResolveInfo> getCallAppsList(PackageManager pm) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:123123"));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return pm.queryIntentActivities(callIntent, PackageManager.ResolveInfoFlags.of(0));
} else {
return pm.queryIntentActivities(callIntent, 0);
}
}
}

@ -0,0 +1,10 @@
# 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: app

@ -0,0 +1,16 @@
# permission_handler_example
Demonstrates how to use the permission_handler plugin.
## Getting Started
This project is a starting point for a Flutter application.
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.

@ -0,0 +1,13 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true

@ -0,0 +1,55 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 33
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.baseflow.permissionhandler.example"
minSdkVersion 16
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baseflow.permissionhandler.example">
<!-- 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>

@ -0,0 +1,103 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.baseflow.permissionhandler.example">
<!--
Internet permissions do not affect the `permission_handler` plugin, but are required if your app needs access to
the internet.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Permissions options for the `contacts` group -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- Permissions options for the `storage` group -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Permissions options for the `camera` group -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- Permissions options for the `sms` group -->
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<!-- Permissions options for the `phone` group -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
<uses-permission android:name="android.permission.USE_SIP"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
<!-- Permissions options for the `calendar` group -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<!-- Permissions options for the `location` group -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- Permissions options for the `microphone` or `speech` group -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Permissions options for the `sensors` group -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- Permissions options for the `accessMediaLocation` group -->
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<!-- Permissions options for the `activityRecognition` group -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<!-- Permissions options for the `ignoreBatteryOptimizations` group -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- Permissions options for the `bluetooth` group -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Permissions options for the `manage external storage` group -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<!-- Permissions options for the `system alert windows` group -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- Permissions options for the `request install packages` group -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- Permissions options for the `access notification policy` group -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
<application
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:label="permission_handler_example"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity android:name="io.flutter.embedding.android.FlutterActivity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

@ -0,0 +1,6 @@
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>

@ -0,0 +1,52 @@
<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"/>
<!-- Permissions options for the `contacts` group -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- Permissions options for the `storage` group -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Permissions options for the `camera` group -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- Permissions options for the `sms` group -->
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<!-- Permissions options for the `phone` group -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
<uses-permission android:name="android.permission.USE_SIP"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<!-- Permissions options for the `calendar` group -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<!-- Permissions options for the `location` group -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Permissions options for the `microphone` or `speech` group -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Permissions options for the `sensors` group -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- Permissions options for the `access notification policy` group -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
</manifest>

@ -0,0 +1,27 @@
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

@ -0,0 +1,15 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}

@ -0,0 +1,141 @@
import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
void main() {
runApp(BaseflowPluginExample(
pluginName: 'Permission Handler',
githubURL: 'https://github.com/Baseflow/flutter-permission-handler',
pubDevURL: 'https://pub.dev/packages/permission_handler',
pages: [PermissionHandlerWidget.createPage()]));
}
///Defines the main theme color
final MaterialColor themeMaterialColor =
BaseflowPluginExample.createMaterialColor(
const Color.fromRGBO(48, 49, 60, 1));
/// A Flutter application demonstrating the functionality of this plugin
class PermissionHandlerWidget extends StatefulWidget {
/// Create a page containing the functionality of this plugin
static ExamplePage createPage() {
return ExamplePage(
Icons.location_on, (context) => PermissionHandlerWidget());
}
@override
_PermissionHandlerWidgetState createState() =>
_PermissionHandlerWidgetState();
}
class _PermissionHandlerWidgetState extends State<PermissionHandlerWidget> {
@override
Widget build(BuildContext context) {
return Center(
child: ListView(
children: Permission.values
.where((permission) {
return permission != Permission.unknown &&
permission != Permission.mediaLibrary &&
permission != Permission.photos &&
permission != Permission.photosAddOnly &&
permission != Permission.reminders &&
permission != Permission.appTrackingTransparency &&
permission != Permission.criticalAlerts;
})
.map((permission) => PermissionWidget(permission))
.toList()),
);
}
}
/// Permission widget containing information about the passed [Permission]
class PermissionWidget extends StatefulWidget {
/// Constructs a [PermissionWidget] for the supplied [Permission]
const PermissionWidget(this._permission);
final Permission _permission;
@override
_PermissionState createState() => _PermissionState(_permission);
}
class _PermissionState extends State<PermissionWidget> {
_PermissionState(this._permission);
final Permission _permission;
final PermissionHandlerPlatform _permissionHandler =
PermissionHandlerPlatform.instance;
PermissionStatus _permissionStatus = PermissionStatus.denied;
@override
void initState() {
super.initState();
_listenForPermissionStatus();
}
void _listenForPermissionStatus() async {
final status = await _permissionHandler.checkPermissionStatus(_permission);
setState(() => _permissionStatus = status);
}
Color getPermissionColor() {
switch (_permissionStatus) {
case PermissionStatus.denied:
return Colors.red;
case PermissionStatus.granted:
return Colors.green;
case PermissionStatus.limited:
return Colors.orange;
default:
return Colors.grey;
}
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
_permission.toString(),
style: Theme.of(context).textTheme.bodyText1,
),
subtitle: Text(
_permissionStatus.toString(),
style: TextStyle(color: getPermissionColor()),
),
trailing: (_permission is PermissionWithService)
? IconButton(
icon: const Icon(
Icons.info,
color: Colors.white,
),
onPressed: () {
checkServiceStatus(
context, _permission as PermissionWithService);
})
: null,
onTap: () {
requestPermission(_permission);
},
);
}
void checkServiceStatus(
BuildContext context, PermissionWithService permission) async {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
(await _permissionHandler.checkServiceStatus(permission)).toString()),
));
}
Future<void> requestPermission(Permission permission) async {
final status = await _permissionHandler.requestPermissions([permission]);
setState(() {
print(status);
_permissionStatus = status[permission] ?? PermissionStatus.denied;
print(_permissionStatus);
});
}
}

@ -0,0 +1,245 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
baseflow_plugin_template:
dependency: "direct main"
description:
path: "."
ref: "v2.1.0"
resolved-ref: "0695ef43c098d75efffe5fd6212390b853f05bf7"
url: "https://github.com/Baseflow/baseflow_plugin_template.git"
source: git
version: "2.1.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
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"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
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: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
permission_handler_android:
dependency: "direct dev"
description:
path: ".."
relative: true
source: path
version: "10.0.0"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.7.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
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"
url_launcher:
dependency: "direct dev"
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.5"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.17"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.17"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.12"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
sdks:
dart: ">=2.17.0-0 <3.0.0"
flutter: ">=2.10.0"

@ -0,0 +1,37 @@
name: permission_handler_android_example
description: Demonstrates how to use the permission_handler_android plugin.
environment:
sdk: ">=2.15.0 <3.0.0"
dependencies:
baseflow_plugin_template:
git:
url: https://github.com/Baseflow/baseflow_plugin_template.git
ref: v2.1.0
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
permission_handler_android:
# When depending on this package from a real application you should use:
# permission_handler_android: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
url_launcher: ^6.0.12
flutter:
uses-material-design: true
assets:
- res/images/baseflow_logo_def_light-02.png
- res/images/poweredByBaseflowLogoLight@3x.png
- packages/baseflow_plugin_template/logo.png
- packages/baseflow_plugin_template/poweredByBaseflow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1,79 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
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"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
permission_handler_platform_interface:
dependency: "direct main"
description:
name: permission_handler_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.7.0"
plugin_platform_interface:
dependency: "direct dev"
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
sdks:
dart: ">=2.15.0 <3.0.0"
flutter: ">=2.8.0"

@ -0,0 +1,25 @@
name: permission_handler_android
description: Permission plugin for Flutter. This plugin provides the Android API to request and check permissions.
version: 10.0.0
homepage: https://github.com/baseflow/flutter-permission-handler
environment:
sdk: ">=2.15.0 <3.0.0"
flutter: ">=2.8.0"
flutter:
plugin:
implements: permission_handler
platforms:
android:
package: com.baseflow.permissionhandler
pluginClass: PermissionHandlerPlugin
dependencies:
flutter:
sdk: flutter
permission_handler_platform_interface: ^3.7.0
dev_dependencies:
flutter_lints: ^1.0.4
plugin_platform_interface: ^2.0.0
Loading…
Cancel
Save