diff --git a/.github/workflows/permission_handler.yaml b/.github/workflows/permission_handler.yaml index 340849441..99f138b23 100644 --- a/.github/workflows/permission_handler.yaml +++ b/.github/workflows/permission_handler.yaml @@ -26,7 +26,7 @@ jobs: # TODO(mvanbeusekom): Manually set to macOS 15 to support Xcode 16 and iOS 18 SDKs. # Currently `macos-latest` is based on macOS 14 and doesn't support iOS 18 SDK. This # should be moved back to `macos-latest` when GitHub Actions images are updated. - runs-on: macos-15 + runs-on: macos-15 env: source-directory: ./permission_handler @@ -42,7 +42,6 @@ jobs: with: distribution: "temurin" # See 'Supported distributions' for available options java-version: "17" - # Make sure the stable version of Flutter is available - uses: subosito/flutter-action@v2 with: @@ -62,18 +61,7 @@ jobs: - name: Run Flutter Analyzer run: flutter analyze working-directory: ${{env.source-directory}} - - # Build Android version of the example App - - name: Run Android build - run: flutter build apk --release - working-directory: ${{env.example-directory}} - - # Build iOS version of the example App - - name: Run iOS build - run: flutter build ios --release --no-codesign - working-directory: ${{env.example-directory}} - - # Run all unit-tests with code coverage + # Run all unit-tests with code coverage - name: Run unit tests run: flutter test --coverage working-directory: ${{env.source-directory}} @@ -85,3 +73,13 @@ jobs: file: ${{env.source-directory}}/coverage/lcov.info # optional flags: unittests # optional name: permission_handler # optional + + # Build Android version of the example App + - name: Run Android build + run: flutter build apk --release + working-directory: ${{env.example-directory}} + + # Build iOS version of the example App + - name: Run iOS build + run: flutter build ios --release --no-codesign + working-directory: ${{env.example-directory}} diff --git a/.github/workflows/permission_handler_android.yaml b/.github/workflows/permission_handler_android.yaml index 0bbe16316..547ff265f 100644 --- a/.github/workflows/permission_handler_android.yaml +++ b/.github/workflows/permission_handler_android.yaml @@ -6,15 +6,15 @@ name: permission_handler_android # events but only for the main branch on: push: - branches: [ main ] + branches: [main] paths: - - 'permission_handler_android/**' - - '.github/workflows/permission_handler_android.yaml' + - "permission_handler_android/**" + - ".github/workflows/permission_handler_android.yaml" pull_request: - branches: [ main ] + branches: [main] paths: - - 'permission_handler_android/**' - - '.github/workflows/permission_handler_android.yaml' + - "permission_handler_android/**" + - ".github/workflows/permission_handler_android.yaml" # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: @@ -26,7 +26,7 @@ jobs: env: source-directory: ./permission_handler_android - example-directory: ./permission_handler_android/example + example-directory: ./permission_handler_android/example # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -36,13 +36,12 @@ jobs: # Make sure JAVA version 17 is installed on build agent. - uses: actions/setup-java@v3 with: - distribution: 'temurin' # See 'Supported distributions' for available options - java-version: '17' - + distribution: "temurin" # See 'Supported distributions' for available options + java-version: "17" # Make sure the stable version of Flutter is available - uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: "stable" # Download all Flutter packages - name: Download dependencies @@ -63,4 +62,3 @@ jobs: - name: Run Android build run: flutter build apk --release working-directory: ${{env.example-directory}} - \ No newline at end of file diff --git a/.github/workflows/permission_handler_platform_interface.yaml b/.github/workflows/permission_handler_platform_interface.yaml index 4f6408b78..46344a92b 100644 --- a/.github/workflows/permission_handler_platform_interface.yaml +++ b/.github/workflows/permission_handler_platform_interface.yaml @@ -6,15 +6,15 @@ name: permission_handler_platform_interface # events but only for the main branch on: push: - branches: [ main ] + branches: [main] paths: - - 'permission_handler_platform_interface/**' - - '.github/workflows/permission_handler_platform_interface.yaml' + - "permission_handler_platform_interface/**" + - ".github/workflows/permission_handler_platform_interface.yaml" pull_request: - branches: [ main ] + branches: [main] paths: - - 'permission_handler_platform_interface/**' - - '.github/workflows/permission_handler_platform_interface.yaml' + - "permission_handler_platform_interface/**" + - ".github/workflows/permission_handler_platform_interface.yaml" # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: @@ -25,17 +25,17 @@ jobs: runs-on: ubuntu-latest env: - source-directory: ./permission_handler_platform_interface + source-directory: ./permission_handler_platform_interface # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 - + # Make sure the stable version of Flutter is available - uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: "stable" # Download all Flutter packages - name: Download dependencies @@ -46,20 +46,21 @@ jobs: - name: Run Dart Format run: dart format --set-exit-if-changed . working-directory: ${{env.source-directory}} - + # Run Flutter Analyzer - name: Run Flutter Analyzer run: flutter analyze working-directory: ${{env.source-directory}} - + # Run all unit-tests with code coverage - name: Run unit tests run: flutter test --coverage working-directory: ${{env.source-directory}} # Upload code coverage information - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v3 with: + token: ${{ secrets.CODECOV_TOKEN }} file: ${{env.source-directory}}/coverage/lcov.info # optional name: permission_handler_platform_interface (Platform Interface Package) # optional - fail_ci_if_error: true \ No newline at end of file + flags: unittests diff --git a/permission_handler/CHANGELOG.md b/permission_handler/CHANGELOG.md index e44cb8f14..df454a31e 100644 --- a/permission_handler/CHANGELOG.md +++ b/permission_handler/CHANGELOG.md @@ -1,3 +1,12 @@ +## 13.0.1 + +- version bump + +## 13.0.0 + +- **BREAKING CHANGE:** , android compilesdk now set to version `compileSdkVersion 37` +- Added support for the new Android 17 permission `ACCESS_LOCAL_NETWORK` + ## 12.0.3 - Rewrites the "iOS - CocaoPods" section in the README.md to match version 8.0.0 of the permission_handler. diff --git a/permission_handler/example/android/app/build.gradle b/permission_handler/example/android/app/build.gradle deleted file mode 100644 index 047e56a5c..000000000 --- a/permission_handler/example/android/app/build.gradle +++ /dev/null @@ -1,55 +0,0 @@ -plugins { - id "com.android.application" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -android { - if (project.android.hasProperty("namespace")) { - namespace 'com.baseflow.permissionhandlerexample' - } - compileSdkVersion 36 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.baseflow.permissionhandler.example" - minSdkVersion flutter.minSdkVersion - targetSdkVersion 34 - 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 '../..' -} diff --git a/permission_handler/example/android/app/build.gradle.kts b/permission_handler/example/android/app/build.gradle.kts new file mode 100644 index 000000000..602003d43 --- /dev/null +++ b/permission_handler/example/android/app/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.application") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.baseflow.permissionhandler.example" + compileSdk = 37 + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.baseflow.permissionhandler.example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = 35 + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + 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.getByName("debug") + } + } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + +flutter { + source = "../.." +} diff --git a/permission_handler/example/android/build.gradle b/permission_handler/example/android/build.gradle deleted file mode 100644 index 8f31e8caf..000000000 --- a/permission_handler/example/android/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/permission_handler/example/android/build.gradle.kts b/permission_handler/example/android/build.gradle.kts new file mode 100644 index 000000000..dbee657bb --- /dev/null +++ b/permission_handler/example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/permission_handler/example/android/gradle.properties b/permission_handler/example/android/gradle.properties index 94adc3a3f..d5da7278a 100644 --- a/permission_handler/example/android/gradle.properties +++ b/permission_handler/example/android/gradle.properties @@ -1,3 +1,6 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true -android.enableJetifier=true +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/permission_handler/example/android/gradle/wrapper/gradle-wrapper.properties b/permission_handler/example/android/gradle/wrapper/gradle-wrapper.properties index 9162f1008..a97e89ca1 100644 --- a/permission_handler/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/permission_handler/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip diff --git a/permission_handler/example/android/settings.gradle b/permission_handler/example/android/settings.gradle deleted file mode 100644 index 8cbe490f3..000000000 --- a/permission_handler/example/android/settings.gradle +++ /dev/null @@ -1,24 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.9.1" apply false -} - -include ":app" \ No newline at end of file diff --git a/permission_handler/example/android/settings.gradle.kts b/permission_handler/example/android/settings.gradle.kts new file mode 100644 index 000000000..c21f0c5b4 --- /dev/null +++ b/permission_handler/example/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "9.0.1" apply false + id("org.jetbrains.kotlin.android") version "2.3.20" apply false +} + +include(":app") diff --git a/permission_handler/example/ios/.gitignore b/permission_handler/example/ios/.gitignore index e96ef602b..8a62ea716 100644 --- a/permission_handler/example/ios/.gitignore +++ b/permission_handler/example/ios/.gitignore @@ -1,3 +1,9 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -18,6 +24,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ @@ -30,3 +37,6 @@ Runner/GeneratedPluginRegistrant.* !default.mode2v3 !default.pbxuser !default.perspectivev3 +# Swift Package Manager +.build/ +*.resolved diff --git a/permission_handler/example/lib/main.dart b/permission_handler/example/lib/main.dart index e37e51302..9dbe35a1d 100644 --- a/permission_handler/example/lib/main.dart +++ b/permission_handler/example/lib/main.dart @@ -18,8 +18,8 @@ void main() { ///Defines the main theme color final MaterialColor themeMaterialColor = BaseflowPluginExample.createMaterialColor( - const Color.fromRGBO(48, 49, 60, 1), - ); + const Color.fromRGBO(48, 49, 60, 1), +); /// A Flutter application demonstrating the functionality of this plugin class PermissionHandlerWidget extends StatefulWidget { @@ -43,41 +43,40 @@ class _PermissionHandlerWidgetState extends State { Widget build(BuildContext context) { return Center( child: ListView( - children: - Permission.values - .where((permission) { - if (Platform.isIOS) { - return permission != Permission.unknown && - permission != Permission.phone && - permission != Permission.sms && - permission != Permission.ignoreBatteryOptimizations && - permission != Permission.accessMediaLocation && - permission != Permission.activityRecognition && - permission != Permission.manageExternalStorage && - permission != Permission.systemAlertWindow && - permission != Permission.requestInstallPackages && - permission != Permission.accessNotificationPolicy && - permission != Permission.bluetoothScan && - permission != Permission.bluetoothAdvertise && - permission != Permission.bluetoothConnect && - permission != Permission.nearbyWifiDevices && - permission != Permission.videos && - permission != Permission.audio && - permission != Permission.scheduleExactAlarm && - permission != Permission.sensorsAlways; - } else { - return permission != Permission.unknown && - permission != Permission.mediaLibrary && - permission != Permission.photosAddOnly && - permission != Permission.reminders && - permission != Permission.bluetooth && - permission != Permission.appTrackingTransparency && - permission != Permission.criticalAlerts && - permission != Permission.assistant; - } - }) - .map((permission) => PermissionWidget(permission)) - .toList(), + children: Permission.values + .where((permission) { + if (Platform.isIOS) { + return permission != Permission.unknown && + permission != Permission.phone && + permission != Permission.sms && + permission != Permission.ignoreBatteryOptimizations && + permission != Permission.accessMediaLocation && + permission != Permission.activityRecognition && + permission != Permission.manageExternalStorage && + permission != Permission.systemAlertWindow && + permission != Permission.requestInstallPackages && + permission != Permission.accessNotificationPolicy && + permission != Permission.bluetoothScan && + permission != Permission.bluetoothAdvertise && + permission != Permission.bluetoothConnect && + permission != Permission.nearbyWifiDevices && + permission != Permission.videos && + permission != Permission.audio && + permission != Permission.scheduleExactAlarm && + permission != Permission.sensorsAlways; + } else { + return permission != Permission.unknown && + permission != Permission.mediaLibrary && + permission != Permission.photosAddOnly && + permission != Permission.reminders && + permission != Permission.bluetooth && + permission != Permission.appTrackingTransparency && + permission != Permission.criticalAlerts && + permission != Permission.assistant; + } + }) + .map((permission) => PermissionWidget(permission)) + .toList(), ), ); } @@ -136,18 +135,17 @@ class _PermissionState extends State { _permissionStatus.toString(), style: TextStyle(color: getPermissionColor()), ), - trailing: - (widget.permission is PermissionWithService) - ? IconButton( - icon: const Icon(Icons.info, color: Colors.white), - onPressed: () { - checkServiceStatus( - context, - widget.permission as PermissionWithService, - ); - }, - ) - : null, + trailing: (widget.permission is PermissionWithService) + ? IconButton( + icon: const Icon(Icons.info, color: Colors.white), + onPressed: () { + checkServiceStatus( + context, + widget.permission as PermissionWithService, + ); + }, + ) + : null, onTap: () { requestPermission(widget.permission); }, diff --git a/permission_handler/example/pubspec.yaml b/permission_handler/example/pubspec.yaml index 8ea895146..4b6545123 100644 --- a/permission_handler/example/pubspec.yaml +++ b/permission_handler/example/pubspec.yaml @@ -2,7 +2,9 @@ name: permission_handler_example description: Demonstrates how to use the permission_handler plugin. environment: - sdk: ^3.7.0 + sdk: ^3.6.0 + flutter: ">=3.24.0" +resolution: workspace dependencies: baseflow_plugin_template: ^2.1.1 @@ -20,6 +22,7 @@ dev_dependencies: flutter_test: sdk: flutter url_launcher: ^6.3.1 + flutter_lints: ^5.0.0 flutter: uses-material-design: true diff --git a/permission_handler/pubspec.yaml b/permission_handler/pubspec.yaml index 60ee8f94e..d720f281c 100644 --- a/permission_handler/pubspec.yaml +++ b/permission_handler/pubspec.yaml @@ -2,11 +2,12 @@ name: permission_handler description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions. repository: https://github.com/baseflow/flutter-permission-handler issue_tracker: https://github.com/Baseflow/flutter-permission-handler/issues -version: 12.0.3 +version: 13.0.1 environment: - sdk: ^3.5.0 + sdk: ^3.6.0 flutter: ">=3.24.0" +resolution: workspace flutter: plugin: @@ -24,11 +25,11 @@ dependencies: flutter: sdk: flutter meta: ^1.7.0 - permission_handler_android: ^13.0.0 - permission_handler_apple: ^9.4.6 + permission_handler_android: ^14.0.1 + permission_handler_apple: ^9.4.8 permission_handler_html: ^0.1.1 - permission_handler_windows: ^0.2.1 - permission_handler_platform_interface: ^4.3.0 + permission_handler_windows: ^0.2.2 + permission_handler_platform_interface: ^4.3.2 dev_dependencies: flutter_lints: ^5.0.0 diff --git a/permission_handler/test/permission_handler_test.dart b/permission_handler/test/permission_handler_test.dart index ae666e9a2..66ddfae12 100644 --- a/permission_handler/test/permission_handler_test.dart +++ b/permission_handler/test/permission_handler_test.dart @@ -23,21 +23,27 @@ void main() { }); test( - // ignore: lines_longer_than_80_chars - 'PermissionActions on Permission: get shouldShowRequestRationale should return true when on android', - () async { - final mockPermissionHandlerPlatform = PermissionHandlerPlatform.instance; - - when(mockPermissionHandlerPlatform - .shouldShowRequestPermissionRationale(Permission.contacts)) - .thenAnswer((_) => Future.value(true)); - - await Permission.contacts.shouldShowRequestRationale; - - verify(mockPermissionHandlerPlatform - .shouldShowRequestPermissionRationale(Permission.contacts)) - .called(1); - }); + // ignore: lines_longer_than_80_chars + 'PermissionActions on Permission: get shouldShowRequestRationale should return true when on android', + () async { + final mockPermissionHandlerPlatform = + PermissionHandlerPlatform.instance; + + when( + mockPermissionHandlerPlatform.shouldShowRequestPermissionRationale( + Permission.contacts, + ), + ).thenAnswer((_) => Future.value(true)); + + await Permission.contacts.shouldShowRequestRationale; + + verify( + mockPermissionHandlerPlatform.shouldShowRequestPermissionRationale( + Permission.contacts, + ), + ).called(1); + }, + ); test('PermissionActions on Permission: request()', () async { final permissionRequest = Permission.contacts.request(); @@ -65,11 +71,14 @@ void main() { expect(isLimited, false); }); - test('PermissionCheckShortcuts on Permission: get isPermanentlyDenied', - () async { - final isPermanentlyDenied = await Permission.contacts.isPermanentlyDenied; - expect(isPermanentlyDenied, false); - }); + test( + 'PermissionCheckShortcuts on Permission: get isPermanentlyDenied', + () async { + final isPermanentlyDenied = + await Permission.contacts.isPermanentlyDenied; + expect(isPermanentlyDenied, false); + }, + ); test('PermissionCheckShortcuts on Permission: get isProvisional', () async { final isProvisional = await Permission.contacts.isProvisional; @@ -77,23 +86,25 @@ void main() { }); test( - // ignore: lines_longer_than_80_chars - 'ServicePermissionActions on PermissionWithService: get ServiceStatus returns the right service status', - () async { - var serviceStatus = await Permission.phone.serviceStatus; + // ignore: lines_longer_than_80_chars + 'ServicePermissionActions on PermissionWithService: get ServiceStatus returns the right service status', + () async { + var serviceStatus = await Permission.phone.serviceStatus; - expect(serviceStatus, ServiceStatus.enabled); - }); + expect(serviceStatus, ServiceStatus.enabled); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'PermissionListActions on List: request() on a list returns a Map', - () async { - var permissionList = []; - final permissionMap = await permissionList.request(); - - expect(permissionMap, isA>()); - }); + // ignore: lines_longer_than_80_chars + 'PermissionListActions on List: request() on a list returns a Map', + () async { + var permissionList = []; + final permissionMap = await permissionList.request(); + + expect(permissionMap, isA>()); + }, + ); test('onDeniedCallback sets onDenied', () async { bool callbackCalled = false; @@ -151,7 +162,8 @@ void main() { .onDeniedCallback(() => callbackCalled.add('Denied')) .onGrantedCallback(() => callbackCalled.add('Granted')) .onPermanentlyDeniedCallback( - () => callbackCalled.add('PermanentlyDenied')) + () => callbackCalled.add('PermanentlyDenied'), + ) .onRestrictedCallback(() => callbackCalled.add('Restricted')) .onLimitedCallback(() => callbackCalled.add('Limited')) .onProvisionalCallback(() => callbackCalled.add('Provisional')) @@ -181,7 +193,8 @@ class MockPermissionHandlerPlatform extends Mock @override Future> requestPermissions( - List permissions) { + List permissions, + ) { var permissionsMap = {}; return Future.value(permissionsMap); } @@ -189,10 +202,7 @@ class MockPermissionHandlerPlatform extends Mock @override Future shouldShowRequestPermissionRationale(Permission? permission) { return super.noSuchMethod( - Invocation.method( - #shouldShowPermissionRationale, - [permission], - ), + Invocation.method(#shouldShowPermissionRationale, [permission]), returnValue: Future.value(true), ); } diff --git a/permission_handler_android/CHANGELOG.md b/permission_handler_android/CHANGELOG.md index dfec82c83..7180a5985 100644 --- a/permission_handler_android/CHANGELOG.md +++ b/permission_handler_android/CHANGELOG.md @@ -1,7 +1,20 @@ +## 14.0.1 + +- Version bump + +## 14.0.0 + +- **BREAKING CHANGES:** When updating to version 14.0.0 make sure to also set the `compileSdkVersion` in the `app/build.gradle` file to `37`. +- Updates Android `compileSdkVersion: 35` to `37` +- Bump compileSDK version to 37 + +## 13.0.2 + +- Added support for the new Android 17 permission `ACCESS_LOCAL_NETWORK` ## 13.0.1 -* fix: Resolve `PermissionRequestInProgressException` when app is relaunched with non-standard launchMode. +- fix: Resolve `PermissionRequestInProgressException` when app is relaunched with non-standard launchMode. ## 13.0.0 diff --git a/permission_handler_android/android/build.gradle b/permission_handler_android/android/build.gradle deleted file mode 100644 index fff4a308b..000000000 --- a/permission_handler_android/android/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -group 'com.baseflow.permissionhandler' -version '1.0' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.0.2' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - // Conditional for compatibility with AGP <4.2. - if (project.android.hasProperty("namespace")) { - namespace 'com.baseflow.permissionhandler' - } - compileSdkVersion 35 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - - defaultConfig { - minSdkVersion 19 - } -} diff --git a/permission_handler_android/android/build.gradle.kts b/permission_handler_android/android/build.gradle.kts new file mode 100644 index 000000000..da4d8033d --- /dev/null +++ b/permission_handler_android/android/build.gradle.kts @@ -0,0 +1,77 @@ +group = "com.baseflow.permissionhandler" +version = "1.0" + +buildscript { + val kotlinVersion = "2.3.20" + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:9.0.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +plugins { + id("com.android.library") +} + +android { + namespace = "com.baseflow.permissionhandler" + + compileSdk = 37 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + sourceSets { + getByName("main") { + java.srcDirs("src/main/kotlin") + } + getByName("test") { + java.srcDirs("src/test/kotlin") + } + } + + defaultConfig { + minSdk = 24 + } + + testOptions { + unitTests { + isIncludeAndroidResources = true + all { + it.useJUnitPlatform() + + it.outputs.upToDateWhen { false } + + it.testLogging { + events("passed", "skipped", "failed", "standardOut", "standardError") + showStandardStreams = true + } + } + } + } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + +dependencies { + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("org.mockito:mockito-core:5.0.0") +} diff --git a/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties b/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties index da9702f9e..d706aba60 100644 --- a/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties +++ b/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/permission_handler_android/android/settings.gradle b/permission_handler_android/android/settings.gradle.kts similarity index 100% rename from permission_handler_android/android/settings.gradle rename to permission_handler_android/android/settings.gradle.kts diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionConstants.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionConstants.java index 0262db176..734aa019f 100644 --- a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionConstants.java +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionConstants.java @@ -1,13 +1,13 @@ package com.baseflow.permissionhandler; import androidx.annotation.IntDef; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; 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; @@ -17,7 +17,6 @@ final class PermissionConstants { static final int PERMISSION_CODE_ACCESS_NOTIFICATION_POLICY = 213; static final int PERMISSION_CODE_SCHEDULE_EXACT_ALARM = 214; - // PERMISSION_GROUP // Deprecated in favor of PERMISSION_GROUP_CALENDAR_WRITE_ONLY and @@ -62,48 +61,50 @@ final class PermissionConstants { static final int PERMISSION_GROUP_CALENDAR_FULL_ACCESS = 37; static final int PERMISSION_GROUP_ASSISTANT = 38; static final int PERMISSION_GROUP_BACKGROUND_REFRESH = 39; + static final int PERMISSION_GROUP_ACCESS_LOCAL_NETWORK = 40; @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_SENSORS_ALWAYS, - 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_NEARBY_WIFI_DEVICES, - PERMISSION_GROUP_VIDEOS, - PERMISSION_GROUP_AUDIO, - PERMISSION_GROUP_SCHEDULE_EXACT_ALARM, - PERMISSION_GROUP_CALENDAR_WRITE_ONLY, - PERMISSION_GROUP_CALENDAR_FULL_ACCESS, - PERMISSION_GROUP_ASSISTANT, + 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_SENSORS_ALWAYS, + 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_NEARBY_WIFI_DEVICES, + PERMISSION_GROUP_VIDEOS, + PERMISSION_GROUP_AUDIO, + PERMISSION_GROUP_SCHEDULE_EXACT_ALARM, + PERMISSION_GROUP_CALENDAR_WRITE_ONLY, + PERMISSION_GROUP_CALENDAR_FULL_ACCESS, + PERMISSION_GROUP_ASSISTANT, + PERMISSION_GROUP_BACKGROUND_REFRESH, + PERMISSION_GROUP_ACCESS_LOCAL_NETWORK, }) - @interface PermissionGroup { - } + @interface PermissionGroup {} //PERMISSION_STATUS static final int PERMISSION_STATUS_DENIED = 0; @@ -115,14 +116,13 @@ final class PermissionConstants { @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.SOURCE) @IntDef({ - PERMISSION_STATUS_DENIED, - PERMISSION_STATUS_GRANTED, - PERMISSION_STATUS_RESTRICTED, - PERMISSION_STATUS_LIMITED, - PERMISSION_STATUS_NEVER_ASK_AGAIN + PERMISSION_STATUS_DENIED, + PERMISSION_STATUS_GRANTED, + PERMISSION_STATUS_RESTRICTED, + PERMISSION_STATUS_LIMITED, + PERMISSION_STATUS_NEVER_ASK_AGAIN, }) - @interface PermissionStatus { - } + @interface PermissionStatus {} //SERVICE_STATUS static final int SERVICE_STATUS_DISABLED = 0; @@ -131,10 +131,9 @@ final class PermissionConstants { @Retention(RetentionPolicy.SOURCE) @IntDef({ - SERVICE_STATUS_DISABLED, - SERVICE_STATUS_ENABLED, - SERVICE_STATUS_NOT_APPLICABLE + SERVICE_STATUS_DISABLED, + SERVICE_STATUS_ENABLED, + SERVICE_STATUS_NOT_APPLICABLE, }) - @interface ServiceStatus { - } -} \ No newline at end of file + @interface ServiceStatus {} +} diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionUtils.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionUtils.java index c56bcfdfb..35cc35e2d 100644 --- a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionUtils.java +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionUtils.java @@ -10,12 +10,10 @@ import android.os.Build; import android.os.Environment; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -23,7 +21,9 @@ import java.util.List; public class PermissionUtils { - final static String SHARED_PREFERENCES_PERMISSION_WAS_DENIED_BEFORE_KEY = "sp_permission_handler_permission_was_denied_before"; + + static final String SHARED_PREFERENCES_PERMISSION_WAS_DENIED_BEFORE_KEY = + "sp_permission_handler_permission_was_denied_before"; @PermissionConstants.PermissionGroup static int parseManifestName(String permission) { @@ -95,45 +95,81 @@ static int parseManifestName(String permission) { return PermissionConstants.PERMISSION_GROUP_AUDIO; case Manifest.permission.SCHEDULE_EXACT_ALARM: return PermissionConstants.PERMISSION_GROUP_SCHEDULE_EXACT_ALARM; + case Manifest.permission.ACCESS_LOCAL_NETWORK: + return PermissionConstants.PERMISSION_GROUP_ACCESS_LOCAL_NETWORK; default: return PermissionConstants.PERMISSION_GROUP_UNKNOWN; } } @TargetApi(22) - static List getManifestNames(Context context, @PermissionConstants.PermissionGroup int permission) { + static List getManifestNames( + Context context, + @PermissionConstants.PermissionGroup int permission + ) { final ArrayList permissionNames = new ArrayList<>(); switch (permission) { case PermissionConstants.PERMISSION_GROUP_CALENDAR_WRITE_ONLY: - if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CALENDAR)) - permissionNames.add(Manifest.permission.WRITE_CALENDAR); + if ( + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.WRITE_CALENDAR + ) + ) permissionNames.add(Manifest.permission.WRITE_CALENDAR); break; - case PermissionConstants.PERMISSION_GROUP_CALENDAR_FULL_ACCESS: case PermissionConstants.PERMISSION_GROUP_CALENDAR: - if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CALENDAR)) - permissionNames.add(Manifest.permission.WRITE_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); + if ( + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.READ_CALENDAR + ) + ) permissionNames.add(Manifest.permission.READ_CALENDAR); break; - case PermissionConstants.PERMISSION_GROUP_CAMERA: - if (hasPermissionInManifest(context, permissionNames, Manifest.permission.CAMERA)) - permissionNames.add(Manifest.permission.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); + 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: case PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE: case PermissionConstants.PERMISSION_GROUP_LOCATION: @@ -141,150 +177,324 @@ static List getManifestNames(Context context, @PermissionConstants.Permi // 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 (permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) - permissionNames.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION); + if ( + permission == + PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS && + 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; } - 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); + 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); + 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.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); - - 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.O && hasPermissionInManifest(context, permissionNames, Manifest.permission.ANSWER_PHONE_CALLS)) - permissionNames.add(Manifest.permission.ANSWER_PHONE_CALLS); + 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.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); + + 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.O && + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.ANSWER_PHONE_CALLS + ) + ) permissionNames.add(Manifest.permission.ANSWER_PHONE_CALLS); break; - case PermissionConstants.PERMISSION_GROUP_SENSORS: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { - if (hasPermissionInManifest(context, permissionNames, Manifest.permission.BODY_SENSORS)) { + if ( + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.BODY_SENSORS + ) + ) { permissionNames.add(Manifest.permission.BODY_SENSORS); } } break; case PermissionConstants.PERMISSION_GROUP_SENSORS_ALWAYS: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (hasPermissionInManifest(context, permissionNames, Manifest.permission.BODY_SENSORS_BACKGROUND)) { - permissionNames.add(Manifest.permission.BODY_SENSORS_BACKGROUND); + if ( + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.BODY_SENSORS_BACKGROUND + ) + ) { + permissionNames.add( + Manifest.permission.BODY_SENSORS_BACKGROUND + ); } } 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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: { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // 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); + String result = determineBluetoothPermission( + context, + Manifest.permission.BLUETOOTH_SCAN + ); if (result != null) { permissionNames.add(result); @@ -297,7 +507,10 @@ static List getManifestNames(Context context, @PermissionConstants.Permi if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // 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); + String result = determineBluetoothPermission( + context, + Manifest.permission.BLUETOOTH_ADVERTISE + ); if (result != null) { permissionNames.add(result); @@ -310,7 +523,10 @@ static List getManifestNames(Context context, @PermissionConstants.Permi if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // 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); + String result = determineBluetoothPermission( + context, + Manifest.permission.BLUETOOTH_CONNECT + ); if (result != null) { permissionNames.add(result); @@ -322,37 +538,85 @@ static List getManifestNames(Context context, @PermissionConstants.Permi case PermissionConstants.PERMISSION_GROUP_NOTIFICATION: // The POST_NOTIFICATIONS permission is introduced in Android TIRAMISU, meaning we should // not handle permissions on pre Android TIRAMISU devices. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && hasPermissionInManifest(context, permissionNames, Manifest.permission.POST_NOTIFICATIONS)) - permissionNames.add(Manifest.permission.POST_NOTIFICATIONS); + 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_NEARBY_WIFI_DEVICES: // The NEARBY_WIFI_DEVICES permission is introduced in Android TIRAMISU, meaning we should // not handle permissions on pre Android TIRAMISU devices. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && hasPermissionInManifest(context, permissionNames, Manifest.permission.NEARBY_WIFI_DEVICES)) - permissionNames.add(Manifest.permission.NEARBY_WIFI_DEVICES); + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.NEARBY_WIFI_DEVICES + ) + ) permissionNames.add(Manifest.permission.NEARBY_WIFI_DEVICES); break; case PermissionConstants.PERMISSION_GROUP_PHOTOS: // The READ_MEDIA_IMAGES permission is introduced in Android TIRAMISU, meaning we should // not handle permissions on pre Android TIRAMISU devices. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_MEDIA_IMAGES)) - permissionNames.add(Manifest.permission.READ_MEDIA_IMAGES); + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.READ_MEDIA_IMAGES + ) + ) permissionNames.add(Manifest.permission.READ_MEDIA_IMAGES); break; case PermissionConstants.PERMISSION_GROUP_VIDEOS: // The READ_MEDIA_VIDEOS permission is introduced in Android TIRAMISU, meaning we should // not handle permissions on pre Android TIRAMISU devices. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_MEDIA_VIDEO)) - permissionNames.add(Manifest.permission.READ_MEDIA_VIDEO); + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.READ_MEDIA_VIDEO + ) + ) permissionNames.add(Manifest.permission.READ_MEDIA_VIDEO); break; case PermissionConstants.PERMISSION_GROUP_AUDIO: // The READ_MEDIA_AUDIO permission is introduced in Android TIRAMISU, meaning we should // not handle permissions on pre Android TIRAMISU devices. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_MEDIA_AUDIO)) - permissionNames.add(Manifest.permission.READ_MEDIA_AUDIO); + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.READ_MEDIA_AUDIO + ) + ) permissionNames.add(Manifest.permission.READ_MEDIA_AUDIO); break; case PermissionConstants.PERMISSION_GROUP_SCHEDULE_EXACT_ALARM: // The SCHEDULE_EXACT_ALARM permission is introduced in Android S, before Android 31 it should alway return Granted - if (hasPermissionInManifest(context, permissionNames, Manifest.permission.SCHEDULE_EXACT_ALARM)) - permissionNames.add(Manifest.permission.SCHEDULE_EXACT_ALARM); + if ( + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.SCHEDULE_EXACT_ALARM + ) + ) permissionNames.add(Manifest.permission.SCHEDULE_EXACT_ALARM); + break; + case PermissionConstants.PERMISSION_GROUP_ACCESS_LOCAL_NETWORK: + // The ACCESS_LOCAL_NETWORK permission is introduced in API level 37 (Cinnamon Bun) + // API 36 was allowed but only with e.g. NEARBY_WIFI_DEVICES, so we should not handle permissions on pre Android 37 devices. + // Prior to 36, this was always allowed. + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN && + hasPermissionInManifest( + context, + permissionNames, + Manifest.permission.ACCESS_LOCAL_NETWORK + ) + ) permissionNames.add(Manifest.permission.ACCESS_LOCAL_NETWORK); break; case PermissionConstants.PERMISSION_GROUP_MEDIA_LIBRARY: case PermissionConstants.PERMISSION_GROUP_REMINDERS: @@ -363,7 +627,11 @@ static List getManifestNames(Context context, @PermissionConstants.Permi return permissionNames; } - private static boolean hasPermissionInManifest(Context context, ArrayList confirmedPermissions, String permission) { + private static boolean hasPermissionInManifest( + Context context, + ArrayList confirmedPermissions, + String permission + ) { try { if (confirmedPermissions != null) { for (String r : confirmedPermissions) { @@ -374,25 +642,37 @@ private static boolean hasPermissionInManifest(Context context, ArrayList(Arrays.asList(info.requestedPermissions)); + 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); + Log.d( + PermissionConstants.LOG_TAG, + "Unable to check manifest for permission: ", + ex + ); } return false; } @@ -459,8 +739,8 @@ private static boolean hasPermissionInManifest(Context context, ArrayList statuses) { - if (statuses.contains(PermissionConstants.PERMISSION_STATUS_NEVER_ASK_AGAIN)) - return PermissionConstants.PERMISSION_STATUS_NEVER_ASK_AGAIN; - if (statuses.contains(PermissionConstants.PERMISSION_STATUS_RESTRICTED)) - return PermissionConstants.PERMISSION_STATUS_RESTRICTED; - if (statuses.contains(PermissionConstants.PERMISSION_STATUS_DENIED)) - return PermissionConstants.PERMISSION_STATUS_DENIED; - if (statuses.contains(PermissionConstants.PERMISSION_STATUS_LIMITED)) - return PermissionConstants.PERMISSION_STATUS_LIMITED; + static Integer strictestStatus( + final @NonNull Collection< + @PermissionConstants.PermissionStatus Integer + > statuses + ) { + if ( + statuses.contains( + PermissionConstants.PERMISSION_STATUS_NEVER_ASK_AGAIN + ) + ) return PermissionConstants.PERMISSION_STATUS_NEVER_ASK_AGAIN; + if ( + statuses.contains(PermissionConstants.PERMISSION_STATUS_RESTRICTED) + ) return PermissionConstants.PERMISSION_STATUS_RESTRICTED; + if ( + statuses.contains(PermissionConstants.PERMISSION_STATUS_DENIED) + ) return PermissionConstants.PERMISSION_STATUS_DENIED; + if ( + statuses.contains(PermissionConstants.PERMISSION_STATUS_LIMITED) + ) return PermissionConstants.PERMISSION_STATUS_LIMITED; return PermissionConstants.PERMISSION_STATUS_GRANTED; } @@ -486,9 +776,11 @@ static Integer strictestStatus(final @NonNull Collection<@PermissionConstants.Pe @PermissionConstants.PermissionStatus static Integer strictestStatus( final @Nullable @PermissionConstants.PermissionStatus Integer statusA, - final @Nullable @PermissionConstants.PermissionStatus Integer statusB) { - - final Collection<@PermissionConstants.PermissionStatus Integer> statuses = new HashSet<>(); + final @Nullable @PermissionConstants.PermissionStatus Integer statusB + ) { + final Collection< + @PermissionConstants.PermissionStatus Integer + > statuses = new HashSet<>(); statuses.add(statusA); statuses.add(statusB); return strictestStatus(statuses); @@ -508,8 +800,8 @@ static Integer strictestStatus( @PermissionConstants.PermissionStatus static int determineDeniedVariant( final @Nullable Activity activity, - final String permissionName) { - + final String permissionName + ) { if (activity == null) { return PermissionConstants.PERMISSION_STATUS_DENIED; } @@ -518,11 +810,15 @@ static int determineDeniedVariant( return PermissionConstants.PERMISSION_STATUS_DENIED; } - final boolean wasDeniedBefore = PermissionUtils.wasPermissionDeniedBefore(activity, permissionName); - final boolean shouldShowRational = !PermissionUtils.isNeverAskAgainSelected(activity, permissionName); + final boolean wasDeniedBefore = + PermissionUtils.wasPermissionDeniedBefore(activity, permissionName); + final boolean shouldShowRational = + !PermissionUtils.isNeverAskAgainSelected(activity, permissionName); //noinspection SimplifiableConditionalExpression - final boolean isDenied = wasDeniedBefore ? !shouldShowRational : shouldShowRational; + final boolean isDenied = wasDeniedBefore + ? !shouldShowRational + : shouldShowRational; if (!wasDeniedBefore && isDenied) { setPermissionDenied(activity, permissionName); @@ -538,24 +834,49 @@ static int determineDeniedVariant( @RequiresApi(api = Build.VERSION_CODES.M) static boolean isNeverAskAgainSelected( @NonNull final Activity activity, - final String name) { - - final boolean shouldShowRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, name); + final String name + ) { + 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)) { + 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)) { + 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)) { + } else if ( + hasPermissionInManifest( + context, + null, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + ) { return Manifest.permission.ACCESS_COARSE_LOCATION; } return null; - } else if (hasPermissionInManifest(context, null, Manifest.permission.ACCESS_FINE_LOCATION)) { + } else if ( + hasPermissionInManifest( + context, + null, + Manifest.permission.ACCESS_FINE_LOCATION + ) + ) { return Manifest.permission.ACCESS_FINE_LOCATION; } @@ -565,13 +886,22 @@ private static String determineBluetoothPermission(Context context, String permi // 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 { + 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)); + return pm.getPackageInfo( + context.getPackageName(), + PackageManager.PackageInfoFlags.of( + PackageManager.GET_PERMISSIONS + ) + ); } else { - return pm.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); + return pm.getPackageInfo( + context.getPackageName(), + PackageManager.GET_PERMISSIONS + ); } } @@ -587,9 +917,16 @@ private static PackageInfo getPackageInfo(Context context) throws PackageManager * @param permissionName the name of the permission * @return whether the permission was denied in the past */ - private static boolean wasPermissionDeniedBefore(final Context context, final String permissionName) { - final SharedPreferences sharedPreferences = context.getSharedPreferences(permissionName, Context.MODE_PRIVATE); - return sharedPreferences.getBoolean(SHARED_PREFERENCES_PERMISSION_WAS_DENIED_BEFORE_KEY, false); + private static boolean wasPermissionDeniedBefore( + final Context context, + final String permissionName + ) { + final SharedPreferences sharedPreferences = + context.getSharedPreferences(permissionName, Context.MODE_PRIVATE); + return sharedPreferences.getBoolean( + SHARED_PREFERENCES_PERMISSION_WAS_DENIED_BEFORE_KEY, + false + ); } /** @@ -602,8 +939,18 @@ private static boolean wasPermissionDeniedBefore(final Context context, final St * @param context context needed for accessing shared preferences. * @param permissionName the name of the permission */ - private static void setPermissionDenied(final Context context, final String permissionName) { - final SharedPreferences sharedPreferences = context.getSharedPreferences(permissionName, Context.MODE_PRIVATE); - sharedPreferences.edit().putBoolean(SHARED_PREFERENCES_PERMISSION_WAS_DENIED_BEFORE_KEY, true).apply(); + private static void setPermissionDenied( + final Context context, + final String permissionName + ) { + final SharedPreferences sharedPreferences = + context.getSharedPreferences(permissionName, Context.MODE_PRIVATE); + sharedPreferences + .edit() + .putBoolean( + SHARED_PREFERENCES_PERMISSION_WAS_DENIED_BEFORE_KEY, + true + ) + .apply(); } } diff --git a/permission_handler_android/example/android/app/build.gradle b/permission_handler_android/example/android/app/build.gradle deleted file mode 100644 index 04ce10422..000000000 --- a/permission_handler_android/example/android/app/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -plugins { - id "com.android.application" - id "dev.flutter.flutter-gradle-plugin" -} - -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' -} - -android { - namespace 'com.baseflow.permissionhandler.example' - compileSdkVersion 35 - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.baseflow.permissionhandler.example" - minSdkVersion flutter.minSdkVersion - targetSdkVersion 35 - 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 '../..' -} diff --git a/permission_handler_android/example/android/app/build.gradle.kts b/permission_handler_android/example/android/app/build.gradle.kts new file mode 100644 index 000000000..602003d43 --- /dev/null +++ b/permission_handler_android/example/android/app/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.application") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.baseflow.permissionhandler.example" + compileSdk = 37 + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.baseflow.permissionhandler.example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = 35 + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + 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.getByName("debug") + } + } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + +flutter { + source = "../.." +} diff --git a/permission_handler_android/example/android/build.gradle b/permission_handler_android/example/android/build.gradle deleted file mode 100644 index 0ed2bee85..000000000 --- a/permission_handler_android/example/android/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} - -// Build the plugin project with warnings enabled. This is here rather than -// in the plugin itself to avoid breaking clients that have different -// warnings (e.g., deprecation warnings from a newer SDK than this project -// builds with). -gradle.projectsEvaluated { - project(":permission_handler_android") { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:all" << "-Werror" - } - } -} diff --git a/permission_handler_android/example/android/build.gradle.kts b/permission_handler_android/example/android/build.gradle.kts new file mode 100644 index 000000000..dbee657bb --- /dev/null +++ b/permission_handler_android/example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/permission_handler_android/example/android/gradle.properties b/permission_handler_android/example/android/gradle.properties index 598d13fee..d5da7278a 100644 --- a/permission_handler_android/example/android/gradle.properties +++ b/permission_handler_android/example/android/gradle.properties @@ -1,3 +1,6 @@ -org.gradle.jvmargs=-Xmx4G +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true -android.enableJetifier=true +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/permission_handler_android/example/android/gradle/wrapper/gradle-wrapper.properties b/permission_handler_android/example/android/gradle/wrapper/gradle-wrapper.properties index db18181ac..2e1113280 100644 --- a/permission_handler_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/permission_handler_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip diff --git a/permission_handler_android/example/android/settings.gradle b/permission_handler_android/example/android/settings.gradle deleted file mode 100644 index 56eb85cb1..000000000 --- a/permission_handler_android/example/android/settings.gradle +++ /dev/null @@ -1,24 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.7.0" apply false -} - -include ":app" \ No newline at end of file diff --git a/permission_handler_android/example/android/settings.gradle.kts b/permission_handler_android/example/android/settings.gradle.kts new file mode 100644 index 000000000..c21f0c5b4 --- /dev/null +++ b/permission_handler_android/example/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "9.0.1" apply false + id("org.jetbrains.kotlin.android") version "2.3.20" apply false +} + +include(":app") diff --git a/permission_handler_android/example/pubspec.yaml b/permission_handler_android/example/pubspec.yaml index e76011501..22839e1ae 100644 --- a/permission_handler_android/example/pubspec.yaml +++ b/permission_handler_android/example/pubspec.yaml @@ -3,6 +3,7 @@ description: Demonstrates how to use the permission_handler_android plugin. environment: sdk: ^3.7.0 +resolution: workspace dependencies: baseflow_plugin_template: ^2.1.2 @@ -13,14 +14,8 @@ dependencies: 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: ../ + flutter_lints: ^5.0.0 + permission_handler_android: ^14.0.1 url_launcher: ^6.0.12 diff --git a/permission_handler_android/pubspec.yaml b/permission_handler_android/pubspec.yaml index b16ec5278..a60c3ed14 100644 --- a/permission_handler_android/pubspec.yaml +++ b/permission_handler_android/pubspec.yaml @@ -1,11 +1,12 @@ name: permission_handler_android description: Permission plugin for Flutter. This plugin provides the Android API to request and check permissions. homepage: https://github.com/baseflow/flutter-permission-handler -version: 13.0.1 +version: 14.0.1 environment: - sdk: ^3.5.0 + sdk: ^3.6.0 flutter: ">=3.24.0" +resolution: workspace flutter: plugin: diff --git a/permission_handler_apple/CHANGELOG.md b/permission_handler_apple/CHANGELOG.md index f60ecef2a..ace8017b1 100644 --- a/permission_handler_apple/CHANGELOG.md +++ b/permission_handler_apple/CHANGELOG.md @@ -1,6 +1,7 @@ ## 9.4.9 * Rewrites copyleft code from stackoverflow to fix compliance issue. +* Added support for the new Android 17 permission `ACCESS_LOCAL_NETWORK` ## 9.4.8 diff --git a/permission_handler_apple/example/lib/main.dart b/permission_handler_apple/example/lib/main.dart index 4750f1ac5..ae9563d62 100644 --- a/permission_handler_apple/example/lib/main.dart +++ b/permission_handler_apple/example/lib/main.dart @@ -32,11 +32,11 @@ class PermissionHandlerWidget extends StatefulWidget { } @override - _PermissionHandlerWidgetState createState() => - _PermissionHandlerWidgetState(); + PermissionHandlerWidgetState createState() => PermissionHandlerWidgetState(); } -class _PermissionHandlerWidgetState extends State { +/// The state of the [PermissionHandlerWidget] that listens for permission status changes. +class PermissionHandlerWidgetState extends State { @override Widget build(BuildContext context) { return Center( diff --git a/permission_handler_apple/example/pubspec.yaml b/permission_handler_apple/example/pubspec.yaml index 71aaacd79..042d6ae27 100644 --- a/permission_handler_apple/example/pubspec.yaml +++ b/permission_handler_apple/example/pubspec.yaml @@ -3,23 +3,21 @@ description: Demonstrates how to use the permission_handler_apple plugin. environment: sdk: ^3.7.0 + flutter: ">=3.24.0" dependencies: baseflow_plugin_template: ^2.1.1 + permission_handler_apple: ^9.4.9 + permission_handler_platform_interface: ^4.3.2 flutter: sdk: flutter +resolution: workspace + dev_dependencies: flutter_test: sdk: flutter - - permission_handler_apple: - # When depending on this package from a real application you should use: - # permission_handler: ^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: ../ + flutter_lints: ^5.0.0 url_launcher: ^6.3.2 diff --git a/permission_handler_apple/ios/.gitignore b/permission_handler_apple/ios/.gitignore index 3ed647344..8a62ea716 100644 --- a/permission_handler_apple/ios/.gitignore +++ b/permission_handler_apple/ios/.gitignore @@ -3,38 +3,40 @@ .sconsign.dblite .svn/ -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser +**/dgph *.mode1v3 *.mode2v3 +*.moved-aside +*.pbxuser *.perspectivev3 - -!default.pbxuser +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. !default.mode1v3 !default.mode2v3 +!default.pbxuser !default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig - # Swift Package Manager .build/ *.resolved diff --git a/permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/PermissionHandlerEnums.h b/permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/PermissionHandlerEnums.h index c153dc500..69cf6620c 100644 --- a/permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/PermissionHandlerEnums.h +++ b/permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/PermissionHandlerEnums.h @@ -163,7 +163,8 @@ typedef NS_ENUM(int, PermissionGroup) { PermissionGroupCalendarWriteOnly, PermissionGroupCalendarFullAccess, PermissionGroupAssistant, - PermissionGroupBackgroundRefresh + PermissionGroupBackgroundRefresh, + PermissionGroupAccessLocalNetwork }; typedef NS_ENUM(int, PermissionStatus) { diff --git a/permission_handler_apple/pubspec.yaml b/permission_handler_apple/pubspec.yaml index a03947478..d913c67b8 100644 --- a/permission_handler_apple/pubspec.yaml +++ b/permission_handler_apple/pubspec.yaml @@ -5,8 +5,9 @@ issue_tracker: https://github.com/Baseflow/flutter-permission-handler/issues version: 9.4.9 environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" + sdk: ^3.6.0 + flutter: ">=3.24.0" +resolution: workspace flutter: plugin: @@ -21,5 +22,5 @@ dependencies: permission_handler_platform_interface: ^4.2.0 dev_dependencies: - flutter_lints: ^1.0.4 + flutter_lints: ^5.0.0 plugin_platform_interface: ^2.0.0 diff --git a/permission_handler_html/CHANGELOG.md b/permission_handler_html/CHANGELOG.md index bfe470242..4349fbf58 100644 --- a/permission_handler_html/CHANGELOG.md +++ b/permission_handler_html/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.4+0 + +* Added support for the new Android 17 permission `ACCESS_LOCAL_NETWORK` + ## 0.1.3+5 - Updates the way how `window.navigator.mediaDevices` is accessed to keep supporting WASM. diff --git a/permission_handler_html/example/lib/main.dart b/permission_handler_html/example/lib/main.dart index e3650eba8..3cc3d0b5b 100644 --- a/permission_handler_html/example/lib/main.dart +++ b/permission_handler_html/example/lib/main.dart @@ -3,29 +3,33 @@ import 'package:flutter/material.dart'; import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; void main() { - runApp(BaseflowPluginExample( + runApp( + BaseflowPluginExample( pluginName: 'Permission Handler', githubURL: 'https://github.com/Baseflow/flutter-permission-handler', pubDevURL: 'https://pub.dev/packages/permission_handler', - pages: [PermissionHandlerWidget.createPage()])); + pages: [PermissionHandlerWidget.createPage()], + ), + ); } ///Defines the main theme color final MaterialColor themeMaterialColor = BaseflowPluginExample.createMaterialColor( - const Color.fromRGBO(48, 49, 60, 1)); + const Color.fromRGBO(48, 49, 60, 1), +); /// A Flutter application demonstrating the functionality of this plugin class PermissionHandlerWidget extends StatefulWidget { /// Creates a [PermissionHandlerWidget]. - const PermissionHandlerWidget({ - super.key, - }); + const PermissionHandlerWidget({super.key}); /// Create a page containing the functionality of this plugin static ExamplePage createPage() { return ExamplePage( - Icons.location_on, (context) => const PermissionHandlerWidget()); + Icons.location_on, + (context) => const PermissionHandlerWidget(), + ); } @override @@ -38,18 +42,19 @@ class _PermissionHandlerWidgetState extends State { 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()), + 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(), + ), ); } } @@ -57,10 +62,7 @@ class _PermissionHandlerWidgetState extends State { /// Permission widget containing information about the passed [Permission] class PermissionWidget extends StatefulWidget { /// Constructs a [PermissionWidget] for the supplied [Permission] - const PermissionWidget( - this._permission, { - super.key, - }); + const PermissionWidget(this._permission, {super.key}); final Permission _permission; @@ -84,8 +86,9 @@ class _PermissionState extends State { void _listenForPermissionStatus() async { await _permissionHandler.checkPermissionStatus(widget._permission).then( - (status) => setState(() => _permissionStatus = status), - onError: (error, st) => debugPrint('$error')); + (status) => setState(() => _permissionStatus = status), + onError: (error, st) => debugPrint('$error'), + ); } Color getPermissionColor() { @@ -114,14 +117,14 @@ class _PermissionState extends State { ), trailing: (widget._permission is PermissionWithService) ? IconButton( - icon: const Icon( - Icons.info, - color: Colors.white, - ), + icon: const Icon(Icons.info, color: Colors.white), onPressed: () { checkServiceStatus( - context, widget._permission as PermissionWithService); - }) + context, + widget._permission as PermissionWithService, + ); + }, + ) : null, onTap: () { requestPermission(widget._permission); @@ -130,18 +133,24 @@ class _PermissionState extends State { } void checkServiceStatus( - BuildContext context, PermissionWithService permission) async { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - (await _permissionHandler.checkServiceStatus(permission)).toString()), - )); + BuildContext context, + PermissionWithService permission, + ) async { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + (await _permissionHandler.checkServiceStatus(permission)).toString(), + ), + ), + ); } Future requestPermission(Permission permission) async { await _permissionHandler.requestPermissions([permission]).then( - (status) => setState(() { - _permissionStatus = status[permission] ?? PermissionStatus.denied; - }), - onError: (error, st) => debugPrint('$error')); + (status) => setState(() { + _permissionStatus = status[permission] ?? PermissionStatus.denied; + }), + onError: (error, st) => debugPrint('$error'), + ); } } diff --git a/permission_handler_html/example/pubspec.yaml b/permission_handler_html/example/pubspec.yaml index f380c161a..122092b5b 100644 --- a/permission_handler_html/example/pubspec.yaml +++ b/permission_handler_html/example/pubspec.yaml @@ -2,8 +2,10 @@ name: permission_handler_html_example description: Demonstrates how to use the permission_handler_html plugin. environment: - sdk: ">=3.0.5 <4.0.0" + sdk: ^3.6.0 + flutter: ">=3.24.0" +resolution: workspace dependencies: permission_handler_platform_interface: ^4.2.0 baseflow_plugin_template: ^2.1.2 @@ -15,6 +17,7 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter + flutter_lints: ^5.0.0 permission_handler_html: # When depending on this package from a real application you should use: diff --git a/permission_handler_html/lib/permission_handler_html.dart b/permission_handler_html/lib/permission_handler_html.dart index 8be2fa7c4..05b8afa0a 100644 --- a/permission_handler_html/lib/permission_handler_html.dart +++ b/permission_handler_html/lib/permission_handler_html.dart @@ -33,22 +33,18 @@ class WebPermissionHandler extends PermissionHandlerPlatform { /// Registers the web plugin implementation. static void registerWith(Registrar registrar) { PermissionHandlerPlatform.instance = WebPermissionHandler( - webDelegate: WebDelegate( - _devices, - _geolocation, - _htmlPermissions, - ), + webDelegate: WebDelegate(_devices, _geolocation, _htmlPermissions), ); } /// Constructs a WebPermissionHandler. - WebPermissionHandler({ - required WebDelegate webDelegate, - }) : _webDelegate = webDelegate; + WebPermissionHandler({required WebDelegate webDelegate}) + : _webDelegate = webDelegate; @override Future> requestPermissions( - List permissions) async { + List permissions, + ) async { return _webDelegate.requestPermissions(permissions); } @@ -64,7 +60,8 @@ class WebPermissionHandler extends PermissionHandlerPlatform { @override Future shouldShowRequestPermissionRationale( - Permission permission) async { + Permission permission, + ) async { return SynchronousFuture(false); } diff --git a/permission_handler_html/lib/web_delegate.dart b/permission_handler_html/lib/web_delegate.dart index 1db991418..0736ab310 100644 --- a/permission_handler_html/lib/web_delegate.dart +++ b/permission_handler_html/lib/web_delegate.dart @@ -61,7 +61,9 @@ class WebDelegate { } Future _permissionStatusState( - String webPermissionName, web.Permissions? permissions) async { + String webPermissionName, + web.Permissions? permissions, + ) async { final webPermissionStatus = await permissions ?.query(_PermissionDescriptor(name: webPermissionName)) .toDart; @@ -91,8 +93,10 @@ class WebDelegate { audioTracks[0].stop(); } } - } on web.DOMException { - return false; + } catch (e) { + if (e.isA()) { + return false; + } } return true; @@ -121,17 +125,19 @@ class WebDelegate { videoTracks[0].stop(); } } - } on web.DOMException { - return false; + } catch (e) { + if (e.isA()) { + return false; + } } return true; } Future _requestNotificationPermission() async { - return web.Notification.requestPermission() - .toDart - .then((permission) => (permission == "granted".toJS)); + return web.Notification.requestPermission().toDart.then( + (permission) => (permission == "granted".toJS), + ); } Future _requestLocationPermission() async { @@ -152,14 +158,16 @@ class WebDelegate { } Future _requestSingularPermission( - Permission permission) async { + Permission permission, + ) async { bool permissionGranted = switch (permission) { Permission.microphone => await _requestMicrophonePermission(), Permission.camera => await _requestCameraPermission(), Permission.notification => await _requestNotificationPermission(), Permission.location => await _requestLocationPermission(), _ => throw UnsupportedError( - 'The ${permission.toString()} permission is currently not supported on web.') + 'The ${permission.toString()} permission is currently not supported on web.', + ), }; if (!permissionGranted) { @@ -173,13 +181,15 @@ class WebDelegate { /// /// Returns a [Map] containing the status per requested [Permission]. Future> requestPermissions( - List permissions) async { + List permissions, + ) async { final Map permissionStatusMap = {}; for (final permission in permissions) { try { - permissionStatusMap[permission] = - await _requestSingularPermission(permission); + permissionStatusMap[permission] = await _requestSingularPermission( + permission, + ); } on UnimplementedError { rethrow; } diff --git a/permission_handler_html/pubspec.yaml b/permission_handler_html/pubspec.yaml index 2b3a022e5..7af165520 100644 --- a/permission_handler_html/pubspec.yaml +++ b/permission_handler_html/pubspec.yaml @@ -1,12 +1,13 @@ name: permission_handler_html description: Permission plugin for Flutter. This plugin provides the web API to request and check permissions. -version: 0.1.3+5 +version: 0.1.4+0 homepage: https://github.com/baseflow/flutter-permission-handler environment: - sdk: ">=3.3.0 <4.0.0" - flutter: ">=3.16.0" + sdk: ^3.5.0 + flutter: ">=3.24.0" +resolution: workspace dependencies: flutter: @@ -19,7 +20,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_lints: ^5.0.0 mockito: ^5.4.2 build_runner: ^2.1.2 test: ^1.24.4 diff --git a/permission_handler_platform_interface/CHANGELOG.md b/permission_handler_platform_interface/CHANGELOG.md index 825a49ee8..a75b9f029 100644 --- a/permission_handler_platform_interface/CHANGELOG.md +++ b/permission_handler_platform_interface/CHANGELOG.md @@ -1,3 +1,11 @@ +## 4.3.2 + +* Fix support for the new Android 17 permission `ACCESS_LOCAL_NETWORK` + +## 4.3.1 + +* Added support for the new Android 17 permission `ACCESS_LOCAL_NETWORK` + ## 4.3.0 - Updates project dependencies. diff --git a/permission_handler_platform_interface/lib/src/method_channel/method_channel_permission_handler.dart b/permission_handler_platform_interface/lib/src/method_channel/method_channel_permission_handler.dart index 5f03e3e7a..ca11e55d0 100644 --- a/permission_handler_platform_interface/lib/src/method_channel/method_channel_permission_handler.dart +++ b/permission_handler_platform_interface/lib/src/method_channel/method_channel_permission_handler.dart @@ -6,8 +6,9 @@ import 'package:flutter/services.dart'; import '../../permission_handler_platform_interface.dart'; import 'utils/codec.dart'; -const MethodChannel _methodChannel = - MethodChannel('flutter.baseflow.com/permissions/methods'); +const MethodChannel _methodChannel = MethodChannel( + 'flutter.baseflow.com/permissions/methods', +); /// An implementation of [PermissionHandlerPlatform] that uses [MethodChannel]s. class MethodChannelPermissionHandler extends PermissionHandlerPlatform { @@ -15,7 +16,9 @@ class MethodChannelPermissionHandler extends PermissionHandlerPlatform { @override Future checkPermissionStatus(Permission permission) async { final status = await _methodChannel.invokeMethod( - 'checkPermissionStatus', permission.value); + 'checkPermissionStatus', + permission.value, + ); return decodePermissionStatus(status); } @@ -52,7 +55,9 @@ class MethodChannelPermissionHandler extends PermissionHandlerPlatform { @override Future checkServiceStatus(Permission permission) async { final status = await _methodChannel.invokeMethod( - 'checkServiceStatus', permission.value); + 'checkServiceStatus', + permission.value, + ); return decodeServiceStatus(status); } @@ -74,10 +79,13 @@ class MethodChannelPermissionHandler extends PermissionHandlerPlatform { /// Returns a [Map] containing the status per requested [Permission]. @override Future> requestPermissions( - List permissions) async { + List permissions, + ) async { final data = encodePermissions(permissions); - final status = - await _methodChannel.invokeMethod('requestPermissions', data); + final status = await _methodChannel.invokeMethod( + 'requestPermissions', + data, + ); return decodePermissionRequestResult(Map.from(status)); } @@ -88,13 +96,16 @@ class MethodChannelPermissionHandler extends PermissionHandlerPlatform { /// returns [false]. @override Future shouldShowRequestPermissionRationale( - Permission permission) async { + Permission permission, + ) async { if (defaultTargetPlatform != TargetPlatform.android) { return false; } final shouldShowRationale = await _methodChannel.invokeMethod( - 'shouldShowRequestPermissionRationale', permission.value); + 'shouldShowRequestPermissionRationale', + permission.value, + ); return shouldShowRationale ?? false; } diff --git a/permission_handler_platform_interface/lib/src/method_channel/utils/codec.dart b/permission_handler_platform_interface/lib/src/method_channel/utils/codec.dart index dc5db075c..99bd0768a 100644 --- a/permission_handler_platform_interface/lib/src/method_channel/utils/codec.dart +++ b/permission_handler_platform_interface/lib/src/method_channel/utils/codec.dart @@ -13,9 +13,14 @@ ServiceStatus decodeServiceStatus(int value) { /// Converts the given [Map] of [int]s into a [Map] with [Permission]s as /// keys and their respective [PermissionStatus] as value. Map decodePermissionRequestResult( - Map value) { - return value.map((key, value) => MapEntry( - Permission.byValue(key), PermissionStatusValue.statusByValue(value))); + Map value, +) { + return value.map( + (key, value) => MapEntry( + Permission.byValue(key), + PermissionStatusValue.statusByValue(value), + ), + ); } /// Converts the given [List] of [Permission]s into a [List] of [int]s which diff --git a/permission_handler_platform_interface/lib/src/permission_handler_platform_interface.dart b/permission_handler_platform_interface/lib/src/permission_handler_platform_interface.dart index 32cb2b722..24685265e 100644 --- a/permission_handler_platform_interface/lib/src/permission_handler_platform_interface.dart +++ b/permission_handler_platform_interface/lib/src/permission_handler_platform_interface.dart @@ -32,7 +32,8 @@ abstract class PermissionHandlerPlatform extends PlatformInterface { /// Checks the current status of the given [Permission]. Future checkPermissionStatus(Permission permission) { throw UnimplementedError( - 'checkPermissionStatus() has not been implemented.'); + 'checkPermissionStatus() has not been implemented.', + ); } /// Checks the current status of the service associated with the given @@ -73,7 +74,8 @@ abstract class PermissionHandlerPlatform extends PlatformInterface { /// /// Returns a [Map] containing the status per requested [Permission]. Future> requestPermissions( - List permissions) { + List permissions, + ) { throw UnimplementedError('requestPermissions() has not been implemented.'); } @@ -83,6 +85,7 @@ abstract class PermissionHandlerPlatform extends PlatformInterface { /// returns [false]. Future shouldShowRequestPermissionRationale(Permission permission) { throw UnimplementedError( - 'shouldShowRequestPermissionRationale() has not been implemented.'); + 'shouldShowRequestPermissionRationale() has not been implemented.', + ); } } diff --git a/permission_handler_platform_interface/lib/src/permissions.dart b/permission_handler_platform_interface/lib/src/permissions.dart index b81a99f28..8eb1f743f 100644 --- a/permission_handler_platform_interface/lib/src/permissions.dart +++ b/permission_handler_platform_interface/lib/src/permissions.dart @@ -326,6 +326,11 @@ class Permission { /// Permission for reading the current background refresh status. (iOS only) static const backgroundRefresh = Permission._(39); + /// Permission for using local network protocols (broad access) + /// + /// Android 17+ (API 37+) + static const accessLocalNetwork = Permission._(40); + /// Returns a list of all possible [PermissionGroup] values. static const List values = [ // ignore: deprecated_member_use_from_same_package @@ -369,6 +374,7 @@ class Permission { calendarFullAccess, assistant, backgroundRefresh, + accessLocalNetwork, ]; static const List _names = [ @@ -412,6 +418,7 @@ class Permission { 'calendarFullAccess', 'assistant', 'backgroundRefresh', + 'accessLocalNetwork', ]; @override diff --git a/permission_handler_platform_interface/pubspec.yaml b/permission_handler_platform_interface/pubspec.yaml index 586671633..157c686d5 100644 --- a/permission_handler_platform_interface/pubspec.yaml +++ b/permission_handler_platform_interface/pubspec.yaml @@ -3,11 +3,12 @@ description: A common platform interface for the permission_handler plugin. homepage: https://github.com/baseflow/flutter-permission-handler/tree/master/permission_handler_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 4.3.0 +version: 4.3.2 environment: - sdk: ^3.5.0 + sdk: ^3.6.0 flutter: ">=3.24.0" +resolution: workspace dependencies: flutter: diff --git a/permission_handler_platform_interface/test/src/method_channel/method_channel_mock.dart b/permission_handler_platform_interface/test/src/method_channel/method_channel_mock.dart index 2b5da964a..77e4671f4 100644 --- a/permission_handler_platform_interface/test/src/method_channel/method_channel_mock.dart +++ b/permission_handler_platform_interface/test/src/method_channel/method_channel_mock.dart @@ -19,8 +19,10 @@ class MethodChannelMock { Future _handler(MethodCall methodCall) async { if (methodCall.method != method) { - throw MissingPluginException('No implementation found for method ' - '$method on channel ${methodChannel.name}'); + throw MissingPluginException( + 'No implementation found for method ' + '$method on channel ${methodChannel.name}', + ); } return Future.delayed(delay, () { diff --git a/permission_handler_platform_interface/test/src/method_channel/method_channel_permission_handler_test.dart b/permission_handler_platform_interface/test/src/method_channel/method_channel_permission_handler_test.dart index c03c279a1..b39410a65 100644 --- a/permission_handler_platform_interface/test/src/method_channel/method_channel_permission_handler_test.dart +++ b/permission_handler_platform_interface/test/src/method_channel/method_channel_permission_handler_test.dart @@ -15,129 +15,140 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('checkPermissionStatus: When checking for permission', () { - test('Should receive granted if user wants access to the requested feature', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkPermissionStatus', - result: PermissionStatus.denied.value, - ); - - final permissionStatus = await MethodChannelPermissionHandler() - .checkPermissionStatus(Permission.contacts); - - expect(permissionStatus, PermissionStatus.denied); - }); - - test('Should receive denied if user denied access to the requested feature', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkPermissionStatus', - result: PermissionStatus.denied.value, - ); - - final permissionStatus = await MethodChannelPermissionHandler() - .checkPermissionStatus(Permission.contacts); - - expect(permissionStatus, PermissionStatus.denied); - }); - test( - // ignore: lines_longer_than_80_chars - 'Should receive restricted if OS denied rights for to the requested feature', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkPermissionStatus', - result: PermissionStatus.restricted.value, - ); + 'Should receive granted if user wants access to the requested feature', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.denied.value, + ); - final permissionStatus = await MethodChannelPermissionHandler() - .checkPermissionStatus(Permission.contacts); + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.contacts); - expect(permissionStatus, PermissionStatus.restricted); - }); + expect(permissionStatus, PermissionStatus.denied); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'Should receive limited if user has authorized this application for limited access', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkPermissionStatus', - result: PermissionStatus.limited.value, - ); + 'Should receive denied if user denied access to the requested feature', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.denied.value, + ); - final permissionStatus = await MethodChannelPermissionHandler() - .checkPermissionStatus(Permission.contacts); + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.contacts); - expect(permissionStatus, PermissionStatus.limited); - }); + expect(permissionStatus, PermissionStatus.denied); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'Should receive permanentlyDenied if user denied access and selected to never show a request for this permission again', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkPermissionStatus', - result: PermissionStatus.permanentlyDenied.value, - ); + // ignore: lines_longer_than_80_chars + 'Should receive restricted if OS denied rights for to the requested feature', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.restricted.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.contacts); + + expect(permissionStatus, PermissionStatus.restricted); + }, + ); - final permissionStatus = await MethodChannelPermissionHandler() - .checkPermissionStatus(Permission.contacts); + test( + // ignore: lines_longer_than_80_chars + 'Should receive limited if user has authorized this application for limited access', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.limited.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.contacts); + + expect(permissionStatus, PermissionStatus.limited); + }, + ); - expect(permissionStatus, PermissionStatus.permanentlyDenied); - }); + test( + // ignore: lines_longer_than_80_chars + 'Should receive permanentlyDenied if user denied access and selected to never show a request for this permission again', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkPermissionStatus', + result: PermissionStatus.permanentlyDenied.value, + ); + + final permissionStatus = await MethodChannelPermissionHandler() + .checkPermissionStatus(Permission.contacts); + + expect(permissionStatus, PermissionStatus.permanentlyDenied); + }, + ); }); group('checkServiceStatus: When checking for service', () { // ignore: lines_longer_than_80_chars test( - 'Should receive disabled if the service for the permission is disabled', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkServiceStatus', - result: ServiceStatus.disabled.value, - ); + 'Should receive disabled if the service for the permission is disabled', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkServiceStatus', + result: ServiceStatus.disabled.value, + ); - final serviceStatus = await MethodChannelPermissionHandler() - .checkServiceStatus(Permission.contacts); + final serviceStatus = await MethodChannelPermissionHandler() + .checkServiceStatus(Permission.contacts); - expect(serviceStatus, ServiceStatus.disabled); - }); + expect(serviceStatus, ServiceStatus.disabled); + }, + ); - test('Should receive enabled if the service for the permission is enabled', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkServiceStatus', - result: ServiceStatus.enabled.value, - ); + test( + 'Should receive enabled if the service for the permission is enabled', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkServiceStatus', + result: ServiceStatus.enabled.value, + ); - final serviceStatus = await MethodChannelPermissionHandler() - .checkServiceStatus(Permission.contacts); + final serviceStatus = await MethodChannelPermissionHandler() + .checkServiceStatus(Permission.contacts); - expect(serviceStatus, ServiceStatus.enabled); - }); + expect(serviceStatus, ServiceStatus.enabled); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'Should receive notApplicable if the permission does not have an associated service on the current platform', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'checkServiceStatus', - result: ServiceStatus.notApplicable.value, - ); - - final serviceStatus = await MethodChannelPermissionHandler() - .checkServiceStatus(Permission.contacts); - - expect(serviceStatus, ServiceStatus.notApplicable); - }); + // ignore: lines_longer_than_80_chars + 'Should receive notApplicable if the permission does not have an associated service on the current platform', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'checkServiceStatus', + result: ServiceStatus.notApplicable.value, + ); + + final serviceStatus = await MethodChannelPermissionHandler() + .checkServiceStatus(Permission.contacts); + + expect(serviceStatus, ServiceStatus.notApplicable); + }, + ); }); group('openAppSettings: When opening the App settings', () { @@ -170,36 +181,39 @@ void main() { group('requestPermissions: When requesting for permission', () { // ignore: lines_longer_than_80_chars - test('returns a Map with all the PermissionStatus of the given permissions', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'requestPermissions', - result: mockPermissionMap, - ); - - final result = await MethodChannelPermissionHandler() - .requestPermissions(mockPermissions); - - expect(result, isA>()); - }); + test( + 'returns a Map with all the PermissionStatus of the given permissions', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'requestPermissions', + result: mockPermissionMap, + ); + + final result = await MethodChannelPermissionHandler() + .requestPermissions(mockPermissions); + + expect(result, isA>()); + }, + ); }); group('shouldShowRequestPermissionRationale:', () { test( - // ignore: lines_longer_than_80_chars - 'should return true when you should show a rationale for requesting permission.', - () async { - MethodChannelMock( - channelName: 'flutter.baseflow.com/permissions/methods', - method: 'shouldShowRequestPermissionRationale', - result: true, - ); - - final shouldShowRationale = await MethodChannelPermissionHandler() - .shouldShowRequestPermissionRationale(mockPermissions.first); - - expect(shouldShowRationale, true); - }); + // ignore: lines_longer_than_80_chars + 'should return true when you should show a rationale for requesting permission.', + () async { + MethodChannelMock( + channelName: 'flutter.baseflow.com/permissions/methods', + method: 'shouldShowRequestPermissionRationale', + result: true, + ); + + final shouldShowRationale = await MethodChannelPermissionHandler() + .shouldShowRequestPermissionRationale(mockPermissions.first); + + expect(shouldShowRationale, true); + }, + ); }); } diff --git a/permission_handler_platform_interface/test/src/method_channel/utils/coded_test.dart b/permission_handler_platform_interface/test/src/method_channel/utils/coded_test.dart index 1742b9fc6..2932a6406 100644 --- a/permission_handler_platform_interface/test/src/method_channel/utils/coded_test.dart +++ b/permission_handler_platform_interface/test/src/method_channel/utils/coded_test.dart @@ -15,9 +15,7 @@ void main() { test( 'decodePermissionRequestResult should convert a map' 'to map', () { - var value = { - 1: 1, - }; + var value = {1: 1}; var permissionMap = decodePermissionRequestResult(value); diff --git a/permission_handler_platform_interface/test/src/permission_handler_platform_interface_test.dart b/permission_handler_platform_interface/test/src/permission_handler_platform_interface_test.dart index c965e72ed..157602eef 100644 --- a/permission_handler_platform_interface/test/src/permission_handler_platform_interface_test.dart +++ b/permission_handler_platform_interface/test/src/permission_handler_platform_interface_test.dart @@ -9,8 +9,10 @@ void main() { group('$PermissionHandlerPlatform', () { test('$MethodChannelPermissionHandler is the default instance', () { - expect(PermissionHandlerPlatform.instance, - isA()); + expect( + PermissionHandlerPlatform.instance, + isA(), + ); }); test('Cannot be implemented with `implements`', () { @@ -30,61 +32,71 @@ void main() { }); test( - // ignore: lines_longer_than_80_chars - 'Default implementation of checkPermissionStatus should throw unimplemented error', - () { - final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); - - expect(() { - permissionHandlerPlatform - .checkPermissionStatus(Permission.accessMediaLocation); - }, throwsUnimplementedError); - }); + // ignore: lines_longer_than_80_chars + 'Default implementation of checkPermissionStatus should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + + expect(() { + permissionHandlerPlatform.checkPermissionStatus( + Permission.accessMediaLocation, + ); + }, throwsUnimplementedError); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'Default implementation of checkServiceStatus should throw unimplemented error', - () { - final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); - - expect(() { - permissionHandlerPlatform - .checkServiceStatus(Permission.accessMediaLocation); - }, throwsUnimplementedError); - }); + // ignore: lines_longer_than_80_chars + 'Default implementation of checkServiceStatus should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + + expect(() { + permissionHandlerPlatform.checkServiceStatus( + Permission.accessMediaLocation, + ); + }, throwsUnimplementedError); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'Default implementation of openAppSettings should throw unimplemented error', - () { - final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); - - expect( - permissionHandlerPlatform.openAppSettings, throwsUnimplementedError); - }); + // ignore: lines_longer_than_80_chars + 'Default implementation of openAppSettings should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + + expect( + permissionHandlerPlatform.openAppSettings, + throwsUnimplementedError, + ); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'Default implementation of requestPermissions should throw unimplemented error', - () { - final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); - var permission = [Permission.accessMediaLocation]; - - expect(() { - permissionHandlerPlatform.requestPermissions(permission); - }, throwsUnimplementedError); - }); + // ignore: lines_longer_than_80_chars + 'Default implementation of requestPermissions should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + var permission = [Permission.accessMediaLocation]; + + expect(() { + permissionHandlerPlatform.requestPermissions(permission); + }, throwsUnimplementedError); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'Default implementation of shouldShowRequestPermissionRationale should throw unimplemented error', - () { - final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); - expect(() { - permissionHandlerPlatform.shouldShowRequestPermissionRationale( - Permission.accessMediaLocation); - }, throwsUnimplementedError); - }); + // ignore: lines_longer_than_80_chars + 'Default implementation of shouldShowRequestPermissionRationale should throw unimplemented error', + () { + final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform(); + expect(() { + permissionHandlerPlatform.shouldShowRequestPermissionRationale( + Permission.accessMediaLocation, + ); + }, throwsUnimplementedError); + }, + ); }); } diff --git a/permission_handler_platform_interface/test/src/permission_status_test.dart b/permission_handler_platform_interface/test/src/permission_status_test.dart index d26ee6b32..3e127754d 100644 --- a/permission_handler_platform_interface/test/src/permission_status_test.dart +++ b/permission_handler_platform_interface/test/src/permission_status_test.dart @@ -32,19 +32,32 @@ void main() { }); test( - // ignore: lines_longer_than_80_chars - 'statusByValue should return right index int that corresponds with the right PermissionStatus', - () { - expect(PermissionStatusValue.statusByValue(0), PermissionStatus.denied); - expect(PermissionStatusValue.statusByValue(1), PermissionStatus.granted); - expect( - PermissionStatusValue.statusByValue(2), PermissionStatus.restricted); - expect(PermissionStatusValue.statusByValue(3), PermissionStatus.limited); - expect(PermissionStatusValue.statusByValue(4), - PermissionStatus.permanentlyDenied); - expect( - PermissionStatusValue.statusByValue(5), PermissionStatus.provisional); - }); + // ignore: lines_longer_than_80_chars + 'statusByValue should return right index int that corresponds with the right PermissionStatus', + () { + expect(PermissionStatusValue.statusByValue(0), PermissionStatus.denied); + expect( + PermissionStatusValue.statusByValue(1), + PermissionStatus.granted, + ); + expect( + PermissionStatusValue.statusByValue(2), + PermissionStatus.restricted, + ); + expect( + PermissionStatusValue.statusByValue(3), + PermissionStatus.limited, + ); + expect( + PermissionStatusValue.statusByValue(4), + PermissionStatus.permanentlyDenied, + ); + expect( + PermissionStatusValue.statusByValue(5), + PermissionStatus.provisional, + ); + }, + ); }); group('PermissionStatusGetters', () { @@ -76,11 +89,15 @@ void main() { expect(await mockFuture(PermissionStatus.restricted).isRestricted, true); expect(await mockFuture(PermissionStatus.limited).isLimited, true); expect( - await mockFuture(PermissionStatus.permanentlyDenied) - .isPermanentlyDenied, - true); + await mockFuture( + PermissionStatus.permanentlyDenied, + ).isPermanentlyDenied, + true, + ); expect( - await mockFuture(PermissionStatus.provisional).isProvisional, true); + await mockFuture(PermissionStatus.provisional).isProvisional, + true, + ); }); test('Getters should return false if statement is not met', () async { @@ -89,7 +106,9 @@ void main() { expect(await mockFuture(PermissionStatus.restricted).isDenied, false); expect(await mockFuture(PermissionStatus.limited).isDenied, false); expect( - await mockFuture(PermissionStatus.permanentlyDenied).isDenied, false); + await mockFuture(PermissionStatus.permanentlyDenied).isDenied, + false, + ); expect(await mockFuture(PermissionStatus.provisional).isDenied, false); }); }); diff --git a/permission_handler_platform_interface/test/src/permissions_test.dart b/permission_handler_platform_interface/test/src/permissions_test.dart index 0f8beb470..78160ddae 100644 --- a/permission_handler_platform_interface/test/src/permissions_test.dart +++ b/permission_handler_platform_interface/test/src/permissions_test.dart @@ -5,7 +5,7 @@ void main() { test('Permission has the right amount of possible Permission values', () { const values = Permission.values; - expect(values.length, 40); + expect(values.length, 41); }); test('check if byValue returns corresponding Permission value', () { @@ -32,47 +32,42 @@ void main() { }); test( - // ignore: lines_longer_than_80_chars - 'equality operator should return true for two instances with the same values', - () { - // Arrange - final firstPermission = Permission.byValue(1); - final secondPermission = Permission.byValue(1); + // ignore: lines_longer_than_80_chars + 'equality operator should return true for two instances with the same values', + () { + // Arrange + final firstPermission = Permission.byValue(1); + final secondPermission = Permission.byValue(1); - // Act & Assert - expect( - firstPermission == secondPermission, - true, - ); - }); + // Act & Assert + expect(firstPermission == secondPermission, true); + }, + ); test( - // ignore: lines_longer_than_80_chars - 'equality operator should return false for two instances with different values', - () { - // Arrange - final firstPermission = Permission.byValue(1); - final secondPermission = Permission.byValue(2); + // ignore: lines_longer_than_80_chars + 'equality operator should return false for two instances with different values', + () { + // Arrange + final firstPermission = Permission.byValue(1); + final secondPermission = Permission.byValue(2); - // Act & Assert - expect( - firstPermission == secondPermission, - false, - ); - }); + // Act & Assert + expect(firstPermission == secondPermission, false); + }, + ); - test('hashCode should be the same for two instances with the same values', - () { - // Arrange - final firstPermission = Permission.byValue(1); - final secondPermission = Permission.byValue(1); + test( + 'hashCode should be the same for two instances with the same values', + () { + // Arrange + final firstPermission = Permission.byValue(1); + final secondPermission = Permission.byValue(1); - // Act & Assert - expect( - firstPermission.hashCode, - secondPermission.hashCode, - ); - }); + // Act & Assert + expect(firstPermission.hashCode, secondPermission.hashCode); + }, + ); test('hashCode should not match for two instances with different values', () { // Arrange @@ -80,9 +75,6 @@ void main() { final secondPermission = Permission.byValue(2); // Act & Assert - expect( - firstPermission.hashCode == secondPermission.hashCode, - false, - ); + expect(firstPermission.hashCode == secondPermission.hashCode, false); }); } diff --git a/permission_handler_platform_interface/test/src/service_status_test.dart b/permission_handler_platform_interface/test/src/service_status_test.dart index b5db02424..6dad5489c 100644 --- a/permission_handler_platform_interface/test/src/service_status_test.dart +++ b/permission_handler_platform_interface/test/src/service_status_test.dart @@ -26,13 +26,17 @@ void main() { }); test( - // ignore: lines_longer_than_80_chars - 'statusByValue should return right index int that corresponds with the right PermissionStatus', - () { - expect(ServiceStatusValue.statusByValue(0), ServiceStatus.disabled); - expect(ServiceStatusValue.statusByValue(1), ServiceStatus.enabled); - expect(ServiceStatusValue.statusByValue(2), ServiceStatus.notApplicable); - }); + // ignore: lines_longer_than_80_chars + 'statusByValue should return right index int that corresponds with the right PermissionStatus', + () { + expect(ServiceStatusValue.statusByValue(0), ServiceStatus.disabled); + expect(ServiceStatusValue.statusByValue(1), ServiceStatus.enabled); + expect( + ServiceStatusValue.statusByValue(2), + ServiceStatus.notApplicable, + ); + }, + ); }); group('ServiceStatusGetters', () { diff --git a/permission_handler_windows/CHANGELOG.md b/permission_handler_windows/CHANGELOG.md index 91c2f5049..f951b347d 100644 --- a/permission_handler_windows/CHANGELOG.md +++ b/permission_handler_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2 + +* Added support for the new Android 17 permission `ACCESS_LOCAL_NETWORK` + ## 0.2.1 * Updates the dependency on `permission_handler_platform_interface` to version 4.1.0 (SiriKit support is only available for iOS and macOS). diff --git a/permission_handler_windows/example/lib/main.dart b/permission_handler_windows/example/lib/main.dart index 58a4dc053..4e5f30ca1 100644 --- a/permission_handler_windows/example/lib/main.dart +++ b/permission_handler_windows/example/lib/main.dart @@ -1,50 +1,62 @@ +// ignore_for_file: avoid_print + 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( + runApp( + BaseflowPluginExample( pluginName: 'Permission Handler', githubURL: 'https://github.com/Baseflow/flutter-permission-handler', pubDevURL: 'https://pub.dev/packages/permission_handler', - pages: [PermissionHandlerWidget.createPage()])); + pages: [PermissionHandlerWidget.createPage()], + ), + ); } ///Defines the main theme color final MaterialColor themeMaterialColor = BaseflowPluginExample.createMaterialColor( - const Color.fromRGBO(48, 49, 60, 1)); + const Color.fromRGBO(48, 49, 60, 1), +); /// A Flutter application demonstrating the functionality of this plugin class PermissionHandlerWidget extends StatefulWidget { + /// Creates a [PermissionHandlerWidget] that listens for permission status changes. + const PermissionHandlerWidget({super.key}); + /// Create a page containing the functionality of this plugin static ExamplePage createPage() { return ExamplePage( - Icons.location_on, (context) => PermissionHandlerWidget()); + Icons.location_on, + (context) => PermissionHandlerWidget(), + ); } @override - _PermissionHandlerWidgetState createState() => - _PermissionHandlerWidgetState(); + PermissionHandlerWidgetState createState() => PermissionHandlerWidgetState(); } -class _PermissionHandlerWidgetState extends State { +/// State for the [PermissionHandlerWidget] that listens for permission status changes. +class PermissionHandlerWidgetState extends State { @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()), + 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(), + ), ); } } @@ -52,18 +64,21 @@ class _PermissionHandlerWidgetState extends State { /// Permission widget containing information about the passed [Permission] class PermissionWidget extends StatefulWidget { /// Constructs a [PermissionWidget] for the supplied [Permission] - const PermissionWidget(this._permission); + const PermissionWidget(this._permission, {super.key}); final Permission _permission; + /// Returns the [Permission] associated with this widget. + Permission get permission => _permission; + @override - _PermissionState createState() => _PermissionState(_permission); + PermissionWidgetState createState() => PermissionWidgetState(); } -class _PermissionState extends State { - _PermissionState(this._permission); - - final Permission _permission; +/// State for the [PermissionWidget] that listens for permission status changes. +class PermissionWidgetState extends State { + /// Constructs a [PermissionWidgetState] for the supplied [PermissionWidget]. + PermissionWidgetState(); final PermissionHandlerPlatform _permissionHandler = PermissionHandlerPlatform.instance; PermissionStatus _permissionStatus = PermissionStatus.denied; @@ -76,10 +91,12 @@ class _PermissionState extends State { } void _listenForPermissionStatus() async { - final status = await _permissionHandler.checkPermissionStatus(_permission); + final status = + await _permissionHandler.checkPermissionStatus(widget.permission); setState(() => _permissionStatus = status); } + /// Returns the color to use for the permission status. Color getPermissionColor() { switch (_permissionStatus) { case PermissionStatus.denied: @@ -97,38 +114,45 @@ class _PermissionState extends State { Widget build(BuildContext context) { return ListTile( title: Text( - _permission.toString(), + widget.permission.toString(), style: Theme.of(context).textTheme.bodyLarge, ), subtitle: Text( _permissionStatus.toString(), style: TextStyle(color: getPermissionColor()), ), - trailing: (_permission is PermissionWithService) + trailing: (widget.permission is PermissionWithService) ? IconButton( - icon: const Icon( - Icons.info, - color: Colors.white, - ), + icon: const Icon(Icons.info, color: Colors.white), onPressed: () { checkServiceStatus( - context, _permission as PermissionWithService); - }) + context, + widget.permission as PermissionWithService, + ); + }, + ) : null, onTap: () { - requestPermission(_permission); + requestPermission(widget.permission); }, ); } + /// Requests permission for the given [Permission]. void checkServiceStatus( - BuildContext context, PermissionWithService permission) async { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - (await _permissionHandler.checkServiceStatus(permission)).toString()), - )); + BuildContext context, + PermissionWithService permission, + ) async { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + (await _permissionHandler.checkServiceStatus(permission)).toString(), + ), + ), + ); } + /// Requests permission for the given [Permission]. Future requestPermission(Permission permission) async { final status = await _permissionHandler.requestPermissions([permission]); diff --git a/permission_handler_windows/example/pubspec.yaml b/permission_handler_windows/example/pubspec.yaml index 2deb8e72c..0c9e1d4f5 100644 --- a/permission_handler_windows/example/pubspec.yaml +++ b/permission_handler_windows/example/pubspec.yaml @@ -1,33 +1,29 @@ -name: permission_handler_windows_example -description: Demonstrates how to use the permission_handler_windows plugin. - -environment: - sdk: ">=2.15.0 <3.0.0" - -dependencies: - baseflow_plugin_template: ^2.1.1 - flutter: - sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter - - permission_handler_windows: - # When depending on this package from a real application you should use: - # permission_handler_windows: ^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 +name: permission_handler_windows_example +description: Demonstrates how to use the permission_handler_windows plugin. + +environment: + sdk: ^3.6.0 + flutter: ">=3.24.0" +resolution: workspace + +dependencies: + baseflow_plugin_template: ^2.1.1 + flutter: + sdk: flutter + permission_handler_windows: ^0.2.2 + permission_handler_platform_interface: ^4.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + 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 diff --git a/permission_handler_windows/pubspec.yaml b/permission_handler_windows/pubspec.yaml index f5553e0d6..b01412999 100644 --- a/permission_handler_windows/pubspec.yaml +++ b/permission_handler_windows/pubspec.yaml @@ -1,6 +1,6 @@ name: permission_handler_windows description: Permission plugin for Flutter. This plugin provides the Windows API to request and check permissions. -version: 0.2.1 +version: 0.2.2 homepage: https://github.com/baseflow/flutter-permission-handler flutter: @@ -21,5 +21,6 @@ dev_dependencies: plugin_platform_interface: ^2.0.0 environment: - sdk: ">=2.12.0 <4.0.0" - flutter: ">=2.0.0" + sdk: ^3.6.0 + flutter: ">=3.24.0" +resolution: workspace diff --git a/permission_handler_windows/windows/permission_constants.h b/permission_handler_windows/windows/permission_constants.h index 1bc10737c..d0c580d68 100644 --- a/permission_handler_windows/windows/permission_constants.h +++ b/permission_handler_windows/windows/permission_constants.h @@ -50,7 +50,8 @@ class PermissionConstants { CALENDAR_WRITE_ONLY = 36, CALENDAR_FULL_ACCESS = 37, ASSISTANT = 38, - BACKGROUND_REFRESH = 39 + BACKGROUND_REFRESH = 39, + ACCESS_LOCAL_NETWORK = 40, }; //PERMISSION_STATUS diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 000000000..56e2c0439 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,21 @@ +name: permission_handler_monorepo +publish_to: none +environment: + sdk: ^3.6.0 + flutter: ">=3.24.0" + +workspace: + - permission_handler + - permission_handler/example + - permission_handler_android + - permission_handler_android/example + - permission_handler_apple + - permission_handler_apple/example + - permission_handler_html + - permission_handler_html/example + - permission_handler_platform_interface + - permission_handler_windows + - permission_handler_windows/example + +dev_dependencies: + flutter_lints: ^5.0.0