diff --git a/.circleci/config.yml b/.circleci/config.yml index 13f201bf0..e48c963cf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,8 @@ jobs: build_and_test: working_directory: ~/sdk docker: - - image: circleci/android:api-30 + - image: cimg/android:2023.07 + resource_class: large steps: - checkout - run: diff --git a/build.gradle b/build.gradle index 2cca7b3f3..d921a36d5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = "1.5.21" + ext.kotlin_version = "1.6.20" repositories { google() mavenCentral() @@ -8,7 +8,7 @@ buildscript { } } dependencies { - classpath "com.android.tools.build:gradle:4.2.2" + classpath "com.android.tools.build:gradle:7.4.2" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.4.32" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9c953e830..db4181846 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed May 26 11:05:21 EDT 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip diff --git a/sdk/build.gradle b/sdk/build.gradle index f7ae41fe3..1135216fa 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -10,7 +10,7 @@ apply plugin: "org.jetbrains.dokka" apply plugin: 'io.radar.mvnpublish' ext { - radarVersion = '3.9.7' + radarVersion = '3.9.8' } String buildNumber = ".${System.currentTimeMillis()}" @@ -29,7 +29,7 @@ if (githubRelease) { } android { - compileSdkVersion 31 + compileSdkVersion 34 compileOptions { coreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 @@ -40,7 +40,7 @@ android { } defaultConfig { minSdkVersion 16 - targetSdkVersion 31 + targetSdkVersion 34 buildConfigField "String", "VERSION_NAME", "\"$radarVersion\"" multiDexEnabled = true } @@ -62,15 +62,15 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) - coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5") + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "androidx.appcompat:appcompat:1.4.0" implementation "androidx.core:core-ktx:1.7.0" implementation "com.google.android.gms:play-services-location:21.0.1" compileOnly "com.huawei.hms:location:6.4.0.300" compileOnly "com.google.android.play:integrity:1.2.0" - testImplementation "androidx.test.ext:junit:1.1.3" - testImplementation "org.robolectric:robolectric:4.5.1" + testImplementation "androidx.test.ext:junit:1.1.5" + testImplementation "org.robolectric:robolectric:4.10" testImplementation 'org.json:json:20211205' testImplementation "com.google.android.play:integrity:1.2.0" } diff --git a/sdk/src/main/AndroidManifest.xml b/sdk/src/main/AndroidManifest.xml index e139781e4..f7a3fb413 100644 --- a/sdk/src/main/AndroidManifest.xml +++ b/sdk/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + Unit)? ) abstract fun getLocationFromGeofenceIntent( diff --git a/sdk/src/main/java/io/radar/sdk/RadarActivityLifecycleCallbacks.kt b/sdk/src/main/java/io/radar/sdk/RadarActivityLifecycleCallbacks.kt index accef3215..838156de8 100644 --- a/sdk/src/main/java/io/radar/sdk/RadarActivityLifecycleCallbacks.kt +++ b/sdk/src/main/java/io/radar/sdk/RadarActivityLifecycleCallbacks.kt @@ -72,7 +72,7 @@ internal class RadarActivityLifecycleCallbacks( override fun dispatchTouchEvent(event: MotionEvent): Boolean { try { val inputDevice = InputDevice.getDevice(event.deviceId) - if (event.getToolType(0) == MotionEvent.TOOL_TYPE_UNKNOWN || inputDevice.isVirtual) { + if (event.getToolType(0) == MotionEvent.TOOL_TYPE_UNKNOWN || inputDevice?.isVirtual == true) { RadarSettings.setSharing(activity.applicationContext, true) } } catch (e: Exception) { diff --git a/sdk/src/main/java/io/radar/sdk/RadarForegroundService.kt b/sdk/src/main/java/io/radar/sdk/RadarForegroundService.kt index 283039846..186cb0b36 100644 --- a/sdk/src/main/java/io/radar/sdk/RadarForegroundService.kt +++ b/sdk/src/main/java/io/radar/sdk/RadarForegroundService.kt @@ -1,7 +1,9 @@ package io.radar.sdk +import android.annotation.SuppressLint import android.app.* import android.content.Intent +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION import android.graphics.Color import android.os.Build import android.os.Bundle @@ -47,6 +49,7 @@ class RadarForegroundService : Service() { return START_STICKY } + @SuppressLint("DiscouragedApi") private fun startForegroundService(extras: Bundle?) { val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager manager.deleteNotificationChannel("RadarSDK") @@ -94,7 +97,11 @@ class RadarForegroundService : Service() { logger.e("Error setting foreground service content intent", RadarLogType.SDK_EXCEPTION, e) } val notification = builder.build() - startForeground(id, notification) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startForeground(id, notification, FOREGROUND_SERVICE_TYPE_LOCATION); + } else { + startForeground(id, notification) + } } override fun onBind(intent: Intent): IBinder? { diff --git a/sdk/src/main/java/io/radar/sdk/RadarGoogleLocationClient.kt b/sdk/src/main/java/io/radar/sdk/RadarGoogleLocationClient.kt index 0ee1ee9ae..9adfba519 100644 --- a/sdk/src/main/java/io/radar/sdk/RadarGoogleLocationClient.kt +++ b/sdk/src/main/java/io/radar/sdk/RadarGoogleLocationClient.kt @@ -125,9 +125,17 @@ internal class RadarGoogleLocationClient( } override fun removeGeofences( - pendingIntent: PendingIntent + pendingIntent: PendingIntent, + block: ((success: Boolean) -> Unit)? ) { - geofencingClient.removeGeofences(pendingIntent) + geofencingClient.removeGeofences(pendingIntent).run { + addOnSuccessListener { + block?.invoke(true) + } + addOnFailureListener { + block?.invoke(false) + } + } } private fun priorityForDesiredAccuracy(desiredAccuracy: RadarTrackingOptions.RadarTrackingOptionsDesiredAccuracy) = diff --git a/sdk/src/main/java/io/radar/sdk/RadarHuaweiLocationClient.kt b/sdk/src/main/java/io/radar/sdk/RadarHuaweiLocationClient.kt index ec82b479b..51a36ceb4 100644 --- a/sdk/src/main/java/io/radar/sdk/RadarHuaweiLocationClient.kt +++ b/sdk/src/main/java/io/radar/sdk/RadarHuaweiLocationClient.kt @@ -135,9 +135,17 @@ internal class RadarHuaweiLocationClient( } override fun removeGeofences( - pendingIntent: PendingIntent + pendingIntent: PendingIntent, + block: ((success: Boolean) -> Unit)? ) { - geofenceService.deleteGeofenceList(pendingIntent) + geofenceService.deleteGeofenceList(pendingIntent).run { + addOnSuccessListener { + block?.invoke(true) + } + addOnFailureListener { + block?.invoke(false) + } + } } override fun getLocationFromGeofenceIntent(intent: Intent): Location? { diff --git a/sdk/src/main/java/io/radar/sdk/RadarLocationManager.kt b/sdk/src/main/java/io/radar/sdk/RadarLocationManager.kt index 11213db37..ad03e1e5f 100644 --- a/sdk/src/main/java/io/radar/sdk/RadarLocationManager.kt +++ b/sdk/src/main/java/io/radar/sdk/RadarLocationManager.kt @@ -5,6 +5,8 @@ import android.app.NotificationManager import android.content.Context import android.content.Intent import android.location.Location +import android.os.Handler +import android.os.Looper import android.os.Build import io.radar.sdk.Radar.RadarLocationCallback import io.radar.sdk.Radar.RadarLocationServicesProvider.HUAWEI @@ -191,10 +193,7 @@ internal class RadarLocationManager( if (!foregroundService.updatesOnly) { this.startForegroundService(foregroundService) } - } else if (RadarForegroundService.started) { - this.stopForegroundService() } - val stopped = RadarState.getStopped(context) if (stopped) { if (options.desiredStoppedUpdateInterval == 0) { @@ -217,14 +216,21 @@ internal class RadarLocationManager( this.startLocationUpdates(options.desiredAccuracy, options.desiredMovingUpdateInterval, options.fastestMovingUpdateInterval) } - if (options.useMovingGeofence && location != null) { - this.replaceBubbleGeofence(location, false) + if (options.useMovingGeofence) { + if (location != null) { + this.replaceBubbleGeofence(location, false) + } } else { this.removeBubbleGeofences() } } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !options.foregroundServiceEnabled && RadarForegroundService.started) { + Handler(Looper.getMainLooper()).postDelayed({ + this.stopForegroundService() + }, 5000) + } } else { - if (RadarForegroundService.started) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && RadarForegroundService.started) { this.stopForegroundService() } this.stopLocationUpdates() @@ -271,13 +277,13 @@ internal class RadarLocationManager( return locationClient.getLocationFromLocationIntent(intent) } - private fun replaceBubbleGeofence(location: Location?, stopped: Boolean) { - if (location == null) { - return + private fun replaceBubbleGeofence(location: Location, stopped: Boolean) { + this.removeBubbleGeofences() { success -> + this.addBubbleGeofence(location, stopped) } + } - this.removeBubbleGeofences() - + private fun addBubbleGeofence(location: Location, stopped: Boolean) { val options = Radar.getTrackingOptions() if (stopped && options.useStoppedGeofence) { @@ -341,8 +347,12 @@ internal class RadarLocationManager( } private fun replaceSyncedGeofences(radarGeofences: Array?) { - this.removeSyncedGeofences() + this.removeSyncedGeofences() { success -> + this.addSyncedGeofences(radarGeofences) + } + } + private fun addSyncedGeofences(radarGeofences: Array?) { val options = Radar.getTrackingOptions() if (!options.syncGeofences || radarGeofences == null) { return @@ -398,14 +408,28 @@ internal class RadarLocationManager( } } - private fun removeBubbleGeofences() { - locationClient.removeGeofences(RadarLocationReceiver.getBubbleGeofencePendingIntent(context)) - logger.d("Removed bubble geofences") + private fun removeBubbleGeofences(block: ((success: Boolean) -> Unit)? = null) { + locationClient.removeGeofences(RadarLocationReceiver.getBubbleGeofencePendingIntent(context)) { success -> + if (success) { + logger.d("Removed bubble geofences") + block?.invoke(true) + } else { + logger.d("Error removing bubble geofences") + block?.invoke(false) + } + } } - private fun removeSyncedGeofences() { - locationClient.removeGeofences(RadarLocationReceiver.getSyncedGeofencesPendingIntent(context)) - logger.d("Removed synced geofences") + private fun removeSyncedGeofences(block: ((success: Boolean) -> Unit)? = null) { + locationClient.removeGeofences(RadarLocationReceiver.getSyncedGeofencesPendingIntent(context)) { success -> + if (success) { + logger.d("Removed synced geofences") + block?.invoke(true) + } else { + logger.d("Error removing synced geofences") + block?.invoke(false) + } + } } private fun removeAllGeofences() { diff --git a/sdk/src/main/java/io/radar/sdk/RadarLocationReceiver.kt b/sdk/src/main/java/io/radar/sdk/RadarLocationReceiver.kt index 6d8bee496..2b1ffd7f2 100644 --- a/sdk/src/main/java/io/radar/sdk/RadarLocationReceiver.kt +++ b/sdk/src/main/java/io/radar/sdk/RadarLocationReceiver.kt @@ -113,11 +113,7 @@ class RadarLocationReceiver : BroadcastReceiver() { return } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !RadarForegroundService.started) { - RadarJobScheduler.scheduleJob(context, location, source) - } else { - Radar.handleLocation(context, location, source) - } + Radar.handleLocation(context, location, source) } ACTION_LOCATION -> { val location = Radar.locationManager.getLocationFromLocationIntent(intent) @@ -127,7 +123,7 @@ class RadarLocationReceiver : BroadcastReceiver() { return } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !RadarForegroundService.started) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !RadarForegroundService.started) { RadarJobScheduler.scheduleJob(context, location, source) } else { Radar.handleLocation(context, location, source) diff --git a/sdk/src/main/java/io/radar/sdk/RadarNotificationHelper.kt b/sdk/src/main/java/io/radar/sdk/RadarNotificationHelper.kt index 0bd0cf608..642d0f14d 100644 --- a/sdk/src/main/java/io/radar/sdk/RadarNotificationHelper.kt +++ b/sdk/src/main/java/io/radar/sdk/RadarNotificationHelper.kt @@ -1,5 +1,6 @@ package io.radar.sdk +import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context @@ -17,6 +18,7 @@ class RadarNotificationHelper { private const val CHANNEL_NAME = "Location" private const val NOTIFICATION_ID = 20160525 // Radar's birthday! + @SuppressLint("DiscouragedApi") internal fun showNotifications(context: Context, events: Array) { if (Build.VERSION.SDK_INT < 26) { return diff --git a/sdk/src/main/java/io/radar/sdk/model/RadarRegion.kt b/sdk/src/main/java/io/radar/sdk/model/RadarRegion.kt index 1ee32b7fa..c31a16921 100644 --- a/sdk/src/main/java/io/radar/sdk/model/RadarRegion.kt +++ b/sdk/src/main/java/io/radar/sdk/model/RadarRegion.kt @@ -115,7 +115,9 @@ class RadarRegion( obj.putOpt(FIELD_PASSED, this.passed) obj.putOpt(FIELD_IN_EXCLUSION_ZONE, this.inExclusionZone) obj.putOpt(FIELD_IN_BUFFER_ZONE, this.inBufferZone) - obj.putOpt(FIELD_DISTANCE_TO_BORDER, this.distanceToBorder) + if (!this.distanceToBorder.isNaN()) { + obj.putOpt(FIELD_DISTANCE_TO_BORDER, this.distanceToBorder) + } return obj } diff --git a/sdk/src/test/java/io/radar/sdk/RadarMockLocationProvider.kt b/sdk/src/test/java/io/radar/sdk/RadarMockLocationProvider.kt index 5e094d896..5eab219b0 100644 --- a/sdk/src/test/java/io/radar/sdk/RadarMockLocationProvider.kt +++ b/sdk/src/test/java/io/radar/sdk/RadarMockLocationProvider.kt @@ -41,7 +41,7 @@ internal class RadarMockLocationProvider() : RadarAbstractLocationClient() { } - override fun removeGeofences(pendingIntent: PendingIntent) { + override fun removeGeofences(pendingIntent: PendingIntent, block: ((success: Boolean) -> Unit)?) { }