Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion permission_handler_android/android/.classpath
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -636,6 +640,34 @@ void shouldShowRequestPermissionRationale(
successCallback.onSuccess(ActivityCompat.shouldShowRequestPermissionRationale(activity, names.get(0)));
}

@PermissionConstants.PermissionStatus
private int determineWhenInUseLocationStatus(@NonNull List<String> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
14 changes: 12 additions & 2 deletions permission_handler_android/example/android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id "com.android.application"
id "org.jetbrains.kotlin.android"
id "dev.flutter.flutter-gradle-plugin"
}

Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
android:label="permission_handler_example"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">

<activity android:name="io.flutter.embedding.android.FlutterActivity"
<activity android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.baseflow.permissionhandler.example

import android.Manifest
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)

MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "location_granularity")
.setMethodCallHandler { call, result ->
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
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 3 additions & 2 deletions permission_handler_android/example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
include ":app"
97 changes: 85 additions & 12 deletions permission_handler_android/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -76,9 +77,14 @@ class PermissionWidget extends StatefulWidget {
class _PermissionState extends State<PermissionWidget> {
_PermissionState();

static const MethodChannel _granularityChannel = MethodChannel(
'location_granularity',
);
final PermissionHandlerPlatform _permissionHandler =
PermissionHandlerPlatform.instance;
PermissionStatus _permissionStatus = PermissionStatus.denied;
bool? _isFineGranted;
bool? _isCoarseGranted;

@override
void initState() {
Expand All @@ -91,7 +97,45 @@ class _PermissionState extends State<PermissionWidget> {
final status = await _permissionHandler.checkPermissionStatus(
widget._permission,
);
if (!mounted) return;
setState(() => _permissionStatus = status);
await _updateGranularity();
}

Future<void> _updateGranularity() async {
if (widget._permission != Permission.location) {
if (_isFineGranted != null || _isCoarseGranted != null) {
setState(() {
_isFineGranted = null;
_isCoarseGranted = null;
});
}
return;
}

try {
final result = await _granularityChannel.invokeMapMethod<String, bool>(
'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() {
Expand All @@ -109,27 +153,54 @@ class _PermissionState extends State<PermissionWidget> {

@override
Widget build(BuildContext context) {
final extraGranularity = granularityLabel();
final subtitleText =
extraGranularity.isNotEmpty
? '${_permissionStatus.toString()} · $extraGranularity'
: _permissionStatus.toString();

final trailingActions = <Widget>[];
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);
},
Expand All @@ -152,8 +223,10 @@ class _PermissionState extends State<PermissionWidget> {
Future<void> requestPermission(Permission permission) async {
final status = await _permissionHandler.requestPermissions([permission]);

if (!mounted) return;
setState(() {
_permissionStatus = status[permission] ?? PermissionStatus.denied;
});
await _updateGranularity();
}
}