Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions sdk/src/main/java/io/radar/sdk/Radar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3693,6 +3693,18 @@ object Radar {
replayBuffer.write(replayParams)
}

internal fun addToBatch(batchParams: JSONObject, options: RadarTrackingOptions) {
replayBuffer.addToBatch(batchParams, options)
}

internal fun shouldFlushBatch(options: RadarTrackingOptions): Boolean {
return replayBuffer.shouldFlushBatch(options)
}

internal fun flushBatch(): Boolean {
return replayBuffer.flushBatch()
}

@JvmStatic
internal fun loadReplayBufferFromSharedPreferences() {
replayBuffer.loadFromSharedPreferences()
Expand Down
19 changes: 19 additions & 0 deletions sdk/src/main/java/io/radar/sdk/RadarApiClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,25 @@ internal class RadarApiClient(
this.getConfig(usage)
}

// Check if batching is enabled and should be used for this track request
val batchingEnabled = (options.batchSize > 0 || options.batchInterval > 0)

if (batchingEnabled &&
source != RadarLocationSource.MANUAL_LOCATION &&
source != RadarLocationSource.FOREGROUND_LOCATION) {

val batchParams = JSONObject(params.toString())

Radar.addToBatch(batchParams, options)

if (Radar.shouldFlushBatch(options)) {
Radar.flushBatch()
}

callback?.onComplete(RadarStatus.SUCCESS)
return
}

val hasReplays = Radar.hasReplays()
var requestParams = params
// before we track, check if replays need to sync
Expand Down
30 changes: 26 additions & 4 deletions sdk/src/main/java/io/radar/sdk/RadarTrackingOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ data class RadarTrackingOptions(
* Determines whether to collect pressure data.
*/
var usePressure: Boolean,

/**
* Determines the time interval between batch events, in seconds.
*/
var batchInterval: Int,

/**
* Determines the number of events per batch.
*/
var batchSize: Int,
) {

/**
Expand Down Expand Up @@ -433,7 +443,9 @@ data class RadarTrackingOptions(
foregroundServiceEnabled = true,
beacons = false,
useMotion = false,
usePressure = false
usePressure = false,
batchInterval = 0,
batchSize = 0
)

/**
Expand Down Expand Up @@ -464,7 +476,9 @@ data class RadarTrackingOptions(
foregroundServiceEnabled = false,
beacons = false,
useMotion = false,
usePressure = false
usePressure = false,
batchInterval = 0,
batchSize = 0
)

/**
Expand Down Expand Up @@ -495,7 +509,9 @@ data class RadarTrackingOptions(
foregroundServiceEnabled = false,
beacons = false,
useMotion = false,
usePressure = false
usePressure = false,
batchInterval = 0,
batchSize = 0
)

internal const val KEY_DESIRED_STOPPED_UPDATE_INTERVAL = "desiredStoppedUpdateInterval"
Expand All @@ -520,6 +536,8 @@ data class RadarTrackingOptions(
internal const val KEY_BEACONS = "beacons"
internal const val KEY_USE_MOTION = "useMotion"
internal const val KEY_USE_PRESSURE = "usePressure"
internal const val KEY_BATCH_INTERVAL = "batchInterval"
internal const val KEY_BATCH_SIZE = "batchSize"
@JvmStatic
fun fromJson(obj: JSONObject): RadarTrackingOptions {
val desiredAccuracy = if (obj.has(KEY_DESIRED_ACCURACY) && obj.get(KEY_DESIRED_ACCURACY) is String) {
Expand Down Expand Up @@ -590,7 +608,9 @@ data class RadarTrackingOptions(
foregroundServiceEnabled = obj.optBoolean(KEY_FOREGROUND_SERVICE_ENABLED, false),
beacons = obj.optBoolean(KEY_BEACONS),
useMotion = obj.optBoolean(KEY_USE_MOTION),
usePressure = obj.optBoolean(KEY_USE_PRESSURE)
usePressure = obj.optBoolean(KEY_USE_PRESSURE),
batchInterval = obj.optInt(KEY_BATCH_INTERVAL),
batchSize = obj.optInt(KEY_BATCH_SIZE)
)
}
}
Expand Down Expand Up @@ -619,6 +639,8 @@ data class RadarTrackingOptions(
obj.put(KEY_BEACONS, beacons)
obj.put(KEY_USE_MOTION, useMotion)
obj.put(KEY_USE_PRESSURE, usePressure)
obj.put(KEY_BATCH_INTERVAL, batchInterval)
obj.put(KEY_BATCH_SIZE, batchSize)
return obj
}

Expand Down
12 changes: 12 additions & 0 deletions sdk/src/main/java/io/radar/sdk/util/RadarReplayBuffer.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.radar.sdk.util

import io.radar.sdk.RadarTrackingOptions
import io.radar.sdk.model.RadarReplay
import org.json.JSONObject

Expand All @@ -12,4 +13,15 @@ internal interface RadarReplayBuffer {
fun getFlushableReplaysStash(): Flushable<RadarReplay>

fun loadFromSharedPreferences()

// Batching methods
fun addToBatch(batchParams: JSONObject, options: RadarTrackingOptions)

fun shouldFlushBatch(options: RadarTrackingOptions): Boolean

fun flushBatch(): Boolean

fun scheduleBatchTimer(options: RadarTrackingOptions)

fun cancelBatchTimer()
}
78 changes: 78 additions & 0 deletions sdk/src/main/java/io/radar/sdk/util/RadarSimpleReplayBuffer.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package io.radar.sdk.util

import io.radar.sdk.Radar
import io.radar.sdk.RadarSettings
import io.radar.sdk.RadarTrackingOptions
import io.radar.sdk.model.RadarReplay
import android.os.Build
import android.os.SystemClock
import android.content.Context
import android.content.SharedPreferences
import android.os.Handler
import android.os.Looper
import androidx.core.content.edit
import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.atomic.AtomicInteger
import org.json.JSONObject
import org.json.JSONArray

Expand All @@ -18,6 +25,9 @@ internal class RadarSimpleReplayBuffer(private val context: Context) : RadarRepl
}

private val buffer = LinkedBlockingDeque<RadarReplay>(MAXIMUM_CAPACITY)
private val handler = Handler(Looper.getMainLooper())
private var batchTimerRunnable: Runnable? = null
private val batchCount = AtomicInteger(0)

override fun getSize(): Int {
return buffer.size
Expand Down Expand Up @@ -75,6 +85,74 @@ internal class RadarSimpleReplayBuffer(private val context: Context) : RadarRepl
}
}

override fun addToBatch(batchParams: JSONObject, options: RadarTrackingOptions) {
val updatedBatchParams = JSONObject(batchParams.toString())
updatedBatchParams.put("replayed", true)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val nowMs = SystemClock.elapsedRealtimeNanos() / 1000000
updatedBatchParams.put("updatedAtMs", nowMs)
}
updatedBatchParams.remove("updatedAtMsDiff")

batchCount.incrementAndGet()
write(updatedBatchParams)

// Schedule timer if interval is set and timer not already running
if (options.batchInterval > 0 && batchTimerRunnable == null) {
scheduleBatchTimer(options)
}
}

override fun shouldFlushBatch(options: RadarTrackingOptions): Boolean {
val currentCount = batchCount.get()

if (currentCount == 0) {
return false
}

if (options.batchSize > 0 && currentCount >= options.batchSize) {
return true
}

return false
}

override fun flushBatch(): Boolean {
val currentCount = batchCount.getAndSet(0)
if (currentCount == 0) {
return false
}

cancelBatchTimer()
Radar.flushReplays()
Radar.logger.d("[BATCH] Batch flushed | count = $currentCount | buffer size = ${buffer.size}")

return true
}

override fun scheduleBatchTimer(options: RadarTrackingOptions) {
if (options.batchInterval <= 0) return

cancelBatchTimer()

batchTimerRunnable = Runnable {
if (batchCount.get() > 0) {
// Timer fired, trigger flush
flushBatch()
}
}

handler.postDelayed(batchTimerRunnable!!, (options.batchInterval * 1000L))
}

override fun cancelBatchTimer() {
batchTimerRunnable?.let { runnable ->
handler.removeCallbacks(runnable)
batchTimerRunnable = null
}
}

private fun getSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
}
Expand Down