Skip to content

Commit 15630ad

Browse files
Merge pull request #40 from icapps/android-compile-issues
Updated plugin to target sdk 33
2 parents f127ee3 + 977d5d3 commit 15630ad

32 files changed

+613
-194
lines changed

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,42 @@ import background_location_tracker
7474
}
7575
```
7676

77+
## Flutter implementation
7778

78-
FAQ:
79+
Make sure you set the `@pragma('vm:entry-point')` to make sure you can find the callback in release.
80+
81+
```
82+
@pragma('vm:entry-point')
83+
void backgroundCallback() {
84+
BackgroundLocationTrackerManager.handleBackgroundUpdated(
85+
(data) async => Repo().update(data),
86+
);
87+
}
88+
89+
Future<void> main() async {
90+
WidgetsFlutterBinding.ensureInitialized();
91+
await BackgroundLocationTrackerManager.initialize(
92+
backgroundCallback,
93+
config: const BackgroundLocationTrackerConfig(
94+
loggingEnabled: true,
95+
androidConfig: AndroidConfig(
96+
notificationIcon: 'explore',
97+
trackingInterval: Duration(seconds: 4),
98+
distanceFilterMeters: null,
99+
),
100+
iOSConfig: IOSConfig(
101+
activityType: ActivityType.FITNESS,
102+
distanceFilterMeters: null,
103+
restartAfterKill: true,
104+
),
105+
),
106+
);
107+
108+
runApp(MyApp());
109+
}
110+
```
111+
112+
# FAQ:
79113

80114
#### I get a Unhandled Exception: MissingPluginException(No implementation found for method .... on channel ...)
81115

android/build.gradle

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,30 @@ group 'com.icapps.background_location_tracker'
22
version '1.0-SNAPSHOT'
33

44
buildscript {
5-
ext.kotlin_version = '1.3.50'
5+
ext.kotlin_version = '1.6.0'
66
repositories {
77
google()
8-
jcenter()
8+
mavenCentral()
99
}
1010

1111
dependencies {
12-
classpath 'com.android.tools.build:gradle:3.5.0'
12+
classpath 'com.android.tools.build:gradle:7.0.3'
1313
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1414
}
1515
}
1616

1717
rootProject.allprojects {
1818
repositories {
19+
mavenCentral()
1920
google()
20-
jcenter()
2121
}
2222
}
2323

2424
apply plugin: 'com.android.library'
2525
apply plugin: 'kotlin-android'
2626

2727
android {
28-
compileSdkVersion 29
28+
compileSdkVersion 33
2929

3030
sourceSets {
3131
main.java.srcDirs += 'src/main/kotlin'
@@ -40,6 +40,6 @@ android {
4040

4141
dependencies {
4242
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
43-
implementation 'com.google.android.gms:play-services-location:17.1.0'
44-
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
43+
implementation 'com.google.android.gms:play-services-location:20.0.0'
44+
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
4545
}

android/gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
33
zipStoreBase=GRADLE_USER_HOME
44
zipStorePath=wrapper/dists
5-
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
5+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.icapps.background_location_tracker">
33

4+
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
45
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
56
<uses-permission android:name="android.permission.WAKE_LOCK" />
67
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

android/src/main/kotlin/com/icapps/background_location_tracker/BackgroundLocationTrackerPlugin.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ class BackgroundLocationTrackerPlugin : FlutterPlugin, MethodCallHandler, Activi
154154
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
155155
}
156156

157-
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
157+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
158+
158159
override fun onActivityDestroyed(activity: Activity) {
159160
if (activity.hashCode() != registrarActivityHashCode) {
160161
return

android/src/main/kotlin/com/icapps/background_location_tracker/MethodCallHelper.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ internal class MethodCallHelper(private val ctx: Context) : MethodChannel.Method
4949
trackingIntervalKey
5050
)
5151
if (!call.checkRequiredFields(keys, result)) return
52-
val callbackHandle = call.argument<Long>(callbackHandleKey)!!
52+
val callbackHandle = getLongArgumentByKey(call, callbackHandleKey)!!
5353
val loggingEnabled = call.argument<Boolean>(loggingEnabledKey)!!
5454
val channelName = call.argument<String>(channelNameKey)!!
5555
val notificationBody = call.argument<String>(notificationBodyKey)!!
5656
val notificationIcon = call.argument<String>(notificationIconKey)
5757
val enableNotificationLocationUpdates = call.argument<Boolean>(enableNotificationLocationUpdatesKey)!!
5858
val cancelTrackingActionText = call.argument<String>(cancelTrackingActionTextKey)!!
5959
val enableCancelTrackingAction = call.argument<Boolean>(enableCancelTrackingActionKey)!!
60-
val trackingInterval = call.argument<Long>(trackingIntervalKey)!!
60+
val trackingInterval = getLongArgumentByKey(call, trackingIntervalKey)!!
6161
val distanceFilter = (call.argument<Double>(distanceFilterKey) ?: 0.0).toFloat()
6262
SharedPrefsUtil.saveLoggingEnabled(ctx, loggingEnabled)
6363
SharedPrefsUtil.saveTrackingInterval(ctx, trackingInterval)
@@ -69,6 +69,14 @@ internal class MethodCallHelper(private val ctx: Context) : MethodChannel.Method
6969
result.success(true)
7070
}
7171

72+
private fun getLongArgumentByKey(call: MethodCall, key: String): Long? {
73+
return try {
74+
call.argument<Number>(key)!!.toLong()
75+
} catch (exception: ClassCastException) {
76+
call.argument(key)
77+
}
78+
}
79+
7280
private fun isTracking(ctx: Context, call: MethodCall, result: MethodChannel.Result) = result.success(SharedPrefsUtil.isTracking(ctx))
7381

7482
private fun startTracking(ctx: Context, call: MethodCall, result: MethodChannel.Result) {

android/src/main/kotlin/com/icapps/background_location_tracker/flutter/FlutterBackgroundManager.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,28 @@ package com.icapps.background_location_tracker.flutter
22

33
import android.content.Context
44
import android.location.Location
5+
import android.os.Handler
6+
import android.os.Looper
57
import com.icapps.background_location_tracker.BackgroundLocationTrackerPlugin
68
import com.icapps.background_location_tracker.utils.Logger
79
import com.icapps.background_location_tracker.utils.SharedPrefsUtil
810
import io.flutter.embedding.engine.FlutterEngine
911
import io.flutter.embedding.engine.dart.DartExecutor
12+
import io.flutter.embedding.engine.loader.FlutterLoader
1013
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry
1114
import io.flutter.plugin.common.MethodCall
1215
import io.flutter.plugin.common.MethodChannel
1316
import io.flutter.view.FlutterCallbackInformation
14-
import io.flutter.view.FlutterMain
1517

1618
internal object FlutterBackgroundManager {
1719
private const val BACKGROUND_CHANNEL_NAME = "com.icapps.background_location_tracker/background_channel"
1820

21+
private val flutterLoader = FlutterLoader()
22+
1923
private fun getInitializedFlutterEngine(ctx: Context): FlutterEngine {
2024
Logger.debug("BackgroundManager", "Creating new engine")
2125

2226
val engine = FlutterEngine(ctx)
23-
FlutterMain.ensureInitializationComplete(ctx, null)
2427
//Backwards compatibility with v1. We register all the user's plugins.
2528
BackgroundLocationTrackerPlugin.pluginRegistryCallback?.registerWith(ShimPluginRegistry(engine))
2629
return engine
@@ -41,10 +44,15 @@ internal object FlutterBackgroundManager {
4144
}
4245
}
4346

44-
val callbackHandle = SharedPrefsUtil.getCallbackHandle(ctx)
45-
val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
46-
val dartBundlePath = FlutterMain.findAppBundlePath()
47-
engine.dartExecutor.executeDartCallback(DartExecutor.DartCallback(ctx.assets, dartBundlePath, callbackInfo))
47+
if (!flutterLoader.initialized()) {
48+
flutterLoader.startInitialization(ctx)
49+
}
50+
flutterLoader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
51+
val callbackHandle = SharedPrefsUtil.getCallbackHandle(ctx)
52+
val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
53+
val dartBundlePath = flutterLoader.findAppBundlePath()
54+
engine.dartExecutor.executeDartCallback(DartExecutor.DartCallback(ctx.assets, dartBundlePath, callbackInfo))
55+
}
4856
}
4957

5058
private fun handleInitialized(call: MethodCall, result: MethodChannel.Result, ctx: Context, channel: MethodChannel, location: Location, engine: FlutterEngine) {
@@ -58,7 +66,7 @@ internal object FlutterBackgroundManager {
5866
engine.destroy()
5967
}
6068

61-
override fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
69+
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
6270
Logger.debug("BackgroundManager", "Got error, destroy engine! $errorCode - $errorMessage : $errorDetails")
6371
engine.destroy()
6472
}

android/src/main/kotlin/com/icapps/background_location_tracker/service/LocationUpdatesService.kt

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import com.google.android.gms.location.LocationCallback
1717
import com.google.android.gms.location.LocationRequest
1818
import com.google.android.gms.location.LocationResult
1919
import com.google.android.gms.location.LocationServices
20+
import com.google.android.gms.location.Priority
2021
import com.icapps.background_location_tracker.flutter.FlutterBackgroundManager
2122
import com.icapps.background_location_tracker.utils.ActivityCounter
2223
import com.icapps.background_location_tracker.utils.Logger
@@ -100,17 +101,26 @@ internal class LocationUpdatesService : Service() {
100101
// and binds with this service. The service should cease to be a foreground service
101102
// when that happens.
102103
Logger.debug(TAG, "OnBind")
103-
stopForeground(true)
104+
stopForegroundService()
104105
changingConfiguration = false
105106
return binder
106107
}
107108

109+
private fun stopForegroundService() {
110+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
111+
stopForeground(STOP_FOREGROUND_REMOVE)
112+
} else {
113+
@Suppress("DEPRECATION")
114+
stopForeground(true)
115+
}
116+
}
117+
108118
override fun onRebind(intent: Intent) {
109119
// Called when a client (MainActivity in case of this sample) returns to the foreground
110120
// and binds once again with this service. The service should cease to be a foreground
111121
// service when that happens.
112122
Logger.debug(TAG, "OnRebind")
113-
stopForeground(true)
123+
stopForegroundService()
114124
changingConfiguration = false
115125
super.onRebind(intent)
116126
}
@@ -153,6 +163,8 @@ internal class LocationUpdatesService : Service() {
153163
} else {
154164
startService(Intent(applicationContext, LocationUpdatesService::class.java))
155165
}
166+
val locationRequest = locationRequest ?: return
167+
val locationCallback = locationCallback ?: return
156168
try {
157169
fusedLocationClient?.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
158170
} catch (unlikely: SecurityException) {
@@ -173,6 +185,7 @@ internal class LocationUpdatesService : Service() {
173185
wakeLock?.release();
174186
}
175187
Logger.debug(TAG, "Removing location updates")
188+
val locationCallback = locationCallback ?: return
176189
try {
177190
fusedLocationClient?.removeLocationUpdates(locationCallback)
178191
SharedPrefsUtil.saveIsTracking(this, false)
@@ -197,7 +210,8 @@ internal class LocationUpdatesService : Service() {
197210
}
198211
}
199212

200-
private fun onNewLocation(location: Location) {
213+
private fun onNewLocation(location: Location?) {
214+
if (location == null) return;
201215
Logger.debug(TAG, "New location: $location")
202216
this.location = location
203217

@@ -220,13 +234,11 @@ internal class LocationUpdatesService : Service() {
220234
private fun createLocationRequest() {
221235
val interval = SharedPrefsUtil.trackingInterval(this)
222236
val distanceFilter = SharedPrefsUtil.distanceFilter(this)
223-
locationRequest = LocationRequest()
224-
locationRequest?.let {
225-
it.interval = interval
226-
it.fastestInterval = interval / 2
227-
it.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
228-
it.setSmallestDisplacement(distanceFilter)
229-
}
237+
locationRequest = LocationRequest.create()
238+
.setInterval(interval)
239+
.setFastestInterval(interval / 2)
240+
.setPriority(Priority.PRIORITY_HIGH_ACCURACY)
241+
.setSmallestDisplacement(distanceFilter)
230242
}
231243

232244
/**

android/src/main/kotlin/com/icapps/background_location_tracker/utils/ActivityCounter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ object ActivityCounter : Application.ActivityLifecycleCallbacks {
3232
}
3333

3434
override fun onActivityStopped(activity: Activity) {}
35-
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
35+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
3636
override fun onActivityDestroyed(activity: Activity) {}
3737
}

android/src/main/kotlin/com/icapps/background_location_tracker/utils/NotificationUtil.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.icapps.background_location_tracker.utils
22

3+
import android.annotation.SuppressLint
34
import android.app.Notification
45
import android.app.NotificationChannel
56
import android.app.NotificationManager
67
import android.app.PendingIntent
78
import android.content.Context
89
import android.content.Intent
10+
import android.content.pm.ServiceInfo
911
import android.location.Location
1012
import android.os.Build
1113
import androidx.core.app.NotificationCompat
@@ -47,9 +49,18 @@ internal object NotificationUtil {
4749
private fun getNotification(context: Context, location: Location?): Notification {
4850
val intent = Intent(context, LocationUpdatesService::class.java)
4951
intent.putExtra(LocationUpdatesService.EXTRA_STARTED_FROM_NOTIFICATION, true)
50-
val cancelTrackingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
52+
val cancelTrackingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
53+
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
54+
} else {
55+
@Suppress("UnspecifiedImmutableFlag")
56+
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
57+
}
5158

52-
val clickPendingIntent = PendingIntent.getActivity(context, 0, context.packageManager.getLaunchIntentForPackage(context.packageName), 0)
59+
val clickPendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
60+
PendingIntent.getActivity(context, 0, context.packageManager.getLaunchIntentForPackage(context.packageName), PendingIntent.FLAG_IMMUTABLE)
61+
} else {
62+
PendingIntent.getActivity(context, 0, context.packageManager.getLaunchIntentForPackage(context.packageName), 0)
63+
}
5364

5465
val title = if (SharedPrefsUtil.isNotificationLocationUpdatesEnabled(context)) {
5566
String.format("Location Update: %s", DateFormat.getDateTimeInstance().format(Date()))
@@ -94,6 +105,10 @@ internal object NotificationUtil {
94105
}
95106

96107
fun startForeground(service: LocationUpdatesService, location: Location?) {
97-
service.startForeground(NOTIFICATION_ID, getNotification(service, location))
108+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
109+
service.startForeground(NOTIFICATION_ID, getNotification(service, location), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
110+
} else {
111+
service.startForeground(NOTIFICATION_ID, getNotification(service, location))
112+
}
98113
}
99114
}

0 commit comments

Comments
 (0)