Skip to content

Commit

Permalink
Merge pull request #275 from radarlabs/beacons
Browse files Browse the repository at this point in the history
Beacons improvements
  • Loading branch information
nickpatrick authored Mar 3, 2023
2 parents 8bc47d4 + de59971 commit 970c4f9
Show file tree
Hide file tree
Showing 13 changed files with 339 additions and 127 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
/captures
.externalNativeBuild
/buildSrc/build
/build
2 changes: 1 addition & 1 deletion sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ apply plugin: "org.jetbrains.dokka"
apply plugin: 'io.radar.mvnpublish'

ext {
radarVersion = '3.5.12'
radarVersion = '3.5.13'
}

String buildNumber = ".${System.currentTimeMillis()}"
Expand Down
24 changes: 11 additions & 13 deletions sdk/src/main/java/io/radar/sdk/Radar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -749,13 +749,7 @@ object Radar {
if (beacons && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
apiClient.searchBeacons(location, 1000, 10, object : RadarApiClient.RadarSearchBeaconsApiCallback {
override fun onComplete(status: RadarStatus, res: JSONObject?, beacons: Array<RadarBeacon>?, uuids: Array<String>?, uids: Array<String>?) {
if (status != RadarStatus.SUCCESS || beacons == null) {
callTrackApi(null)

return
}

if (!uuids.isNullOrEmpty() || !uids.isNullOrEmpty()) {
if (!uuids.isNullOrEmpty() || !uids.isNullOrEmpty()) {
beaconManager.startMonitoringBeaconUUIDs(uuids, uids)

beaconManager.rangeBeaconUUIDs(uuids, uids, false, object : RadarBeaconCallback {
Expand All @@ -769,7 +763,7 @@ object Radar {
callTrackApi(beacons)
}
})
} else {
} else if (beacons != null) {
beaconManager.startMonitoringBeacons(beacons)

beaconManager.rangeBeacons(beacons, false, object : RadarBeaconCallback {
Expand All @@ -783,9 +777,11 @@ object Radar {
callTrackApi(beacons)
}
})
} else {
callTrackApi(null)
}
}
})
}, false)
} else {
callTrackApi(null)
}
Expand Down Expand Up @@ -2625,13 +2621,14 @@ object Radar {
}

/**
* Sends Radar log events to the server
* Flushes debug logs to the server.
*/
@JvmStatic
internal fun flushLogs() {
if (!initialized || !isTestKey()) {
return
}

val flushable = logBuffer.getFlushableLogsStash()
val logs = flushable.get()
if (logs.isNotEmpty()) {
Expand Down Expand Up @@ -2664,10 +2661,11 @@ object Radar {
@JvmStatic
internal fun isTestKey(): Boolean {
val key = RadarSettings.getPublishableKey(this.context)
val userDebug = RadarSettings.getUserDebug(this.context)
return if (key == null) {
false
} else {
key.startsWith("prj_test") || key.startsWith("org_test")
key.startsWith("prj_test") || key.startsWith("org_test") || userDebug
}
}

Expand Down Expand Up @@ -2768,12 +2766,12 @@ object Radar {
locationManager.handleLocation(location, source)
}

internal fun handleBeacons(context: Context, scanResults: ArrayList<ScanResult>?) {
internal fun handleBeacons(context: Context, beacons: Array<RadarBeacon>?, source: RadarLocationSource) {
if (!initialized) {
initialize(context)
}

locationManager.handleBeacons(scanResults)
locationManager.handleBeacons(beacons, source)
}

internal fun handleBootCompleted(context: Context) {
Expand Down
36 changes: 34 additions & 2 deletions sdk/src/main/java/io/radar/sdk/RadarApiClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ internal class RadarApiClient(
}
}

RadarSettings.setUserDebug(context, user.debug)

Radar.sendLocation(location, user)

if (events.isNotEmpty()) {
Expand Down Expand Up @@ -721,7 +723,8 @@ internal class RadarApiClient(
location: Location,
radius: Int,
limit: Int?,
callback: RadarSearchBeaconsApiCallback
callback: RadarSearchBeaconsApiCallback,
cache: Boolean
) {
val publishableKey = RadarSettings.getPublishableKey(context)
if (publishableKey == null) {
Expand All @@ -730,10 +733,23 @@ internal class RadarApiClient(
return
}

if (cache && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val lastBeacons = RadarState.getLastBeacons(context)
val lastBeaconUUIDs = RadarState.getLastBeaconUUIDs(context)
val lastBeaconUIDs = RadarState.getLastBeaconUIDs(context)

logger.d("Using cached search beacons response | lastBeaconUUIDs = ${lastBeaconUUIDs?.joinToString(",")}; lastBeaconUIDs = ${lastBeaconUIDs?.joinToString(",")}")

callback.onComplete(RadarStatus.SUCCESS, null, lastBeacons, lastBeaconUUIDs, lastBeaconUIDs)

return
}

val queryParams = StringBuilder()
queryParams.append("near=${location.latitude},${location.longitude}")
queryParams.append("&radius=${radius}")
queryParams.append("&limit=${limit}")
queryParams.append("&installId=${RadarSettings.getInstallId(context)}")

val host = RadarSettings.getHost(context)
val uri = Uri.parse(host).buildUpon()
Expand All @@ -746,7 +762,17 @@ internal class RadarApiClient(
apiHelper.request(context, "GET", url, headers, null, false, object : RadarApiHelper.RadarApiCallback {
override fun onComplete(status: RadarStatus, res: JSONObject?) {
if (status != RadarStatus.SUCCESS || res == null) {
callback.onComplete(status)
var lastBeacons: Array<RadarBeacon>? = null
var lastBeaconUUIDs: Array<String>? = null
var lastBeaconUIDs: Array<String>? = null

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
lastBeacons = RadarState.getLastBeacons(context)
lastBeaconUUIDs = RadarState.getLastBeaconUUIDs(context)
lastBeaconUIDs = RadarState.getLastBeaconUIDs(context)
}

callback.onComplete(status, res, lastBeacons, lastBeaconUUIDs, lastBeaconUIDs)

return
}
Expand All @@ -767,6 +793,12 @@ internal class RadarApiClient(
}.filter { uid -> uid.isNotEmpty() }.toTypedArray()
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
RadarState.setLastBeacons(context, beacons)
RadarState.setLastBeaconUUIDs(context, uuids)
RadarState.setLastBeaconUIDs(context, uids)
}

callback.onComplete(RadarStatus.SUCCESS, res, beacons, uuids, uids)
}
})
Expand Down
54 changes: 34 additions & 20 deletions sdk/src/main/java/io/radar/sdk/RadarBeaconManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ internal class RadarBeaconManager(
}

try {
val scanSettings = getScanSettings(ScanSettings.SCAN_MODE_LOW_POWER, 20000)
val scanSettings = getScanSettings(ScanSettings.SCAN_MODE_LOW_POWER, ScanSettings.CALLBACK_TYPE_FIRST_MATCH or ScanSettings.CALLBACK_TYPE_MATCH_LOST, 0, ScanSettings.MATCH_MODE_STICKY)

logger.d("Starting monitoring beacons")

Expand Down Expand Up @@ -236,7 +236,7 @@ internal class RadarBeaconManager(
}

try {
val scanSettings = getScanSettings(ScanSettings.SCAN_MODE_LOW_POWER, 20000)
val scanSettings = getScanSettings(ScanSettings.SCAN_MODE_LOW_POWER, ScanSettings.CALLBACK_TYPE_FIRST_MATCH or ScanSettings.CALLBACK_TYPE_MATCH_LOST, 0, ScanSettings.MATCH_MODE_STICKY)

logger.d("Starting monitoring beacon UUIDs")

Expand Down Expand Up @@ -357,22 +357,21 @@ internal class RadarBeaconManager(
return
}

val scanMode = if (background) ScanSettings.SCAN_MODE_LOW_POWER else ScanSettings.SCAN_MODE_LOW_LATENCY
val scanSettings = getScanSettings(scanMode, 0)
val scanSettings = getScanSettings(ScanSettings.SCAN_MODE_LOW_LATENCY, ScanSettings.CALLBACK_TYPE_ALL_MATCHES, 0, ScanSettings.MATCH_MODE_AGGRESSIVE)

val beaconManager = this

this.scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult?) {
super.onScanResult(callbackType, result)

beaconManager.handleScanResult(result)
beaconManager.handleScanResult(callbackType, result)
}

override fun onBatchScanResults(results: MutableList<ScanResult>?) {
super.onBatchScanResults(results)

results?.forEach { result -> beaconManager.handleScanResult(result) }
results?.forEach { result -> beaconManager.handleScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, result) }
}

override fun onScanFailed(errorCode: Int) {
Expand Down Expand Up @@ -500,22 +499,21 @@ internal class RadarBeaconManager(
return
}

val scanMode = if (background) ScanSettings.SCAN_MODE_LOW_POWER else ScanSettings.SCAN_MODE_LOW_LATENCY
val scanSettings = getScanSettings(scanMode, 0)
val scanSettings = getScanSettings(ScanSettings.SCAN_MODE_LOW_LATENCY, ScanSettings.CALLBACK_TYPE_ALL_MATCHES, 0, ScanSettings.MATCH_MODE_AGGRESSIVE)

val beaconManager = this

this.scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult?) {
super.onScanResult(callbackType, result)

beaconManager.handleScanResult(result)
beaconManager.handleScanResult(callbackType, result)
}

override fun onBatchScanResults(results: MutableList<ScanResult>?) {
super.onBatchScanResults(results)

results?.forEach { result -> beaconManager.handleScanResult(result) }
results?.forEach { result -> beaconManager.handleScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, result) }
}

override fun onScanFailed(errorCode: Int) {
Expand Down Expand Up @@ -573,25 +571,41 @@ internal class RadarBeaconManager(
this.nearbyBeacons.clear()
}

internal fun handleScanResults(scanResults: ArrayList<ScanResult>?) {
if (scanResults == null || scanResults.isEmpty()) {
logger.d("No scan results to handle")
internal fun handleBeacons(beacons: Array<RadarBeacon>?, source: Radar.RadarLocationSource) {
if (beacons.isNullOrEmpty()) {
logger.d("No beacons to handle")

return
}

scanResults.forEach { scanResult ->
this.handleScanResult(scanResult, false)
beacons.forEach { beacon ->
if (source == Radar.RadarLocationSource.BEACON_EXIT) {
logger.d("Handling beacon exit | beacon.type = ${beacon.type}; beacon.uuid = ${beacon.uuid}; beacon.major = ${beacon.major}; beacon.minor = ${beacon.minor}; beacon.rssi = ${beacon.rssi}")

nearbyBeacons.remove(beacon)
} else {
logger.d("Handling beacon entry | beacon.type = ${beacon.type}; beacon.uuid = ${beacon.uuid}; beacon.major = ${beacon.major}; beacon.minor = ${beacon.minor}; beacon.rssi = ${beacon.rssi}")

nearbyBeacons.add(beacon)
}
}
}

internal fun handleScanResult(result: ScanResult?, ranging: Boolean = true) {
internal fun handleScanResult(callbackType: Int, result: ScanResult?, ranging: Boolean = true) {
logger.d("Handling scan result")

result?.scanRecord?.let { scanRecord -> RadarBeaconUtils.getBeacon(result, scanRecord) }?.let { beacon ->
logger.d("Ranged beacon | beacon.type = ${beacon.type}; beacon.uuid = ${beacon.uuid}; beacon.major = ${beacon.major}; beacon.minor = ${beacon.minor}; beacon.rssi = ${beacon.rssi}")

nearbyBeacons.add(beacon)
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) {
logger.d("Handling beacon exit | beacon.type = ${beacon.type}; beacon.uuid = ${beacon.uuid}; beacon.major = ${beacon.major}; beacon.minor = ${beacon.minor}; beacon.rssi = ${beacon.rssi}")

nearbyBeacons.remove(beacon)
} else {
logger.d("Handling beacon entry | beacon.type = ${beacon.type}; beacon.uuid = ${beacon.uuid}; beacon.major = ${beacon.major}; beacon.minor = ${beacon.minor}; beacon.rssi = ${beacon.rssi}")

nearbyBeacons.add(beacon)
}
}

if (this.nearbyBeacons.size == this.beacons.size && ranging) {
Expand All @@ -612,12 +626,12 @@ internal class RadarBeaconManager(
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH) && adapter != null && adapter.bluetoothLeScanner != null
}

private fun getScanSettings(scanMode: Int, reportDelay: Long): ScanSettings {
private fun getScanSettings(scanMode: Int, callbackType: Int, reportDelay: Long, matchMode: Int): ScanSettings {
return ScanSettings.Builder()
.setScanMode(scanMode)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setCallbackType(callbackType)
.setReportDelay(reportDelay)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setMatchMode(matchMode)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
.build()
}
Expand Down
64 changes: 64 additions & 0 deletions sdk/src/main/java/io/radar/sdk/RadarBeaconUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.os.Build
import android.os.ParcelUuid
import androidx.annotation.RequiresApi
import io.radar.sdk.model.RadarBeacon
import org.json.JSONObject
import java.nio.ByteBuffer
import java.util.*

Expand All @@ -17,6 +18,69 @@ internal object RadarBeaconUtils {
private val EDDYSTONE_SERVICE_UUID = ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB")
private val HEX = "0123456789abcdef".toCharArray()

fun beaconsForScanResults(scanResults: ArrayList<ScanResult>?): Array<RadarBeacon> {
if (scanResults.isNullOrEmpty()) {
return arrayOf()
}

val beacons = mutableListOf<RadarBeacon>()

scanResults.forEach { result ->
result.scanRecord?.let { scanRecord -> getBeacon(result, scanRecord) }?.let { beacon ->
beacons.add(beacon)
}
}

return beacons.toTypedArray()
}

fun stringSetForBeacons(beacons: Array<RadarBeacon>?): Set<String>? {
if (beacons == null) {
return null
}

val arr = stringArrayForBeacons(beacons)

return arr.toSet()
}

fun stringArrayForBeacons(beacons: Array<RadarBeacon>): Array<String> {
val arr = mutableListOf<String>()

beacons.forEach { beacon ->
arr.add(beacon.toJson().toString())
}

return arr.toTypedArray()
}

fun beaconsForStringSet(set: Set<String>?): Array<RadarBeacon>? {
if (set == null) {
return null
}

val arr = set.toTypedArray()

return beaconsForStringArray(arr)
}

fun beaconsForStringArray(arr: Array<String>?): Array<RadarBeacon>? {
if (arr == null) {
return null
}

val beacons = mutableListOf<RadarBeacon>()

arr.forEach { str ->
val beacon = RadarBeacon.fromJson(JSONObject(str))
if (beacon != null) {
beacons.add(beacon)
}
}

return beacons.toTypedArray()
}

fun getScanFilterForBeacon(beacon: RadarBeacon): ScanFilter? {
if (beacon.type == RadarBeacon.RadarBeaconType.EDDYSTONE) {
val uid = beacon.uuid
Expand Down
Loading

0 comments on commit 970c4f9

Please sign in to comment.