Skip to content

Commit

Permalink
Improve permissions (#52)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
lukaspili authored Feb 5, 2020
1 parent 8c2307f commit 87cb75b
Show file tree
Hide file tree
Showing 78 changed files with 1,457 additions and 1,289 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## [1.1.0]

- **Breaking change :** `Geolocation.requestLocationPermission` now takes a named parameter for permission
- **Breaking change :** New `GeolocationResultErrorType.permissionNotGranted` type. Previous meaning for `permissionDenied` is now divided in two different states:
- `permissionNotGranted`: User didn't accept nor decline the locationn permission request yet
- `permissionDenied`: User specifically declined the permission request
- 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).
- Fix background pause/resume on iOS
- Refactor iOS internal structure

## [1.0.2]

- Fix `Accuracy.nearestTenMeters` on iOS
Expand Down
65 changes: 28 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,13 @@ The plugin is under active development and the following features are planned so
| :----------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------: |
| ![](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) |

## Installation
### Installation

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

```yaml
dependencies:
geolocation: ^1.0.2
```
## Import
#### Android

Package is called geolocation, with Geolocation being the base class.
```dart
import 'package:geolocation/geolocation.dart';
```

**Note:** There is a known issue for integrating swift written plugin into Flutter project created with Objective-C template.
See issue [Flutter#16049](https://github.com/flutter/flutter/issues/16049) for help on integration.

### AndroidX Requirement

You may need to updated your '/android/gradle.properties' to include use of AndroidX.
Geolocation is dependent on AndroidX. Make sure to include the following settings to 'android/gradle.properties':

```
android.useAndroidX=true
Expand Down Expand Up @@ -99,13 +83,13 @@ Note that `ACCESS_FINE_LOCATION` permission includes `ACCESS_COARSE_LOCATION`.
## API

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

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

### Check if location service is operational

API documentation: https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/isLocationOperational.html
API documentation: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/isLocationOperational.html

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

### Request location permission

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

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

API documentation: https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/requestLocationPermission.html
API documentation: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/requestLocationPermission.html

```dart
final GeolocationResult result = await Geolocation.requestLocationPermission(const LocationPermission(
android: LocationPermissionAndroid.fine,
ios: LocationPermissionIOS.always,
));
final GeolocationResult result = await Geolocation.requestLocationPermission(
const LocationPermission(
android: LocationPermissionAndroid.fine,
ios: LocationPermissionIOS.always,
),
openSettingsIfDenied: true,
);
if(result.isSuccessful) {
// location permission is granted (or was already granted before making the request)
} else {
// location permission is not granted
// user might have denied, but it's also possible that location service is not enabled, restricted, and user never saw the permission request dialog
// 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.
}
```

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

- Last known location (best on Android):
https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/lastKnownLocation.html
https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/lastKnownLocation.html
- Single location update (best on iOS):
https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/singleLocationUpdate.html
https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/singleLocationUpdate.html
- Current location (best of both worlds, tries to retrieve last known location on Android, otherwise requests a single location update):
https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/currentLocation.html
https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/currentLocation.html

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

### Continuous location updates

API documentation: https://pub.dartlang.org/documentation/geolocation/0.2.1/geolocation/Geolocation/locationUpdates.html
API documentation: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/locationUpdates.html

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

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

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

```dart
LocationResult result = await Geolocation.lastKnownLocation();
Expand All @@ -214,10 +201,14 @@ if (result.isSuccessful) {
// location services disabled on device
// might be that GPS is turned off, or parental control (android)
break;
case GeolocationResultErrorType.permissionNotGranted:
// location has not been requested yet
// app must request permission in order to access the location
break;
case GeolocationResultErrorType.permissionDenied:
// user denied location permission request
// rejection is final on iOS, and can be on Android
// user will need to manually allow the app from the settings
// user denied the location permission for the app
// rejection is final on iOS, and can be on Android if user checks `don't ask again`
// user will need to manually allow the app from the settings, see requestLocationPermission(openSettingsIfDenied: true)
break;
case GeolocationResultErrorType.playServicesUnavailable:
// android only
Expand Down
2 changes: 0 additions & 2 deletions android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,3 @@
.DS_Store
/build
/captures
.project
.settings
10 changes: 5 additions & 5 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
group 'io.intheloup.geolocation'
group 'app.loup.geolocation'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.3.41'
ext.kotlin_version = '1.3.61'
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.android.tools.build:gradle:3.5.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down Expand Up @@ -40,10 +40,10 @@ android {
}

dependencies {
implementation 'androidx.core:core:1.0.2'
implementation 'androidx.core:core:1.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-RC2'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-RC2"
api "com.google.android.gms:play-services-location:17.0.0"
implementation 'com.squareup.moshi:moshi:1.5.0'
}
}
3 changes: 2 additions & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
android.enableJetifier=true
5 changes: 5 additions & 0 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
2 changes: 1 addition & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.intheloup.geolocation">
package="app.loup.geolocation">
</manifest>
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// Copyright (c) 2018 Loup Inc.
// Licensed under Apache License v2.0

package io.intheloup.geolocation
package app.loup.geolocation

import app.loup.geolocation.data.*
import com.squareup.moshi.Moshi
import io.intheloup.geolocation.data.LocationUpdatesRequest
import io.intheloup.geolocation.data.Permission
import io.intheloup.geolocation.data.Priority
import io.intheloup.geolocation.data.Result

object Codec {

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

fun decodePermissionRequest(arguments: Any?): PermissionRequest =
moshi.adapter(PermissionRequest::class.java).fromJson(arguments!! as String)!!

}
140 changes: 140 additions & 0 deletions android/src/main/kotlin/app/loup/geolocation/GeolocationPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package app.loup.geolocation

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import androidx.annotation.NonNull
import app.loup.geolocation.helper.log
import app.loup.geolocation.location.LocationClient
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry.Registrar

public class GeolocationPlugin : FlutterPlugin, ActivityAware, Application.ActivityLifecycleCallbacks {

companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val instance = GeolocationPlugin()
register(instance, registrar.activeContext(), registrar.messenger())

instance.locationClient.activity = registrar.activity()

registrar.addRequestPermissionsResultListener(instance.locationClient.permissionResultListener)
registrar.addActivityResultListener(instance.locationClient.activityResultListener)
registrar.activity().application.registerActivityLifecycleCallbacks(instance)
}

private fun register(instance: GeolocationPlugin, context: Context, binaryMessenger: BinaryMessenger) {
instance.locationClient = LocationClient(context)
instance.handler = Handler(instance.locationClient)

val methodChannel = MethodChannel(binaryMessenger, "geolocation/location")
val eventChannel = EventChannel(binaryMessenger, "geolocation/locationUpdates")

methodChannel.setMethodCallHandler(instance.handler)
eventChannel.setStreamHandler(instance.handler)
}
}

private lateinit var locationClient: LocationClient
private lateinit var handler: Handler
private var activityBinding: ActivityPluginBinding? = null

private fun attachToActivity(binding: ActivityPluginBinding) {
if (activityBinding != null) {
detachFromActivity()
}
activityBinding = binding

locationClient.activity = binding.activity

binding.addRequestPermissionsResultListener(locationClient.permissionResultListener)
binding.addActivityResultListener(locationClient.activityResultListener)
binding.activity.application.registerActivityLifecycleCallbacks(this)
}

private fun detachFromActivity() {
val binding = activityBinding ?: return

locationClient.activity = null

binding.removeRequestPermissionsResultListener(locationClient.permissionResultListener)
binding.removeActivityResultListener(locationClient.activityResultListener)
binding.activity.application.unregisterActivityLifecycleCallbacks(this)

activityBinding = null
}


// FlutterPlugin

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
register(this, flutterPluginBinding.applicationContext, flutterPluginBinding.binaryMessenger)
}

override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
}


// ActivityAware

override fun onAttachedToActivity(binding: ActivityPluginBinding) {
attachToActivity(binding)
}

override fun onDetachedFromActivity() {
detachFromActivity()
}

override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
onAttachedToActivity(binding)
}

override fun onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity()
}


// Application.ActivityLifecycleCallbacks

override fun onActivityPaused(activity: Activity?) {
locationClient.pause()
}

override fun onActivityResumed(activity: Activity?) {
locationClient.resume()
}

override fun onActivityStarted(activity: Activity?) {

}

override fun onActivityDestroyed(activity: Activity?) {

}

override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {

}

override fun onActivityStopped(activity: Activity?) {

}

override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {

}


object Intents {
const val LocationPermissionRequestId = 12234
const val LocationPermissionSettingsRequestId = 12230
const val EnableLocationSettingsRequestId = 12237
}
}
Loading

0 comments on commit 87cb75b

Please sign in to comment.