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(); } }