From 616386d10b6b120696ca1a5103e4db7c8e7bd715 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Wed, 20 Mar 2024 19:03:51 +0400 Subject: [PATCH] Update dependencies, fix code style changes --- .editorconfig | 2 + CHANGELOG.md | 5 + gradle.properties | 1 - gradle/libs.versions.toml | 20 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- sdk-sample/build.gradle.kts | 1 + sdk/build.gradle.kts | 11 ++- sdk/gradle.properties | 2 +- .../cxense/ConversionEventConverter.kt | 48 +++++----- .../java/io/piano/android/cxense/CxApi.kt | 11 +-- .../java/io/piano/android/cxense/CxenseSdk.kt | 37 ++++---- .../piano/android/cxense/EventRepository.kt | 94 +++++++++---------- .../io/piano/android/cxense/JsonAdapters.kt | 13 ++- .../android/cxense/PageViewEventConverter.kt | 82 ++++++++-------- .../cxense/PerformanceEventConverter.kt | 71 +++++++------- .../io/piano/android/cxense/SdkInterceptor.kt | 28 +++--- .../java/io/piano/android/cxense/SendTask.kt | 11 +-- .../android/cxense/UserAgentInterceptor.kt | 13 ++- .../piano/android/cxense/UserAgentProvider.kt | 59 ++++++------ .../piano/android/cxense/db/DatabaseHelper.kt | 63 ++++++------- .../io/piano/android/cxense/db/EventRecord.kt | 4 +- settings.gradle.kts | 4 +- 22 files changed, 283 insertions(+), 299 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0888577..f395f18 100644 --- a/.editorconfig +++ b/.editorconfig @@ -399,6 +399,7 @@ ij_groovy_while_on_new_line = false ij_groovy_wrap_long_lines = false [*.{kt,kts}] +ktlint_code_style = android_studio ktlint_standard_import-ordering = disabled insert_final_newline = true ij_kotlin_align_in_columns_case_branch = false @@ -407,6 +408,7 @@ ij_kotlin_align_multiline_extends_list = false ij_kotlin_align_multiline_method_parentheses = false ij_kotlin_align_multiline_parameters = true ij_kotlin_align_multiline_parameters_in_calls = false +ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = false ij_kotlin_assignment_wrap = normal ij_kotlin_blank_lines_after_class_header = 0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f7f187..6ee7516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Piano DMP & Content SDK for Android +## v2.5.1-SNAPSHOT +* Updated dependencies: + - org.jetbrains.kotlinx:kotlinx-coroutines-core [1.7.3 -> 1.8.0] + https://github.com/Kotlin/kotlinx.coroutines + ## v2.5.0 * Android 14 compatibility * Fix `sdkv=unspecified` issue diff --git a/gradle.properties b/gradle.properties index 05371aa..6174fab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,6 @@ org.gradle.caching=true android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=false -android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false android.nonFinalResIds=false org.gradle.configuration-cache=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5e68934..ae824a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,20 +1,20 @@ [versions] # Plugins kotlin = "1.8.21" -android = "8.1.1" -versionUpdater = "0.47.0" -ktlint = "11.5.1" +android = "8.3.1" +versionUpdater = "0.51.0" +ktlint = "12.1.0" dokka = "1.8.10" -mavenRelease = "0.25.3" +mavenRelease = "0.28.0" moshiIR = "0.22.1" secrets = "2.0.1" # AndroidX libraries compatLibrary = "1.6.1" -annotationsLibrary = "1.6.0" -materialLibrary = "1.9.0" +annotationsLibrary = "1.7.1" +materialLibrary = "1.11.0" startupLibrary = "1.1.1" -recyclerView = "1.3.1" +recyclerView = "1.3.2" # Third party Libraries googleAdsId = "18.0.1" @@ -23,12 +23,12 @@ okhttp = "4.11.0" moshi = "1.15.0" timber = "5.0.1" viewBindingProperty = "1.5.9" -coroutines = "1.7.3" +coroutines = "1.8.0" # Test Libraries junit = "4.13.2" -mockitoKotlin = "5.1.0" -mockitoCore = "5.5.0" +mockitoKotlin = "5.2.1" +mockitoCore = "5.11.0" [plugins] android-library = { id = "com.android.library", version.ref = "android" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fde43c7..ce30841 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip diff --git a/sdk-sample/build.gradle.kts b/sdk-sample/build.gradle.kts index d4afd5d..9857e95 100644 --- a/sdk-sample/build.gradle.kts +++ b/sdk-sample/build.gradle.kts @@ -23,6 +23,7 @@ android { } } buildFeatures { + buildConfig = true viewBinding = true } diff --git a/sdk/build.gradle.kts b/sdk/build.gradle.kts index ef2fa3f..b21c5bd 100644 --- a/sdk/build.gradle.kts +++ b/sdk/build.gradle.kts @@ -6,8 +6,12 @@ plugins { alias(libs.plugins.moshiIR) } +@Suppress("PropertyName") val GROUP: String by project + +@Suppress("PropertyName") val VERSION_NAME: String by project + group = GROUP version = VERSION_NAME @@ -20,13 +24,16 @@ android { buildConfigField("String", "SDK_VERSION", """"$version"""") buildConfigField("String", "SDK_NAME", """"cxense"""") buildConfigField("String", "SDK_ENDPOINT", """"https://api.cxense.com"""") - buildConfigField("String", "AUTHORITY", """LIBRARY_PACKAGE_NAME + ".$authority"""") + buildConfigField("String", "AUTHORITY", """"io.piano.android.cxense.$authority"""") manifestPlaceholders += "authority" to authority testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("cxensesdk.pro") } + buildFeatures { + buildConfig = true + } buildTypes { named("release") { @@ -53,7 +60,7 @@ kotlin { } ktlint { - version.set("0.50.0") + version.set("1.2.1") android.set(true) } diff --git a/sdk/gradle.properties b/sdk/gradle.properties index 3f3091a..ea78827 100644 --- a/sdk/gradle.properties +++ b/sdk/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=2.5.0 +VERSION_NAME=2.5.1-SNAPSHOT GROUP=io.piano.android POM_NAME=Piano DMP & Piano Content POM_ARTIFACT_ID=cxense diff --git a/sdk/src/main/java/io/piano/android/cxense/ConversionEventConverter.kt b/sdk/src/main/java/io/piano/android/cxense/ConversionEventConverter.kt index 3edbaa6..7107c46 100644 --- a/sdk/src/main/java/io/piano/android/cxense/ConversionEventConverter.kt +++ b/sdk/src/main/java/io/piano/android/cxense/ConversionEventConverter.kt @@ -15,31 +15,29 @@ class ConversionEventConverter( ) : EventConverter() { override fun canConvert(event: Event): Boolean = event is ConversionEvent - override fun toEventRecord(event: Event): EventRecord? = - (event as? ConversionEvent)?.run { - EventRecord( - ConversionEvent.EVENT_TYPE, - eventId, - jsonAdapter.toJson(this), - mergeKey = mergeKey - ) - } + override fun toEventRecord(event: Event): EventRecord? = (event as? ConversionEvent)?.run { + EventRecord( + ConversionEvent.EVENT_TYPE, + eventId, + jsonAdapter.toJson(this), + mergeKey = mergeKey + ) + } - override fun update(oldRecord: EventRecord, event: Event): EventRecord = - with(event as ConversionEvent) { - jsonAdapter.fromJson(oldRecord.data)?.let { old -> - toEventRecord( - ConversionEvent( - identities.takeUnless { it.isEmpty() } ?: old.identities, - siteId, - consentOptions.takeUnless { it.isEmpty() } ?: old.consentOptions, - productId, - funnelStep, - price ?: old.price, - renewalFrequency ?: old.renewalFrequency, - eventType - ) + override fun update(oldRecord: EventRecord, event: Event): EventRecord = with(event as ConversionEvent) { + jsonAdapter.fromJson(oldRecord.data)?.let { old -> + toEventRecord( + ConversionEvent( + identities.takeUnless { it.isEmpty() } ?: old.identities, + siteId, + consentOptions.takeUnless { it.isEmpty() } ?: old.consentOptions, + productId, + funnelStep, + price ?: old.price, + renewalFrequency ?: old.renewalFrequency, + eventType ) - } ?: oldRecord - } + ) + } ?: oldRecord + } } diff --git a/sdk/src/main/java/io/piano/android/cxense/CxApi.kt b/sdk/src/main/java/io/piano/android/cxense/CxApi.kt index d3a4ce4..dd15c64 100644 --- a/sdk/src/main/java/io/piano/android/cxense/CxApi.kt +++ b/sdk/src/main/java/io/piano/android/cxense/CxApi.kt @@ -97,15 +97,8 @@ interface CxApi { fun trackUrlClick(@Url url: String): Call @GET - fun getPersisted( - @Url url: String, - @Query("persisted") persistentId: String, - ): Call + fun getPersisted(@Url url: String, @Query("persisted") persistentId: String): Call @POST - fun postPersisted( - @Url url: String, - @Query("persisted") persistentId: String, - @Body data: Any, - ): Call + fun postPersisted(@Url url: String, @Query("persisted") persistentId: String, @Body data: Any): Call } diff --git a/sdk/src/main/java/io/piano/android/cxense/CxenseSdk.kt b/sdk/src/main/java/io/piano/android/cxense/CxenseSdk.kt index 3c0fbe6..7e35ef4 100644 --- a/sdk/src/main/java/io/piano/android/cxense/CxenseSdk.kt +++ b/sdk/src/main/java/io/piano/android/cxense/CxenseSdk.kt @@ -36,7 +36,8 @@ import java.util.concurrent.TimeUnit @Suppress("unused", "MemberVisibilityCanBePrivate") // Public API. class CxenseSdk( private val executor: ScheduledExecutorService, - @Suppress("unused", "MemberVisibilityCanBePrivate") // Public API. + // Public API. + @Suppress("unused", "MemberVisibilityCanBePrivate") val configuration: CxenseConfiguration, private val advertisingIdProvider: AdvertisingIdProvider, private val userProvider: UserProvider, @@ -98,13 +99,12 @@ class CxenseSdk( * @param events the events that should be pushed. */ @Suppress("unused", "MemberVisibilityCanBePrivate") // Public API. - fun pushEvents(vararg events: Event) = - executor.execute { - eventRepository.putEventsInDatabase(events) - if (configuration.sendEventsAtPush) { - flushEventQueue() - } + fun pushEvents(vararg events: Event) = executor.execute { + eventRepository.putEventsInDatabase(events) + if (configuration.sendEventsAtPush) { + flushEventQueue() } + } /** * Tracks active time for the given page view event. @@ -435,18 +435,17 @@ class CxenseSdk( private fun LoadCallback.transform() = ApiCallback(this, errorParser) private fun createGenericCallback(callback: LoadCallback) = object : LoadCallback { - override fun onSuccess(data: ResponseBody) = - try { - val callbackClazz = callback::class.java.genericInterfaces.first() as ParameterizedType - - @Suppress("UNCHECKED_CAST") - val clazz = callbackClazz.actualTypeArguments.first() as Class - val jsonAdapter = moshi.adapter(clazz) - val reader = JsonReader.of(data.source()) - callback.onSuccess(requireNotNull(jsonAdapter.fromJson(reader))) - } catch (e: Exception) { - callback.onError(e) - } + override fun onSuccess(data: ResponseBody) = try { + val callbackClazz = callback::class.java.genericInterfaces.first() as ParameterizedType + + @Suppress("UNCHECKED_CAST") + val clazz = callbackClazz.actualTypeArguments.first() as Class + val jsonAdapter = moshi.adapter(clazz) + val reader = JsonReader.of(data.source()) + callback.onSuccess(requireNotNull(jsonAdapter.fromJson(reader))) + } catch (e: Exception) { + callback.onError(e) + } override fun onError(throwable: Throwable) = callback.onError(throwable) }.transform() diff --git a/sdk/src/main/java/io/piano/android/cxense/EventRepository.kt b/sdk/src/main/java/io/piano/android/cxense/EventRepository.kt index 636d130..8dd4104 100644 --- a/sdk/src/main/java/io/piano/android/cxense/EventRepository.kt +++ b/sdk/src/main/java/io/piano/android/cxense/EventRepository.kt @@ -35,11 +35,10 @@ class EventRepository( fun putEventRecordInDatabase(eventRecord: EventRecord): Long = databaseHelper.save(eventRecord) - fun deleteOutdatedEvents(outdatePeriod: Long): Int = - databaseHelper.delete( - "${EventRecord.TIME} < ?", - (System.currentTimeMillis() - outdatePeriod).toString() - ) + fun deleteOutdatedEvents(outdatePeriod: Long): Int = databaseHelper.delete( + "${EventRecord.TIME} < ?", + (System.currentTimeMillis() - outdatePeriod).toString() + ) fun getNotSubmittedPvEvents() = getEvents( "$NOT_SENT_FILTER AND ${EventRecord.TYPE} = ?", @@ -60,32 +59,29 @@ class EventRepository( selection: String?, selectionArgs: Array?, limit: String? = null, - ): List = - databaseHelper.query( - selection = selection, - selectionArgs = selectionArgs, - orderBy = "${EventRecord.TIME} ASC", - limit = limit - ).map { it.toEventRecord() } + ): List = databaseHelper.query( + selection = selection, + selectionArgs = selectionArgs, + orderBy = "${EventRecord.TIME} ASC", + limit = limit + ).map { it.toEventRecord() } - fun getPvEventFromDatabase(eventId: String): EventRecord? = - databaseHelper.query( - selection = "${EventRecord.CUSTOM_ID} = ? AND ${EventRecord.TYPE} = ?", - selectionArgs = arrayOf(eventId, PageViewEvent.EVENT_TYPE), - orderBy = "${EventRecord.TIME} DESC", - limit = "1" - ).firstOrNull()?.toEventRecord() + fun getPvEventFromDatabase(eventId: String): EventRecord? = databaseHelper.query( + selection = "${EventRecord.CUSTOM_ID} = ? AND ${EventRecord.TYPE} = ?", + selectionArgs = arrayOf(eventId, PageViewEvent.EVENT_TYPE), + orderBy = "${EventRecord.TIME} DESC", + limit = "1" + ).firstOrNull()?.toEventRecord() - fun getEventStatuses(): List = - databaseHelper.query( - columns = arrayOf(EventRecord.CUSTOM_ID, EventRecord.IS_SENT), - orderBy = "${EventRecord.TIME} ASC" - ).map { - EventStatus( - it.getAsString(EventRecord.CUSTOM_ID), - it.getAsBoolean(EventRecord.IS_SENT) - ) - } + fun getEventStatuses(): List = databaseHelper.query( + columns = arrayOf(EventRecord.CUSTOM_ID, EventRecord.IS_SENT), + orderBy = "${EventRecord.TIME} ASC" + ).map { + EventStatus( + it.getAsString(EventRecord.CUSTOM_ID), + it.getAsBoolean(EventRecord.IS_SENT) + ) + } fun putEventTime(eventId: String, activeTime: Long) { try { @@ -107,28 +103,26 @@ class EventRepository( } } - private fun EventConverter.buildEventRecord(e: Event): EventRecord? = - getEvents( - "$NOT_SENT_FILTER AND ${EventRecord.MERGE_KEY} = ? AND ${EventRecord.TIME} > ?", - arrayOf(e.mergeKey.toString(), (System.currentTimeMillis() - configuration.eventsMergePeriod).toString()), - limit = "1" - ).firstOrNull()?.let { - update(it, e) - } ?: toEventRecord(e) + private fun EventConverter.buildEventRecord(e: Event): EventRecord? = getEvents( + "$NOT_SENT_FILTER AND ${EventRecord.MERGE_KEY} = ? AND ${EventRecord.TIME} > ?", + arrayOf(e.mergeKey.toString(), (System.currentTimeMillis() - configuration.eventsMergePeriod).toString()), + limit = "1" + ).firstOrNull()?.let { + update(it, e) + } ?: toEventRecord(e) - private fun ContentValues.toEventRecord(): EventRecord = - EventRecord( - getAsString(EventRecord.TYPE), - getAsString(EventRecord.CUSTOM_ID), - getAsString(EventRecord.DATA), - getAsString(EventRecord.CKP), - getAsString(EventRecord.RND), - getAsLong(EventRecord.TIME), - getAsLong(EventRecord.SPENT_TIME), - getAsInteger(EventRecord.MERGE_KEY), - getAsLong(BaseColumns._ID), - getAsBoolean(EventRecord.IS_SENT) - ) + private fun ContentValues.toEventRecord(): EventRecord = EventRecord( + getAsString(EventRecord.TYPE), + getAsString(EventRecord.CUSTOM_ID), + getAsString(EventRecord.DATA), + getAsString(EventRecord.CKP), + getAsString(EventRecord.RND), + getAsLong(EventRecord.TIME), + getAsLong(EventRecord.SPENT_TIME), + getAsInteger(EventRecord.MERGE_KEY), + getAsLong(BaseColumns._ID), + getAsBoolean(EventRecord.IS_SENT) + ) companion object { private const val NOT_SENT_FILTER = "${EventRecord.IS_SENT} = 0" diff --git a/sdk/src/main/java/io/piano/android/cxense/JsonAdapters.kt b/sdk/src/main/java/io/piano/android/cxense/JsonAdapters.kt index 75868df..95970fc 100644 --- a/sdk/src/main/java/io/piano/android/cxense/JsonAdapters.kt +++ b/sdk/src/main/java/io/piano/android/cxense/JsonAdapters.kt @@ -58,13 +58,12 @@ class EventsRequestAdapter : JsonAdapter() { object WidgetItemAdapter { @FromJson - fun fromJson(props: Map): WidgetItem = - WidgetItem( - props[TITLE]?.toString(), - props[URL]?.toString(), - props[CLICK_URL]?.toString(), - props.filterKeys { it !in listOf(TITLE, URL, CLICK_URL) } - ) + fun fromJson(props: Map): WidgetItem = WidgetItem( + props[TITLE]?.toString(), + props[URL]?.toString(), + props[CLICK_URL]?.toString(), + props.filterKeys { it !in listOf(TITLE, URL, CLICK_URL) } + ) @Suppress("UNUSED_PARAMETER") @ToJson diff --git a/sdk/src/main/java/io/piano/android/cxense/PageViewEventConverter.kt b/sdk/src/main/java/io/piano/android/cxense/PageViewEventConverter.kt index 5f112b2..66a66ff 100644 --- a/sdk/src/main/java/io/piano/android/cxense/PageViewEventConverter.kt +++ b/sdk/src/main/java/io/piano/android/cxense/PageViewEventConverter.kt @@ -71,57 +71,55 @@ class PageViewEventConverter( return result.filterNotNullValues().toMap() } - private fun List.toPairs(): List> = - filterNot { - it.userId.isEmpty() - }.flatMapIndexed { i, id -> - listOf( - "$EXTERNAL_USER_KEY$i" to id.userType, - "$EXTERNAL_USER_VALUE$i" to id.userId - ) - } + private fun List.toPairs(): List> = filterNot { + it.userId.isEmpty() + }.flatMapIndexed { i, id -> + listOf( + "$EXTERNAL_USER_KEY$i" to id.userType, + "$EXTERNAL_USER_VALUE$i" to id.userId + ) + } internal fun extractQueryData(eventRecord: EventRecord, fixUserIdFunc: () -> String): Map = requireNotNull(mapAdapter.fromJson(eventRecord.data)).let { if (it[CKP].isNullOrEmpty()) it + (CKP to fixUserIdFunc()) else it } - override fun toEventRecord(event: Event): EventRecord? = - (event as? PageViewEvent)?.run { - EventRecord( - PageViewEvent.EVENT_TYPE, - eventId, - mapAdapter.toJson(toQueryMap()), - userId, - rnd, - time, - mergeKey = mergeKey - ) - } + override fun toEventRecord(event: Event): EventRecord? = (event as? PageViewEvent)?.run { + EventRecord( + PageViewEvent.EVENT_TYPE, + eventId, + mapAdapter.toJson(toQueryMap()), + userId, + rnd, + time, + mergeKey = mergeKey + ) + } - override fun update(oldRecord: EventRecord, event: Event): EventRecord = - with(event as PageViewEvent) { - mapAdapter.fromJson(oldRecord.data)?.let { old -> - val oldIds = old.keys.filter { it.startsWith(EXTERNAL_USER_KEY) } - val queryMap = toQueryMap(skipExternalIds = true) - listOf(RND, TIME) - val ids = externalUserIds.toSet() + oldIds.map { ExternalUserId(it, old[it].orEmpty()) } - val map = old - oldIds + queryMap + ids.take(PageViewEvent.MAX_EXTERNAL_USER_IDS).toPairs() - oldRecord.copy( - data = mapAdapter.toJson(map) - ) - } ?: oldRecord - } + override fun update(oldRecord: EventRecord, event: Event): EventRecord = with(event as PageViewEvent) { + mapAdapter.fromJson(oldRecord.data)?.let { old -> + val oldIds = old.keys.filter { it.startsWith(EXTERNAL_USER_KEY) } + val queryMap = toQueryMap(skipExternalIds = true) - listOf(RND, TIME) + val ids = externalUserIds.toSet() + oldIds.map { ExternalUserId(it, old[it].orEmpty()) } + val map = old - oldIds + queryMap + ids.take(PageViewEvent.MAX_EXTERNAL_USER_IDS).toPairs() + oldRecord.copy( + data = mapAdapter.toJson(map) + ) + } ?: oldRecord + } - internal fun updateActiveTimeData(data: String, activeTime: Long): String = + internal fun updateActiveTimeData(data: String, activeTime: Long): String = requireNotNull( + mapAdapter.fromJson(data) + ).let { // some black magic with map - requireNotNull(mapAdapter.fromJson(data)).let { - val map = it + mapOf( - ACTIVE_RND to it[RND].toString(), - ACTIVE_TIME to it[TIME].toString(), - ACTIVE_SPENT_TIME to activeTime.toString() - ) - mapAdapter.toJson(map) - } + val map = it + mapOf( + ACTIVE_RND to it[RND].toString(), + ACTIVE_TIME to it[TIME].toString(), + ACTIVE_SPENT_TIME to activeTime.toString() + ) + mapAdapter.toJson(map) + } @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") internal inline fun Sequence>.filterNotNullValues(): Sequence> = diff --git a/sdk/src/main/java/io/piano/android/cxense/PerformanceEventConverter.kt b/sdk/src/main/java/io/piano/android/cxense/PerformanceEventConverter.kt index 460ce7c..cda1bca 100644 --- a/sdk/src/main/java/io/piano/android/cxense/PerformanceEventConverter.kt +++ b/sdk/src/main/java/io/piano/android/cxense/PerformanceEventConverter.kt @@ -47,46 +47,43 @@ class PerformanceEventConverter( segments to pairs.toMap() } - override fun toEventRecord(event: Event): EventRecord? = - (event as? PerformanceEvent)?.run { - EventRecord( - eventType, - eventId, - jsonAdapter.toJson(this), - prnd, - rnd, - TimeUnit.SECONDS.toMillis(time), - mergeKey = mergeKey - ) - } + override fun toEventRecord(event: Event): EventRecord? = (event as? PerformanceEvent)?.run { + EventRecord( + eventType, + eventId, + jsonAdapter.toJson(this), + prnd, + rnd, + TimeUnit.SECONDS.toMillis(time), + mergeKey = mergeKey + ) + } - override fun update(oldRecord: EventRecord, event: Event): EventRecord = - with(event as PerformanceEvent) { - jsonAdapter.fromJson(oldRecord.data)?.let { old -> - toEventRecord( - PerformanceEvent( - eventId ?: old.eventId, - identities.takeUnless { it.isEmpty() } ?: old.identities, - siteId, - origin, - eventType, - prnd ?: old.prnd, - old.time, - segments?.takeUnless { it.isEmpty() } ?: old.segments, - customParameters.takeUnless { it.isEmpty() } ?: old.customParameters, - consentOptions.takeUnless { it.isEmpty() } ?: old.consentOptions, - old.rnd - ) + override fun update(oldRecord: EventRecord, event: Event): EventRecord = with(event as PerformanceEvent) { + jsonAdapter.fromJson(oldRecord.data)?.let { old -> + toEventRecord( + PerformanceEvent( + eventId ?: old.eventId, + identities.takeUnless { it.isEmpty() } ?: old.identities, + siteId, + origin, + eventType, + prnd ?: old.prnd, + old.time, + segments?.takeUnless { it.isEmpty() } ?: old.segments, + customParameters.takeUnless { it.isEmpty() } ?: old.customParameters, + consentOptions.takeUnless { it.isEmpty() } ?: old.consentOptions, + old.rnd ) - } ?: oldRecord - } + ) + } ?: oldRecord + } - internal fun prepareKey(objectName: String, nameKey: String, valueKey: String, name: String): String = - listOf( - objectName, - "$nameKey:$name", - valueKey - ).joinToString(separator = "/") + internal fun prepareKey(objectName: String, nameKey: String, valueKey: String, name: String): String = listOf( + objectName, + "$nameKey:$name", + valueKey + ).joinToString(separator = "/") companion object { private const val CONSENT = "con" diff --git a/sdk/src/main/java/io/piano/android/cxense/SdkInterceptor.kt b/sdk/src/main/java/io/piano/android/cxense/SdkInterceptor.kt index a45d02a..b40cf80 100644 --- a/sdk/src/main/java/io/piano/android/cxense/SdkInterceptor.kt +++ b/sdk/src/main/java/io/piano/android/cxense/SdkInterceptor.kt @@ -12,22 +12,20 @@ internal class SdkInterceptor( private val sdkName: String, private val sdkVersion: String, ) : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response = - chain.proceed( - chain.request() - .run { - newBuilder() - .url(url.addSdkParams()) - .build() - } - ) + override fun intercept(chain: Interceptor.Chain): Response = chain.proceed( + chain.request() + .run { + newBuilder() + .url(url.addSdkParams()) + .build() + } + ) - private fun HttpUrl.addSdkParams() = - newBuilder() - .addQueryParameter(ARG_SDK_NAME, sdkName) - .addQueryParameter(ARG_SDK_PLATFORM, VALUE_SDK_PLATFORM) - .addQueryParameter(ARG_SDK_VERSION, sdkVersion) - .build() + private fun HttpUrl.addSdkParams() = newBuilder() + .addQueryParameter(ARG_SDK_NAME, sdkName) + .addQueryParameter(ARG_SDK_PLATFORM, VALUE_SDK_PLATFORM) + .addQueryParameter(ARG_SDK_VERSION, sdkVersion) + .build() companion object { const val ARG_SDK_NAME = "sdk" diff --git a/sdk/src/main/java/io/piano/android/cxense/SendTask.kt b/sdk/src/main/java/io/piano/android/cxense/SendTask.kt index b0ba084..3748932 100644 --- a/sdk/src/main/java/io/piano/android/cxense/SendTask.kt +++ b/sdk/src/main/java/io/piano/android/cxense/SendTask.kt @@ -21,12 +21,11 @@ class SendTask( var sendCallback: CxenseSdk.DispatchEventsCallback?, ) : Runnable { - private fun EventRecord.toEventStatus(e: Exception? = null) = - EventStatus( - customId, - isSent, - e - ) + private fun EventRecord.toEventStatus(e: Exception? = null) = EventStatus( + customId, + isSent, + e + ) private fun List.notifyCallback(e: Exception? = null) = map { it.toEventStatus(e) }.notifyCallback() diff --git a/sdk/src/main/java/io/piano/android/cxense/UserAgentInterceptor.kt b/sdk/src/main/java/io/piano/android/cxense/UserAgentInterceptor.kt index 0b25d35..936641d 100644 --- a/sdk/src/main/java/io/piano/android/cxense/UserAgentInterceptor.kt +++ b/sdk/src/main/java/io/piano/android/cxense/UserAgentInterceptor.kt @@ -10,11 +10,10 @@ import okhttp3.Response internal class UserAgentInterceptor( private val userAgentProvider: UserAgentProvider, ) : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response = - chain.proceed( - chain.request() - .newBuilder() - .header("User-Agent", userAgentProvider.userAgent) - .build() - ) + override fun intercept(chain: Interceptor.Chain): Response = chain.proceed( + chain.request() + .newBuilder() + .header("User-Agent", userAgentProvider.userAgent) + .build() + ) } diff --git a/sdk/src/main/java/io/piano/android/cxense/UserAgentProvider.kt b/sdk/src/main/java/io/piano/android/cxense/UserAgentProvider.kt index c85365c..1b2698a 100644 --- a/sdk/src/main/java/io/piano/android/cxense/UserAgentProvider.kt +++ b/sdk/src/main/java/io/piano/android/cxense/UserAgentProvider.kt @@ -17,36 +17,35 @@ internal class UserAgentProvider( ) { val userAgent: String by lazy { "cx-sdk/$sdkVersion ${context.getDefaultUserAgent()}" } - private fun Context.getDefaultUserAgent(): String = - runCatching { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && packageName != Application.getProcessName()) { - WebView.setDataDirectorySuffix(WEBVIEW_SUFFIX) - } - WebSettings.getDefaultUserAgent(this) - }.recover { - /* - This block is needed as attempt to avoid problem with Android System WebView - service's update during which any requests to WebViews will be finished - with android.content.pm.PackageManager$NameNotFoundException. - - What is important here, that 'user-agent' is required param in Cxense Insight API, - so, we need to provide it. We can use 'http.agent' property's value here, but - it provides less details about device than WebView. That is why property's value - is used without defaultUserAgent field's initialization. - - Best practise here - always using WebView's 'user-agent' string. - - Bug in Android issue tracker can be found here: - https://code.google.com/p/android/issues/detail?id=175124 - - Good explanation of the problem can be found here: - https://bugs.chromium.org/p/chromium/issues/detail?id=506369 - */ - Timber.e(it) - System.getProperty("http.agent") ?: "" - }.map { - it.filter { c -> c == '\t' || c in '\u0020'..'\u007e' } - }.getOrDefault("") + private fun Context.getDefaultUserAgent(): String = runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && packageName != Application.getProcessName()) { + WebView.setDataDirectorySuffix(WEBVIEW_SUFFIX) + } + WebSettings.getDefaultUserAgent(this) + }.recover { + /* + This block is needed as attempt to avoid problem with Android System WebView + service's update during which any requests to WebViews will be finished + with android.content.pm.PackageManager$NameNotFoundException. + + What is important here, that 'user-agent' is required param in Cxense Insight API, + so, we need to provide it. We can use 'http.agent' property's value here, but + it provides less details about device than WebView. That is why property's value + is used without defaultUserAgent field's initialization. + + Best practise here - always using WebView's 'user-agent' string. + + Bug in Android issue tracker can be found here: + https://code.google.com/p/android/issues/detail?id=175124 + + Good explanation of the problem can be found here: + https://bugs.chromium.org/p/chromium/issues/detail?id=506369 + */ + Timber.e(it) + System.getProperty("http.agent") ?: "" + }.map { + it.filter { c -> c == '\t' || c in '\u0020'..'\u007e' } + }.getOrDefault("") companion object { private const val WEBVIEW_SUFFIX = "CxenseSDK" diff --git a/sdk/src/main/java/io/piano/android/cxense/db/DatabaseHelper.kt b/sdk/src/main/java/io/piano/android/cxense/db/DatabaseHelper.kt index cb11eb8..df4ffac 100644 --- a/sdk/src/main/java/io/piano/android/cxense/db/DatabaseHelper.kt +++ b/sdk/src/main/java/io/piano/android/cxense/db/DatabaseHelper.kt @@ -51,24 +51,22 @@ class DatabaseHelper( } } - fun save(eventRecord: EventRecord): Long = - eventRecord.id?.let { id -> - writableDatabase.update( - EventRecord.TABLE_NAME, - eventRecord.toContentValues(), - "${BaseColumns._ID} = ?", - arrayOf(id.toString()) - ).toLong() - } ?: writableDatabase.insert( + fun save(eventRecord: EventRecord): Long = eventRecord.id?.let { id -> + writableDatabase.update( EventRecord.TABLE_NAME, - null, - eventRecord.toContentValues() - ) + eventRecord.toContentValues(), + "${BaseColumns._ID} = ?", + arrayOf(id.toString()) + ).toLong() + } ?: writableDatabase.insert( + EventRecord.TABLE_NAME, + null, + eventRecord.toContentValues() + ) - fun delete(eventRecord: EventRecord): Int = - eventRecord.id?.let { id -> - delete("${BaseColumns._ID} = ?", id.toString()) - } ?: -1 + fun delete(eventRecord: EventRecord): Int = eventRecord.id?.let { id -> + delete("${BaseColumns._ID} = ?", id.toString()) + } ?: -1 fun delete(whereClause: String?, vararg whereArgs: String): Int = writableDatabase.delete(EventRecord.TABLE_NAME, whereClause, whereArgs) @@ -81,29 +79,28 @@ class DatabaseHelper( having: String? = null, orderBy: String? = "${EventRecord.TIME} ASC", limit: String? = null, - ): List = - readableDatabase.query( - EventRecord.TABLE_NAME, - columns, - selection, - selectionArgs, - groupBy, - having, - orderBy, - limit - ).use { c -> - generateSequence { if (c.moveToNext()) c else null } - .map(Companion::cursorRowToContentValues) - .toList() - } + ): List = readableDatabase.query( + EventRecord.TABLE_NAME, + columns, + selection, + selectionArgs, + groupBy, + having, + orderBy, + limit + ).use { c -> + generateSequence { if (c.moveToNext()) c else null } + .map(Companion::cursorRowToContentValues) + .toList() + } companion object { const val DATABASE_VERSION = 2 private const val DATABASE_NAME = "tracks.db" @JvmStatic - private fun cursorRowToContentValues(c: Cursor): ContentValues = - ContentValues().apply { + private fun cursorRowToContentValues(c: Cursor): ContentValues = ContentValues() + .apply { for (i in 0 until c.columnCount) { val name = c.columnNames[i] when (c.getType(i)) { diff --git a/sdk/src/main/java/io/piano/android/cxense/db/EventRecord.kt b/sdk/src/main/java/io/piano/android/cxense/db/EventRecord.kt index 04421de..c427dcd 100644 --- a/sdk/src/main/java/io/piano/android/cxense/db/EventRecord.kt +++ b/sdk/src/main/java/io/piano/android/cxense/db/EventRecord.kt @@ -22,8 +22,8 @@ data class EventRecord( var isSent: Boolean = false, ) { - fun toContentValues(): ContentValues = - ContentValues().apply { + fun toContentValues(): ContentValues = ContentValues() + .apply { put(TYPE, eventType) put(CUSTOM_ID, customId) put(DATA, data) diff --git a/settings.gradle.kts b/settings.gradle.kts index 6735c9d..1c41f5a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,8 +15,8 @@ dependencyResolutionManagement { } plugins { - id("com.gradle.enterprise") version "3.13" - id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" + id("com.gradle.enterprise") version "3.16.2" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } includeBuild("gradle/plugins")