Skip to content

Commit 5822d73

Browse files
authored
Merge pull request #879 from CleverTap/develop
Release Core 7.5.2
2 parents b263672 + 727be6d commit 5822d73

File tree

26 files changed

+1532
-925
lines changed

26 files changed

+1532
-925
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
## CHANGE LOG.
2+
### September 11, 2025
3+
* [CleverTap Android SDK v7.5.2](docs/CTCORECHANGELOG.md)
4+
25
### August 28, 2025
36
* [CleverTap Android SDK v7.5.1](docs/CTCORECHANGELOG.md)
47

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ We publish the SDK to `mavenCentral` as an `AAR` file. Just declare it as depend
2626

2727
```groovy
2828
dependencies {
29-
implementation "com.clevertap.android:clevertap-android-sdk:7.5.1"
29+
implementation "com.clevertap.android:clevertap-android-sdk:7.5.2"
3030
}
3131
```
3232

3333
Alternatively, you can download and add the AAR file included in this repo in your Module libs directory and tell gradle to install it like this:
3434

3535
```groovy
3636
dependencies {
37-
implementation (name: "clevertap-android-sdk-7.5.1", ext: 'aar')
37+
implementation (name: "clevertap-android-sdk-7.5.2", ext: 'aar')
3838
}
3939
```
4040

@@ -46,7 +46,7 @@ Add the Firebase Messaging library and Android Support Library v4 as dependencie
4646

4747
```groovy
4848
dependencies {
49-
implementation "com.clevertap.android:clevertap-android-sdk:7.5.1"
49+
implementation "com.clevertap.android:clevertap-android-sdk:7.5.2"
5050
implementation "androidx.core:core:1.13.0"
5151
implementation "com.google.firebase:firebase-messaging:24.0.0"
5252
implementation "com.google.android.gms:play-services-ads:23.6.0" // Required only if you enable Google ADID collection in the SDK (turned off by default).

clevertap-core/src/main/java/com/clevertap/android/sdk/CTXtensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ fun JSONArray?.orEmptyArray(): JSONArray {
190190
* @return A List of elements of type [T] extracted from the JSONArray.
191191
* @reified T The expected type of elements in the list.
192192
*/
193-
inline fun <reified T> JSONArray.toList(): List<T> {
193+
inline fun <reified T> JSONArray.toList(): MutableList<T> {
194194
val list = mutableListOf<T>()
195195
for (index in 0 until length()) {
196196
val element = get(index)

clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,10 +435,9 @@ internal object CleverTapFactory {
435435
)
436436
controllerManager.inAppController = inAppController
437437

438+
val batchListener = CompositeBatchListener()
438439
val appLaunchListener = AppLaunchListener()
439440
appLaunchListener.addListener(inAppController.onAppLaunchEventSent)
440-
441-
val batchListener = CompositeBatchListener()
442441
batchListener.addListener(appLaunchListener)
443442
batchListener.addListener(FetchInAppListener(callbackManager))
444443
callbackManager.batchListener = batchListener

clevertap-core/src/main/java/com/clevertap/android/sdk/db/BaseDatabaseManager.kt

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,46 @@ internal interface BaseDatabaseManager {
1010

1111
fun clearQueues(context: Context)
1212

13+
/**
14+
* Gets queued events from the database
15+
* @param context Android context
16+
* @param batchSize Number of events to fetch (typically 50)
17+
* @param eventGroup Type of events to fetch
18+
* @return QueueData containing events and their IDs for cleanup
19+
*/
1320
fun getQueuedEvents(
1421
context: Context,
1522
batchSize: Int,
16-
previousQueue: QueueData?,
1723
eventGroup: EventGroup
1824
): QueueData
1925

20-
fun getQueuedDBEvents(context: Context, batchSize: Int, previousQueue: QueueData?): QueueData
21-
22-
fun getQueue(context: Context, table: Table, batchSize: Int, previousQueue: QueueData?): QueueData
26+
/**
27+
* Fetches a combined batch of events from both events and profileEvents tables
28+
* Returns QueueData with events data and ids, also if there are more events to fetch
29+
*/
30+
fun getCombinedQueuedEvents(context: Context, batchSize: Int): QueueData
2331

2432
fun queueEventToDB(context: Context, event: JSONObject, type: Int)
2533

2634
fun queuePushNotificationViewedEventToDB(context: Context, event: JSONObject)
2735

28-
fun getPushNotificationViewedQueuedEvents(context: Context, batchSize: Int, previousQueue: QueueData?): QueueData
36+
fun getPushNotificationViewedQueuedEvents(context: Context, batchSize: Int): QueueData
37+
38+
/**
39+
* Cleans up successfully sent events from the database
40+
* @param context Android context
41+
* @param eventIds List of event IDs to clean up from events table
42+
* @param profileEventIds List of event IDs to clean up from profileEvents table
43+
* @return true if cleanup successful, false otherwise
44+
*/
45+
fun cleanupSentEvents(
46+
context: Context,
47+
eventIds: List<String>,
48+
profileEventIds: List<String>
49+
) : Boolean
50+
51+
fun cleanupPushNotificationEvents(
52+
context: Context,
53+
ids: List<String>
54+
) : Boolean
2955
}

clevertap-core/src/main/java/com/clevertap/android/sdk/db/DBAdapter.kt

Lines changed: 126 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -452,24 +452,6 @@ internal class DBAdapter(context: Context, config: CleverTapInstanceConfig) {
452452
cleanInternal(PUSH_NOTIFICATIONS, 0)
453453
}
454454

455-
/**
456-
* Removes sent events with an _id <= last_id from table
457-
*
458-
* @param lastId the last id to delete
459-
* @param table the table to remove events
460-
*/
461-
@WorkerThread
462-
@Synchronized
463-
fun cleanupEventsFromLastId(lastId: String, table: Table) {
464-
val tName = table.tableName
465-
try {
466-
dbHelper.writableDatabase.delete(tName, "${Column.ID} <= ?", arrayOf(lastId))
467-
} catch (e: SQLiteException) {
468-
logger.verbose("Error removing sent data from table $tName Recreating DB")
469-
deleteDB()
470-
}
471-
}
472-
473455
@Synchronized
474456
fun storePushNotificationId(id: String?, ttl: Long) {
475457
if (id == null) {
@@ -517,39 +499,146 @@ internal class DBAdapter(context: Context, config: CleverTapInstanceConfig) {
517499
* @return JSONObject containing the max row ID and a JSONArray of the JSONObject events or null
518500
*/
519501
@Synchronized
520-
fun fetchEvents(table: Table, limit: Int): JSONObject? {
502+
fun fetchEvents(table: Table, limit: Int): QueueData {
503+
val queueData = QueueData()
504+
521505
val tName = table.tableName
522-
var lastId: String? = null
523-
val events = JSONArray()
524506
try {
525507
dbHelper.readableDatabase.query(
526-
tName, null, null, null, null, null, "${Column.CREATED_AT} ASC", limit.toString()
508+
tName,
509+
arrayOf(Column.ID, Column.DATA, Column.CREATED_AT),
510+
null, null, null, null,
511+
"${Column.CREATED_AT} ASC",
512+
(limit + 1).toString()
527513
)?.use { cursor ->
514+
val rowCount = cursor.count
515+
queueData.hasMore = rowCount > limit
516+
517+
var pos = 0
528518
while (cursor.moveToNext()) {
529-
if (cursor.isLast) {
530-
lastId = cursor.getString(cursor.getColumnIndexOrThrow(Column.ID))
519+
if (pos == limit) {
520+
break
531521
}
522+
val id = cursor.getString(cursor.getColumnIndexOrThrow(Column.ID))
523+
val eventData = cursor.getString(cursor.getColumnIndexOrThrow(Column.DATA))
524+
532525
try {
533-
val j = JSONObject(cursor.getString(cursor.getColumnIndexOrThrow(Column.DATA)))
534-
events.put(j)
526+
val jsonEvent = JSONObject(eventData)
527+
queueData.data.put(jsonEvent)
528+
529+
if (table == Table.PROFILE_EVENTS) {
530+
queueData.profileEventIds.add(id)
531+
} else {
532+
queueData.eventIds.add(id)
533+
}
534+
535535
} catch (e: JSONException) {
536-
// Ignore
536+
logger.verbose("Error parsing event data for id: $id from table: $tName", e)
537537
}
538+
pos++
538539
}
539540
}
540-
} catch (e: Exception) { // SQLiteException | IllegalArgumentException
541-
logger.verbose("Could not fetch records out of database $tName.", e)
542-
lastId = null
541+
} catch (e: Exception) {
542+
logger.verbose("Could not fetch records from table $tName", e)
543543
}
544544

545-
return lastId?.let {
546-
try {
547-
val ret = JSONObject()
548-
ret.put(it, events)
549-
ret
550-
} catch (e: JSONException) {
551-
null
545+
val size = if (table == Table.PROFILE_EVENTS) {
546+
queueData.profileEventIds.size
547+
} else {
548+
queueData.eventIds.size
549+
}
550+
logger.verbose("Fetched $size events from $tName")
551+
return queueData
552+
}
553+
554+
/**
555+
* Fetches a combined batch of events from both events and profileEvents tables
556+
* Prioritizes profileEvents table first, then fills remaining slots from events
557+
*
558+
* @param batchSize The maximum number of events to fetch (typically 50)
559+
* @return QueueData containing the events and their IDs for cleanup
560+
*/
561+
@Synchronized
562+
fun fetchCombinedEvents(batchSize: Int): QueueData {
563+
val combinedQueueData = QueueData()
564+
565+
// First priority: Fetch from profileEvents table using the base fetchEvents method
566+
val profileData = fetchEvents(Table.PROFILE_EVENTS, batchSize)
567+
568+
// Add profile events to combined data
569+
for (i in 0 until profileData.data.length()) {
570+
combinedQueueData.data.put(profileData.data.getJSONObject(i))
571+
}
572+
combinedQueueData.profileEventIds.addAll(profileData.profileEventIds)
573+
combinedQueueData.hasMore = profileData.hasMore
574+
575+
// Calculate remaining slots for normal events
576+
val eventsNeeded = batchSize - combinedQueueData.profileEventIds.size
577+
578+
// Second priority: Fill remaining slots from events table
579+
if (eventsNeeded > 0 || combinedQueueData.hasMore.not()) {
580+
val eventsData = fetchEvents(Table.EVENTS, eventsNeeded)
581+
582+
// Add events to combined data
583+
for (i in 0 until eventsData.data.length()) {
584+
combinedQueueData.data.put(eventsData.data.getJSONObject(i))
585+
}
586+
combinedQueueData.eventIds.addAll(eventsData.eventIds)
587+
combinedQueueData.hasMore = eventsData.hasMore
588+
}
589+
590+
logger.verbose("Fetched combined batch: ${combinedQueueData.profileEventIds.size} profile events, ${combinedQueueData.eventIds.size} events")
591+
592+
return combinedQueueData
593+
}
594+
595+
/**
596+
* Removes sent events with an _id <= last_id from table
597+
*
598+
* @param lastId the last id to delete
599+
* @param table the table to remove events
600+
*/
601+
@WorkerThread
602+
@Synchronized
603+
fun cleanupEventsFromLastId(lastId: String, table: Table) {
604+
val tName = table.tableName
605+
try {
606+
dbHelper.writableDatabase.delete(tName, "${Column.ID} <= ?", arrayOf(lastId))
607+
} catch (e: SQLiteException) {
608+
logger.verbose("Error removing sent data from table $tName Recreating DB")
609+
deleteDB()
610+
}
611+
}
612+
613+
/**
614+
* Cleans up events from the profileEvents table by their IDs
615+
*
616+
* @param events List of profile event IDs to delete
617+
*/
618+
@WorkerThread
619+
@Synchronized
620+
fun cleanupEventsByIds(table: Table, events: List<String>) {
621+
if (events.isEmpty()) {
622+
return
623+
}
624+
625+
val tName = table.tableName
626+
627+
try {
628+
// Process in chunks if the list is too large
629+
val chunkSize = 100
630+
events.chunked(chunkSize).forEach { chunk ->
631+
val placeholders = chunk.joinToString(",") { "?" }
632+
val deletedCount = dbHelper.writableDatabase.delete(
633+
tName,
634+
"${Column.ID} IN ($placeholders)",
635+
chunk.toTypedArray()
636+
)
637+
logger.verbose("Deleted $deletedCount events from $tName")
552638
}
639+
} catch (e: SQLiteException) {
640+
logger.verbose("Error removing events from $tName", e)
641+
deleteDB()
553642
}
554643
}
555644

0 commit comments

Comments
 (0)