Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bkonyi committed Sep 16, 2018
0 parents commit d6c88bb
Show file tree
Hide file tree
Showing 96 changed files with 3,135 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
@@ -0,0 +1,8 @@
.DS_Store
.dart_tool/

.packages
.pub/
pubspec.lock

build/
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,3 @@
## 0.0.1

* TODO: Describe initial release.
1 change: 1 addition & 0 deletions LICENSE
@@ -0,0 +1 @@
TODO: Add your license here.
107 changes: 107 additions & 0 deletions README.md
@@ -0,0 +1,107 @@
# Geofencing

A sample geofencing plugin with background execution support for Flutter.

## Getting Started
This plugin works on both Android and iOS. Follow the instructions in the following sections for the
platforms which are to be targeted.

### Android

Add the following lines to your `AndroidManifest.xml` to register the background service for
geofencing:

```xml
<receiver android:name="io.flutter.plugins.geofencing.GeofencingBroadcastReceiver"
android:enabled="true" android:exported="true"/>
<service android:name="io.flutter.plugins.geofencing.GeofencingService"
android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true"/>
```

Also request the correct permissions for geofencing:

```xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
```

Finally, create either `Application.kt` or `Application.java` in the same directory as `MainActivity`.

For `Application.kt`, use the following:

```kotlin
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate();
GeofencingService.setPluginRegistrant(this);
}

override fun registerWith(registry: PluginRegistry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
```

For `Application.java`, use the following:

```java
public class Application extends FlutterApplication implements PluginRegistrantCallback {
@Override
public void onCreate() {
super.onCreate();
GeofencingService.setPluginRegistrant(this);
}

@Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
```

Which must also be referenced in `AndroidManifest.xml`:

```xml
<application
android:name=".Application"
...
```

### iOS

Add the following lines to your Info.plist:

```xml
<dict>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>YOUR DESCRIPTION HERE</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>YOUR DESCRIPTION HERE</string>
...
```

And request the correct permissions for geofencing:

```xml
<dict>
...
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>location-services</string>
<string>gps</string>
<string>armv7</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
...
</dict>
```

### Need Help?

For help getting started with Flutter, view our online
[documentation](https://flutter.io/).

For help on editing plugin code, view the [documentation](https://flutter.io/developing-packages/#edit-plugin-package).
8 changes: 8 additions & 0 deletions android/.gitignore
@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
45 changes: 45 additions & 0 deletions android/build.gradle
@@ -0,0 +1,45 @@
group 'io.flutter.plugins.geofencing'
version '1.0-SNAPSHOT'

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

dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

rootProject.allprojects {
repositories {
google()
jcenter()
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 27

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation "com.google.android.gms:play-services-location:15.0.1"
}
1 change: 1 addition & 0 deletions android/gradle.properties
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1536M
1 change: 1 addition & 0 deletions android/settings.gradle
@@ -0,0 +1 @@
rootProject.name = 'geofencing'
3 changes: 3 additions & 0 deletions android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.flutter.plugins.geofencing">
</manifest>
@@ -0,0 +1,22 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.geofencing

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import io.flutter.view.FlutterMain


class GeofencingBroadcastReceiver : BroadcastReceiver() {
companion object {
private const val TAG = "GeofencingBroadcastReceiver"
}
override fun onReceive(context: Context, intent: Intent) {
FlutterMain.ensureInitializationComplete(context, null)
GeofencingService.enqueueWork(context, intent)
}
}
@@ -0,0 +1,137 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.geofencing

import android.Manifest
import android.app.Activity
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofencingRequest
import com.google.android.gms.location.LocationServices
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.PluginRegistry.Registrar

class GeofencingPlugin(context: Context, activity: Activity?) : MethodCallHandler {
private val mContext = context
private val mActivity = activity
private val mGeofencingClient = LocationServices.getGeofencingClient(mContext)

companion object {
@JvmStatic
private val TAG = "GeofencingPlugin"
@JvmStatic
val SHARED_PREFERENCES_KEY = "geofencing_plugin_cache"
@JvmStatic
val CALLBACK_HANDLE_KEY = "callback_handle"
@JvmStatic
val CALLBACK_DISPATCHER_HANDLE_KEY = "callback_dispatch_handler"
@JvmStatic
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)

@JvmStatic
fun registerWith(registrar: Registrar) {
val plugin = GeofencingPlugin(registrar.context(), registrar.activity())
val channel = MethodChannel(registrar.messenger(), "plugins.flutter.io/geofencing_plugin")
channel.setMethodCallHandler(plugin)
}
}

override fun onMethodCall(call: MethodCall, result: Result) {
val args = call.arguments() as? ArrayList<*>
when(call.method) {
"GeofencingPlugin.initializeService" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mActivity?.requestPermissions(REQUIRED_PERMISSIONS, 12312)
}
initializeService(args)
result.success(true)
}
"GeofencingPlugin.registerGeofence" -> registerGeofence(args, result)
"GeofencingPlugin.removeGeofence" -> removeGeofence(args, result)
else -> result.notImplemented()
}
}

private fun initializeService(args: ArrayList<*>?) {
Log.d(TAG, "Initializing GeofencingService")
val callbackHandle = args!![0] as Long
mContext.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
.edit()
.putLong(CALLBACK_DISPATCHER_HANDLE_KEY, callbackHandle)
.apply()
}

private fun getGeofencingRequest(geofence: Geofence, initialTrigger: Int): GeofencingRequest {
return GeofencingRequest.Builder().apply {
setInitialTrigger(initialTrigger)
addGeofence(geofence)
}.build()
}

private fun getGeofencePendingIndent(callbackHandle: Long): PendingIntent {
val intent = Intent(mContext, GeofencingBroadcastReceiver::class.java)
.putExtra(CALLBACK_HANDLE_KEY, callbackHandle)
return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}

private fun registerGeofence(args: ArrayList<*>?, result: Result) {
val callbackHandle = args!![0] as Long
val id = args[1] as String
val lat = args[2] as Double
val long = args[3] as Double
val radius = (args[4] as Double).toFloat()
val fenceTriggers = args[5] as Int
val initialTriggers = args[6] as Int
val expirationDuration = (args[7] as Int).toLong()
val loiteringDelay = args[8] as Int
val notificationResponsiveness = args[9] as Int
val geofence = Geofence.Builder()
.setRequestId(id)
.setCircularRegion(lat, long, radius)
.setTransitionTypes(initialTriggers)
.setLoiteringDelay(loiteringDelay)
.setNotificationResponsiveness(notificationResponsiveness)
.setExpirationDuration(expirationDuration)
.build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
(mContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_DENIED)) {
val msg = "'registerGeofence' requires the ACCESS_FINE_LOCATION permission."
Log.w(TAG, msg)
result.error(msg, null, null)
}
mGeofencingClient.addGeofences(getGeofencingRequest(geofence, fenceTriggers),
getGeofencePendingIndent(callbackHandle))?.run {
addOnSuccessListener {
Log.i(TAG, "Successfully added geofence")
result.success(true)
}
addOnFailureListener {
Log.e(TAG, "Failed to add geofence: $it")
result.error(it.toString(), null, null)
}
}
}

private fun removeGeofence(args: ArrayList<*>?, result: Result) {
val ids = listOf(args!![0] as String)
mGeofencingClient.removeGeofences(ids).run {
addOnSuccessListener {
result.success(true)
}
addOnFailureListener {
result.error(it.toString(), null, null)
}
}
}
}

0 comments on commit d6c88bb

Please sign in to comment.