From 61294a4935509c42df61eb589673fc31300263a9 Mon Sep 17 00:00:00 2001 From: Bojan Date: Fri, 5 Aug 2022 18:24:37 +0200 Subject: [PATCH 1/5] Add readme logo. --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 63b77fc..cb03b8a 100644 --- a/README.md +++ b/README.md @@ -367,9 +367,13 @@ limitations under the License. ``` ## Credits - Maintained and sponsored by [Infinum](http://www.infinum.com). - - - +

+ + + + + + +

From c2234e57a227de1dddc58741394e7df150178601 Mon Sep 17 00:00:00 2001 From: Bojan Date: Tue, 23 Aug 2022 17:05:40 +0200 Subject: [PATCH 2/5] WIP Android 13 compatibility. --- config.gradle | 6 +++--- .../com/infinum/collar/ui/presentation/BundleMapper.kt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config.gradle b/config.gradle index 0ca44fe..ecd240d 100644 --- a/config.gradle +++ b/config.gradle @@ -5,9 +5,9 @@ ext { buildConfig = [ "minSdk" : 19, - "compileSdk": 32, - "targetSdk" : 32, - "buildTools": "32.0.0" + "compileSdk": 33, + "targetSdk" : 33, + "buildTools": "33.0.0" ] releaseConfig = [ "group" : "com.infinum.collar", diff --git a/ui/src/main/kotlin/com/infinum/collar/ui/presentation/BundleMapper.kt b/ui/src/main/kotlin/com/infinum/collar/ui/presentation/BundleMapper.kt index 5fb55e5..371aef4 100644 --- a/ui/src/main/kotlin/com/infinum/collar/ui/presentation/BundleMapper.kt +++ b/ui/src/main/kotlin/com/infinum/collar/ui/presentation/BundleMapper.kt @@ -15,6 +15,7 @@ internal object BundleMapper { val iterator = keys.iterator() while (iterator.hasNext()) { val key = iterator.next() + @Suppress("DEPRECATION") val value = bundle.get(key) map[key] = value?.let { when (value) { From be3e595057baf82e52cbfc88cb34ce0067d26710 Mon Sep 17 00:00:00 2001 From: Bojan Date: Tue, 23 Aug 2022 18:51:26 +0200 Subject: [PATCH 3/5] WIP Android 13 compatibility. --- .idea/compiler.xml | 1 - CHANGELOG.md | 7 ++ annotations/build.gradle | 4 +- build.gradle | 1 - config.gradle | 2 +- core/build.gradle | 72 ++++++------------- core/publish.gradle | 15 +--- core/src/main/AndroidManifest.xml | 1 - .../main/kotlin/com/infinum/collar/Collar.kt | 6 +- .../main/kotlin/com/infinum/collar/Event.kt | 4 +- .../META-INF/proguard/collar-core.pro} | 0 generator/build.gradle | 4 +- gradle/libs.versions.toml | 2 +- lint/build.gradle | 4 +- plugin/build.gradle | 2 +- processor/build.gradle | 4 +- .../collectors/AnalyticsEventsCollector.kt | 4 +- .../collar/processor/extensions/Element.kt | 24 +++---- .../processor/models/EventParameterHolder.kt | 2 +- .../processor/specs/AnalyticsEventsSpec.kt | 25 ++++--- .../validators/AnalyticsEventsValidator.kt | 2 +- .../collar/sample/KotlinMainActivity.kt | 7 -- .../com/infinum/collar/ui/LiveCollector.kt | 6 +- .../{BundleMapper.kt => ParametersMapper.kt} | 43 +++++------ 24 files changed, 102 insertions(+), 140 deletions(-) delete mode 100644 core/src/main/AndroidManifest.xml rename core/{proguard-rules.txt => src/main/resources/META-INF/proguard/collar-core.pro} (100%) rename ui/src/main/kotlin/com/infinum/collar/ui/presentation/{BundleMapper.kt => ParametersMapper.kt} (58%) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 4a3b661..3811f0e 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -6,7 +6,6 @@ - diff --git a/CHANGELOG.md b/CHANGELOG.md index c2deb74..5e589dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Change Log ========== +## Version 1.3.7 + +_2022-MM-DD_ + +* Make core module pure Kotlin. +* Replace Bundle with Map dependency. + ## Version 1.3.6 _2022-08-09_ diff --git a/annotations/build.gradle b/annotations/build.gradle index bceaef3..f6fa795 100644 --- a/annotations/build.gradle +++ b/annotations/build.gradle @@ -14,7 +14,7 @@ java { compileKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() freeCompilerArgs += [ '-Xjvm-default=all' ] @@ -22,7 +22,7 @@ compileKotlin { } compileTestKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() } } diff --git a/build.gradle b/build.gradle index 550a525..33fba8b 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,6 @@ task clean(type: Delete) { } task lintAll(dependsOn: [ - ':core:lintRelease', ':ui:lintRelease', ':ui-no-op:lintRelease' ]) { diff --git a/config.gradle b/config.gradle index ecd240d..a376bc8 100644 --- a/config.gradle +++ b/config.gradle @@ -1,7 +1,7 @@ ext { def major = 1 def minor = 3 - def patch = 6 + def patch = 7 buildConfig = [ "minSdk" : 19, diff --git a/core/build.gradle b/core/build.gradle index 9d46947..da8d0b3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,47 +1,33 @@ plugins { - id "com.android.library" - id "kotlin-android" + id "java-library" + id "org.jetbrains.kotlin.jvm" } -android { - buildFeatures { - buildConfig = false - } - - compileSdkVersion buildConfig.compileSdk - buildToolsVersion buildConfig.buildTools - - defaultConfig { - minSdkVersion buildConfig.minSdk - targetSdkVersion buildConfig.targetSdk - versionCode releaseConfig.versionCode - versionName releaseConfig.version - } +kotlin { + explicitApi() +} - buildTypes { - debug { - debuggable true - minifyEnabled false - } - release { - debuggable false - minifyEnabled true - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.txt" - } - } +java { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 +} - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } +compileKotlin { kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() freeCompilerArgs += [ - '-Xjvm-default=all', - '-Xexplicit-api=strict' + '-Xjvm-default=all' ] } - packagingOptions { +} +compileTestKotlin { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} + +/* + packagingOptions { exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE.txt' @@ -52,27 +38,15 @@ android { exclude 'META-INF/ASL2.0' exclude("META-INF/*.kotlin_module") } - - sourceSets.each { - it.java.srcDirs += "src/$it.name/kotlin" - } - - publishing { - singleVariant('release') { - withSourcesJar() - withJavadocJar() - } - } -} + */ dependencies { api libs.libraryannotations implementation libs.kotlin.core compileOnly libs.annotations - implementation libs.androidx.core testImplementation "junit:junit:4.13.2" - testImplementation 'org.mockito:mockito-core:3.11.2' + testImplementation 'org.mockito:mockito-core:4.5.1' } -apply from: "publish.gradle" \ No newline at end of file +apply from: "publish.gradle" diff --git a/core/publish.gradle b/core/publish.gradle index 7785d45..70e2935 100644 --- a/core/publish.gradle +++ b/core/publish.gradle @@ -3,7 +3,7 @@ apply plugin: "signing" task sourcesJar(type: Jar, dependsOn: assemble) { archiveClassifier.set("sources") - from android.sourceSets.main.java.source + from sourceSets.main.allSource } task javadocsJar(type: Jar, dependsOn: "dokkaJavadoc") { @@ -33,11 +33,12 @@ afterEvaluate { } publications { release(MavenPublication) { + from components.java + groupId = releaseConfig.group artifactId = "collar-core" version = releaseConfig.version - artifact bundleReleaseAar artifact sourcesJar artifact javadocsJar @@ -64,16 +65,6 @@ afterEvaluate { url = "https://github.com/infinum/android-collar" } } - pom.withXml { - def root = asNode() - def dependenciesNode = root.appendNode("dependencies") - configurations.implementation.allDependencies.each { - def dependencyNode = dependenciesNode.appendNode("dependency") - dependencyNode.appendNode("groupId", it.group) - dependencyNode.appendNode("artifactId", it.name) - dependencyNode.appendNode("version", it.version) - } - } signing { sign publishing.publications.release } diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml deleted file mode 100644 index 87efe16..0000000 --- a/core/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/core/src/main/kotlin/com/infinum/collar/Collar.kt b/core/src/main/kotlin/com/infinum/collar/Collar.kt index 12c7893..b072fc0 100644 --- a/core/src/main/kotlin/com/infinum/collar/Collar.kt +++ b/core/src/main/kotlin/com/infinum/collar/Collar.kt @@ -1,7 +1,5 @@ package com.infinum.collar -import android.os.Bundle - /** * Singleton object entry point for screen names, events and properties collection. */ @@ -58,11 +56,11 @@ public object Collar { * @param params value. */ @JvmStatic - public fun trackEvent(eventName: String, params: Bundle): Unit = + public fun trackEvent(eventName: String, params: Map): Unit = collector?.onEvent( Event( name = eventName, - params = if (params.isEmpty) null else params + params = params.ifEmpty { null } ) ) ?: Unit diff --git a/core/src/main/kotlin/com/infinum/collar/Event.kt b/core/src/main/kotlin/com/infinum/collar/Event.kt index e3bc08a..0579eca 100644 --- a/core/src/main/kotlin/com/infinum/collar/Event.kt +++ b/core/src/main/kotlin/com/infinum/collar/Event.kt @@ -1,7 +1,5 @@ package com.infinum.collar -import android.os.Bundle - /** * This is the container model for the triggered tracking analytics event. */ @@ -15,5 +13,5 @@ public data class Event( /** * Optional parameters of the tracked analytics event. */ - val params: Bundle? = null + val params: Map? = null ) diff --git a/core/proguard-rules.txt b/core/src/main/resources/META-INF/proguard/collar-core.pro similarity index 100% rename from core/proguard-rules.txt rename to core/src/main/resources/META-INF/proguard/collar-core.pro diff --git a/generator/build.gradle b/generator/build.gradle index 1868cbf..2e08644 100644 --- a/generator/build.gradle +++ b/generator/build.gradle @@ -15,7 +15,7 @@ java { compileKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() freeCompilerArgs += [ '-Xjvm-default=all', '-opt-in=kotlinx.serialization.ExperimentalSerializationApi' @@ -24,7 +24,7 @@ compileKotlin { } compileTestKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bb1c140..31ab19f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -collar = "1.3.6" +collar = "1.3.7" gradle = "7.2.2" lint = "30.2.1" kotlin = "1.7.10" diff --git a/lint/build.gradle b/lint/build.gradle index 9978494..5535ebe 100644 --- a/lint/build.gradle +++ b/lint/build.gradle @@ -14,7 +14,7 @@ java { compileKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() freeCompilerArgs += [ '-Xjvm-default=all' ] @@ -22,7 +22,7 @@ compileKotlin { } compileTestKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() } } diff --git a/plugin/build.gradle b/plugin/build.gradle index 6c767ed..c7d28e2 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -21,7 +21,7 @@ compileKotlin { } compileTestKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() } } diff --git a/processor/build.gradle b/processor/build.gradle index 82be258..a92dde7 100644 --- a/processor/build.gradle +++ b/processor/build.gradle @@ -15,7 +15,7 @@ java { compileKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() freeCompilerArgs += [ '-Xjvm-default=all', '-opt-in=com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview' @@ -24,7 +24,7 @@ compileKotlin { } compileTestKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() } } diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt b/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt index f78c00a..05fa4ec 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt @@ -5,7 +5,7 @@ import com.infinum.collar.annotations.EventName import com.infinum.collar.annotations.EventParameterName import com.infinum.collar.processor.extensions.asClassName import com.infinum.collar.processor.extensions.fieldElements -import com.infinum.collar.processor.extensions.resolveMethod +import com.infinum.collar.processor.extensions.isSupported import com.infinum.collar.processor.extensions.toLowerSnakeCase import com.infinum.collar.processor.models.AnalyticsEventsHolder import com.infinum.collar.processor.models.EventHolder @@ -48,7 +48,7 @@ internal class AnalyticsEventsCollector( .map { fieldParameter -> EventParameterHolder( enabled = parameterEnabled(fieldParameter), - method = fieldParameter.resolveMethod(), + isSupported = fieldParameter.isSupported(), resolvedName = parameterName(fieldParameter), variableName = fieldParameter.simpleName.toString() ) diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt b/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt index 9925cb8..c9bba68 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt @@ -31,20 +31,20 @@ internal fun Element.constructorParameterNames(): List = internal fun Element.fieldElements(): List = ElementFilter.fieldsIn(this.enclosedElements).orEmpty() -internal fun VariableElement.resolveMethod(): String = +internal fun VariableElement.isSupported(): Boolean = this.asType().toString().let { when (it) { - "java.lang.String" -> "putString" - "boolean" -> "putBoolean" - "byte" -> "putByte" - "char" -> "putChar" - "double" -> "putDouble" - "float" -> "putFloat" - "int" -> "putInt" - "long" -> "putLong" - "short" -> "putShort" - "android.os.Bundle" -> "putBundle" - else -> "" + "java.lang.String" -> true + "boolean" -> true + "byte" -> true + "char" -> true + "double" -> true + "float" -> true + "int" -> true + "long" -> true + "short" -> true + "kotlin.collections.Map" -> true + else -> false } } diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/models/EventParameterHolder.kt b/processor/src/main/kotlin/com/infinum/collar/processor/models/EventParameterHolder.kt index 690045d..b8e2513 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/models/EventParameterHolder.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/models/EventParameterHolder.kt @@ -2,7 +2,7 @@ package com.infinum.collar.processor.models internal data class EventParameterHolder( val enabled: Boolean, - val method: String, + val isSupported: Boolean, val resolvedName: String, val variableName: String ) : Holder diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/specs/AnalyticsEventsSpec.kt b/processor/src/main/kotlin/com/infinum/collar/processor/specs/AnalyticsEventsSpec.kt index a55f1f6..e045306 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/specs/AnalyticsEventsSpec.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/specs/AnalyticsEventsSpec.kt @@ -20,12 +20,12 @@ internal class AnalyticsEventsSpec( private const val STATEMENT_EVENT_CLASS_START = "is %T -> %T.%L(" private const val STATEMENT_EVENT_CLASS_END = ")" private const val STATEMENT_EVENT_NAME = "%S," - private const val STATEMENT_BUNDLE_EMPTY = "%T()" - private const val STATEMENT_BUNDLE_START = "%T().apply {" - private const val STATEMENT_BUNDLE_END = "}" - private const val STATEMENT_EVENT_PARAMETER = "%L(%S, %L.%L)" + private const val STATEMENT_BUNDLE_EMPTY = "mapOf()" + private const val STATEMENT_BUNDLE_START = "mapOf(" + private const val STATEMENT_BUNDLE_END = ")" + private const val STATEMENT_EVENT_PARAMETER = "%S to %L.%L" - private val CLASS_BUNDLE = ClassName("android.os", "Bundle") +// private val CLASS_BUNDLE = ClassName("android.os", "Bundle") } override fun parameterName(): String = PARAMETER_NAME_EVENT @@ -64,22 +64,21 @@ internal class AnalyticsEventsSpec( .build() private fun eventParametersEmpty(builder: CodeBlock.Builder): CodeBlock.Builder = - builder.addStatement(STATEMENT_BUNDLE_EMPTY, CLASS_BUNDLE) + builder.addStatement(STATEMENT_BUNDLE_EMPTY) private fun eventParameters(builder: CodeBlock.Builder, holder: EventHolder): CodeBlock.Builder = with(builder) { - addStatement(STATEMENT_BUNDLE_START, CLASS_BUNDLE) + addStatement(STATEMENT_BUNDLE_START) indent() holder.eventParameters .filter { parameterHolder -> parameterHolder.enabled } - .withIndex() - .forEach { + .forEachIndexed { index, eventParameterHolder -> addStatement( - STATEMENT_EVENT_PARAMETER, - it.value.method, - it.value.resolvedName, + STATEMENT_EVENT_PARAMETER + .plus(",".takeIf { index != holder.eventParameters.size - 1 }.orEmpty()), + eventParameterHolder.resolvedName, parameterName(), - it.value.variableName + eventParameterHolder.variableName ) } unindent() diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/validators/AnalyticsEventsValidator.kt b/processor/src/main/kotlin/com/infinum/collar/processor/validators/AnalyticsEventsValidator.kt index 5236a4a..d72c657 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/validators/AnalyticsEventsValidator.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/validators/AnalyticsEventsValidator.kt @@ -118,7 +118,7 @@ internal class AnalyticsEventsValidator( } private fun validateParametersType(holder: EventHolder): Boolean { - val invalidParameterTypes = holder.eventParameters.filter { it.method.isBlank() } + val invalidParameterTypes = holder.eventParameters.filter { it.isSupported.not() } return if (invalidParameterTypes.isNotEmpty()) { messager.showWarning( "Event parameters ${invalidParameterTypes.joinToString(", ") { it.variableName }}" + diff --git a/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt b/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt index 54f726a..07d21c8 100644 --- a/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt +++ b/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt @@ -4,9 +4,6 @@ import android.app.Activity import android.content.Intent import android.os.Bundle import com.infinum.collar.annotations.ScreenName -// import com.infinum.collar.sample.analytics.trackingplan.TrackingPlanEvents -// import com.infinum.collar.sample.analytics.trackingplan.TrackingPlanScreens -// import com.infinum.collar.sample.analytics.trackingplan.trackEvent import com.infinum.collar.sample.databinding.ActivityMainKotlinBinding import com.infinum.collar.trackScreen @@ -20,10 +17,6 @@ class KotlinMainActivity : Activity() { setContentView(viewBinding.root) -// trackEvent( -// TrackingPlanEvents.AlexaConnect("", "") -// ) - viewBinding.buttonProduceEvent3.setOnClickListener { trackEvent( AnalyticsEvent.EventThree( diff --git a/ui/src/main/kotlin/com/infinum/collar/ui/LiveCollector.kt b/ui/src/main/kotlin/com/infinum/collar/ui/LiveCollector.kt index 0587297..803b828 100644 --- a/ui/src/main/kotlin/com/infinum/collar/ui/LiveCollector.kt +++ b/ui/src/main/kotlin/com/infinum/collar/ui/LiveCollector.kt @@ -13,7 +13,7 @@ import com.infinum.collar.ui.domain.Repositories import com.infinum.collar.ui.domain.entities.models.EntityParameters import com.infinum.collar.ui.domain.settings.models.SettingsParameters import com.infinum.collar.ui.extensions.redact -import com.infinum.collar.ui.presentation.BundleMapper +import com.infinum.collar.ui.presentation.ParametersMapper import com.infinum.collar.ui.presentation.notifications.inapp.InAppNotificationFactory import com.infinum.collar.ui.presentation.notifications.system.SystemNotificationFactory import kotlinx.coroutines.Dispatchers @@ -101,7 +101,9 @@ public open class LiveCollector( CollarEntity( type = EntityType.EVENT, name = event.name.redact(configuration.redactedKeywords), - parameters = event.params?.let { BundleMapper.toMap(it, configuration.redactedKeywords) } + parameters = event.params?.let { + ParametersMapper.toRedactedString(it, configuration.redactedKeywords) + } ).let { saveEntity(it) if (settings.showSystemNotifications) { diff --git a/ui/src/main/kotlin/com/infinum/collar/ui/presentation/BundleMapper.kt b/ui/src/main/kotlin/com/infinum/collar/ui/presentation/ParametersMapper.kt similarity index 58% rename from ui/src/main/kotlin/com/infinum/collar/ui/presentation/BundleMapper.kt rename to ui/src/main/kotlin/com/infinum/collar/ui/presentation/ParametersMapper.kt index 371aef4..342e978 100644 --- a/ui/src/main/kotlin/com/infinum/collar/ui/presentation/BundleMapper.kt +++ b/ui/src/main/kotlin/com/infinum/collar/ui/presentation/ParametersMapper.kt @@ -5,34 +5,37 @@ import com.infinum.collar.ui.extensions.redact import timber.log.Timber @Suppress("ComplexMethod") -internal object BundleMapper { +internal object ParametersMapper { - fun toMap(bundle: Bundle, redactedKeywords: Set): String { + @Suppress("NestedBlockDepth") + fun toRedactedString(parameterMap: Map, redactedKeywords: Set): String { val map = mutableMapOf() - val keys: Set = bundle.keySet() + val keys: Set = parameterMap.keys val iterator = keys.iterator() while (iterator.hasNext()) { val key = iterator.next() - @Suppress("DEPRECATION") - val value = bundle.get(key) + val value = parameterMap[key] + map[key] = value?.let { - when (value) { - is String -> bundle.getString(key) - is Boolean -> bundle.getBoolean(key).toString() - is Byte -> bundle.getByte(key).toString() - is Char -> bundle.getChar(key).toString() - is Double -> bundle.getDouble(key).toString() - is Float -> bundle.getFloat(key).toString() - is Int -> bundle.getInt(key).toString() - is Long -> bundle.getLong(key).toString() - is Short -> bundle.getShort(key).toString() - is CharSequence -> bundle.getCharSequence(key).toString() - is Bundle -> toMap( - bundle.getBundle(key) ?: Bundle.EMPTY, - redactedKeywords - ) + when (it) { + is String -> parameterMap[key].toString() + is Boolean -> parameterMap[key].toString() + is Byte -> parameterMap[key].toString() + is Char -> parameterMap[key].toString() + is Double -> parameterMap[key].toString() + is Float -> parameterMap[key].toString() + is Int -> parameterMap[key].toString() + is Long -> parameterMap[key].toString() + is Short -> parameterMap[key].toString() + is CharSequence -> parameterMap[key].toString() + is Map<*, *> -> { + @Suppress("UNCHECKED_CAST") + (parameterMap[key] as? Map)?.let { childMap -> + toRedactedString(childMap, redactedKeywords) + } + } else -> { Timber.w( "Illegal value type ${value.javaClass.canonicalName} for key \"$key\"" From d82e508b2af86df5aec16d2dd56987bbc2180536 Mon Sep 17 00:00:00 2001 From: Bojan Date: Wed, 24 Aug 2022 13:06:21 +0200 Subject: [PATCH 4/5] Migrate to map and fix supported parameters check. --- .../collectors/AnalyticsEventsCollector.kt | 8 +- .../collar/processor/extensions/Element.kt | 52 ++++-- .../AnalyticsEventsSubprocessor.kt | 2 +- .../infinum/collar/sample/ChildFragment.java | 16 +- .../infinum/collar/sample/MainActivity.java | 159 ++++++++---------- .../infinum/collar/sample/AnalyticsEvent.kt | 8 +- .../collar/sample/KotlinMainActivity.kt | 6 +- 7 files changed, 128 insertions(+), 123 deletions(-) diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt b/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt index 05fa4ec..9834cef 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/collectors/AnalyticsEventsCollector.kt @@ -13,9 +13,13 @@ import com.infinum.collar.processor.models.EventParameterHolder import javax.annotation.processing.RoundEnvironment import javax.lang.model.element.Element import javax.lang.model.element.TypeElement +import javax.lang.model.util.Elements +import javax.lang.model.util.Types internal class AnalyticsEventsCollector( - private val roundEnvironment: RoundEnvironment + private val roundEnvironment: RoundEnvironment, + private val elementUtils: Elements, + private val typeUtils: Types ) : Collector { companion object { @@ -48,7 +52,7 @@ internal class AnalyticsEventsCollector( .map { fieldParameter -> EventParameterHolder( enabled = parameterEnabled(fieldParameter), - isSupported = fieldParameter.isSupported(), + isSupported = fieldParameter.isSupported(typeUtils, elementUtils), resolvedName = parameterName(fieldParameter), variableName = fieldParameter.simpleName.toString() ) diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt b/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt index c9bba68..b3a580c 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/extensions/Element.kt @@ -10,7 +10,11 @@ import javax.lang.model.element.NestingKind import javax.lang.model.element.PackageElement import javax.lang.model.element.TypeElement import javax.lang.model.element.VariableElement +import javax.lang.model.type.DeclaredType +import javax.lang.model.type.TypeKind import javax.lang.model.util.ElementFilter +import javax.lang.model.util.Elements +import javax.lang.model.util.Types internal fun Element.isSealedClass(): Boolean = this.getAnnotation(Metadata::class.java) @@ -31,22 +35,42 @@ internal fun Element.constructorParameterNames(): List = internal fun Element.fieldElements(): List = ElementFilter.fieldsIn(this.enclosedElements).orEmpty() -internal fun VariableElement.isSupported(): Boolean = - this.asType().toString().let { - when (it) { - "java.lang.String" -> true - "boolean" -> true - "byte" -> true - "char" -> true - "double" -> true - "float" -> true - "int" -> true - "long" -> true - "short" -> true - "kotlin.collections.Map" -> true - else -> false +@Suppress("NestedBlockDepth", "ReturnCount") +internal fun VariableElement.isSupported(typeUtils: Types, elementUtils: Elements): Boolean { + val type = this.asType() + val kind = this.asType().kind + + if (kind.isPrimitive) { + return true + } else { + if (kind == TypeKind.DECLARED) { + val isString = typeUtils.isAssignable( + type, + elementUtils.getTypeElement(String::class.java.canonicalName).asType() + ) + if (isString) { + return true + } else { + val mapElement: TypeElement = elementUtils.getTypeElement(Map::class.java.canonicalName) + val isMap = typeUtils.isAssignable(typeUtils.erasure(type), mapElement.asType()) + return if (isMap) { + val hasStringKeys = (type as? DeclaredType)?.typeArguments?.firstOrNull()?.let { + typeUtils.isAssignable( + it, + elementUtils.getTypeElement(String::class.java.canonicalName).asType() + ) + } ?: false + + hasStringKeys + } else { + false + } + } + } else { + return false } } +} internal fun TypeElement.asClassName(): ClassName { fun isClassOrInterface(e: Element) = e.kind.isClass || e.kind.isInterface diff --git a/processor/src/main/kotlin/com/infinum/collar/processor/subprocessors/AnalyticsEventsSubprocessor.kt b/processor/src/main/kotlin/com/infinum/collar/processor/subprocessors/AnalyticsEventsSubprocessor.kt index ae957f0..921f563 100644 --- a/processor/src/main/kotlin/com/infinum/collar/processor/subprocessors/AnalyticsEventsSubprocessor.kt +++ b/processor/src/main/kotlin/com/infinum/collar/processor/subprocessors/AnalyticsEventsSubprocessor.kt @@ -10,7 +10,7 @@ import javax.annotation.processing.RoundEnvironment internal class AnalyticsEventsSubprocessor : CommonSubprocessor() { override fun process(roundEnvironment: RoundEnvironment) { - val collector = AnalyticsEventsCollector(roundEnvironment) + val collector = AnalyticsEventsCollector(roundEnvironment, elementUtils, typeUtils) val validator = AnalyticsEventsValidator(processorOptions, typeUtils, messager) collector.collect().run { diff --git a/sample/src/main/java/com/infinum/collar/sample/ChildFragment.java b/sample/src/main/java/com/infinum/collar/sample/ChildFragment.java index 8c8fe55..7b65c41 100644 --- a/sample/src/main/java/com/infinum/collar/sample/ChildFragment.java +++ b/sample/src/main/java/com/infinum/collar/sample/ChildFragment.java @@ -33,19 +33,11 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - viewBinding.buttonProduceEvent2.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - CollarAnalyticsEvent.trackEvent(new AnalyticsEvent.Event2("bla", 0)); - } - }); + viewBinding.buttonProduceEvent2.setOnClickListener(v -> + CollarAnalyticsEvent.trackEvent(new AnalyticsEvent.Event2("bla", 0)) + ); - viewBinding.buttonShowKotlinMain.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showKotlinMainScreen(); - } - }); + viewBinding.buttonShowKotlinMain.setOnClickListener(v -> showKotlinMainScreen()); } @Override diff --git a/sample/src/main/java/com/infinum/collar/sample/MainActivity.java b/sample/src/main/java/com/infinum/collar/sample/MainActivity.java index 8748824..e29eba7 100644 --- a/sample/src/main/java/com/infinum/collar/sample/MainActivity.java +++ b/sample/src/main/java/com/infinum/collar/sample/MainActivity.java @@ -3,20 +3,16 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.view.View; -import android.widget.CompoundButton; - -import java.util.UUID; import com.infinum.collar.Collar; import com.infinum.collar.CollarScreenNames; import com.infinum.collar.annotations.ScreenName; - -import com.infinum.collar.sample.AnalyticsEvent; -import com.infinum.collar.sample.KotlinScreenNames; -import com.infinum.collar.sample.UserProperty; import com.infinum.collar.sample.databinding.ActivityMainBinding; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + @ScreenName(value = KotlinScreenNames.MAIN_SCREEN_JAVA) public class MainActivity extends Activity { @@ -29,87 +25,72 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(viewBinding.getRoot()); viewBinding.analyticsCollectionSwitch.setOnCheckedChangeListener( - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - Collar.setAnalyticsCollectionStatus(isChecked); - } - } + (buttonView, isChecked) -> Collar.setAnalyticsCollectionStatus(isChecked) ); viewBinding.buttonProduceEvent1.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - CollarAnalyticsEvent.trackEvent(new AnalyticsEvent.Event1( - "awesome", - true, - Byte.MAX_VALUE, - Character.MAX_VALUE, - 7.11, - 31.5f, - 18, - 2L, - Short.MAX_VALUE, - buildBundle(), - "awesome", - true, - Byte.MAX_VALUE, - Character.MAX_VALUE, - 7.11, - 31.5f, - 18, - 2L, - Short.MAX_VALUE, - "awesome", - true, - Byte.MAX_VALUE, - Character.MAX_VALUE, - 7.11, - 31.5f, - 18, - 2L, - Short.MAX_VALUE, - "awesome", - true, - Byte.MAX_VALUE, - Character.MAX_VALUE, - 7.11, - 31.5f, - 18, - 2L, - Short.MAX_VALUE, - buildBundle(), - "awesome", - true, - Byte.MAX_VALUE, - Character.MAX_VALUE, - 7.11, - 31.5f, - 18, - 2L, - Short.MAX_VALUE, - "awesome", - true, - Byte.MAX_VALUE, - Character.MAX_VALUE, - 7.11, - 31.5f, - 18, - 2L, - Short.MAX_VALUE - )); - } - } + v -> CollarAnalyticsEvent.trackEvent(new AnalyticsEvent.Event1( + "awesome", + true, + Byte.MAX_VALUE, + Character.MAX_VALUE, + 7.11, + 31.5f, + 18, + 2L, + Short.MAX_VALUE, + buildMap(), + "awesome", + true, + Byte.MAX_VALUE, + Character.MAX_VALUE, + 7.11, + 31.5f, + 18, + 2L, + Short.MAX_VALUE, + "awesome", + true, + Byte.MAX_VALUE, + Character.MAX_VALUE, + 7.11, + 31.5f, + 18, + 2L, + Short.MAX_VALUE, + "awesome", + true, + Byte.MAX_VALUE, + Character.MAX_VALUE, + 7.11, + 31.5f, + 18, + 2L, + Short.MAX_VALUE, + buildMap(), + "awesome", + true, + Byte.MAX_VALUE, + Character.MAX_VALUE, + 7.11, + 31.5f, + 18, + 2L, + Short.MAX_VALUE, + "awesome", + true, + Byte.MAX_VALUE, + Character.MAX_VALUE, + 7.11, + 31.5f, + 18, + 2L, + Short.MAX_VALUE + )) ); - viewBinding.buttonShowChild.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showChildScreen(); - } - }); + viewBinding.buttonShowChild.setOnClickListener(v -> showChildScreen()); CollarUserProperty.trackProperty(new UserProperty.MyUUID(UUID.randomUUID().toString())); CollarUserProperty.trackProperty(new UserProperty.LanguageType("Java")); @@ -125,11 +106,11 @@ private void showChildScreen() { startActivity(new Intent(this, ChildActivity.class)); } - private Bundle buildBundle() { - final Bundle bundle = new Bundle(); - bundle.putInt("4", 4); - bundle.putInt("5", 5); - bundle.putInt("6", 6); - return bundle; + private Map buildMap() { + Map map = new HashMap<>(); + map.put("4", 4); + map.put("5", 5); + map.put("6", 6); + return map; } } \ No newline at end of file diff --git a/sample/src/main/kotlin/com/infinum/collar/sample/AnalyticsEvent.kt b/sample/src/main/kotlin/com/infinum/collar/sample/AnalyticsEvent.kt index 644b812..f57193d 100644 --- a/sample/src/main/kotlin/com/infinum/collar/sample/AnalyticsEvent.kt +++ b/sample/src/main/kotlin/com/infinum/collar/sample/AnalyticsEvent.kt @@ -1,6 +1,5 @@ package com.infinum.collar.sample -import android.os.Bundle import com.infinum.collar.annotations.AnalyticsEvents import com.infinum.collar.annotations.EventName import com.infinum.collar.annotations.EventParameterName @@ -19,7 +18,7 @@ sealed class AnalyticsEvent { val myInt: Int, val myLong: Long, val myShort: Short, - val myBundle: Bundle, + val myMap: Map, val myOtherString: String, val myOtherBoolean: Boolean, val myOtherByte: Byte, @@ -47,7 +46,7 @@ sealed class AnalyticsEvent { val myInt2: Int, val myLong2: Long, val myShort2: Short, - val myBundle2: Bundle, + val myMap2: Map, val myOtherString2: String, val myOtherBoolean2: Boolean, val myOtherByte2: Byte, @@ -80,7 +79,8 @@ sealed class AnalyticsEvent { @EventName("event3") data class EventThree( val myString: String, - val myBoolean: Boolean + val myBoolean: Boolean, + val myMap: Map ) : AnalyticsEvent() @EventName("event4") diff --git a/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt b/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt index 07d21c8..2949d50 100644 --- a/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt +++ b/sample/src/main/kotlin/com/infinum/collar/sample/KotlinMainActivity.kt @@ -21,7 +21,11 @@ class KotlinMainActivity : Activity() { trackEvent( AnalyticsEvent.EventThree( myString = "cool", - myBoolean = false + myBoolean = false, + myMap = mapOf( + "1" to 1, + "2" to 2 + ) ) ) } From 02d8ba6821d71e87338ba3dc9291c87c5ee7104c Mon Sep 17 00:00:00 2001 From: Bojan Date: Wed, 24 Aug 2022 13:12:31 +0200 Subject: [PATCH 5/5] Update readme and changelog --- CHANGELOG.md | 3 ++- README.md | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e589dc..4f8411c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,11 @@ Change Log ## Version 1.3.7 -_2022-MM-DD_ +_2022-08-24_ * Make core module pure Kotlin. * Replace Bundle with Map dependency. +* Fix Android 13 compatibility issues. ## Version 1.3.6 diff --git a/README.md b/README.md index 6930422..0907b20 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ buildscript { mavenCentral() } dependencies { - classpath "com.infinum.collar:collar-plugin:1.3.6" + classpath "com.infinum.collar:collar-plugin:1.3.7" } } ``` @@ -42,7 +42,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.infinum.collar:collar-plugin:1.3.6") + classpath("com.infinum.collar:collar-plugin:1.3.7") } } ``` @@ -77,7 +77,7 @@ Collar.attach(object : Collector { analyticsProvider.sendScreenName(screenName = screen.name) override fun onEvent(event: Event) = - analyticsProvider.sendEvent(eventName = event.name, eventParameters = event.params ?: Bundle.EMPTY) + analyticsProvider.sendEvent(eventName = event.name, eventParameters = event.params ?: mapOf()) override fun onProperty(property: Property) = analyticsProvider.sendProperty(property.name, property.value) @@ -261,13 +261,13 @@ You can search, filter and clear all sent analytics. In your app `build.gradle` or `build.gradle.kts` add: **Groovy** ```gradle -debugImplementation "com.infinum.collar:collar-ui:1.3.6" -releaseImplementation "com.infinum.collar:collar-ui-no-op:1.3.6" +debugImplementation "com.infinum.collar:collar-ui:1.3.7" +releaseImplementation "com.infinum.collar:collar-ui-no-op:1.3.7" ``` **KotlinDSL** ```kotlin -debugImplementation("com.infinum.collar:collar-ui:1.3.6") -releaseImplementation("com.infinum.collar:collar-ui-no-op:1.3.6") +debugImplementation("com.infinum.collar:collar-ui:1.3.7") +releaseImplementation("com.infinum.collar:collar-ui-no-op:1.3.7") ``` In order to start tracking with UI you must use _LiveCollector_ as in this example: @@ -287,7 +287,7 @@ Collar.attach(object : LiveCollector(configuration) { override fun onEvent(event: Event) = super.onEvent(event).run { - analyticsProvider.sendEvent(eventName = event.name, eventParameters = event.params ?: Bundle.EMPTY) + analyticsProvider.sendEvent(eventName = event.name, eventParameters = event.params ?: mapOf()) } override fun onProperty(property: Property) =