Skip to content

Commit 87cb75b

Browse files
author
Lukas Piliszczuk
authored
Improve permissions (#52)
* Differenciate permission not granted / permission denied (wip). * Upgrade project structure to post v1.12. * Clean. * Update changelog. * Update readme. * Open settings if denied for android. * Update description. * Add plateforms to pubspec.
1 parent 8c2307f commit 87cb75b

File tree

78 files changed

+1457
-1289
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+1457
-1289
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## [1.1.0]
2+
3+
- **Breaking change :** `Geolocation.requestLocationPermission` now takes a named parameter for permission
4+
- **Breaking change :** New `GeolocationResultErrorType.permissionNotGranted` type. Previous meaning for `permissionDenied` is now divided in two different states:
5+
- `permissionNotGranted`: User didn't accept nor decline the locationn permission request yet
6+
- `permissionDenied`: User specifically declined the permission request
7+
- Ability to open settings when requesting permission, and user already declined the permission previously: `Geolocation.requestLocationPermission(openSettingsIfDenied: true)` (opening the settings as fallback is now the default behaviour).
8+
- Fix background pause/resume on iOS
9+
- Refactor iOS internal structure
10+
111
## [1.0.2]
212

313
- Fix `Accuracy.nearestTenMeters` on iOS

README.md

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,13 @@ The plugin is under active development and the following features are planned so
2222
| :----------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------: |
2323
| ![](https://github.com/loup-v/geolocation/blob/master/doc/android_screenshot.jpg?raw=true) | ![](https://github.com/loup-v/geolocation/blob/master/doc/ios_screenshot.jpg?raw=true) |
2424

25-
## Installation
25+
### Installation
2626

27-
Add geolocation to your pubspec.yaml:
27+
Follow the instructions: https://pub.dev/packages/geolocation#-installing-tab-
2828

29-
```yaml
30-
dependencies:
31-
geolocation: ^1.0.2
32-
```
33-
34-
## Import
29+
#### Android
3530

36-
Package is called geolocation, with Geolocation being the base class.
37-
38-
```dart
39-
import 'package:geolocation/geolocation.dart';
40-
```
41-
42-
**Note:** There is a known issue for integrating swift written plugin into Flutter project created with Objective-C template.
43-
See issue [Flutter#16049](https://github.com/flutter/flutter/issues/16049) for help on integration.
44-
45-
### AndroidX Requirement
46-
47-
You may need to updated your '/android/gradle.properties' to include use of AndroidX.
31+
Geolocation is dependent on AndroidX. Make sure to include the following settings to 'android/gradle.properties':
4832

4933
```
5034
android.useAndroidX=true
@@ -99,13 +83,13 @@ Note that `ACCESS_FINE_LOCATION` permission includes `ACCESS_COARSE_LOCATION`.
9983
## API
10084

10185
For more complete documentation on all usage, check the API documentation:
102-
https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/geolocation-library.html
86+
https://pub.dartlang.org/documentation/geolocation/latest/geolocation/geolocation-library.html
10387

10488
You can also check the example project that showcase a comprehensive usage of Geolocation plugin.
10589

10690
### Check if location service is operational
10791

108-
API documentation: https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/isLocationOperational.html
92+
API documentation: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/isLocationOperational.html
10993

11094
```dart
11195
final GeolocationResult result = await Geolocation.isLocationOperational();
@@ -118,24 +102,27 @@ if(result.isSuccessful) {
118102

119103
### Request location permission
120104

121-
On Android (api 23+) and iOS, geolocation needs to request permission at runtime.
105+
On Android (api 23+) and iOS, apps need to request location permission at runtime.
122106

123107
_Note: You are not required to request permission manually.
124108
Geolocation plugin will request permission automatically if it's needed, when you make a location request._
125109

126-
API documentation: https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/requestLocationPermission.html
110+
API documentation: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/requestLocationPermission.html
127111

128112
```dart
129-
final GeolocationResult result = await Geolocation.requestLocationPermission(const LocationPermission(
130-
android: LocationPermissionAndroid.fine,
131-
ios: LocationPermissionIOS.always,
132-
));
113+
final GeolocationResult result = await Geolocation.requestLocationPermission(
114+
const LocationPermission(
115+
android: LocationPermissionAndroid.fine,
116+
ios: LocationPermissionIOS.always,
117+
),
118+
openSettingsIfDenied: true,
119+
);
133120
134121
if(result.isSuccessful) {
135122
// location permission is granted (or was already granted before making the request)
136123
} else {
137124
// location permission is not granted
138-
// user might have denied, but it's also possible that location service is not enabled, restricted, and user never saw the permission request dialog
125+
// user might have denied, but it's also possible that location service is not enabled, restricted, and user never saw the permission request dialog. Check the result.error.type for details.
139126
}
140127
```
141128

@@ -144,11 +131,11 @@ if(result.isSuccessful) {
144131
Geolocation offers three methods:
145132

146133
- Last known location (best on Android):
147-
https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/lastKnownLocation.html
134+
https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/lastKnownLocation.html
148135
- Single location update (best on iOS):
149-
https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/singleLocationUpdate.html
136+
https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/singleLocationUpdate.html
150137
- Current location (best of both worlds, tries to retrieve last known location on Android, otherwise requests a single location update):
151-
https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/currentLocation.html
138+
https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/currentLocation.html
152139

153140
```dart
154141
// get last known location, which is a future rather than a stream (best for android)
@@ -170,7 +157,7 @@ StreamSubscription<LocationResult> subscription = Geolocation.currentLocation(ac
170157

171158
### Continuous location updates
172159

173-
API documentation: https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/locationUpdates.html
160+
API documentation: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/locationUpdates.html
174161

175162
```dart
176163
StreamSubscription<LocationResult> subscription = Geolocation.locationUpdates(
@@ -193,7 +180,7 @@ subscription.cancel();
193180

194181
Location request return either a `LocationResult` future or a stream of `LocationResult`.
195182

196-
API documentation: https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/LocationResult-class.html
183+
API documentation: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/LocationResult-class.html
197184

198185
```dart
199186
LocationResult result = await Geolocation.lastKnownLocation();
@@ -214,10 +201,14 @@ if (result.isSuccessful) {
214201
// location services disabled on device
215202
// might be that GPS is turned off, or parental control (android)
216203
break;
204+
case GeolocationResultErrorType.permissionNotGranted:
205+
// location has not been requested yet
206+
// app must request permission in order to access the location
207+
break;
217208
case GeolocationResultErrorType.permissionDenied:
218-
// user denied location permission request
219-
// rejection is final on iOS, and can be on Android
220-
// user will need to manually allow the app from the settings
209+
// user denied the location permission for the app
210+
// rejection is final on iOS, and can be on Android if user checks `don't ask again`
211+
// user will need to manually allow the app from the settings, see requestLocationPermission(openSettingsIfDenied: true)
221212
break;
222213
case GeolocationResultErrorType.playServicesUnavailable:
223214
// android only

android/.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,3 @@
66
.DS_Store
77
/build
88
/captures
9-
.project
10-
.settings

android/build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
group 'io.intheloup.geolocation'
1+
group 'app.loup.geolocation'
22
version '1.0-SNAPSHOT'
33

44
buildscript {
5-
ext.kotlin_version = '1.3.41'
5+
ext.kotlin_version = '1.3.61'
66
repositories {
77
google()
88
jcenter()
99
}
1010

1111
dependencies {
12-
classpath 'com.android.tools.build:gradle:3.3.0'
12+
classpath 'com.android.tools.build:gradle:3.5.3'
1313
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1414
}
1515
}
@@ -40,10 +40,10 @@ android {
4040
}
4141

4242
dependencies {
43-
implementation 'androidx.core:core:1.0.2'
43+
implementation 'androidx.core:core:1.1.0'
4444
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
4545
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-RC2'
4646
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-RC2"
4747
api "com.google.android.gms:play-services-location:17.0.0"
4848
implementation 'com.squareup.moshi:moshi:1.5.0'
49-
}
49+
}

android/gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
org.gradle.jvmargs=-Xmx1536M
2+
android.enableR8=true
23
android.useAndroidX=true
3-
android.enableJetifier=true
4+
android.enableJetifier=true
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
zipStoreBase=GRADLE_USER_HOME
4+
zipStorePath=wrapper/dists
5+
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2-
package="io.intheloup.geolocation">
2+
package="app.loup.geolocation">
33
</manifest>

android/src/main/kotlin/io/intheloup/geolocation/Codec.kt renamed to android/src/main/kotlin/app/loup/geolocation/Codec.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
// Copyright (c) 2018 Loup Inc.
22
// Licensed under Apache License v2.0
33

4-
package io.intheloup.geolocation
4+
package app.loup.geolocation
55

6+
import app.loup.geolocation.data.*
67
import com.squareup.moshi.Moshi
7-
import io.intheloup.geolocation.data.LocationUpdatesRequest
8-
import io.intheloup.geolocation.data.Permission
9-
import io.intheloup.geolocation.data.Priority
10-
import io.intheloup.geolocation.data.Result
118

129
object Codec {
1310

@@ -29,4 +26,7 @@ object Codec {
2926
fun decodeLocationUpdatesRequest(arguments: Any?): LocationUpdatesRequest =
3027
moshi.adapter(LocationUpdatesRequest::class.java).fromJson(arguments!! as String)!!
3128

29+
fun decodePermissionRequest(arguments: Any?): PermissionRequest =
30+
moshi.adapter(PermissionRequest::class.java).fromJson(arguments!! as String)!!
31+
3232
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package app.loup.geolocation
2+
3+
import android.app.Activity
4+
import android.app.Application
5+
import android.content.Context
6+
import android.os.Bundle
7+
import androidx.annotation.NonNull
8+
import app.loup.geolocation.helper.log
9+
import app.loup.geolocation.location.LocationClient
10+
import io.flutter.embedding.engine.plugins.FlutterPlugin
11+
import io.flutter.embedding.engine.plugins.activity.ActivityAware
12+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
13+
import io.flutter.plugin.common.BinaryMessenger
14+
import io.flutter.plugin.common.EventChannel
15+
import io.flutter.plugin.common.MethodChannel
16+
import io.flutter.plugin.common.PluginRegistry.Registrar
17+
18+
public class GeolocationPlugin : FlutterPlugin, ActivityAware, Application.ActivityLifecycleCallbacks {
19+
20+
companion object {
21+
@JvmStatic
22+
fun registerWith(registrar: Registrar) {
23+
val instance = GeolocationPlugin()
24+
register(instance, registrar.activeContext(), registrar.messenger())
25+
26+
instance.locationClient.activity = registrar.activity()
27+
28+
registrar.addRequestPermissionsResultListener(instance.locationClient.permissionResultListener)
29+
registrar.addActivityResultListener(instance.locationClient.activityResultListener)
30+
registrar.activity().application.registerActivityLifecycleCallbacks(instance)
31+
}
32+
33+
private fun register(instance: GeolocationPlugin, context: Context, binaryMessenger: BinaryMessenger) {
34+
instance.locationClient = LocationClient(context)
35+
instance.handler = Handler(instance.locationClient)
36+
37+
val methodChannel = MethodChannel(binaryMessenger, "geolocation/location")
38+
val eventChannel = EventChannel(binaryMessenger, "geolocation/locationUpdates")
39+
40+
methodChannel.setMethodCallHandler(instance.handler)
41+
eventChannel.setStreamHandler(instance.handler)
42+
}
43+
}
44+
45+
private lateinit var locationClient: LocationClient
46+
private lateinit var handler: Handler
47+
private var activityBinding: ActivityPluginBinding? = null
48+
49+
private fun attachToActivity(binding: ActivityPluginBinding) {
50+
if (activityBinding != null) {
51+
detachFromActivity()
52+
}
53+
activityBinding = binding
54+
55+
locationClient.activity = binding.activity
56+
57+
binding.addRequestPermissionsResultListener(locationClient.permissionResultListener)
58+
binding.addActivityResultListener(locationClient.activityResultListener)
59+
binding.activity.application.registerActivityLifecycleCallbacks(this)
60+
}
61+
62+
private fun detachFromActivity() {
63+
val binding = activityBinding ?: return
64+
65+
locationClient.activity = null
66+
67+
binding.removeRequestPermissionsResultListener(locationClient.permissionResultListener)
68+
binding.removeActivityResultListener(locationClient.activityResultListener)
69+
binding.activity.application.unregisterActivityLifecycleCallbacks(this)
70+
71+
activityBinding = null
72+
}
73+
74+
75+
// FlutterPlugin
76+
77+
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
78+
register(this, flutterPluginBinding.applicationContext, flutterPluginBinding.binaryMessenger)
79+
}
80+
81+
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
82+
}
83+
84+
85+
// ActivityAware
86+
87+
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
88+
attachToActivity(binding)
89+
}
90+
91+
override fun onDetachedFromActivity() {
92+
detachFromActivity()
93+
}
94+
95+
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
96+
onAttachedToActivity(binding)
97+
}
98+
99+
override fun onDetachedFromActivityForConfigChanges() {
100+
onDetachedFromActivity()
101+
}
102+
103+
104+
// Application.ActivityLifecycleCallbacks
105+
106+
override fun onActivityPaused(activity: Activity?) {
107+
locationClient.pause()
108+
}
109+
110+
override fun onActivityResumed(activity: Activity?) {
111+
locationClient.resume()
112+
}
113+
114+
override fun onActivityStarted(activity: Activity?) {
115+
116+
}
117+
118+
override fun onActivityDestroyed(activity: Activity?) {
119+
120+
}
121+
122+
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
123+
124+
}
125+
126+
override fun onActivityStopped(activity: Activity?) {
127+
128+
}
129+
130+
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
131+
132+
}
133+
134+
135+
object Intents {
136+
const val LocationPermissionRequestId = 12234
137+
const val LocationPermissionSettingsRequestId = 12230
138+
const val EnableLocationSettingsRequestId = 12237
139+
}
140+
}

0 commit comments

Comments
 (0)