From b7ca30ea39091e75b8e57ba0f63f20e50c924ec0 Mon Sep 17 00:00:00 2001 From: Nikolay Unuchek Date: Fri, 5 Jun 2020 17:08:01 +0300 Subject: [PATCH 1/8] updated gradle 3.3.0 - 4.0.0 fixed generate for module --- build.gradle | 14 ++- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- library_module/.gitignore | 1 + library_module/build.gradle | 55 +++++++++ library_module/consumer-rules.pro | 0 library_module/proguard-rules.pro | 21 ++++ .../library_view/ExampleInstrumentedTest.kt | 24 ++++ library_module/src/main/AndroidManifest.xml | 5 + .../github/library_view/LibraryModule.java | 10 ++ .../github/library_view/ExampleUnitTest.kt | 17 +++ sample/build.gradle | 13 ++- .../main/java/com/orhanobut/sample/Foo.java | 11 -- .../java/com/orhanobut/sample/JavaObject.java | 9 ++ .../java/com/orhanobut/sample/KotlinObject.kt | 9 ++ .../com/orhanobut/sample/MainActivity.java | 9 +- .../com/orhanobut/sample/TrackingTest.java | 2 +- settings.gradle | 1 + tracklytics-plugin/build.gradle | 4 +- .../weaving/plugin/TracklyticsPlugin.groovy | 107 ++++++++++-------- tracklytics-runtime/build.gradle | 100 ++++++++-------- 21 files changed, 291 insertions(+), 127 deletions(-) create mode 100644 library_module/.gitignore create mode 100644 library_module/build.gradle create mode 100644 library_module/consumer-rules.pro create mode 100644 library_module/proguard-rules.pro create mode 100644 library_module/src/androidTest/java/com/github/library_view/ExampleInstrumentedTest.kt create mode 100644 library_module/src/main/AndroidManifest.xml create mode 100644 library_module/src/main/java/com/github/library_view/LibraryModule.java create mode 100644 library_module/src/test/java/com/github/library_view/ExampleUnitTest.kt delete mode 100644 sample/src/main/java/com/orhanobut/sample/Foo.java create mode 100644 sample/src/main/java/com/orhanobut/sample/JavaObject.java create mode 100644 sample/src/main/java/com/orhanobut/sample/KotlinObject.kt diff --git a/build.gradle b/build.gradle index a68e1c9..b43886e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,21 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - repositories { + ext { + kotlin_version = '1.3.72' + } + repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:4.0.0' classpath 'org.gradle.api.plugins:gradle-nexus-plugin:0.7' classpath 'org.aspectj:aspectjtools:1.8.10' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - // NOTE: Do not place your application dependencies here; they belong + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } @@ -45,7 +49,7 @@ subprojects { project -> jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:4.0.0' } } diff --git a/gradle.properties b/gradle.properties index 1231919..c10fe4b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=2.1.0 +VERSION_NAME=2.1.2 GROUP=com.orhanobut.tracklytics POM_DESCRIPTION=Android analytics tracklytics diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 895c38f..33448f6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jan 22 16:58:26 MSK 2019 +#Fri Jun 05 15:00:40 MSK 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/library_module/.gitignore b/library_module/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/library_module/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/library_module/build.gradle b/library_module/build.gradle new file mode 100644 index 0000000..9876c7b --- /dev/null +++ b/library_module/build.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'com.orhanobut.tracklytics' + +buildscript { + repositories { + mavenCentral() + mavenLocal() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.2' + } +} + +repositories { +// mavenCentral() + + // NOTE: This is only needed when developing the plugin! + mavenLocal() +} + +android { + compileSdkVersion 26 + buildToolsVersion "28.0.3" + + compileOptions { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + } + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + +} \ No newline at end of file diff --git a/library_module/consumer-rules.pro b/library_module/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/library_module/proguard-rules.pro b/library_module/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/library_module/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/library_module/src/androidTest/java/com/github/library_view/ExampleInstrumentedTest.kt b/library_module/src/androidTest/java/com/github/library_view/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..a3714f6 --- /dev/null +++ b/library_module/src/androidTest/java/com/github/library_view/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.github.library_view + +import android.support.test.InstrumentationRegistry +import android.support.test.runner.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.github.library_view.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/library_module/src/main/AndroidManifest.xml b/library_module/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c9eb6ea --- /dev/null +++ b/library_module/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/library_module/src/main/java/com/github/library_view/LibraryModule.java b/library_module/src/main/java/com/github/library_view/LibraryModule.java new file mode 100644 index 0000000..99462d9 --- /dev/null +++ b/library_module/src/main/java/com/github/library_view/LibraryModule.java @@ -0,0 +1,10 @@ +package com.github.library_view; + +import com.orhanobut.tracklytics.TrackEvent; + +public class LibraryModule { + + @TrackEvent("library_module_event_java") + public void track() { + } +} diff --git a/library_module/src/test/java/com/github/library_view/ExampleUnitTest.kt b/library_module/src/test/java/com/github/library_view/ExampleUnitTest.kt new file mode 100644 index 0000000..4aaf358 --- /dev/null +++ b/library_module/src/test/java/com/github/library_view/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.github.library_view + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index f4cca54..3e2a41b 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -9,13 +9,13 @@ buildscript { mavenLocal() } dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.20" - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.2' } } repositories { - mavenCentral() +// mavenCentral() // NOTE: This is only needed when developing the plugin! mavenLocal() @@ -46,8 +46,11 @@ android { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' testImplementation 'com.google.truth:truth:0.28' - implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.20" + implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72" + implementation project(":library_module") + + } \ No newline at end of file diff --git a/sample/src/main/java/com/orhanobut/sample/Foo.java b/sample/src/main/java/com/orhanobut/sample/Foo.java deleted file mode 100644 index 774fd94..0000000 --- a/sample/src/main/java/com/orhanobut/sample/Foo.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.orhanobut.sample; - -import com.orhanobut.tracklytics.TrackEvent; - -public class Foo { - - @TrackEvent("event_java") - public void trackFoo() { - - } -} diff --git a/sample/src/main/java/com/orhanobut/sample/JavaObject.java b/sample/src/main/java/com/orhanobut/sample/JavaObject.java new file mode 100644 index 0000000..b317f1a --- /dev/null +++ b/sample/src/main/java/com/orhanobut/sample/JavaObject.java @@ -0,0 +1,9 @@ +package com.orhanobut.sample; + +import com.orhanobut.tracklytics.TrackEvent; + +class JavaObject { + @TrackEvent("event_java_object") + void track() { + } +} diff --git a/sample/src/main/java/com/orhanobut/sample/KotlinObject.kt b/sample/src/main/java/com/orhanobut/sample/KotlinObject.kt new file mode 100644 index 0000000..92246df --- /dev/null +++ b/sample/src/main/java/com/orhanobut/sample/KotlinObject.kt @@ -0,0 +1,9 @@ +package com.orhanobut.sample + +import com.orhanobut.tracklytics.TrackEvent + +class KotlinObject { + @TrackEvent("event_kotlin_object") + fun track() { + } +} \ No newline at end of file diff --git a/sample/src/main/java/com/orhanobut/sample/MainActivity.java b/sample/src/main/java/com/orhanobut/sample/MainActivity.java index 3f4fe1a..35a18fc 100644 --- a/sample/src/main/java/com/orhanobut/sample/MainActivity.java +++ b/sample/src/main/java/com/orhanobut/sample/MainActivity.java @@ -4,7 +4,7 @@ import android.os.Bundle; import android.util.Log; import android.view.View; - +import com.github.library_view.LibraryModule; import com.orhanobut.tracklytics.Attribute; import com.orhanobut.tracklytics.Event; import com.orhanobut.tracklytics.EventLogListener; @@ -44,7 +44,12 @@ public class MainActivity extends Activity implements Trackable { @TrackEvent("button_click") @FixedAttribute(key = "button_name", value = "Login") @Override public void onClick(View v) { - + onItemSelected(2); + String id = userId(); + onLoggedIn(new User(id,"e@e.com"),id); + new KotlinObject().track(); + new JavaObject().track(); + new LibraryModule().track(); } }); } diff --git a/sample/src/test/java/com/orhanobut/sample/TrackingTest.java b/sample/src/test/java/com/orhanobut/sample/TrackingTest.java index 19f43cb..b967eda 100644 --- a/sample/src/test/java/com/orhanobut/sample/TrackingTest.java +++ b/sample/src/test/java/com/orhanobut/sample/TrackingTest.java @@ -33,7 +33,7 @@ public class TrackingTest { } @Test public void confirmJavaAspects() { - new Foo().trackFoo(); + new KotlinObject().track(); assertThat(triggeredEvents).containsKey("event_java"); } diff --git a/settings.gradle b/settings.gradle index 2ce0d77..0a13918 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ +include ':library_module' include ':tracklytics-plugin' include ':sample' include ':tracklytics-runtime' diff --git a/tracklytics-plugin/build.gradle b/tracklytics-plugin/build.gradle index 3881760..05261e6 100644 --- a/tracklytics-plugin/build.gradle +++ b/tracklytics-plugin/build.gradle @@ -6,9 +6,9 @@ dependencies { implementation localGroovy() implementation 'org.aspectj:aspectjtools:1.8.10' implementation 'org.aspectj:aspectjrt:1.8.10' - implementation 'com.android.tools.build:gradle:3.3.0' + implementation 'com.android.tools.build:gradle:4.0.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' } modifyPom { diff --git a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy index 4f50b4d..a750e21 100644 --- a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy +++ b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy @@ -8,56 +8,67 @@ import org.gradle.api.tasks.compile.JavaCompile class TracklyticsPlugin implements Plugin { - @Override - void apply(Project project) { + @Override + void apply(Project project) { - project.dependencies { - implementation 'org.aspectj:aspectjrt:1.8.10' - implementation 'com.orhanobut.tracklytics:tracklytics-runtime:2.1.0' - } + project.dependencies { + implementation 'org.aspectj:aspectjrt:1.8.10' + implementation 'com.orhanobut.tracklytics:tracklytics-runtime:2.1.2' + compileOnly "org.aspectj:aspectjrt:1.8.10" + } - project.android.applicationVariants.all { variant -> - JavaCompile javaCompile - if (variant.hasProperty('javaCompileProvider')) { - // Android 3.3.0+ - javaCompile = variant.javaCompileProvider.get() - } else { - javaCompile = variant.javaCompile - } + if (project.android.hasProperty("libraryVariants")) { + project.android.libraryVariants.all { variant -> + JavaCompile javaCompile + if (variant.hasProperty('javaCompileProvider')) { + // Android 3.3.0+ + javaCompile = variant.javaCompileProvider.get() + } else { + javaCompile = variant.javaCompile + } - // Gets the variant name and capitalize the first character - def variantName = variant.name[0].toUpperCase() + variant.name[1..-1].toLowerCase() + // Gets the variant name and capitalize the first character + def variantName = variant.name[0].toUpperCase() + variant.name[1..-1].toLowerCase() - // Weave the binary for the actual code - // CompileSources task is invoked after java and kotlin compilers and copy kotlin classes - // That's the moment we have the finalized byte code and we can weave the aspects - project.tasks.findByName('compile' + variantName + 'Sources')?.doLast { - def destinationDir = javaCompile.destinationDir.toString() - def classPath = javaCompile.classpath.asPath - def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) - String[] args = [ - "-showWeaveInfo", - "-1.7", - "-inpath", destinationDir, - "-aspectpath", classPath, - "-d", destinationDir, - "-classpath", classPath, - "-bootclasspath", bootClassPath - ] - new Main().run(args, new MessageHandler(true)); - println("----------------------------------------------") - println("--------------Tracklytics Weave---------------") - println("----------------------------------------------") - println("destinationDir: $destinationDir") - println("classPath: $classPath") - println("bootClassPath: $bootClassPath") - println("----------------------------------------------") - } + javaCompile.doLast { + runWithArgs(javaCompile, project, variantName, "libraryVariants javaCompile") + } + } + } else if (project.android.hasProperty("applicationVariants")) { + project.android.applicationVariants.all { variant -> + JavaCompile javaCompile + if (variant.hasProperty('javaCompileProvider')) { + // Android 3.3.0+ + javaCompile = variant.javaCompileProvider.get() + } else { + javaCompile = variant.javaCompile + } - // Weave the binary for unit tests - // compile unit tests task is invoked after the byte code is finalized - // This is the time that we can weave the aspects onto byte code - project.tasks.findByName('compile' + variantName + 'UnitTestSources')?.doLast { + // Gets the variant name and capitalize the first character + def variantName = variant.name[0].toUpperCase() + variant.name[1..-1].toLowerCase() + + // Weave the binary for the actual code + // CompileSources task is invoked after java and kotlin compilers and copy kotlin classes + // That's the moment we have the finalized byte code and we can weave the aspects + project.tasks.findByName('compile' + variantName + 'Sources')?.doLast { + runWithArgs(javaCompile, project, variantName, "applicationVariants "+'compile' + variantName + 'Sources') + } + + // Weave the binary for unit tests + // compile unit tests task is invoked after the byte code is finalized + // This is the time that we can weave the aspects onto byte code + project.tasks.findByName('compile' + variantName + 'UnitTestSources')?.doLast { + runWithArgs(javaCompile, project, variantName, "applicationVariants "+'compile' + variantName + 'UnitTestSources') + } + } + } else { + throw RuntimeException("\'apply plugin: \'com.orhanobut.tracklytics\'\' should be added after " + + "\'apply plugin: \'com.android.application' or \'apply plugin: \'com.android.library\'\'") + } + + } + + private static void runWithArgs(JavaCompile javaCompile, Project project, variantName, type) { def destinationDir = javaCompile.destinationDir.toString() def classPath = javaCompile.classpath.asPath def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) @@ -72,13 +83,13 @@ class TracklyticsPlugin implements Plugin { ] new Main().run(args, new MessageHandler(true)); println("----------------------------------------------") - println("--------------Tracklytics Weave---------------") + println("--------------Tracklytics Weave ($type)---------------") println("----------------------------------------------") + println("variantName: $variantName") println("destinationDir: $destinationDir") println("classPath: $classPath") println("bootClassPath: $bootClassPath") println("----------------------------------------------") - } } - } + } \ No newline at end of file diff --git a/tracklytics-runtime/build.gradle b/tracklytics-runtime/build.gradle index 1bbfabf..d57ab37 100644 --- a/tracklytics-runtime/build.gradle +++ b/tracklytics-runtime/build.gradle @@ -2,72 +2,72 @@ import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'org.aspectj:aspectjtools:1.8.10' - } + repositories { + jcenter() + } + dependencies { + classpath 'org.aspectj:aspectjtools:1.8.10' + } } apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion - defaultConfig { - minSdkVersion rootProject.ext.minSdkVersion - consumerProguardFiles 'consumer-proguard-rules.pro' - } + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + consumerProguardFiles 'consumer-proguard-rules.pro' + } - lintOptions { - textReport true - textOutput 'stdout' - } - buildToolsVersion "28.0.3" + lintOptions { + textReport true + textOutput 'stdout' + } + buildToolsVersion "28.0.3" } dependencies { - compileOnly "org.aspectj:aspectjrt:1.8.10" + compileOnly "org.aspectj:aspectjrt:1.8.10" - testImplementation 'junit:junit:4.12' - testImplementation 'com.google.truth:truth:0.28' - testImplementation "org.mockito:mockito-core:1.10.19" + testImplementation 'junit:junit:4.13' + testImplementation 'com.google.truth:truth:0.28' + testImplementation "org.mockito:mockito-core:3.3.3" } android.libraryVariants.all { variant -> - JavaCompile javaCompile - if (variant.hasProperty('javaCompileProvider')) { - // Android 3.3.0+ - javaCompile = variant.javaCompileProvider.get() - } else { - javaCompile = variant.javaCompile - } - javaCompile.doLast { - def destinationDir = javaCompile.destinationDir.toString() - def classPath = javaCompile.classpath.asPath - def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) - String[] args = [ - "-showWeaveInfo", - "-1.7", - "-inpath", destinationDir, - "-aspectpath", classPath, - "-d", destinationDir, - "-classpath", classPath, - "-bootclasspath", bootClassPath - ] + JavaCompile javaCompile + if (variant.hasProperty('javaCompileProvider')) { + // Android 3.3.0+ + javaCompile = variant.javaCompileProvider.get() + } else { + javaCompile = variant.javaCompile + } + javaCompile.doLast { + def destinationDir = javaCompile.destinationDir.toString() + def classPath = javaCompile.classpath.asPath + def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) + String[] args = [ + "-showWeaveInfo", + "-1.7", + "-inpath", destinationDir, + "-aspectpath", classPath, + "-d", destinationDir, + "-classpath", classPath, + "-bootclasspath", bootClassPath + ] - new Main().run(args, new MessageHandler(true)); - println("----------------------------------------------") - println("---------Tracklytics-runtime Weave------------") - println("----------------------------------------------") - println("destinationDir: $destinationDir") - println("classPath: $classPath") - println("bootClassPath: $bootClassPath") - println("----------------------------------------------") - } + new Main().run(args, new MessageHandler(true)); + println("----------------------------------------------") + println("---------Tracklytics-runtime Weave------------") + println("----------------------------------------------") + println("destinationDir: $destinationDir") + println("classPath: $classPath") + println("bootClassPath: $bootClassPath") + println("----------------------------------------------") + } } apply from: rootProject.file('gradle/maven_push.gradle') From fd38c64531c94f7ed61e3e00c5e8ee934e727764 Mon Sep 17 00:00:00 2001 From: Nikolay Unuchek Date: Fri, 5 Jun 2020 17:35:17 +0300 Subject: [PATCH 2/8] track events for kotlin funcs --- gradle.properties | 2 +- library_module/build.gradle | 2 +- ...Module.java => LibraryModuleJavaObject.java} | 2 +- .../library_view/LibraryModuleKotlinObject.kt | 9 +++++++++ sample/build.gradle | 2 +- .../java/com/orhanobut/sample/MainActivity.java | 8 +++++--- .../weaving/plugin/TracklyticsPlugin.groovy | 17 ++++++++++++++--- tracklytics-runtime/build.gradle | 15 +++++++++++++-- 8 files changed, 45 insertions(+), 12 deletions(-) rename library_module/src/main/java/com/github/library_view/{LibraryModule.java => LibraryModuleJavaObject.java} (80%) create mode 100644 library_module/src/main/java/com/github/library_view/LibraryModuleKotlinObject.kt diff --git a/gradle.properties b/gradle.properties index c10fe4b..706c00c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=2.1.2 +VERSION_NAME=2.1.3 GROUP=com.orhanobut.tracklytics POM_DESCRIPTION=Android analytics tracklytics diff --git a/library_module/build.gradle b/library_module/build.gradle index 9876c7b..b497658 100644 --- a/library_module/build.gradle +++ b/library_module/build.gradle @@ -10,7 +10,7 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.2' + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.3' } } diff --git a/library_module/src/main/java/com/github/library_view/LibraryModule.java b/library_module/src/main/java/com/github/library_view/LibraryModuleJavaObject.java similarity index 80% rename from library_module/src/main/java/com/github/library_view/LibraryModule.java rename to library_module/src/main/java/com/github/library_view/LibraryModuleJavaObject.java index 99462d9..beee11d 100644 --- a/library_module/src/main/java/com/github/library_view/LibraryModule.java +++ b/library_module/src/main/java/com/github/library_view/LibraryModuleJavaObject.java @@ -2,7 +2,7 @@ import com.orhanobut.tracklytics.TrackEvent; -public class LibraryModule { +public class LibraryModuleJavaObject { @TrackEvent("library_module_event_java") public void track() { diff --git a/library_module/src/main/java/com/github/library_view/LibraryModuleKotlinObject.kt b/library_module/src/main/java/com/github/library_view/LibraryModuleKotlinObject.kt new file mode 100644 index 0000000..b34cc61 --- /dev/null +++ b/library_module/src/main/java/com/github/library_view/LibraryModuleKotlinObject.kt @@ -0,0 +1,9 @@ +package com.github.library_view + +import com.orhanobut.tracklytics.TrackEvent + +class LibraryModuleKotlinObject { + @TrackEvent("library_module_event_kotlin") + fun track() { + } +} \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 3e2a41b..d3ecbac 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -10,7 +10,7 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.2' + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.3' } } diff --git a/sample/src/main/java/com/orhanobut/sample/MainActivity.java b/sample/src/main/java/com/orhanobut/sample/MainActivity.java index 35a18fc..0caf7ef 100644 --- a/sample/src/main/java/com/orhanobut/sample/MainActivity.java +++ b/sample/src/main/java/com/orhanobut/sample/MainActivity.java @@ -4,7 +4,8 @@ import android.os.Bundle; import android.util.Log; import android.view.View; -import com.github.library_view.LibraryModule; +import com.github.library_view.LibraryModuleJavaObject; +import com.github.library_view.LibraryModuleKotlinObject; import com.orhanobut.tracklytics.Attribute; import com.orhanobut.tracklytics.Event; import com.orhanobut.tracklytics.EventLogListener; @@ -47,9 +48,10 @@ public class MainActivity extends Activity implements Trackable { onItemSelected(2); String id = userId(); onLoggedIn(new User(id,"e@e.com"),id); - new KotlinObject().track(); new JavaObject().track(); - new LibraryModule().track(); + new KotlinObject().track(); + new LibraryModuleJavaObject().track(); + new LibraryModuleKotlinObject().track(); } }); } diff --git a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy index a750e21..393e872 100644 --- a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy +++ b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy @@ -13,7 +13,7 @@ class TracklyticsPlugin implements Plugin { project.dependencies { implementation 'org.aspectj:aspectjrt:1.8.10' - implementation 'com.orhanobut.tracklytics:tracklytics-runtime:2.1.2' + implementation 'com.orhanobut.tracklytics:tracklytics-runtime:2.1.3' compileOnly "org.aspectj:aspectjrt:1.8.10" } @@ -72,7 +72,7 @@ class TracklyticsPlugin implements Plugin { def destinationDir = javaCompile.destinationDir.toString() def classPath = javaCompile.classpath.asPath def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) - String[] args = [ + String[] javaArgs = [ "-showWeaveInfo", "-1.7", "-inpath", destinationDir, @@ -81,7 +81,18 @@ class TracklyticsPlugin implements Plugin { "-classpath", classPath, "-bootclasspath", bootClassPath ] - new Main().run(args, new MessageHandler(true)); + String[] kotlinArgs = ["-showWeaveInfo", + "-1.8", + "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + variantName.toLowerCase(), + "-aspectpath", classPath, + "-d", project.buildDir.path + "/tmp/kotlin-classes/" + variantName.toLowerCase(), + "-classpath", classPath, + "-bootclasspath", bootClassPath] + + MessageHandler handler = new MessageHandler(true) + new Main().run(javaArgs, handler) + new Main().run(kotlinArgs, handler) + println("----------------------------------------------") println("--------------Tracklytics Weave ($type)---------------") println("----------------------------------------------") diff --git a/tracklytics-runtime/build.gradle b/tracklytics-runtime/build.gradle index d57ab37..c76450a 100644 --- a/tracklytics-runtime/build.gradle +++ b/tracklytics-runtime/build.gradle @@ -49,7 +49,7 @@ android.libraryVariants.all { variant -> def destinationDir = javaCompile.destinationDir.toString() def classPath = javaCompile.classpath.asPath def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) - String[] args = [ + String[] javaArgs = [ "-showWeaveInfo", "-1.7", "-inpath", destinationDir, @@ -59,10 +59,21 @@ android.libraryVariants.all { variant -> "-bootclasspath", bootClassPath ] - new Main().run(args, new MessageHandler(true)); + String[] kotlinArgs = ["-showWeaveInfo", + "-1.8", + "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + variant, + "-aspectpath", classPath, + "-d", project.buildDir.path + "/tmp/kotlin-classes/" + variant, + "-classpath", classPath, + "-bootclasspath", bootClassPath] + + MessageHandler handler = new MessageHandler(true) + new Main().run(javaArgs, handler) + new Main().run(kotlinArgs, handler) println("----------------------------------------------") println("---------Tracklytics-runtime Weave------------") println("----------------------------------------------") + println("variant: $variant") println("destinationDir: $destinationDir") println("classPath: $classPath") println("bootClassPath: $bootClassPath") From 088ee34beec83f68abf14993504ffd8d1b6c5b9b Mon Sep 17 00:00:00 2001 From: Nikolay Unuchek Date: Mon, 8 Jun 2020 14:57:48 +0300 Subject: [PATCH 3/8] added possibility to use the plugin with productFlavors of android --- README.MD | 9 +- build.gradle | 114 ++++++++-------- gradle.properties | 2 +- sample/build.gradle | 103 ++++++++------ .../properties/version/version.properties | 3 + .../weaving/plugin/TracklyticsPlugin.groovy | 129 +++++++++++++----- tracklytics-runtime/build.gradle | 25 ++-- 7 files changed, 239 insertions(+), 146 deletions(-) create mode 100644 tracklytics-plugin/properties/version/version.properties diff --git a/README.MD b/README.MD index 3ff164e..f350ec1 100644 --- a/README.MD +++ b/README.MD @@ -15,12 +15,17 @@ Add the following code block to in your app/build.gradle. ```groovy buildscript { dependencies { - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.0' + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.9' } } apply plugin: 'com.android.application' -apply plugin: 'com.orhanobut.tracklytics' // Must be added after com.android.application + +android { +... +} + +apply plugin: 'com.orhanobut.tracklytics' // Must be added at end of file ``` diff --git a/build.gradle b/build.gradle index b43886e..cd9dd88 100644 --- a/build.gradle +++ b/build.gradle @@ -1,85 +1,85 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext { - kotlin_version = '1.3.72' - } + ext { + kotlin_version = '1.3.72' + } repositories { - google() - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' - classpath 'org.gradle.api.plugins:gradle-nexus-plugin:0.7' - classpath 'org.aspectj:aspectjtools:1.8.10' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.0' + classpath 'org.gradle.api.plugins:gradle-nexus-plugin:0.7' + classpath 'org.aspectj:aspectjtools:1.8.10' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } } subprojects { project -> - group = GROUP - version = VERSION_NAME - - apply plugin: 'checkstyle' - apply plugin: 'maven' + group = GROUP + version = VERSION_NAME - task checkstyle(type: Checkstyle) { - configFile rootProject.file('checkstyle.xml') - source 'src/main/java' - ignoreFailures false - showViolations true - include '**/*.java' + apply plugin: 'checkstyle' + apply plugin: 'maven' - classpath = files() - } + task checkstyle(type: Checkstyle) { + configFile rootProject.file('checkstyle.xml') + source 'src/main/java' + ignoreFailures false + showViolations true + include '**/*.java' - afterEvaluate { - if (project.tasks.findByName('check')) { - check.dependsOn('checkstyle') + classpath = files() } - } - buildscript { - repositories { - google() - jcenter() + afterEvaluate { + if (project.tasks.findByName('check')) { + check.dependsOn('checkstyle') + } } - dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' + + buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.0' + } } - } - repositories { - google() - jcenter() - } + repositories { + google() + jcenter() + } } task clean(type: Delete) { - delete rootProject.buildDir + delete rootProject.buildDir } ext { - minSdkVersion = 10 - targetSdkVersion = 26 - compileSdkVersion = 26 - buildToolsVersion = "28.0.3" - sourceCompatibilityVersion = JavaVersion.VERSION_1_7 - targetCompatibilityVersion = JavaVersion.VERSION_1_7 + minSdkVersion = 10 + targetSdkVersion = 26 + compileSdkVersion = 26 + buildToolsVersion = "28.0.3" + sourceCompatibilityVersion = JavaVersion.VERSION_1_7 + targetCompatibilityVersion = JavaVersion.VERSION_1_7 } ext.deps = [ - // AspectJ - aspectjRuntime: "org.aspectj:aspectjrt:1.8.10", - aspectjTools : "org.aspectj:aspectjtools:1.8.10", + // AspectJ + aspectjRuntime: "org.aspectj:aspectjrt:1.8.10", + aspectjTools : "org.aspectj:aspectjtools:1.8.10", - // Test dependencies - junit : 'junit:junit:4.12', - truth : 'com.google.truth:truth:0.28', - mockito : "org.mockito:mockito-core:1.10.19" + // Test dependencies + junit : 'junit:junit:4.12', + truth : 'com.google.truth:truth:0.28', + mockito : "org.mockito:mockito-core:1.10.19" ] \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 706c00c..766d341 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=2.1.3 +VERSION_NAME=2.1.9 GROUP=com.orhanobut.tracklytics POM_DESCRIPTION=Android analytics tracklytics diff --git a/sample/build.gradle b/sample/build.gradle index d3ecbac..3892793 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,56 +1,83 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -apply plugin: 'com.orhanobut.tracklytics' buildscript { - repositories { - mavenCentral() - mavenLocal() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.3' - } + repositories { + mavenCentral() + mavenLocal() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.9' + } } repositories { // mavenCentral() - // NOTE: This is only needed when developing the plugin! - mavenLocal() + // NOTE: This is only needed when developing the plugin! + mavenLocal() } android { - compileSdkVersion 26 - buildToolsVersion "28.0.3" - - compileOptions { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - } - - defaultConfig { - applicationId "com.orhanobut.sample" - minSdkVersion 16 - targetSdkVersion 26 - versionCode 1 - versionName "1.0" - } - - sourceSets { - test.java.srcDirs += 'src/test/kotlin' - - main.java.srcDirs += 'src/main/kotlin' - } + compileSdkVersion 26 + buildToolsVersion "28.0.3" + + compileOptions { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + } + + defaultConfig { + applicationId "com.orhanobut.sample" + minSdkVersion 16 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + } + + sourceSets { + test.java.srcDirs += 'src/test/kotlin' + + main.java.srcDirs += 'src/main/kotlin' + } + + buildTypes { + release { + minifyEnabled true + debuggable false + } + stage { + versionNameSuffix "-stage" + } + debug { + versionNameSuffix '-debug' + debuggable true + minifyEnabled false + } + } + + flavorDimensions "develop" + + productFlavors { + production { + } + payment { + } + label { + } + develop { + } + } } dependencies { - testImplementation 'junit:junit:4.13' - testImplementation 'com.google.truth:truth:0.28' - - implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72" - implementation project(":library_module") + testImplementation 'junit:junit:4.13' + testImplementation 'com.google.truth:truth:0.28' + implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72" + implementation project(":library_module") +} -} \ No newline at end of file +apply plugin: 'com.orhanobut.tracklytics' diff --git a/tracklytics-plugin/properties/version/version.properties b/tracklytics-plugin/properties/version/version.properties new file mode 100644 index 0000000..840217d --- /dev/null +++ b/tracklytics-plugin/properties/version/version.properties @@ -0,0 +1,3 @@ +#Tue Sep 05 14:02:16 EEST 2017 +VERSION_NAME=2.1.9 +VERSION_CODE=12 \ No newline at end of file diff --git a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy index 393e872..17214c2 100644 --- a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy +++ b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy @@ -11,13 +11,25 @@ class TracklyticsPlugin implements Plugin { @Override void apply(Project project) { + println("----------------------------------------------") + println("--------------Tracklytics Weave---------------") + project.dependencies { + println("-----------add dependencies------------------------") + implementation 'org.aspectj:aspectjrt:1.8.10' implementation 'com.orhanobut.tracklytics:tracklytics-runtime:2.1.3' compileOnly "org.aspectj:aspectjrt:1.8.10" } + if (!project.hasProperty("android")) { + throw RuntimeException("\'apply plugin: \'com.orhanobut.tracklytics\'\' should be added after " + + "\'apply plugin: \'com.android.application' or \'apply plugin: \'com.android.library\'\'") + } + if (project.android.hasProperty("libraryVariants")) { + /*if the plugin is added to android library module*/ + println("-----------libraryVariants------------------------") project.android.libraryVariants.all { variant -> JavaCompile javaCompile if (variant.hasProperty('javaCompileProvider')) { @@ -27,38 +39,78 @@ class TracklyticsPlugin implements Plugin { javaCompile = variant.javaCompile } - // Gets the variant name and capitalize the first character - def variantName = variant.name[0].toUpperCase() + variant.name[1..-1].toLowerCase() - javaCompile.doLast { - runWithArgs(javaCompile, project, variantName, "libraryVariants javaCompile") + def dirName = variant.name + runWithArgs(javaCompile, project, dirName, "libraryVariants javaCompile") } } } else if (project.android.hasProperty("applicationVariants")) { - project.android.applicationVariants.all { variant -> - JavaCompile javaCompile - if (variant.hasProperty('javaCompileProvider')) { - // Android 3.3.0+ - javaCompile = variant.javaCompileProvider.get() - } else { - javaCompile = variant.javaCompile - } + /*if the plugin is added to android application module*/ + println("-----------applicationVariants------------------------") + + if (project.android.productFlavors.size() == 0) { + /*if android application hasn't flavors*/ + println("-----------hasn't flavors------------------------") + project.android.applicationVariants.all { variant -> + JavaCompile javaCompile + if (variant.hasProperty('javaCompileProvider')) { + // Android 3.3.0+ + javaCompile = variant.javaCompileProvider.get() + } else { + javaCompile = variant.javaCompile + } - // Gets the variant name and capitalize the first character - def variantName = variant.name[0].toUpperCase() + variant.name[1..-1].toLowerCase() + // Gets the variant name and capitalize the first character + def taskPartName = variant.name[0].toUpperCase() + variant.name[1..-1].toLowerCase() - // Weave the binary for the actual code - // CompileSources task is invoked after java and kotlin compilers and copy kotlin classes - // That's the moment we have the finalized byte code and we can weave the aspects - project.tasks.findByName('compile' + variantName + 'Sources')?.doLast { - runWithArgs(javaCompile, project, variantName, "applicationVariants "+'compile' + variantName + 'Sources') + // Weave the binary for the actual code + // CompileSources task is invoked after java and kotlin compilers and copy kotlin classes + // That's the moment we have the finalized byte code and we can weave the aspects + project.tasks.findByName('compile' + taskPartName + 'Sources')?.doLast { + def dirName = variant.name + runWithArgs(javaCompile, project, dirName, "applicationVariants " + 'compile' + taskPartName + 'Sources') + } + + // Weave the binary for unit tests + // compile unit tests task is invoked after the byte code is finalized + // This is the time that we can weave the aspects onto byte code + project.tasks.findByName('compile' + taskPartName + 'UnitTestSources')?.doLast { + def dirName = variant.name + runWithArgs(javaCompile, project, dirName, "applicationVariants " + 'compile' + taskPartName + 'UnitTestSources') + } } + } else { + /*if android application has flavors*/ + println("-----------has flavors ${project.android.productFlavors.size()}------------------------") + project.android.applicationVariants.all { variant -> + JavaCompile javaCompile + if (variant.hasProperty('javaCompileProvider')) { + // Android 3.3.0+ + javaCompile = variant.javaCompileProvider.get() + } else { + javaCompile = variant.javaCompile + } + + def variantName = variant.name + println("variantName: ${variantName}") - // Weave the binary for unit tests - // compile unit tests task is invoked after the byte code is finalized - // This is the time that we can weave the aspects onto byte code - project.tasks.findByName('compile' + variantName + 'UnitTestSources')?.doLast { - runWithArgs(javaCompile, project, variantName, "applicationVariants "+'compile' + variantName + 'UnitTestSources') + project.android.productFlavors.all { flavor -> + def flavorName = flavor.name + if (variantName.toLowerCase().contains(flavorName.toLowerCase())) { + println("flavor: ${flavorName}") + project.android.buildTypes.all { buildType -> + def buildTypeName = buildType.name + if (variantName.toLowerCase().contains(buildTypeName.toLowerCase())) { + println("buildType: ${buildTypeName}") + def taskPartName = flavorName[0].toUpperCase() + flavorName[1..-1].toLowerCase() + buildTypeName[0].toUpperCase() + buildTypeName[1..-1].toLowerCase() + project.tasks.findByName('compile' + taskPartName + 'Sources')?.doLast { + def dirName = flavorName + buildTypeName[0].toUpperCase() + buildTypeName[1..-1].toLowerCase() + runWithArgs(javaCompile, project, dirName, "applicationVariants " + 'compile' + taskPartName + 'Sources') + } + } + } + } + } } } } else { @@ -68,26 +120,27 @@ class TracklyticsPlugin implements Plugin { } - private static void runWithArgs(JavaCompile javaCompile, Project project, variantName, type) { + private static void runWithArgs(JavaCompile javaCompile, Project project, dirName, type) { def destinationDir = javaCompile.destinationDir.toString() - def classPath = javaCompile.classpath.asPath def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) String[] javaArgs = [ "-showWeaveInfo", "-1.7", "-inpath", destinationDir, - "-aspectpath", classPath, + "-aspectpath", javaCompile.classpath.asPath/*javaCompile.classpath.asFileTree.filter { !it.canonicalPath.contains("transforms") }.asPath*/, "-d", destinationDir, - "-classpath", classPath, + "-classpath", javaCompile.classpath.asPath, + "-bootclasspath", bootClassPath + ] + String[] kotlinArgs = [ + "-showWeaveInfo", + "-1.8", + "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + dirName, + "-aspectpath", javaCompile.classpath.asPath/*javaCompile.classpath.asFileTree.filter { !it.canonicalPath.contains("transforms") }.asPath*/, + "-d", project.buildDir.path + "/tmp/kotlin-classes/" + dirName, + "-classpath", javaCompile.classpath.asPath, "-bootclasspath", bootClassPath ] - String[] kotlinArgs = ["-showWeaveInfo", - "-1.8", - "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + variantName.toLowerCase(), - "-aspectpath", classPath, - "-d", project.buildDir.path + "/tmp/kotlin-classes/" + variantName.toLowerCase(), - "-classpath", classPath, - "-bootclasspath", bootClassPath] MessageHandler handler = new MessageHandler(true) new Main().run(javaArgs, handler) @@ -96,10 +149,12 @@ class TracklyticsPlugin implements Plugin { println("----------------------------------------------") println("--------------Tracklytics Weave ($type)---------------") println("----------------------------------------------") - println("variantName: $variantName") + println("dirName: $dirName") println("destinationDir: $destinationDir") - println("classPath: $classPath") + println("classPath: ${javaCompile.classpath.asPath}") println("bootClassPath: $bootClassPath") + println("javaArgs: $javaArgs") + println("kotlinArgs: $kotlinArgs") println("----------------------------------------------") } diff --git a/tracklytics-runtime/build.gradle b/tracklytics-runtime/build.gradle index c76450a..b87b6f7 100644 --- a/tracklytics-runtime/build.gradle +++ b/tracklytics-runtime/build.gradle @@ -47,25 +47,26 @@ android.libraryVariants.all { variant -> } javaCompile.doLast { def destinationDir = javaCompile.destinationDir.toString() - def classPath = javaCompile.classpath.asPath def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) String[] javaArgs = [ "-showWeaveInfo", "-1.7", "-inpath", destinationDir, - "-aspectpath", classPath, + "-aspectpath", javaCompile.classpath.asPath/*javaCompile.classpath.asFileTree.filter {!it.canonicalPath.contains("transforms")}.asPath*/, "-d", destinationDir, - "-classpath", classPath, + "-classpath", javaCompile.classpath.asPath, "-bootclasspath", bootClassPath ] - String[] kotlinArgs = ["-showWeaveInfo", - "-1.8", - "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + variant, - "-aspectpath", classPath, - "-d", project.buildDir.path + "/tmp/kotlin-classes/" + variant, - "-classpath", classPath, - "-bootclasspath", bootClassPath] + String[] kotlinArgs = [ + "-showWeaveInfo", + "-1.8", + "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + variant, + "-aspectpath", javaCompile.classpath.asPath/*javaCompile.classpath.asFileTree.filter {!it.canonicalPath.contains("transforms")}.asPath*/, + "-d", project.buildDir.path + "/tmp/kotlin-classes/" + variant, + "-classpath", javaCompile.classpath.asPath, + "-bootclasspath", bootClassPath + ] MessageHandler handler = new MessageHandler(true) new Main().run(javaArgs, handler) @@ -75,8 +76,10 @@ android.libraryVariants.all { variant -> println("----------------------------------------------") println("variant: $variant") println("destinationDir: $destinationDir") - println("classPath: $classPath") + println("classPath: ${javaCompile.classpath.asPath}") println("bootClassPath: $bootClassPath") + println("javaArgs: $javaArgs") + println("kotlinArgs: $kotlinArgs") println("----------------------------------------------") } } From c5fc3a5600bd2ac7e8177753a967c82e919aaf9a Mon Sep 17 00:00:00 2001 From: Nikolay Unuchek Date: Mon, 8 Jun 2020 16:23:56 +0300 Subject: [PATCH 4/8] fixed java.lang.IllegalStateException: Expecting .,<, or ;, but found authapi while unpacking Lcom/google/android/gms/common/api/internal/BaseImplementation$ApiMethodImpl; --- README.MD | 2 +- gradle.properties | 2 +- library_module/build.gradle | 2 +- sample/build.gradle | 2 +- .../weaving/plugin/TracklyticsPlugin.groovy | 17 +++++++++++------ 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.MD b/README.MD index f350ec1..8d70b60 100644 --- a/README.MD +++ b/README.MD @@ -15,7 +15,7 @@ Add the following code block to in your app/build.gradle. ```groovy buildscript { dependencies { - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.9' + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.2.1' } } diff --git a/gradle.properties b/gradle.properties index 766d341..fd2d896 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=2.1.9 +VERSION_NAME=2.2.1 GROUP=com.orhanobut.tracklytics POM_DESCRIPTION=Android analytics tracklytics diff --git a/library_module/build.gradle b/library_module/build.gradle index b497658..4ba31d0 100644 --- a/library_module/build.gradle +++ b/library_module/build.gradle @@ -10,7 +10,7 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.3' + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.2.1' } } diff --git a/sample/build.gradle b/sample/build.gradle index 3892793..d0606dd 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" - classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.1.9' + classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.2.1' } } diff --git a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy index 17214c2..8c208cc 100644 --- a/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy +++ b/tracklytics-plugin/src/main/groovy/tracklytics/weaving/plugin/TracklyticsPlugin.groovy @@ -18,7 +18,7 @@ class TracklyticsPlugin implements Plugin { println("-----------add dependencies------------------------") implementation 'org.aspectj:aspectjrt:1.8.10' - implementation 'com.orhanobut.tracklytics:tracklytics-runtime:2.1.3' + implementation 'com.orhanobut.tracklytics:tracklytics-runtime:2.2.1' compileOnly "org.aspectj:aspectjrt:1.8.10" } @@ -123,22 +123,26 @@ class TracklyticsPlugin implements Plugin { private static void runWithArgs(JavaCompile javaCompile, Project project, dirName, type) { def destinationDir = javaCompile.destinationDir.toString() def bootClassPath = project.android.bootClasspath.join(File.pathSeparator) + def classPath = javaCompile.classpath.asPath + def aspectPath = javaCompile.classpath.asFileTree.filter { + it.canonicalPath.contains("tracklytics-runtime") + }.asPath String[] javaArgs = [ "-showWeaveInfo", "-1.7", "-inpath", destinationDir, - "-aspectpath", javaCompile.classpath.asPath/*javaCompile.classpath.asFileTree.filter { !it.canonicalPath.contains("transforms") }.asPath*/, + "-aspectpath", aspectPath, "-d", destinationDir, - "-classpath", javaCompile.classpath.asPath, + "-classpath", classPath, "-bootclasspath", bootClassPath ] String[] kotlinArgs = [ "-showWeaveInfo", "-1.8", "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + dirName, - "-aspectpath", javaCompile.classpath.asPath/*javaCompile.classpath.asFileTree.filter { !it.canonicalPath.contains("transforms") }.asPath*/, + "-aspectpath", aspectPath, "-d", project.buildDir.path + "/tmp/kotlin-classes/" + dirName, - "-classpath", javaCompile.classpath.asPath, + "-classpath", classPath, "-bootclasspath", bootClassPath ] @@ -151,7 +155,8 @@ class TracklyticsPlugin implements Plugin { println("----------------------------------------------") println("dirName: $dirName") println("destinationDir: $destinationDir") - println("classPath: ${javaCompile.classpath.asPath}") + println("classPath: $classPath") + println "aspectpath: $aspectPath" println("bootClassPath: $bootClassPath") println("javaArgs: $javaArgs") println("kotlinArgs: $kotlinArgs") From 3ba7641c2c55e308b6a18bf30693a9f205dd99ab Mon Sep 17 00:00:00 2001 From: Nikolay Unuchek Date: Mon, 8 Jun 2020 16:24:26 +0300 Subject: [PATCH 5/8] removed dep files --- tracklytics-plugin/properties/version/version.properties | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 tracklytics-plugin/properties/version/version.properties diff --git a/tracklytics-plugin/properties/version/version.properties b/tracklytics-plugin/properties/version/version.properties deleted file mode 100644 index 840217d..0000000 --- a/tracklytics-plugin/properties/version/version.properties +++ /dev/null @@ -1,3 +0,0 @@ -#Tue Sep 05 14:02:16 EEST 2017 -VERSION_NAME=2.1.9 -VERSION_CODE=12 \ No newline at end of file From 9f1b7ee230e2fa92ac498991708269b3054d6a6e Mon Sep 17 00:00:00 2001 From: Nikolay Unuchek Date: Fri, 17 Jul 2020 17:18:40 +0300 Subject: [PATCH 6/8] updated README.MD --- README.MD | 15 + .../tracklytics/TracklyticsAspectTest.java | 1282 +++++++++-------- 2 files changed, 683 insertions(+), 614 deletions(-) diff --git a/README.MD b/README.MD index 8d70b60..560b827 100644 --- a/README.MD +++ b/README.MD @@ -254,6 +254,21 @@ Use [Bee](https://github.com/orhanobut/bee) to monitor your events ### How it works +### ProGuard + +Depending on your ProGuard (DexGuard) config and usage, you may need to include the following lines in your proguard +``` +-keep class com.orhanobut.tracklytics.** { *; } +-keepclassmembers class com.orhanobut.tracklytics.** { + *; +} +-keep interface com.orhanobut.tracklytics.** { *; } +-keep @interface com.orhanobut.tracklytics.** { *; } +-keep enum com.orhanobut.tracklytics.** { *; } +-keepattributes Attribute, FixedAttribute, FixedAttributes, RemoveSuperAttribute, TrackableAttribute, TrackEvent, TrackSuperAttribute, TransformAttribute, TransformAttributeMap + +``` + ### Licence
 Copyright 2017 Orhan Obut
diff --git a/tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAspectTest.java b/tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAspectTest.java
index 7a400f8..282645a 100644
--- a/tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAspectTest.java
+++ b/tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAspectTest.java
@@ -19,675 +19,729 @@
 @SuppressWarnings("ALL")
 public class TracklyticsAspectTest {
 
-  @Mock ProceedingJoinPoint joinPoint;
-  @Mock MethodSignature methodSignature;
-
-  private final Map superAttributes = new HashMap<>();
-
-  private TracklyticsAspect aspect;
-  private TrackEvent trackEvent;
-  private Map attributes;
-  private AspectListener aspectListener;
-
-  @Before public void setup() throws Exception {
-    initMocks(this);
-
-    aspectListener = new AspectListener() {
-      @Override public void onAspectEventTriggered(TrackEvent trackEvent, Map attributes) {
-        TracklyticsAspectTest.this.trackEvent = trackEvent;
-        TracklyticsAspectTest.this.attributes = attributes;
-      }
-
-      @Override public void onAspectSuperAttributeAdded(String key, Object value) {
-        superAttributes.put(key, value);
-      }
-
-      @Override public void onAspectSuperAttributeRemoved(String key) {
-        superAttributes.remove(key);
-      }
-    };
-
-    aspect = new TracklyticsAspect();
-    aspect.subscribe(aspectListener);
-
-    when(joinPoint.getSignature()).thenReturn(methodSignature);
-  }
-
-  private Method invokeMethod(Class klass, String methodName, Class... parameterTypes) throws Throwable {
-    Method method = initMethod(klass, methodName, parameterTypes);
-    Object instance = new Object();
-    when(joinPoint.getThis()).thenReturn(instance);
-
-    aspect.weaveJoinPointTrackEvent(joinPoint);
-    return method;
-  }
-
-  private Method initMethod(Class klass, String name, Class... parameterTypes) throws Throwable {
-    Method method = klass.getMethod(name, parameterTypes);
-    when(methodSignature.getMethod()).thenReturn(method);
-    return method;
-  }
-
-  @Test public void trackEventWithoutAttributes() throws Throwable {
-    class Foo {
-      @TrackEvent("title") public void foo() {
-      }
-    }
-    invokeMethod(Foo.class, "foo");
-
-    ArgumentCaptor argument = ArgumentCaptor.forClass(Map.class);
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .noAttributes();
-  }
-
-  @Test public void useReturnValueAsAttribute() throws Throwable {
-    class Foo {
-      @TrackEvent("title") @Attribute("key") public String foo() {
-        return "test";
-      }
-    }
-
-    when(joinPoint.proceed()).thenReturn("test");
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("title")
-        .noTags()
-        .noFilters()
-        .attribute("key", "test");
-  }
-
-  @Test public void useReturnValueAndParametersAsAttributes() throws Throwable {
-    class Foo {
-      @TrackEvent("title") @Attribute("key1") public String foo(@Attribute("key2") String param) {
-        return "test";
-      }
-    }
-
-    when(joinPoint.proceed()).thenReturn("test");
-    when(joinPoint.getArgs()).thenReturn(new Object[]{"param"});
-    invokeMethod(Foo.class, "foo", String.class);
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "test")
-        .attribute("key2", "param");
-  }
-
-  @Test public void useDefaultValueWhenThereIsNoReturnValue() throws Throwable {
-    class Foo {
-      @TrackEvent("title")
-      @Attribute(value = "key1", defaultValue = "defaultValue") public void foo() {
-      }
-    }
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "defaultValue");
-  }
-
-  @Test public void useReturnValueWhenItIsNotNull() throws Throwable {
-    class Foo {
-      @TrackEvent("title")
-      @Attribute(value = "key1", defaultValue = "defaulValue") public String foo() {
-        return "returnValue";
-      }
-    }
-    when(joinPoint.proceed()).thenReturn("returnValue");
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "returnValue");
-  }
-
-  @Test public void useDefaultValueWhenParameterValueIsNull() throws Throwable {
-    class Foo {
-      @TrackEvent("title") public void foo(@Attribute(value = "key1", defaultValue = "default") String val) {
-      }
-    }
-
-    when(joinPoint.getArgs()).thenReturn(new Object[]{null});
-    invokeMethod(Foo.class, "foo", String.class);
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "default");
-  }
-
-  @Test public void fixedAttributeOnMethodScope() throws Throwable {
-    class Foo {
-      @TrackEvent("title")
-      @FixedAttribute(key = "key1", value = "value") public String foo() {
-        return "returnValue";
-      }
-    }
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value");
-  }
-
-  @Test public void fixedAttributeOnClassScope() throws Throwable {
-    @FixedAttributes({
-        @FixedAttribute(key = "key1", value = "value1"),
-        @FixedAttribute(key = "key2", value = "value2")
-    })
-    @FixedAttribute(key = "key3", value = "value3")
-    class Foo {
-      @TrackEvent("title")
-      @FixedAttribute(key = "key4", value = "value4")
-      public void foo() {
-      }
-    }
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value1")
-        .attribute("key2", "value2")
-        .attribute("key3", "value3")
-        .attribute("key4", "value4");
-  }
-
-  @Test public void fixedAttributeAndAttributeAtSameTime() throws Throwable {
-    class Foo {
-      @TrackEvent("title")
-      @Attribute("key1")
-      @FixedAttribute(key = "key2", value = "value2")
-      public String foo() {
-        return "value1";
-      }
-    }
-
-    when(joinPoint.proceed()).thenReturn("value1");
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value1")
-        .attribute("key2", "value2");
-  }
-
-  @Test public void fixedAttributes() throws Throwable {
-    class Foo {
-      @TrackEvent("title")
-      @FixedAttributes({
-          @FixedAttribute(key = "key1", value = "value1"),
-          @FixedAttribute(key = "key2", value = "value2")
-      })
-      @FixedAttribute(key = "key3", value = "value3")
-      public void foo() {
-      }
-    }
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value1")
-        .attribute("key2", "value2")
-        .attribute("key3", "value3");
-  }
-
-  @Test public void superAttribute() throws Throwable {
-    class Foo {
-      @TrackEvent("title")
-      @Attribute(value = "key1", isSuper = true)
-      public String foo(@Attribute(value = "key2", isSuper = true) String value) {
-        return "value1";
-      }
-    }
-
-    when(joinPoint.proceed()).thenReturn("value1");
-    when(joinPoint.getArgs()).thenReturn(new Object[]{"value2"});
-
-    invokeMethod(Foo.class, "foo", String.class);
-
-    assertThat(superAttributes).containsExactly("key1", "value1", "key2", "value2");
-  }
-
-  @Test public void superFixedAttribute() throws Throwable {
-    class Foo {
-      @TrackEvent("title")
-      @FixedAttributes({
-          @FixedAttribute(key = "key1", value = "value1"),
-          @FixedAttribute(key = "key2", value = "value2", isSuper = true)
-      })
-      @FixedAttribute(key = "key3", value = "value3", isSuper = true)
-      public String foo() {
-        return "returnValue";
-      }
-    }
-
-    when(joinPoint.proceed()).thenReturn("value1");
-    invokeMethod(Foo.class, "foo");
-
-    assertThat(superAttributes).containsExactly("key2", "value2", "key3", "value3");
-  }
+    @Mock
+    ProceedingJoinPoint joinPoint;
+    @Mock
+    MethodSignature methodSignature;
 
-  @Test public void superTransformAttribute() throws Throwable {
-    class Foo {
-      @TrackEvent("event")
-      @TransformAttributeMap(
-          keys = {0, 1},
-          values = {"value1", "value2"}
-      )
-      @TransformAttribute(value = "key1", isSuper = true)
-      public int foo(@TransformAttribute(value = "key2", isSuper = true) Integer val) {
-        return 0;
-      }
+    private final Map superAttributes = new HashMap<>();
+
+    private TracklyticsAspect aspect;
+    private TrackEvent trackEvent;
+    private Map attributes;
+    private AspectListener aspectListener;
+
+    @Before
+    public void setup() throws Exception {
+        initMocks(this);
+
+        aspectListener = new AspectListener() {
+            @Override
+            public void onAspectEventTriggered(TrackEvent trackEvent, Map attributes) {
+                TracklyticsAspectTest.this.trackEvent = trackEvent;
+                TracklyticsAspectTest.this.attributes = attributes;
+            }
+
+            @Override
+            public void onAspectSuperAttributeAdded(String key, Object value) {
+                superAttributes.put(key, value);
+            }
+
+            @Override
+            public void onAspectSuperAttributeRemoved(String key) {
+                superAttributes.remove(key);
+            }
+        };
+
+        aspect = new TracklyticsAspect();
+        aspect.subscribe(aspectListener);
+
+        when(joinPoint.getSignature()).thenReturn(methodSignature);
     }
 
-    when(joinPoint.proceed()).thenReturn(0);
-    when(joinPoint.getArgs()).thenReturn(new Object[]{1});
-    invokeMethod(Foo.class, "foo", Integer.class);
+    private Method invokeMethod(Class klass, String methodName, Class... parameterTypes) throws Throwable {
+        Method method = initMethod(klass, methodName, parameterTypes);
+        Object instance = new Object();
+        when(joinPoint.getThis()).thenReturn(instance);
 
-    assertThat(superAttributes).containsExactly("key1", "value1", "key2", "value2");
-  }
+        aspect.weaveJoinPointTrackEvent(joinPoint);
+        return method;
+    }
+
+    private Method initMethod(Class klass, String name, Class... parameterTypes) throws Throwable {
+        Method method = klass.getMethod(name, parameterTypes);
+        when(methodSignature.getMethod()).thenReturn(method);
+        return method;
+    }
 
-  @Test public void trackable() throws Throwable {
-    class Bar implements Trackable {
+    @Test
+    public void trackEventWithoutAttributes() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            public void foo() {
+            }
+        }
+        invokeMethod(Foo.class, "foo");
+
+        ArgumentCaptor argument = ArgumentCaptor.forClass(Map.class);
 
-      @Override public Map getTrackableAttributes() {
-        Map values = new HashMap<>();
-        values.put("key1", "value1");
-        values.put("key2", "value2");
-        return values;
-      }
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .noAttributes();
     }
 
-    class Foo {
-      @TrackEvent("title") public void foo(@TrackableAttribute Bar bar) {
-      }
+    @Test
+    public void useReturnValueAsAttribute() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute("key")
+            public String foo() {
+                return "test";
+            }
+        }
+
+        when(joinPoint.proceed()).thenReturn("test");
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("title")
+                .noTags()
+                .noFilters()
+                .attribute("key", "test");
     }
 
-    when(joinPoint.getArgs()).thenReturn(new Object[]{new Bar()});
+    @Test
+    public void useReturnValueAndParametersAsAttributes() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute("key1")
+            public String foo(@Attribute("key2") String param) {
+                return "test";
+            }
+        }
+
+        when(joinPoint.proceed()).thenReturn("test");
+        when(joinPoint.getArgs()).thenReturn(new Object[]{"param"});
+        invokeMethod(Foo.class, "foo", String.class);
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "test")
+                .attribute("key2", "param");
+    }
+
+    @Test
+    public void useDefaultValueWhenThereIsNoReturnValue() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute(value = "key1", defaultValue = "defaultValue")
+            public void foo() {
+            }
+        }
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "defaultValue");
+    }
+
+    @Test
+    public void useReturnValueWhenItIsNotNull() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute(value = "key1", defaultValue = "defaulValue")
+            public String foo() {
+                return "returnValue";
+            }
+        }
+        when(joinPoint.proceed()).thenReturn("returnValue");
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "returnValue");
+    }
+
+    @Test
+    public void useDefaultValueWhenParameterValueIsNull() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            public void foo(@Attribute(value = "key1", defaultValue = "default") String val) {
+            }
+        }
 
-    invokeMethod(Foo.class, "foo", Bar.class);
+        when(joinPoint.getArgs()).thenReturn(new Object[]{null});
+        invokeMethod(Foo.class, "foo", String.class);
 
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value1")
-        .attribute("key2", "value2");
-  }
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "default");
+    }
 
-  @Test public void ignoreNullValuesOnTrackable() throws Throwable {
-    class Bar implements Trackable {
+    @Test
+    public void fixedAttributeOnMethodScope() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttribute(key = "key1", value = "value")
+            public String foo() {
+                return "returnValue";
+            }
+        }
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value");
+    }
+
+    @Test
+    public void fixedAttributeOnClassScope() throws Throwable {
+        @FixedAttributes({
+                @FixedAttribute(key = "key1", value = "value1"),
+                @FixedAttribute(key = "key2", value = "value2")
+        })
+        @FixedAttribute(key = "key3", value = "value3")
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttribute(key = "key4", value = "value4")
+            public void foo() {
+            }
+        }
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value1")
+                .attribute("key2", "value2")
+                .attribute("key3", "value3")
+                .attribute("key4", "value4");
+    }
+
+    @Test
+    public void fixedAttributeAndAttributeAtSameTime() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute("key1")
+            @FixedAttribute(key = "key2", value = "value2")
+            public String foo() {
+                return "value1";
+            }
+        }
 
-      @Override public Map getTrackableAttributes() {
-        return null;
-      }
+        when(joinPoint.proceed()).thenReturn("value1");
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value1")
+                .attribute("key2", "value2");
+    }
+
+    @Test
+    public void fixedAttributes() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttributes({
+                    @FixedAttribute(key = "key1", value = "value1"),
+                    @FixedAttribute(key = "key2", value = "value2")
+            })
+            @FixedAttribute(key = "key3", value = "value3")
+            public void foo() {
+            }
+        }
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value1")
+                .attribute("key2", "value2")
+                .attribute("key3", "value3");
+    }
+
+    @Test
+    public void superAttribute() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute(value = "key1", isSuper = true)
+            public String foo(@Attribute(value = "key2", isSuper = true) String value) {
+                return "value1";
+            }
+        }
+
+        when(joinPoint.proceed()).thenReturn("value1");
+        when(joinPoint.getArgs()).thenReturn(new Object[]{"value2"});
+
+        invokeMethod(Foo.class, "foo", String.class);
+
+        assertThat(superAttributes).containsExactly("key1", "value1", "key2", "value2");
+    }
+
+    @Test
+    public void superFixedAttribute() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttributes({
+                    @FixedAttribute(key = "key1", value = "value1"),
+                    @FixedAttribute(key = "key2", value = "value2", isSuper = true)
+            })
+            @FixedAttribute(key = "key3", value = "value3", isSuper = true)
+            public String foo() {
+                return "returnValue";
+            }
+        }
+
+        when(joinPoint.proceed()).thenReturn("value1");
+        invokeMethod(Foo.class, "foo");
+
+        assertThat(superAttributes).containsExactly("key2", "value2", "key3", "value3");
+    }
+
+    @Test
+    public void superTransformAttribute() throws Throwable {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(
+                    keys = {0, 1},
+                    values = {"value1", "value2"}
+            )
+            @TransformAttribute(value = "key1", isSuper = true)
+            public int foo(@TransformAttribute(value = "key2", isSuper = true) Integer val) {
+                return 0;
+            }
+        }
+
+        when(joinPoint.proceed()).thenReturn(0);
+        when(joinPoint.getArgs()).thenReturn(new Object[]{1});
+        invokeMethod(Foo.class, "foo", Integer.class);
+
+        assertThat(superAttributes).containsExactly("key1", "value1", "key2", "value2");
     }
 
-    class Foo {
-      @TrackEvent("title") public void foo(@TrackableAttribute Bar bar) {
-      }
+    @Test
+    public void trackable() throws Throwable {
+        class Bar implements Trackable {
+
+            @Override
+            public Map getTrackableAttributes() {
+                Map values = new HashMap<>();
+                values.put("key1", "value1");
+                values.put("key2", "value2");
+                return values;
+            }
+        }
+
+        class Foo {
+            @TrackEvent("title")
+            public void foo(@TrackableAttribute Bar bar) {
+            }
+        }
+
+        when(joinPoint.getArgs()).thenReturn(new Object[]{new Bar()});
+
+        invokeMethod(Foo.class, "foo", Bar.class);
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value1")
+                .attribute("key2", "value2");
     }
 
-    when(joinPoint.getArgs()).thenReturn(new Object[]{new Bar()});
+    @Test
+    public void ignoreNullValuesOnTrackable() throws Throwable {
+        class Bar implements Trackable {
 
-    invokeMethod(Foo.class, "foo", Bar.class);
+            @Override
+            public Map getTrackableAttributes() {
+                return null;
+            }
+        }
+
+        class Foo {
+            @TrackEvent("title")
+            public void foo(@TrackableAttribute Bar bar) {
+            }
+        }
 
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .noAttributes();
-  }
+        when(joinPoint.getArgs()).thenReturn(new Object[]{new Bar()});
 
-  @Test public void throwExceptionWhenTrackableAnnotationNotMatchWithValue() throws Throwable {
+        invokeMethod(Foo.class, "foo", Bar.class);
 
-    class Foo {
-      @TrackEvent("title") public void foo(@TrackableAttribute String bar) {
-      }
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .noAttributes();
     }
 
-    when(joinPoint.getArgs()).thenReturn(new Object[]{"sdfsd"});
+    @Test
+    public void throwExceptionWhenTrackableAnnotationNotMatchWithValue() throws Throwable {
 
-    try {
-      invokeMethod(Foo.class, "foo", String.class);
+        class Foo {
+            @TrackEvent("title")
+            public void foo(@TrackableAttribute String bar) {
+            }
+        }
+
+        when(joinPoint.getArgs()).thenReturn(new Object[]{"sdfsd"});
 
-      fail("Should throw exception");
-    } catch (Exception e) {
-      assertThat(e).hasMessage("Trackable interface must be implemented for the parameter type");
+        try {
+            invokeMethod(Foo.class, "foo", String.class);
+
+            fail("Should throw exception");
+        } catch (Exception e) {
+            assertThat(e).hasMessage("Trackable interface must be implemented for the parameter type");
+        }
     }
-  }
 
-  @Test public void methodParameterWithoutAnnotation() throws Throwable {
-    class Foo {
-      @TrackEvent("title") public void foo(@Attribute("Key") String bar, String param2) {
-      }
+    @Test
+    public void methodParameterWithoutAnnotation() throws Throwable {
+        class Foo {
+            @TrackEvent("title")
+            public void foo(@Attribute("Key") String bar, String param2) {
+            }
+        }
+
+        when(joinPoint.getArgs()).thenReturn(new Object[]{"sdfsd"});
+
+        invokeMethod(Foo.class, "foo", String.class, String.class);
+
+        try {
+            aspect.weaveJoinPointTrackEvent(joinPoint);
+        } catch (Exception e) {
+            fail("Method parameters without annotation should be accepted");
+        }
     }
 
-    when(joinPoint.getArgs()).thenReturn(new Object[]{"sdfsd"});
+    @Test
+    public void classWideAttributeInAnonymousClass() throws Throwable {
+        @FixedAttribute(key = "key1", value = "value1")
+        class Foo {
+
+            @FixedAttribute(key = "key2", value = "value2")
+            class Inner {
 
-    invokeMethod(Foo.class, "foo", String.class, String.class);
+                @TrackEvent("title")
+                public void bar() {
+                }
+            }
+        }
+
+        invokeMethod(Foo.Inner.class, "bar");
+
+        assertTrack()
+                .event("title")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value1")
+                .attribute("key2", "value2");
+    }
+
+    @Test
+    public void transformAttributeForParameters() throws Throwable {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(
+                    keys = {0, 1},
+                    values = {"value1", "value2"}
+            )
+            public void foo(@TransformAttribute("key1") Integer type) {
+            }
+        }
 
-    try {
-      aspect.weaveJoinPointTrackEvent(joinPoint);
-    } catch (Exception e) {
-      fail("Method parameters without annotation should be accepted");
+        when(joinPoint.getArgs()).thenReturn(new Object[]{0});
+        invokeMethod(Foo.class, "foo", Integer.class);
+
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value1");
+    }
+
+    @Test
+    public void transformAttributeMapInvalidState() throws Throwable {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(
+                    keys = {0, 1},
+                    values = {"value1"}
+            )
+            public void foo(@TransformAttribute("key1") Integer type) {
+            }
+        }
+
+        when(joinPoint.getArgs()).thenReturn(new Object[]{0});
+
+        try {
+            invokeMethod(Foo.class, "foo", Integer.class);
+        } catch (Exception e) {
+            assertThat(e).hasMessage("TransformAttributeMap keys and values must have same length");
+        }
     }
-  }
 
-  @Test public void classWideAttributeInAnonymousClass() throws Throwable {
-    @FixedAttribute(key = "key1", value = "value1")
-    class Foo {
+    @Test
+    public void transformAttributeWithoutTransformAttributeMap() throws Throwable {
+        class Foo {
+            @TrackEvent("event")
+            public void foo(@TransformAttribute("key1") Integer type) {
+            }
+        }
 
-      @FixedAttribute(key = "key2", value = "value2")
-      class Inner {
+        when(joinPoint.getArgs()).thenReturn(new Object[]{0});
 
-        @TrackEvent("title")
-        public void bar() {
+        try {
+            invokeMethod(Foo.class, "foo", Integer.class);
+        } catch (Exception e) {
+            assertThat(e).hasMessage("Method must have TransformAttributeMap when TransformAttribute is used");
         }
-      }
     }
 
-    invokeMethod(Foo.Inner.class, "bar");
+    @Test
+    public void transformAttributeForReturnValue() throws Throwable {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(
+                    keys = {0, 1},
+                    values = {"value1", "value2"}
+            )
+            @TransformAttribute("key1")
+            public int foo() {
+                return 1;
+            }
+        }
+
+        when(joinPoint.proceed()).thenReturn(1);
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "value2");
+    }
+
+    @Test
+    public void transformAttributeDefaultValue() throws Throwable {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(
+                    keys = {0, 1},
+                    values = {"value1", "value2"}
+            )
+            @TransformAttribute(value = "key1", defaultValue = "default1")
+            public String foo(@TransformAttribute(value = "key2", defaultValue = "default2") Integer val) {
+                return null;
+            }
+        }
 
-    assertTrack()
-        .event("title")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value1")
-        .attribute("key2", "value2");
-  }
+        when(joinPoint.getArgs()).thenReturn(new Object[]{null});
+        invokeMethod(Foo.class, "foo", Integer.class);
 
-  @Test public void transformAttributeForParameters() throws Throwable {
-    class Foo {
-      @TrackEvent("event")
-      @TransformAttributeMap(
-          keys = {0, 1},
-          values = {"value1", "value2"}
-      )
-      public void foo(@TransformAttribute("key1") Integer type) {
-      }
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .attribute("key1", "default1")
+                .attribute("key2", "default2");
     }
 
-    when(joinPoint.getArgs()).thenReturn(new Object[]{0});
-    invokeMethod(Foo.class, "foo", Integer.class);
+    @Test
+    public void trackableAttributeForCurrentClass() throws Throwable {
+        class Foo implements Trackable {
+
+            @Override
+            public Map getTrackableAttributes() {
+                Map map = new HashMap<>();
+                map.put("key", "value");
+                return map;
+            }
 
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value1");
-  }
+            @TrackEvent("event")
+            @TrackableAttribute
+            public void foo() {
+            }
+        }
+
+        initMethod(Foo.class, "foo");
+        when(joinPoint.getThis()).thenReturn(new Foo());
+        aspect.weaveJoinPointTrackEvent(joinPoint);
 
-  @Test public void transformAttributeMapInvalidState() throws Throwable {
-    class Foo {
-      @TrackEvent("event")
-      @TransformAttributeMap(
-          keys = {0, 1},
-          values = {"value1"}
-      )
-      public void foo(@TransformAttribute("key1") Integer type) {
-      }
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .attribute("key", "value");
     }
 
-    when(joinPoint.getArgs()).thenReturn(new Object[]{0});
+    @Test
+    public void doNotUseTrackableAttributesWhenTrackableAttributeNotExists() throws Throwable {
+        class Foo implements Trackable {
+
+            @Override
+            public Map getTrackableAttributes() {
+                Map map = new HashMap<>();
+                map.put("key", "value");
+                return map;
+            }
 
-    try {
-      invokeMethod(Foo.class, "foo", Integer.class);
-    } catch (Exception e) {
-      assertThat(e).hasMessage("TransformAttributeMap keys and values must have same length");
+            @TrackEvent("event")
+            public void foo() {
+            }
+        }
+
+        when(joinPoint.getThis()).thenReturn(new Foo());
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .noAttributes();
+    }
+
+    @Test
+    public void ignoreNullValueOnTrackableAttributeForCurrentClass() throws Throwable {
+        class Foo implements Trackable {
+
+            @Override
+            public Map getTrackableAttributes() {
+                return null;
+            }
+
+            @TrackEvent("event")
+            @TrackableAttribute
+            public void foo() {
+            }
+        }
+
+        initMethod(Foo.class, "foo");
+        when(joinPoint.getThis()).thenReturn(new Foo());
+        aspect.weaveJoinPointTrackEvent(joinPoint);
+
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .noAttributes();
+    }
+
+    @Test
+    public void overrideClassWideAttributeOnMethodWhenAttributesAreSame() throws Throwable {
+        @FixedAttribute(key = "key", value = "class")
+        @FixedAttributes(
+                @FixedAttribute(key = "key1", value = "class1")
+        )
+        class Foo {
+
+            @TrackEvent("event")
+            @FixedAttribute(key = "key", value = "method")
+            @FixedAttributes(
+                    @FixedAttribute(key = "key1", value = "method1")
+            )
+            public void foo() {
+            }
+        }
+
+        invokeMethod(Foo.class, "foo");
+
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .attribute("key", "method")
+                .attribute("key1", "method1");
     }
-  }
 
-  @Test public void transformAttributeWithoutTransformAttributeMap() throws Throwable {
-    class Foo {
-      @TrackEvent("event")
-      public void foo(@TransformAttribute("key1") Integer type) {
-      }
+    @Test
+    public void useThisClassWhenCalledFromSuperClass() throws Throwable {
+        @FixedAttribute(key = "key0", value = "value0")
+        class Base {
+
+            @TrackEvent("event")
+            public void base() {
+            }
+        }
+
+        @FixedAttribute(key = "key", value = "value")
+        @FixedAttributes(
+                @FixedAttribute(key = "key2", value = "value2")
+        )
+        class Foo extends Base {
+        }
+
+        initMethod(Foo.class, "base");
+        when(joinPoint.getThis()).thenReturn(new Foo());
+        aspect.weaveJoinPointTrackEvent(joinPoint);
+
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .noTags()
+                .attribute("key0", "value0")
+                .attribute("key", "value")
+                .attribute("key2", "value2");
+    }
+
+    @Test
+    public void filters() throws Throwable {
+        class Foo {
+            @TrackEvent(value = "event", filters = {100, 200})
+            public void foo() {
+            }
+        }
+
+        invokeMethod(Foo.class, "foo");
+
+        int[] tags = {100, 200};
+
+        assertTrack()
+                .event("event")
+                .noTags()
+                .filters(100, 200)
+                .noAttributes();
     }
 
-    when(joinPoint.getArgs()).thenReturn(new Object[]{0});
+    @Test
+    public void tags() throws Throwable {
+        class Foo {
+            @TrackEvent(value = "event", tags = {"abc", "123"})
+            public void foo() {
+            }
+        }
+
+        invokeMethod(Foo.class, "foo");
 
-    try {
-      invokeMethod(Foo.class, "foo", Integer.class);
-    } catch (Exception e) {
-      assertThat(e).hasMessage("Method must have TransformAttributeMap when TransformAttribute is used");
-    }
-  }
+        int[] tags = {100, 200};
 
-  @Test public void transformAttributeForReturnValue() throws Throwable {
-    class Foo {
-      @TrackEvent("event")
-      @TransformAttributeMap(
-          keys = {0, 1},
-          values = {"value1", "value2"}
-      )
-      @TransformAttribute("key1")
-      public int foo() {
-        return 1;
-      }
-    }
-
-    when(joinPoint.proceed()).thenReturn(1);
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "value2");
-  }
-
-  @Test public void transformAttributeDefaultValue() throws Throwable {
-    class Foo {
-      @TrackEvent("event")
-      @TransformAttributeMap(
-          keys = {0, 1},
-          values = {"value1", "value2"}
-      )
-      @TransformAttribute(value = "key1", defaultValue = "default1")
-      public String foo(@TransformAttribute(value = "key2", defaultValue = "default2") Integer val) {
-        return null;
-      }
-    }
-
-    when(joinPoint.getArgs()).thenReturn(new Object[]{null});
-    invokeMethod(Foo.class, "foo", Integer.class);
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .attribute("key1", "default1")
-        .attribute("key2", "default2");
-  }
-
-  @Test public void trackableAttributeForCurrentClass() throws Throwable {
-    class Foo implements Trackable {
-
-      @Override public Map getTrackableAttributes() {
-        Map map = new HashMap<>();
-        map.put("key", "value");
-        return map;
-      }
-
-      @TrackEvent("event")
-      @TrackableAttribute
-      public void foo() {
-      }
-    }
-
-    initMethod(Foo.class, "foo");
-    when(joinPoint.getThis()).thenReturn(new Foo());
-    aspect.weaveJoinPointTrackEvent(joinPoint);
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .attribute("key", "value");
-  }
-
-  @Test public void doNotUseTrackableAttributesWhenTrackableAttributeNotExists() throws Throwable {
-    class Foo implements Trackable {
-
-      @Override public Map getTrackableAttributes() {
-        Map map = new HashMap<>();
-        map.put("key", "value");
-        return map;
-      }
-
-      @TrackEvent("event")
-      public void foo() {
-      }
-    }
-
-    when(joinPoint.getThis()).thenReturn(new Foo());
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .noAttributes();
-  }
-
-  @Test public void ignoreNullValueOnTrackableAttributeForCurrentClass() throws Throwable {
-    class Foo implements Trackable {
-
-      @Override public Map getTrackableAttributes() {
-        return null;
-      }
-
-      @TrackEvent("event")
-      @TrackableAttribute
-      public void foo() {
-      }
-    }
-
-    initMethod(Foo.class, "foo");
-    when(joinPoint.getThis()).thenReturn(new Foo());
-    aspect.weaveJoinPointTrackEvent(joinPoint);
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .noAttributes();
-  }
-
-  @Test public void overrideClassWideAttributeOnMethodWhenAttributesAreSame() throws Throwable {
-    @FixedAttribute(key = "key", value = "class")
-    @FixedAttributes(
-        @FixedAttribute(key = "key1", value = "class1")
-    )
-    class Foo {
-
-      @TrackEvent("event")
-      @FixedAttribute(key = "key", value = "method")
-      @FixedAttributes(
-          @FixedAttribute(key = "key1", value = "method1")
-      )
-      public void foo() {
-      }
-    }
-
-    invokeMethod(Foo.class, "foo");
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .attribute("key", "method")
-        .attribute("key1", "method1");
-  }
-
-  @Test public void useThisClassWhenCalledFromSuperClass() throws Throwable {
-    @FixedAttribute(key = "key0", value = "value0")
-    class Base {
-
-      @TrackEvent("event")
-      public void base() {
-      }
-    }
-
-    @FixedAttribute(key = "key", value = "value")
-    @FixedAttributes(
-        @FixedAttribute(key = "key2", value = "value2")
-    )
-    class Foo extends Base {
-    }
-
-    initMethod(Foo.class, "base");
-    when(joinPoint.getThis()).thenReturn(new Foo());
-    aspect.weaveJoinPointTrackEvent(joinPoint);
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .noTags()
-        .attribute("key0", "value0")
-        .attribute("key", "value")
-        .attribute("key2", "value2");
-  }
-
-  @Test public void filters() throws Throwable {
-    class Foo {
-      @TrackEvent(value = "event", filters = {100, 200})
-      public void foo() {
-      }
+        assertTrack()
+                .event("event")
+                .noFilters()
+                .tags("abc", "123")
+                .noAttributes();
     }
 
-    invokeMethod(Foo.class, "foo");
-
-    int[] tags = {100, 200};
-
-    assertTrack()
-        .event("event")
-        .noTags()
-        .filters(100, 200)
-        .noAttributes();
-  }
-
-  @Test public void tags() throws Throwable {
-    class Foo {
-      @TrackEvent(value = "event", tags = {"abc", "123"})
-      public void foo() {
-      }
-    }
-
-    invokeMethod(Foo.class, "foo");
-
-    int[] tags = {100, 200};
-
-    assertTrack()
-        .event("event")
-        .noFilters()
-        .tags("abc", "123")
-        .noAttributes();
-  }
-
-  AssertTracker assertTrack() {
-    return new AssertTracker(trackEvent, attributes);
-  }
+    AssertTracker assertTrack() {
+        return new AssertTracker(trackEvent, attributes);
+    }
 
 }

From 5b110a68dbfef2da168ac1553bd01ce3df83432f Mon Sep 17 00:00:00 2001
From: Nikolay Unuchek 
Date: Fri, 17 Jul 2020 17:45:06 +0300
Subject: [PATCH 7/8] updated README.MD

---
 README.MD | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/README.MD b/README.MD
index 560b827..69252d2 100644
--- a/README.MD
+++ b/README.MD
@@ -259,13 +259,18 @@ Use [Bee](https://github.com/orhanobut/bee) to monitor your events
 Depending on your ProGuard (DexGuard) config and usage, you may need to include the following lines in your proguard
 ```
 -keep class com.orhanobut.tracklytics.** { *; }
--keepclassmembers class com.orhanobut.tracklytics.** {
-    *;
-}
+-keepclassmembers class com.orhanobut.tracklytics.** { *; }
 -keep interface com.orhanobut.tracklytics.** { *; }
 -keep @interface com.orhanobut.tracklytics.** { *; }
 -keep enum com.orhanobut.tracklytics.** { *; }
--keepattributes Attribute, FixedAttribute, FixedAttributes, RemoveSuperAttribute, TrackableAttribute, TrackEvent, TrackSuperAttribute, TransformAttribute, TransformAttributeMap
+-keepattributes Attribute,FixedAttribute,FixedAttributes,RemoveSuperAttribute,TrackableAttribute,TrackEvent,TrackSuperAttribute,TransformAttribute,TransformAttributeMap
+-keepattributes *Annotation*
+
+-keepclasseswithmembers class * {
+@com.orhanobut.tracklytics.TrackEvent *;
+}
+
+-dontwarn com.orhanobut.tracklytics.*
 
 ```
 

From 2c83d255d834e16f15122164e0a782de3e651a75 Mon Sep 17 00:00:00 2001
From: Nikolay Unuchek 
Date: Thu, 25 Feb 2021 18:06:56 +0300
Subject: [PATCH 8/8] updated libs updated tests

---
 .idea/compiler.xml                            |  18 +-
 .idea/kotlinc.xml                             |   4 +-
 build.gradle                                  |   6 +-
 gradle/wrapper/gradle-wrapper.properties      |   4 +-
 library_module/build.gradle                   |   6 +-
 sample/build.gradle                           |  10 +-
 .../java/com/orhanobut/sample/JavaObject.java |   2 +
 .../kotlin/com/orhanobut/sample/FooKotlin.kt  |   3 +-
 .../com/orhanobut/sample/TrackingTest.java    |  16 +-
 tracklytics-plugin/build.gradle               |   2 +-
 tracklytics-runtime/build.gradle              |   6 +-
 .../com/orhanobut/tracklytics/Trackable.java  |   3 +
 .../tracklytics/TracklyticsAttrTest.kt        | 661 ++++++++++++++++++
 13 files changed, 703 insertions(+), 38 deletions(-)
 create mode 100644 tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAttrTest.kt

diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 96cc43e..61a9130 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,22 +1,6 @@
 
 
   
-    
-    
-      
-      
-      
-      
-      
-      
-      
-      
-      
-    
-    
-      
-        
-      
-    
+    
   
 
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index 3097f31..57f05c9 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,7 +1,7 @@
 
 
   
-    
 
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index cd9dd88..504c993 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,14 +2,14 @@
 
 buildscript {
     ext {
-        kotlin_version = '1.3.72'
+        kotlin_version = '1.4.30'
     }
     repositories {
         google()
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.0.0'
+        classpath 'com.android.tools.build:gradle:4.1.2'
         classpath 'org.gradle.api.plugins:gradle-nexus-plugin:0.7'
         classpath 'org.aspectj:aspectjtools:1.8.10'
         classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
@@ -49,7 +49,7 @@ subprojects { project ->
             jcenter()
         }
         dependencies {
-            classpath 'com.android.tools.build:gradle:4.0.0'
+            classpath 'com.android.tools.build:gradle:4.1.2'
         }
     }
 
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 33448f6..6a70e27 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Jun 05 15:00:40 MSK 2020
+#Thu Feb 25 17:33:23 MSK 2021
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/library_module/build.gradle b/library_module/build.gradle
index 4ba31d0..1797a73 100644
--- a/library_module/build.gradle
+++ b/library_module/build.gradle
@@ -9,7 +9,7 @@ buildscript {
         mavenLocal()
     }
     dependencies {
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.2.1'
     }
 }
@@ -22,7 +22,7 @@ repositories {
 }
 
 android {
-  compileSdkVersion 26
+  compileSdkVersion 29
   buildToolsVersion "28.0.3"
 
   compileOptions {
@@ -32,7 +32,7 @@ android {
 
     defaultConfig {
         minSdkVersion 16
-        targetSdkVersion 26
+        targetSdkVersion 29
         versionCode 1
         versionName "1.0"
 
diff --git a/sample/build.gradle b/sample/build.gradle
index d0606dd..75c9f09 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -8,7 +8,7 @@ buildscript {
         mavenLocal()
     }
     dependencies {
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath 'com.orhanobut.tracklytics:tracklytics-plugin:2.2.1'
     }
 }
@@ -21,7 +21,7 @@ repositories {
 }
 
 android {
-    compileSdkVersion 26
+    compileSdkVersion 29
     buildToolsVersion "28.0.3"
 
     compileOptions {
@@ -32,7 +32,7 @@ android {
     defaultConfig {
         applicationId "com.orhanobut.sample"
         minSdkVersion 16
-        targetSdkVersion 26
+        targetSdkVersion 29
         versionCode 1
         versionName "1.0"
     }
@@ -73,10 +73,10 @@ android {
 }
 
 dependencies {
-    testImplementation 'junit:junit:4.13'
+    testImplementation 'junit:junit:4.13.1'
     testImplementation 'com.google.truth:truth:0.28'
 
-    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
     implementation project(":library_module")
 }
 
diff --git a/sample/src/main/java/com/orhanobut/sample/JavaObject.java b/sample/src/main/java/com/orhanobut/sample/JavaObject.java
index b317f1a..3c44b22 100644
--- a/sample/src/main/java/com/orhanobut/sample/JavaObject.java
+++ b/sample/src/main/java/com/orhanobut/sample/JavaObject.java
@@ -1,7 +1,9 @@
 package com.orhanobut.sample;
 
+import com.orhanobut.tracklytics.FixedAttribute;
 import com.orhanobut.tracklytics.TrackEvent;
 
+@FixedAttribute(key="screen_name", value = "FooKotlin")
 class JavaObject {
     @TrackEvent("event_java_object")
     void track() {
diff --git a/sample/src/main/kotlin/com/orhanobut/sample/FooKotlin.kt b/sample/src/main/kotlin/com/orhanobut/sample/FooKotlin.kt
index a287e6e..66c24ac 100644
--- a/sample/src/main/kotlin/com/orhanobut/sample/FooKotlin.kt
+++ b/sample/src/main/kotlin/com/orhanobut/sample/FooKotlin.kt
@@ -2,11 +2,12 @@ package com.orhanobut.sample
 
 import com.orhanobut.tracklytics.FixedAttribute
 import com.orhanobut.tracklytics.TrackEvent
+import com.orhanobut.tracklytics.TransformAttribute
 
 @FixedAttribute(key="screen_name", value = "FooKotlin")
 open class FooKotlin {
 
   @TrackEvent("event_kotlin")
-  open fun trackFoo() {
+  open fun trackFoo(@TransformAttribute("fun_attribute") attribute: String?) {
   }
 }
\ No newline at end of file
diff --git a/sample/src/test/java/com/orhanobut/sample/TrackingTest.java b/sample/src/test/java/com/orhanobut/sample/TrackingTest.java
index b967eda..6290c05 100644
--- a/sample/src/test/java/com/orhanobut/sample/TrackingTest.java
+++ b/sample/src/test/java/com/orhanobut/sample/TrackingTest.java
@@ -27,14 +27,26 @@ public class TrackingTest {
   }
 
   @Test public void confirmKotlinAspects() {
-    new FooKotlin().trackFoo();
+    new FooKotlin().trackFoo("any_value");
 
     assertThat(triggeredEvents).containsKey("event_kotlin");
+    Event event = triggeredEvents.get("event_kotlin");
+    Map attributes = event.attributes;
+    assertThat(attributes).containsKey("screen_name");
+    assertThat(attributes).containsKey("fun_attribute");
+    assertThat(attributes.get("fun_attribute")).isEqualTo("any_value");
+  }
+  @Test public void confirmJavaObjectAspects() {
+    new JavaObject().track();
+
+    assertThat(triggeredEvents).containsKey("event_java_object");
+    Event event = triggeredEvents.get("event_java_object");
+    assertThat(event.attributes).containsKey("screen_name");
   }
 
   @Test public void confirmJavaAspects() {
     new KotlinObject().track();
 
-    assertThat(triggeredEvents).containsKey("event_java");
+    assertThat(triggeredEvents).containsKey("event_kotlin_object");
   }
 }
\ No newline at end of file
diff --git a/tracklytics-plugin/build.gradle b/tracklytics-plugin/build.gradle
index 05261e6..eb31774 100644
--- a/tracklytics-plugin/build.gradle
+++ b/tracklytics-plugin/build.gradle
@@ -8,7 +8,7 @@ dependencies {
   implementation 'org.aspectj:aspectjrt:1.8.10'
   implementation 'com.android.tools.build:gradle:4.0.0'
 
-  testImplementation 'junit:junit:4.13'
+  testImplementation 'junit:junit:4.13.1'
 }
 
 modifyPom {
diff --git a/tracklytics-runtime/build.gradle b/tracklytics-runtime/build.gradle
index b87b6f7..5bfc972 100644
--- a/tracklytics-runtime/build.gradle
+++ b/tracklytics-runtime/build.gradle
@@ -31,10 +31,12 @@ android {
 
 dependencies {
     compileOnly "org.aspectj:aspectjrt:1.8.10"
+    testImplementation "org.aspectj:aspectjrt:1.8.10"
 
-    testImplementation 'junit:junit:4.13'
+    testImplementation 'junit:junit:4.13.1'
     testImplementation 'com.google.truth:truth:0.28'
-    testImplementation "org.mockito:mockito-core:3.3.3"
+    testImplementation "org.mockito:mockito-core:3.7.7"
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
 }
 
 android.libraryVariants.all { variant ->
diff --git a/tracklytics-runtime/src/main/java/com/orhanobut/tracklytics/Trackable.java b/tracklytics-runtime/src/main/java/com/orhanobut/tracklytics/Trackable.java
index b616036..385b87d 100644
--- a/tracklytics-runtime/src/main/java/com/orhanobut/tracklytics/Trackable.java
+++ b/tracklytics-runtime/src/main/java/com/orhanobut/tracklytics/Trackable.java
@@ -1,5 +1,7 @@
 package com.orhanobut.tracklytics;
 
+import org.jetbrains.annotations.Nullable;
+
 import java.util.Map;
 
 /**
@@ -9,5 +11,6 @@
 @SuppressWarnings("WeakerAccess")
 public interface Trackable {
 
+  @Nullable
   Map getTrackableAttributes();
 }
diff --git a/tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAttrTest.kt b/tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAttrTest.kt
new file mode 100644
index 0000000..85624b2
--- /dev/null
+++ b/tracklytics-runtime/src/test/java/com/orhanobut/tracklytics/TracklyticsAttrTest.kt
@@ -0,0 +1,661 @@
+package com.orhanobut.tracklytics
+
+import com.google.common.truth.Truth
+import junit.framework.Assert
+import org.aspectj.lang.ProceedingJoinPoint
+import org.aspectj.lang.reflect.MethodSignature
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import java.lang.reflect.Method
+import java.util.HashMap
+
+class TracklyticsAttrTest {
+    @Mock
+    lateinit var joinPoint: ProceedingJoinPoint
+
+    @Mock
+    lateinit var methodSignature: MethodSignature
+
+    private val superAttributes: MutableMap = HashMap()
+    lateinit var aspect: TracklyticsAspect
+    private var trackEvent: TrackEvent? = null
+    private var attributes: Map? = null
+    private var aspectListener: AspectListener? = null
+
+    @Before
+    @Throws(Exception::class)
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        aspectListener = object : AspectListener {
+            override fun onAspectEventTriggered(trackEvent: TrackEvent, attributes: Map) {
+                this@TracklyticsAttrTest.trackEvent = trackEvent
+                this@TracklyticsAttrTest.attributes = attributes
+            }
+
+            override fun onAspectSuperAttributeAdded(key: String, value: Any) {
+                superAttributes[key] = value
+            }
+
+            override fun onAspectSuperAttributeRemoved(key: String) {
+                superAttributes.remove(key)
+            }
+        }
+        aspect = TracklyticsAspect()
+        TracklyticsAspect.subscribe(aspectListener)
+        Mockito.`when`(joinPoint.signature).thenReturn(methodSignature)
+    }
+
+    @Throws(Throwable::class)
+    private fun invokeMethod(klass: Class<*>, methodName: String, vararg parameterTypes: Class<*>): Method {
+        val method = initMethod(klass, methodName, *parameterTypes)
+        val instance = Any()
+        Mockito.`when`(joinPoint.getThis()).thenReturn(instance)
+        aspect.weaveJoinPointTrackEvent(joinPoint)
+        return method
+    }
+
+    @Throws(Throwable::class)
+    private fun initMethod(klass: Class<*>, name: String, vararg parameterTypes: Class<*>): Method {
+        val method = klass.getMethod(name, *parameterTypes)
+        Mockito.`when`(methodSignature.method).thenReturn(method)
+        return method
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun trackEventWithoutAttributes() {
+        class Foo {
+            @TrackEvent("title")
+            fun foo() {
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        val argument = ArgumentCaptor.forClass(
+            MutableMap::class.java
+        )
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .noAttributes()
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun useReturnValueAsAttribute() {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute("key")
+            fun foo(): String {
+                return "test"
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn("test")
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("title")
+            .noTags()
+            .noFilters()
+            .attribute("key", "test")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun useReturnValueAndParametersAsAttributes() {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute("key1")
+            fun foo(@Attribute("key2") param: String?): String {
+                return "test"
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn("test")
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf("param"))
+        invokeMethod(Foo::class.java, "foo", String::class.java)
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "test")
+            .attribute("key2", "param")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun useDefaultValueWhenThereIsNoReturnValue() {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute(value = "key1", defaultValue = "defaultValue")
+            fun foo() {
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "defaultValue")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun useReturnValueWhenItIsNotNull() {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute(value = "key1", defaultValue = "defaulValue")
+            fun foo(): String {
+                return "returnValue"
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn("returnValue")
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "returnValue")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun useDefaultValueWhenParameterValueIsNull() {
+        class Foo {
+            @TrackEvent("title")
+            fun foo(@Attribute(value = "key1", defaultValue = "default") `val`: String?) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(null))
+        invokeMethod(Foo::class.java, "foo", String::class.java)
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "default")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun fixedAttributeOnMethodScope() {
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttribute(key = "key1", value = "value")
+            fun foo(): String {
+                return "returnValue"
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun fixedAttributeOnClassScope() {
+        @FixedAttributes(FixedAttribute(key = "key1", value = "value1"), FixedAttribute(key = "key2", value = "value2"))
+        @FixedAttribute(key = "key3", value = "value3")
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttribute(key = "key4", value = "value4")
+            fun foo() {
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value1")
+            .attribute("key2", "value2")
+            .attribute("key3", "value3")
+            .attribute("key4", "value4")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun fixedAttributeAndAttributeAtSameTime() {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute("key1")
+            @FixedAttribute(key = "key2", value = "value2")
+            fun foo(): String {
+                return "value1"
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn("value1")
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value1")
+            .attribute("key2", "value2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun fixedAttributes() {
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttributes(FixedAttribute(key = "key1", value = "value1"), FixedAttribute(key = "key2", value = "value2"))
+            @FixedAttribute(key = "key3", value = "value3")
+            fun foo() {
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value1")
+            .attribute("key2", "value2")
+            .attribute("key3", "value3")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun superAttribute() {
+        class Foo {
+            @TrackEvent("title")
+            @Attribute(value = "key1", isSuper = true)
+            fun foo(@Attribute(value = "key2", isSuper = true) value: String?): String {
+                return "value1"
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn("value1")
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf("value2"))
+        invokeMethod(Foo::class.java, "foo", String::class.java)
+        Truth.assertThat(superAttributes).containsExactly("key1", "value1", "key2", "value2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun superFixedAttribute() {
+        class Foo {
+            @TrackEvent("title")
+            @FixedAttributes(FixedAttribute(key = "key1", value = "value1"), FixedAttribute(key = "key2", value = "value2", isSuper = true))
+            @FixedAttribute(key = "key3", value = "value3", isSuper = true)
+            fun foo(): String {
+                return "returnValue"
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn("value1")
+        invokeMethod(Foo::class.java, "foo")
+        Truth.assertThat(superAttributes).containsExactly("key2", "value2", "key3", "value3")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun superTransformAttribute() {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(keys = [0, 1], values = ["value1", "value2"])
+            @TransformAttribute(value = "key1", isSuper = true)
+            fun foo(@TransformAttribute(value = "key2", isSuper = true) value: Int): Int {
+                return 0
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn(0)
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(1))
+        invokeMethod(Foo::class.java, "foo", Int::class.java)
+        Truth.assertThat(superAttributes).containsExactly("key1", "value1", "key2", "value2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun trackable() {
+        class Bar : Trackable {
+            override fun getTrackableAttributes(): Map {
+                val values: MutableMap = HashMap()
+                values["key1"] = "value1"
+                values["key2"] = "value2"
+                return values
+            }
+        }
+
+        class Foo {
+            @TrackEvent("title")
+            fun foo(@TrackableAttribute bar: Bar?) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(Bar()))
+        invokeMethod(Foo::class.java, "foo", Bar::class.java)
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value1")
+            .attribute("key2", "value2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun ignoreNullValuesOnTrackable() {
+        class Bar : Trackable {
+            override fun getTrackableAttributes(): Map? {
+                return null
+            }
+        }
+
+        class Foo {
+            @TrackEvent("title")
+            fun foo(@TrackableAttribute bar: Bar?) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(Bar()))
+        invokeMethod(Foo::class.java, "foo", Bar::class.java)
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .noAttributes()
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun throwExceptionWhenTrackableAnnotationNotMatchWithValue() {
+        class Foo {
+            @TrackEvent("title")
+            fun foo(@TrackableAttribute bar: String?) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf("sdfsd"))
+        try {
+            invokeMethod(Foo::class.java, "foo", String::class.java)
+            Assert.fail("Should throw exception")
+        } catch (e: Exception) {
+            Truth.assertThat(e).hasMessage("Trackable interface must be implemented for the parameter type")
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun methodParameterWithoutAnnotation() {
+        class Foo {
+            @TrackEvent("title")
+            fun foo(@Attribute("Key") bar: String?, param2: String?) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf("sdfsd"))
+        invokeMethod(Foo::class.java, "foo", String::class.java, String::class.java)
+        try {
+            aspect.weaveJoinPointTrackEvent(joinPoint)
+        } catch (e: Exception) {
+            Assert.fail("Method parameters without annotation should be accepted")
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun classWideAttributeInAnonymousClass() {
+        @FixedAttribute(key = "key1", value = "value1")
+        class Foo {
+            @FixedAttribute(key = "key2", value = "value2")
+            inner class Inner {
+                @TrackEvent("title")
+                fun bar() {
+                }
+            }
+        }
+        invokeMethod(Foo.Inner::class.java, "bar")
+        assertTrack()
+            .event("title")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value1")
+            .attribute("key2", "value2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun transformAttributeForParameters() {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(keys = [0, 1], values = ["value1", "value2"])
+            fun foo(@TransformAttribute("key1") type: Int) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(0))
+        invokeMethod(Foo::class.java, "foo", Int::class.java)
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value1")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun transformAttributeMapInvalidState() {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(keys = [0, 1], values = ["value1"])
+            fun foo(@TransformAttribute("key1") type: Int) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(0))
+        try {
+            invokeMethod(Foo::class.java, "foo", Int::class.java)
+        } catch (e: Exception) {
+            Truth.assertThat(e).hasMessage("TransformAttributeMap keys and values must have same length")
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun transformAttributeWithoutTransformAttributeMap() {
+        class Foo {
+            @TrackEvent("event")
+            fun foo(@TransformAttribute("key1") type: Int) {
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(0))
+        try {
+            invokeMethod(Foo::class.java, "foo", Int::class.java)
+        } catch (e: Exception) {
+            Truth.assertThat(e).hasMessage("Method must have TransformAttributeMap when TransformAttribute is used")
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun transformAttributeForReturnValue() {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(keys = [0, 1], values = ["value1", "value2"])
+            @TransformAttribute("key1")
+            fun foo(): Int {
+                return 1
+            }
+        }
+        Mockito.`when`(joinPoint.proceed()).thenReturn(1)
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "value2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun transformAttributeDefaultValue() {
+        class Foo {
+            @TrackEvent("event")
+            @TransformAttributeMap(keys = [0, 1], values = ["value1", "value2"])
+            @TransformAttribute(value = "key1", defaultValue = "default1")
+            fun foo(@TransformAttribute(value = "key2", defaultValue = "default2") `val`: Int): String? {
+                return null
+            }
+        }
+        Mockito.`when`(joinPoint.args).thenReturn(arrayOf(null))
+        invokeMethod(Foo::class.java, "foo", Int::class.java)
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .attribute("key1", "default1")
+            .attribute("key2", "default2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun trackableAttributeForCurrentClass() {
+        class Foo : Trackable {
+            override fun getTrackableAttributes(): Map {
+                val map: MutableMap = HashMap()
+                map["key"] = "value"
+                return map
+            }
+
+            @TrackEvent("event")
+            @TrackableAttribute
+            fun foo() {
+            }
+        }
+        initMethod(Foo::class.java, "foo")
+        Mockito.`when`(joinPoint.getThis()).thenReturn(Foo())
+        aspect.weaveJoinPointTrackEvent(joinPoint)
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .attribute("key", "value")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun doNotUseTrackableAttributesWhenTrackableAttributeNotExists() {
+        class Foo : Trackable {
+            override fun getTrackableAttributes(): Map {
+                val map: MutableMap = HashMap()
+                map["key"] = "value"
+                return map
+            }
+
+            @TrackEvent("event")
+            fun foo() {
+            }
+        }
+        Mockito.`when`(joinPoint.getThis()).thenReturn(Foo())
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .noAttributes()
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun ignoreNullValueOnTrackableAttributeForCurrentClass() {
+        class Foo : Trackable {
+            override fun getTrackableAttributes(): Map? {
+                return null
+            }
+
+            @TrackEvent("event")
+            @TrackableAttribute
+            fun foo() {
+            }
+        }
+        initMethod(Foo::class.java, "foo")
+        Mockito.`when`(joinPoint.getThis()).thenReturn(Foo())
+        aspect.weaveJoinPointTrackEvent(joinPoint)
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .noAttributes()
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun overrideClassWideAttributeOnMethodWhenAttributesAreSame() {
+        @FixedAttribute(key = "key", value = "class")
+        @FixedAttributes(FixedAttribute(key = "key1", value = "class1"))
+        class Foo {
+            @TrackEvent("event")
+            @FixedAttribute(key = "key", value = "method")
+            @FixedAttributes(FixedAttribute(key = "key1", value = "method1"))
+            fun foo() {
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .attribute("key", "method")
+            .attribute("key1", "method1")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun useThisClassWhenCalledFromSuperClass() {
+        @FixedAttribute(key = "key0", value = "value0")
+        open class Base {
+            @TrackEvent("event")
+            fun base() {
+            }
+        }
+
+        @FixedAttribute(key = "key", value = "value")
+        @FixedAttributes(FixedAttribute(key = "key2", value = "value2"))
+        class Foo : Base()
+        initMethod(Foo::class.java, "base")
+        Mockito.`when`(joinPoint.getThis()).thenReturn(Foo())
+        aspect.weaveJoinPointTrackEvent(joinPoint)
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .noTags()
+            .attribute("key0", "value0")
+            .attribute("key", "value")
+            .attribute("key2", "value2")
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun filters() {
+        class Foo {
+            @TrackEvent(value = "event", filters = [100, 200])
+            fun foo() {
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        val tags = intArrayOf(100, 200)
+        assertTrack()
+            .event("event")
+            .noTags()
+            .filters(100, 200)
+            .noAttributes()
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun tags() {
+        class Foo {
+            @TrackEvent(value = "event", tags = ["abc", "123"])
+            fun foo() {
+            }
+        }
+        invokeMethod(Foo::class.java, "foo")
+        val tags = intArrayOf(100, 200)
+        assertTrack()
+            .event("event")
+            .noFilters()
+            .tags("abc", "123")
+            .noAttributes()
+    }
+
+    private fun assertTrack(): AssertTracker {
+        return AssertTracker(trackEvent, attributes)
+    }
+}
\ No newline at end of file