diff --git a/permission_handler_android/android/.classpath b/permission_handler_android/android/.classpath
index 4a04201ca..0a3280e2a 100644
--- a/permission_handler_android/android/.classpath
+++ b/permission_handler_android/android/.classpath
@@ -1,6 +1,6 @@
-
+
diff --git a/permission_handler_android/android/.settings/org.eclipse.buildship.core.prefs b/permission_handler_android/android/.settings/org.eclipse.buildship.core.prefs
index 90b6897fb..984f45e86 100644
--- a/permission_handler_android/android/.settings/org.eclipse.buildship.core.prefs
+++ b/permission_handler_android/android/.settings/org.eclipse.buildship.core.prefs
@@ -1,11 +1,11 @@
-arguments=
+arguments=--init-script /var/folders/c4/xw633d9525n5dfzwvdny38700000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/c4/xw633d9525n5dfzwvdny38700000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
auto.sync=false
build.scans.enabled=false
-connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.1.1))
+connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
-java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home
+java.home=/Users/kingkan/.vscode/extensions/redhat.java-1.50.0-darwin-arm64/jre/21.0.9-macosx-aarch64
jvm.arguments=
offline.mode=false
override.workspace.settings=true
diff --git a/permission_handler_android/android/.settings/org.eclipse.jdt.core.prefs b/permission_handler_android/android/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..e9186c3e3
--- /dev/null
+++ b/permission_handler_android/android/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
+org.eclipse.jdt.core.compiler.compliance=21
+org.eclipse.jdt.core.compiler.source=21
diff --git a/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties b/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties
index da9702f9e..09523c0e5 100644
--- a/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties
+++ b/permission_handler_android/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
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-8.9-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionManager.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionManager.java
index 8da9ee2ef..bf9243216 100644
--- a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionManager.java
+++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionManager.java
@@ -244,7 +244,7 @@ public boolean onRequestPermissionsResult(
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION) {
@PermissionConstants.PermissionStatus int permissionStatus =
- PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
+ determinePermissionStatus(PermissionConstants.PERMISSION_GROUP_LOCATION);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
@@ -502,6 +502,10 @@ private int determinePermissionStatus(final @PermissionConstants.PermissionGroup
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
+ if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION || permission == PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE) {
+ return determineWhenInUseLocationStatus(names);
+ }
+
final boolean requiresExplicitPermission = context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M;
if (requiresExplicitPermission) {
@@ -636,6 +640,34 @@ void shouldShowRequestPermissionRationale(
successCallback.onSuccess(ActivityCompat.shouldShowRequestPermissionRationale(activity, names.get(0)));
}
+ @PermissionConstants.PermissionStatus
+ private int determineWhenInUseLocationStatus(@NonNull List names) {
+ final boolean coarseInManifest = names.contains(Manifest.permission.ACCESS_COARSE_LOCATION);
+ final boolean fineInManifest = names.contains(Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final boolean coarseGranted = coarseInManifest
+ && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED;
+ final boolean fineGranted = fineInManifest
+ && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED;
+
+ if (coarseGranted || fineGranted) {
+ return PermissionConstants.PERMISSION_STATUS_GRANTED;
+ }
+
+ if (fineInManifest) {
+ return PermissionUtils.determineDeniedVariant(activity, Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+ if (coarseInManifest) {
+ return PermissionUtils.determineDeniedVariant(activity, Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+
+ return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
+ ? PermissionConstants.PERMISSION_STATUS_GRANTED
+ : PermissionConstants.PERMISSION_STATUS_DENIED;
+ }
+
@PermissionConstants.PermissionStatus
private int checkNotificationPermissionStatus() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
diff --git a/permission_handler_android/example/android/.settings/org.eclipse.buildship.core.prefs b/permission_handler_android/example/android/.settings/org.eclipse.buildship.core.prefs
index 98515123a..984f45e86 100644
--- a/permission_handler_android/example/android/.settings/org.eclipse.buildship.core.prefs
+++ b/permission_handler_android/example/android/.settings/org.eclipse.buildship.core.prefs
@@ -1,11 +1,11 @@
-arguments=
+arguments=--init-script /var/folders/c4/xw633d9525n5dfzwvdny38700000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/c4/xw633d9525n5dfzwvdny38700000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
-java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home
+java.home=/Users/kingkan/.vscode/extensions/redhat.java-1.50.0-darwin-arm64/jre/21.0.9-macosx-aarch64
jvm.arguments=
offline.mode=false
override.workspace.settings=true
diff --git a/permission_handler_android/example/android/app/build.gradle b/permission_handler_android/example/android/app/build.gradle
index 04ce10422..1ea8e1f62 100644
--- a/permission_handler_android/example/android/app/build.gradle
+++ b/permission_handler_android/example/android/app/build.gradle
@@ -1,5 +1,6 @@
plugins {
id "com.android.application"
+ id "org.jetbrains.kotlin.android"
id "dev.flutter.flutter-gradle-plugin"
}
@@ -28,17 +29,26 @@ if (flutterVersionName == null) {
android {
namespace 'com.baseflow.permissionhandler.example'
- compileSdkVersion 35
+ compileSdkVersion 36
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
+ targetSdkVersion 36
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = "17"
+ }
+
buildTypes {
release {
// TODO: Add your own signing config for the release build.
diff --git a/permission_handler_android/example/android/app/src/main/AndroidManifest.xml b/permission_handler_android/example/android/app/src/main/AndroidManifest.xml
index c9457c995..343b10e33 100644
--- a/permission_handler_android/example/android/app/src/main/AndroidManifest.xml
+++ b/permission_handler_android/example/android/app/src/main/AndroidManifest.xml
@@ -103,7 +103,7 @@
android:label="permission_handler_example"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
-
+ when (call.method) {
+ "status" -> {
+ val fineGranted = isGranted(Manifest.permission.ACCESS_FINE_LOCATION)
+ val coarseGranted = isGranted(Manifest.permission.ACCESS_COARSE_LOCATION)
+ result.success(mapOf("fine" to fineGranted, "coarse" to coarseGranted))
+ }
+
+ else -> result.notImplemented()
+ }
+ }
+ }
+
+ private fun isGranted(permission: String): Boolean {
+ return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
+ }
+}
diff --git a/permission_handler_android/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/permission_handler_android/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
deleted file mode 100644
index e793a000d..000000000
--- a/permission_handler_android/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.example.example
-
-import io.flutter.embedding.android.FlutterActivity
-
-class MainActivity: FlutterActivity() {
-}
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..9162f1008 100644
--- a/permission_handler_android/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/permission_handler_android/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.10.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
diff --git a/permission_handler_android/example/android/settings.gradle b/permission_handler_android/example/android/settings.gradle
index 56eb85cb1..d17bcaa14 100644
--- a/permission_handler_android/example/android/settings.gradle
+++ b/permission_handler_android/example/android/settings.gradle
@@ -18,7 +18,8 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
- id "com.android.application" version "8.7.0" apply false
+ id "org.jetbrains.kotlin.android" version "2.1.0" apply false
+ id "com.android.application" version "8.9.1" apply false
}
-include ":app"
\ No newline at end of file
+include ":app"
diff --git a/permission_handler_android/example/lib/main.dart b/permission_handler_android/example/lib/main.dart
index 4a0a6e052..48af2cc66 100644
--- a/permission_handler_android/example/lib/main.dart
+++ b/permission_handler_android/example/lib/main.dart
@@ -1,5 +1,6 @@
import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
void main() {
@@ -76,9 +77,14 @@ class PermissionWidget extends StatefulWidget {
class _PermissionState extends State {
_PermissionState();
+ static const MethodChannel _granularityChannel = MethodChannel(
+ 'location_granularity',
+ );
final PermissionHandlerPlatform _permissionHandler =
PermissionHandlerPlatform.instance;
PermissionStatus _permissionStatus = PermissionStatus.denied;
+ bool? _isFineGranted;
+ bool? _isCoarseGranted;
@override
void initState() {
@@ -91,7 +97,45 @@ class _PermissionState extends State {
final status = await _permissionHandler.checkPermissionStatus(
widget._permission,
);
+ if (!mounted) return;
setState(() => _permissionStatus = status);
+ await _updateGranularity();
+ }
+
+ Future _updateGranularity() async {
+ if (widget._permission != Permission.location) {
+ if (_isFineGranted != null || _isCoarseGranted != null) {
+ setState(() {
+ _isFineGranted = null;
+ _isCoarseGranted = null;
+ });
+ }
+ return;
+ }
+
+ try {
+ final result = await _granularityChannel.invokeMapMethod(
+ 'status',
+ );
+ if (!mounted) return;
+ setState(() {
+ _isFineGranted = result?['fine'];
+ _isCoarseGranted = result?['coarse'];
+ });
+ } catch (_) {
+ if (!mounted) return;
+ setState(() {
+ _isFineGranted = null;
+ _isCoarseGranted = null;
+ });
+ }
+ }
+
+ String granularityLabel() {
+ if (widget._permission != Permission.location) return '';
+ if (_isFineGranted == true) return 'Precise';
+ if (_isCoarseGranted == true) return 'Approximate';
+ return '';
}
Color getPermissionColor() {
@@ -109,27 +153,54 @@ class _PermissionState extends State {
@override
Widget build(BuildContext context) {
+ final extraGranularity = granularityLabel();
+ final subtitleText =
+ extraGranularity.isNotEmpty
+ ? '${_permissionStatus.toString()} ยท $extraGranularity'
+ : _permissionStatus.toString();
+
+ final trailingActions = [];
+ if (widget._permission is PermissionWithService) {
+ trailingActions.add(
+ IconButton(
+ icon: const Icon(Icons.info, color: Colors.white),
+ onPressed: () {
+ checkServiceStatus(
+ context,
+ widget._permission as PermissionWithService,
+ );
+ },
+ ),
+ );
+ }
+
+ final isApproximateOnly =
+ widget._permission == Permission.location &&
+ _isFineGranted != true &&
+ _isCoarseGranted == true;
+ if (isApproximateOnly) {
+ trailingActions.add(
+ IconButton(
+ icon: const Icon(Icons.my_location, color: Colors.white),
+ tooltip: 'Request precise',
+ onPressed: () => requestPermission(widget._permission),
+ ),
+ );
+ }
+
return ListTile(
title: Text(
widget._permission.toString(),
style: Theme.of(context).textTheme.bodyLarge,
),
subtitle: Text(
- _permissionStatus.toString(),
+ subtitleText,
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,
+ trailingActions.isEmpty
+ ? null
+ : Row(mainAxisSize: MainAxisSize.min, children: trailingActions),
onTap: () {
requestPermission(widget._permission);
},
@@ -152,8 +223,10 @@ class _PermissionState extends State {
Future requestPermission(Permission permission) async {
final status = await _permissionHandler.requestPermissions([permission]);
+ if (!mounted) return;
setState(() {
_permissionStatus = status[permission] ?? PermissionStatus.denied;
});
+ await _updateGranularity();
}
}