diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 681f41ae..3628614f 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,5 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 8e7c6662..54baa27b 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -31,5 +31,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 03451793..32a98187 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,27 @@
Change Log
==========
+## Version 1.1.6
+
+_2020-09-01_
+
+ * **BREAKING**: Removed activity field from Screen class due to Firebase deprecated _setCurrentScreen_ method.
+ * **BREAKING**: Implement LiveCollector configuration class.
+ * **BREAKING**: Rename Collar plugin extension parameter _filePath_ to _fileName_.
+ * Update Kotlin to 1.4.0.
+ * Update various dependencies.
+ * Implement latest changes on GeneratorTask.
+ * Update Gradle wrapper to 6.6.
+ * Set explicit dependency version for Kotlin Reflect to avoid adding multiple versions resolution.
+ * Add Javadoc to exposed classes and methods.
+ * Update KotlinPoet to 1.6.0.
+ * Update Detekt to 1.11.0.
+ * Implement redaction feature.
+ * Replace Moshi with KotlinX Serialization in generators
+ * Make description optional in generator models
+ * Make members optional in AnalyticsModel
+ * Make GenerateTask inherit from SourceTask
+
## Version 1.1.5
_2020-04-28_
diff --git a/README.md b/README.md
index c2dfa808..cd82b51a 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ buildscript {
maven { url "https://dl.bintray.com/infinum/android" }
}
dependencies {
- classpath "co.infinum.collar:collar-plugin:1.1.5"
+ classpath "co.infinum.collar:collar-plugin:1.1.6"
}
}
```
@@ -46,7 +46,7 @@ buildscript {
maven(url = "https://dl.bintray.com/infinum/android")
}
dependencies {
- classpath("co.infinum.collar:collar-plugin:1.1.5")
+ classpath("co.infinum.collar:collar-plugin:1.1.6")
}
}
```
@@ -250,28 +250,32 @@ javaCompileOptions {
### Plugin extension
```gradle
collar {
- version "1.1.5"
+ version "1.1.6"
}
```
You can set a specific _Collar_ version to be used.
+
## Debug UI
+
+
+
A separate package and no-op package is provided if you want to visually track what has been sent through Collar.
You can search, filter and clear all sent analytics.
In your app `build.gradle` or `build.gradle.kts` add:
**Groovy**
```gradle
-debugImplementation "co.infinum.collar:collar-ui:1.1.5"
-releaseImplementation "co.infinum.collar:collar-ui-no-op:1.1.5"
+debugImplementation "co.infinum.collar:collar-ui:1.1.6"
+releaseImplementation "co.infinum.collar:collar-ui-no-op:1.1.6"
```
**KotlinDSL**
```kotlin
-debugImplementation("co.infinum.collar:collar-ui:1.1.5")
-releaseImplementation("co.infinum.collar:collar-ui-no-op:1.1.5")
+debugImplementation("co.infinum.collar:collar-ui:1.1.6")
+releaseImplementation("co.infinum.collar:collar-ui-no-op:1.1.6")
```
In order to start tracking with UI you must use _LiveCollector_ as in this example:
```kotlin
- Collar.attach(object : LiveCollector(true, false) {
+ Collar.attach(object : LiveCollector() {
override fun onScreen(screen: Screen) =
super.onScreen(screen).run {
@@ -289,10 +293,11 @@ In order to start tracking with UI you must use _LiveCollector_ as in this examp
}
})
```
-If you put the first parameter *showSystemNotification* as *true* in *LiveCollector*, a notification will show once analytics are gathered and clicking on it will open a dedicated screen.
-Second parameter *showInAppNotification* with value *true* in *LiveCollector* will show a Snackbar-ish popup once analytics are gathered inside the current running Activity.
+_LiveCollector_ constructor has a _Configuration_ parameter that consists of following members.
+If you put the first parameter *showSystemNotification* as *true* in *Configuration*, a notification will show once analytics are gathered and clicking on it will open a dedicated screen.
+Second parameter *showInAppNotification* with value *true* in *Configuration* will show a Snackbar-ish popup once analytics are gathered inside the current running Activity.
These parameters are default values per collector session but can be changed via _CollarActivity_ menu and will remain valid until the next session.
-Otherwise if set to *false* notification will **not** be shown but you can always run the UI with following command of getting the launch Intent:
+Otherwise if set to *false* notification will **not** be shown but you can always run the UI with following command of getting the launch Intent instead of clicking the actual notification:
```kotlin
startActivity(
CollarUi.launchIntent().apply {
@@ -300,31 +305,37 @@ Otherwise if set to *false* notification will **not** be shown but you can alway
}
)
```
-Also you can use a dedicated method with default Intent setup:
+Alternatively, you can use a dedicated method with default Intent setup:
```kotlin
CollarUi.show()
```
+Third parameter in *Configuration* is a set of keywords to redact if found in screen names, analytics events names and parameters and user properties names or values.
+
+ 
+
+### Redaction
+In order to prevent potential leaks of user sensitive data, developers have an option to implement a set of keywords to be replaced by a • in length of the matched keyword.
+This set of keywords is provided to _LiveCollector_ via _Configuration_.
-
+
## Tasks
### Generate
Gradle plugin supports code generation from a JSON formatted file.
-You will need to specify `filePath` and `packageName` in `collar` plugin extension.
+You will need to specify `fileName` and `packageName` in `collar` plugin extension.
For example:
```
collar {
- version "1.1.5"
+ version "1.1.6"
filePath = "example.json"
packageName = "co.infinum.collar.sample.analytics.generated"
- variant = "main" // main by default
}
```
JSON file has to be formatted in the same way as it is in `sample` project module.
-If you don't want to use this task simply don't specify mandatory data.
-Using this file is just a temporary and fetching the tracking plan will be implemented soon in future releases.
+If you don't want to use this task simply don't specify data parameters in plugin extension.
+Using this file is just a temporary solution and fetching the tracking plan will be implemented soon in future releases.
To run the task you can:
diff --git a/annotations/src/main/kotlin/co/infinum/collar/annotations/EventName.kt b/annotations/src/main/kotlin/co/infinum/collar/annotations/EventName.kt
index 15052784..d93c1efb 100644
--- a/annotations/src/main/kotlin/co/infinum/collar/annotations/EventName.kt
+++ b/annotations/src/main/kotlin/co/infinum/collar/annotations/EventName.kt
@@ -1,5 +1,12 @@
package co.infinum.collar.annotations
+/**
+ * An analytics event name annotation
+ *
+ * @property value Holds the actual name of the event. If empty, class name will be taken and formatted into snake case.
+ * @property enabled Determines if this annotation will be processed or skipped.
+ * @constructor Default values are provided with an empty value and enabled annotation ready for processing.
+ */
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class EventName(
diff --git a/annotations/src/main/kotlin/co/infinum/collar/annotations/EventParameterName.kt b/annotations/src/main/kotlin/co/infinum/collar/annotations/EventParameterName.kt
index 3949b9da..35a1e5ce 100644
--- a/annotations/src/main/kotlin/co/infinum/collar/annotations/EventParameterName.kt
+++ b/annotations/src/main/kotlin/co/infinum/collar/annotations/EventParameterName.kt
@@ -1,5 +1,12 @@
package co.infinum.collar.annotations
+/**
+ * An event parameter name annotation
+ *
+ * @property value Holds the actual name of the event parameter name. If empty, actual field name will be taken.
+ * @property enabled Determines if this annotation will be processed or skipped.
+ * @constructor Default values are provided with an empty value and enabled annotation ready for processing.
+ */
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class EventParameterName(
diff --git a/annotations/src/main/kotlin/co/infinum/collar/annotations/PropertyName.kt b/annotations/src/main/kotlin/co/infinum/collar/annotations/PropertyName.kt
index 5b096567..fcd25658 100644
--- a/annotations/src/main/kotlin/co/infinum/collar/annotations/PropertyName.kt
+++ b/annotations/src/main/kotlin/co/infinum/collar/annotations/PropertyName.kt
@@ -1,5 +1,12 @@
package co.infinum.collar.annotations
+/**
+ * A user property name annotation
+ *
+ * @property value Holds the actual name of the user property. If empty, actual field name will be taken.
+ * @property enabled Determines if this annotation will be processed or skipped.
+ * @constructor Default values are provided with an empty value and enabled annotation ready for processing.
+ */
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class PropertyName(
diff --git a/build.gradle b/build.gradle
index ec2ccdcf..8b67a4da 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
"compileSdk": 29,
"targetSdk" : 29,
- "buildTools": "29.0.3"
+ "buildTools": "30.0.1"
]
apply from: "dependencies.gradle"
@@ -36,19 +36,23 @@ allprojects {
maven { url "https://dl.bintray.com/infinum/android" }
}
-// configurations.all {
-// resolutionStrategy {
-// dependencySubstitution {
-// substitute module(packages.collar.annotations) with project(':annotations')
-// substitute module(packages.collar.core) with project(':core')
-// substitute module(packages.collar.ui) with project(':ui')
-// substitute module(packages.collar.ui_no_op) with project(':ui-no-op')
-// substitute module(packages.collar.processor) with project(':processor')
-// substitute module(packages.collar.generator) with project(':generator')
-// substitute module(packages.collar.plugin) with project(':plugin')
-// }
-// }
-// }
+ def buildProperties = new Properties()
+ file(rootDir.absolutePath+"/build.properties").withInputStream { buildProperties.load(it) }
+ if (buildProperties.getProperty("build.debug").toBoolean()) {
+ configurations.all {
+ resolutionStrategy {
+ dependencySubstitution {
+ substitute module(packages.collar.annotations) with project(':annotations')
+ substitute module(packages.collar.core) with project(':core')
+ substitute module(packages.collar.ui) with project(':ui')
+ substitute module(packages.collar.ui_no_op) with project(':ui-no-op')
+ substitute module(packages.collar.processor) with project(':processor')
+ substitute module(packages.collar.generator) with project(':generator')
+ substitute module(packages.collar.plugin) with project(':plugin')
+ }
+ }
+ }
+ }
}
subprojects {
diff --git a/build.properties b/build.properties
new file mode 100644
index 00000000..b6db6f86
--- /dev/null
+++ b/build.properties
@@ -0,0 +1 @@
+build.debug=false
\ No newline at end of file
diff --git a/core/src/main/kotlin/co/infinum/collar/Collar.kt b/core/src/main/kotlin/co/infinum/collar/Collar.kt
index d2aaf8c9..2f68147a 100644
--- a/core/src/main/kotlin/co/infinum/collar/Collar.kt
+++ b/core/src/main/kotlin/co/infinum/collar/Collar.kt
@@ -1,28 +1,50 @@
package co.infinum.collar
-import android.app.Activity
import android.os.Bundle
+/**
+ * Singleton object entry point for screen names, events and properties collection.
+ */
object Collar {
private var collector: Collector? = null
+ /**
+ * Attach a collector to running process.
+ *
+ * @param collector interface or class implementing such interface.
+ */
@JvmStatic
fun attach(collector: Collector) {
this.collector = collector
}
- fun trackScreen(activity: Activity, screenName: String) =
+ /**
+ * Track screen names using a direct value.
+ *
+ * @param screenName value.
+ */
+ fun trackScreen(screenName: String) =
collector?.onScreen(
Screen(
- activity = activity,
name = screenName
)
)
+ /**
+ * Track screen names using a provided wrapper class.
+ *
+ * @param screen wrapper class.
+ */
fun trackScreen(screen: Screen) =
collector?.onScreen(screen)
+ /**
+ * Track events using direct values for event name and optional event parameters.
+ *
+ * @param eventName value.
+ * @param params value.
+ */
fun trackEvent(eventName: String, params: Bundle) =
collector?.onEvent(
Event(
@@ -31,9 +53,21 @@ object Collar {
)
)
+ /**
+ * Track events using a provided wrapper class.
+ *
+ * @param event wrapper class.
+ */
fun trackEvent(event: Event) =
collector?.onEvent(event)
+ /**
+ * Track user properties using direct values for property name and optional property value.
+ * If value is set as 'null', property is cleared.
+ *
+ * @param name value.
+ * @param value value.
+ */
fun trackProperty(name: String, value: String?) =
collector?.onProperty(
Property(
@@ -42,6 +76,11 @@ object Collar {
)
)
+ /**
+ * Track user properties using a provided wrapper class.
+ *
+ * @param property wrapper class.
+ */
fun trackProperty(property: Property) =
collector?.onProperty(property)
}
diff --git a/core/src/main/kotlin/co/infinum/collar/Collector.kt b/core/src/main/kotlin/co/infinum/collar/Collector.kt
index f1bae4e5..1953a680 100644
--- a/core/src/main/kotlin/co/infinum/collar/Collector.kt
+++ b/core/src/main/kotlin/co/infinum/collar/Collector.kt
@@ -1,16 +1,32 @@
package co.infinum.collar
/**
- * Collar aggregates all tracked analytics events.
- * Once any event is ready, Collar emits the event to the collector.
+ * Aggregation collector for all tracked screen names, analytics events and user properties.
+ * By default, has no implementation or UI.
*
- * This is a good place to implement your analytics tools such as Firebase, Amplitude, Mixpanel, etc.
+ * Implementation or invocation of this interface is intended for implementing analytics tools
+ * such as Firebase, Amplitude, Mixpanel, etc.
*/
interface Collector {
+ /**
+ * Invoked when a new screen is emitted.
+ *
+ * @param screen wrapper class.
+ */
fun onScreen(screen: Screen)
+ /**
+ * Invoked when a new analytics event is emitted.
+ *
+ * @param event wrapper class.
+ */
fun onEvent(event: Event)
+ /**
+ * Invoked when a new user property is emitted.
+ *
+ * @param property wrapper class.
+ */
fun onProperty(property: Property)
}
diff --git a/core/src/main/kotlin/co/infinum/collar/Event.kt b/core/src/main/kotlin/co/infinum/collar/Event.kt
index b378aee0..1cc31170 100644
--- a/core/src/main/kotlin/co/infinum/collar/Event.kt
+++ b/core/src/main/kotlin/co/infinum/collar/Event.kt
@@ -6,6 +6,14 @@ import android.os.Bundle
* This is the container model for the triggered tracking analytics event.
*/
data class Event(
+
+ /**
+ * Name of the tracked analytics event.
+ */
val name: String,
+
+ /**
+ * Optional parameters of the tracked analytics event.
+ */
val params: Bundle? = null
)
diff --git a/core/src/main/kotlin/co/infinum/collar/Property.kt b/core/src/main/kotlin/co/infinum/collar/Property.kt
index 0ba3d3fb..0cfa0964 100644
--- a/core/src/main/kotlin/co/infinum/collar/Property.kt
+++ b/core/src/main/kotlin/co/infinum/collar/Property.kt
@@ -4,6 +4,14 @@ package co.infinum.collar
* This is the container model for the user property tracking.
*/
data class Property(
+
+ /**
+ * Name of the tracked user property.
+ */
val name: String,
+
+ /**
+ * Optional value of the tracked user property.
+ */
val value: String?
)
diff --git a/core/src/main/kotlin/co/infinum/collar/Screen.kt b/core/src/main/kotlin/co/infinum/collar/Screen.kt
index c071bbf5..58d825ae 100644
--- a/core/src/main/kotlin/co/infinum/collar/Screen.kt
+++ b/core/src/main/kotlin/co/infinum/collar/Screen.kt
@@ -1,11 +1,12 @@
package co.infinum.collar
-import android.app.Activity
-
/**
* This is the container model for the triggered screen tracking.
*/
data class Screen(
- val activity: Activity,
+
+ /**
+ * Name of the tracked screen.
+ */
val name: String
)
diff --git a/dependencies.gradle b/dependencies.gradle
index 72d1bc10..322dfabe 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -1,40 +1,43 @@
ext.collar = [
- "group" : "co.infinum.collar",
- "version": "1.1.5",
- "versionCode": 1*100*100 + 1*100 + 5
+ "group" : "co.infinum.collar",
+ "version" : "1.1.6",
+ "versionCode": 1 * 100 * 100 + 1 * 100 + 6
]
ext.versions = [
- "collar" : "1.1.5",
- "gradle" : '3.6.3',
- "kotlin" : "1.3.72",
- "poet" : "1.5.0",
- "poet_metadata": "1.5.0",
+ "collar" : "1.1.6",
+ "gradle" : "4.0.1",
+ "kotlin" : "1.4.0",
+ "poet" : "1.6.0",
+ "poet_metadata": "1.6.0",
+ "serialization": "1.0.0-RC",
"annotations" : "19.0.0",
"incap" : "0.2",
"bintray" : "1.8.5",
- "core" : "1.2.0",
- "appcompat" : "1.1.0",
- "fragment" : "1.2.4",
+ "core" : "1.3.1",
+ "appcompat" : "1.2.0",
+ "fragment" : "1.2.5",
"lifecycle" : "2.2.0",
"viewmodel" : "2.2.0",
"livedata" : "2.2.0",
"recyclerview" : "1.1.0",
"room" : "2.2.5",
- "design" : "1.1.0",
- "moshi" : "1.9.2",
+ "design" : "1.2.0",
"maven" : "2.0",
- "detekt" : "1.7.4",
+ "detekt" : "1.11.0",
"dokka" : "0.10.1"
]
ext.packages = [
"gradle" : "com.android.tools.build:gradle:${versions.gradle}",
"kotlin" : [
- "plugin" : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}",
- "core" : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
- "poet" : "com.squareup:kotlinpoet:${versions.poet}",
- "poet_metadata": "com.squareup:kotlinpoet-metadata:${versions.poet_metadata}"
+ "plugin" : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}",
+ "core" : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
+ "reflect" : "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}",
+ "poet" : "com.squareup:kotlinpoet:${versions.poet}",
+ "poet_metadata" : "com.squareup:kotlinpoet-metadata:${versions.poet_metadata}",
+ "serialization_plugin": "org.jetbrains.kotlin:kotlin-serialization:${versions.kotlin}",
+ "serialization_core" : "org.jetbrains.kotlinx:kotlinx-serialization-core:${versions.serialization}"
],
"annotations": "org.jetbrains:annotations-java5:${versions.annotations}",
"incap" : [
@@ -66,9 +69,6 @@ ext.packages = [
"runtime" : "androidx.room:room-runtime:${versions.room}"
]
],
- "moshi" : [
- "kotlin": "com.squareup.moshi:moshi-kotlin:${versions.moshi}"
- ],
"google" : [
"design": "com.google.android.material:material:${versions.design}"
],
diff --git a/generator/build.gradle b/generator/build.gradle
index 445c9d3c..92f89e3e 100644
--- a/generator/build.gradle
+++ b/generator/build.gradle
@@ -1,5 +1,14 @@
+buildscript {
+ repositories { jcenter() }
+
+ dependencies {
+ classpath packages.kotlin.serialization_plugin
+ }
+}
+
apply plugin: 'java-library'
apply plugin: 'kotlin'
+apply plugin: 'kotlinx-serialization'
compileKotlin {
kotlinOptions {
@@ -21,7 +30,8 @@ test {
dependencies {
implementation packages.kotlin.core
implementation packages.kotlin.poet
- implementation packages.moshi.kotlin
+ implementation packages.kotlin.reflect
+ implementation packages.kotlin.serialization_core
testImplementation "junit:junit:4.13"
testImplementation 'org.mockito:mockito-core:3.3.3'
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/CollarGenerator.kt b/generator/src/main/kotlin/co/infinum/collar/generator/CollarGenerator.kt
index 48a8828e..15a1478e 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/CollarGenerator.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/CollarGenerator.kt
@@ -5,27 +5,43 @@ import co.infinum.collar.generator.generators.Generator
import co.infinum.collar.generator.generators.ScreensGenerator
import co.infinum.collar.generator.generators.UserPropertiesGenerator
import co.infinum.collar.generator.models.AnalyticsModel
-import co.infinum.collar.generator.providers.MoshiProvider
+import kotlinx.serialization.SerializationException
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
import java.io.File
+/**
+ * Generator class used to parse JSON into according Kotlin models for Collar processor consumption.
+ */
class CollarGenerator {
+ /**
+ * Generate models from provided JSON.
+ * Parameters are passed from Collar plugin extension to a Gradle task.
+ *
+ * @param filePath absolute path for the provided JSON file.
+ * @param output absolute path for the generated classes.
+ * @param packageName package name for the generated classes.
+ */
fun generate(filePath: String, output: String, packageName: String): Boolean =
- MoshiProvider.provide()
- .value
- .adapter(AnalyticsModel::class.java)
- .fromJson(File(filePath).readText(Charsets.UTF_8))
- ?.let {
- generators(it, output, packageName)
- .fold(true, { accumulator: Boolean, generator: Generator -> accumulator && generator.generate() })
- } ?: false
-
- private fun generators(analyticsModel: AnalyticsModel, output: String, packageName: String) =
- with(analyticsModel) {
- listOf(
- ScreensGenerator(screens, output, packageName),
- UserPropertiesGenerator(userProperties, output, packageName),
- EventsGenerator(events, output, packageName)
- )
+ try {
+ Json
+ .decodeFromString(File(filePath).readText(Charsets.UTF_8))
+ .run {
+ listOf(
+ ScreensGenerator(screens, output, packageName),
+ UserPropertiesGenerator(properties, output, packageName),
+ EventsGenerator(events, output, packageName)
+ )
+ .fold(
+ initial = true,
+ operation = { accumulator: Boolean, generator: Generator ->
+ accumulator && generator.generate()
+ }
+ )
+ }
+ } catch (exception: SerializationException) {
+ exception.printStackTrace()
+ false
}
}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/extensions/String.kt b/generator/src/main/kotlin/co/infinum/collar/generator/extensions/String.kt
index 19068c15..19eddb2f 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/extensions/String.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/extensions/String.kt
@@ -1,5 +1,16 @@
package co.infinum.collar.generator.extensions
-internal fun String.toCamelCase(): String = split(" ").map { it.capitalize() }.joinToString("")
+import co.infinum.collar.generator.models.DataType
+import java.util.Locale
-internal fun String.hasDigit(): Boolean = this.any { it.isDigit() }
+internal fun String.toCamelCase(): String = split(" ", "_").joinToString("") { it.capitalize(Locale.getDefault()) }
+
+internal fun String.isFirstCharDigit(): Boolean = this.firstOrNull()?.isDigit() ?: false
+
+internal fun String.toEnumValue(): String {
+ var parameterValueEnumName = this.toUpperCase(Locale.ENGLISH).replace(" ", "_").replace(".", "_")
+ if (this.isFirstCharDigit()) {
+ parameterValueEnumName = "${DataType.NUMBER.name.toUpperCase(Locale.ENGLISH)}_$parameterValueEnumName"
+ }
+ return parameterValueEnumName
+}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/generators/CommonGenerator.kt b/generator/src/main/kotlin/co/infinum/collar/generator/generators/CommonGenerator.kt
index fc63877b..7ee2dcc5 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/generators/CommonGenerator.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/generators/CommonGenerator.kt
@@ -11,18 +11,21 @@ internal abstract class CommonGenerator(
companion object {
internal const val ANNOTATION_FORMAT = "value = %S"
+ internal const val DEFAULT_INDENT = " "
}
- override fun file(): FileSpec.Builder = FileSpec.builder(packageName, className)
+ override fun file(): FileSpec.Builder = FileSpec.builder(packageName, className).indent(DEFAULT_INDENT)
override fun write(fileSpec: FileSpec) = fileSpec.writeTo(Paths.get(outputPath))
override fun generate(): Boolean {
- write(
- file()
- .addType(type())
- .build()
- )
+ type()?.let {
+ write(
+ file()
+ .addType(it)
+ .build()
+ )
+ }
return true
}
}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/generators/EventsGenerator.kt b/generator/src/main/kotlin/co/infinum/collar/generator/generators/EventsGenerator.kt
index f4de97b6..e3f81d3e 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/generators/EventsGenerator.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/generators/EventsGenerator.kt
@@ -1,6 +1,8 @@
package co.infinum.collar.generator.generators
import co.infinum.collar.generator.extensions.toCamelCase
+import co.infinum.collar.generator.extensions.toEnumValue
+import co.infinum.collar.generator.models.DataType
import co.infinum.collar.generator.models.Event
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
@@ -9,101 +11,121 @@ import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
+import java.util.Locale
internal class EventsGenerator(
- private val events: List,
+ private val items: List?,
outputPath: String,
packageName: String
) : CommonGenerator(outputPath, packageName, CLASS_NAME) {
companion object {
- const val CLASS_NAME = "AnalyticsEvents"
+ const val CLASS_NAME = "TrackingPlanEvents"
}
@Suppress("LongMethod", "NestedBlockDepth")
- override fun type() =
- TypeSpec.classBuilder(CLASS_NAME)
- .addAnnotation(ClassName(Generator.COLLAR_ANNOTATION_PACKAGE, Generator.COLLAR_ANNOTATION_ANALYTICS_EVENTS))
- .addModifiers(KModifier.SEALED)
- .apply {
- events.forEach { event ->
- val name = event.name.toCamelCase()
- val eventClass = TypeSpec.classBuilder(name)
- val constructorBuilder = FunSpec.constructorBuilder()
+ override fun type(): TypeSpec? =
+ items?.takeIf { it.isNotEmpty() }?.let {
+ TypeSpec.classBuilder(CLASS_NAME)
+ .addAnnotation(
+ ClassName(
+ Generator.COLLAR_ANNOTATION_PACKAGE,
+ Generator.COLLAR_ANNOTATION_ANALYTICS_EVENTS
+ )
+ )
+ .addModifiers(KModifier.SEALED)
+ .apply {
+ it.forEach { event ->
+ val name = event.name.toCamelCase()
+ val eventClass = TypeSpec.classBuilder(name)
+ val constructorBuilder = FunSpec.constructorBuilder()
- event.parameters.forEach {
- val eventParameterName = ClassName(
- Generator.COLLAR_ANNOTATION_PACKAGE,
- Generator.COLLAR_ANNOTATION_EVENT_PARAMETER_NAME
- )
- val constructorParamAnnotation = AnnotationSpec.builder(eventParameterName)
- .addMember(ANNOTATION_FORMAT, it.name).build()
+ event.parameters?.forEach { parameter ->
+ val eventParameterName = ClassName(
+ Generator.COLLAR_ANNOTATION_PACKAGE,
+ Generator.COLLAR_ANNOTATION_EVENT_PARAMETER_NAME
+ )
+ val constructorParamAnnotation = AnnotationSpec.builder(eventParameterName)
+ .addMember(ANNOTATION_FORMAT, parameter.name).build()
- val type = GeneratorUtils.getClassName(it)
+ val type = when {
+ parameter.values?.isNotEmpty() == true -> DataType.TEXT.className
+ else ->
+ DataType(parameter.type)?.className ?: error("${parameter.type} is not supported")
+ }
- val constructorParamBuilder = ParameterSpec.builder(
- GeneratorUtils.getParameterName(it.name), type
- ).apply {
- addKdoc(it.description)
- addAnnotation(constructorParamAnnotation)
- }
- constructorBuilder.addParameter(constructorParamBuilder.build())
+ val constructorParamBuilder = ParameterSpec.builder(
+ parameter.name.toCamelCase().decapitalize(Locale.ENGLISH),
+ type
+ ).apply {
+ parameter.description
+ ?.takeIf { it.isNotBlank() }
+ ?.let { addKdoc(it) }
+ addAnnotation(constructorParamAnnotation)
+ }
+ constructorBuilder.addParameter(constructorParamBuilder.build())
- if (it.values?.isNotEmpty() == true) {
- val enumBuilder = TypeSpec.enumBuilder(GeneratorUtils.getParameterEnumName(it.name))
- .primaryConstructor(
- FunSpec.constructorBuilder()
- .addParameter("value", String::class)
- .build()
- )
- .addProperty(
- PropertySpec.builder("value", String::class)
- .initializer("value")
- .build()
- )
- .addFunction(
- FunSpec.builder("toString")
- .addModifiers(KModifier.OVERRIDE)
- .addStatement("return value")
- .build()
+ if (parameter.values?.isNotEmpty() == true) {
+ val enumBuilder = TypeSpec.enumBuilder(
+ parameter.name.toCamelCase()
)
+ .primaryConstructor(
+ FunSpec.constructorBuilder()
+ .addParameter("value", String::class, KModifier.PRIVATE)
+ .build()
+ )
+ .addProperty(
+ PropertySpec.builder("value", String::class)
+ .initializer("value")
+ .build()
+ )
+ .addFunction(
+ FunSpec.builder("toString")
+ .addModifiers(KModifier.OVERRIDE)
+ .addStatement("return value")
+ .build()
+ )
- it.values.forEach { value ->
- enumBuilder.addEnumConstant(
- GeneratorUtils.getParameterValueEnumName(value), TypeSpec.anonymousClassBuilder()
- .addSuperclassConstructorParameter("%S", value)
- .build()
- )
- }
+ parameter.values.forEach { value ->
+ enumBuilder.addEnumConstant(
+ value.toEnumValue(),
+ TypeSpec.anonymousClassBuilder()
+ .addSuperclassConstructorParameter("%S", value)
+ .build()
+ )
+ }
- eventClass.addType(enumBuilder.build())
- }
+ eventClass.addType(enumBuilder.build())
+ }
- eventClass.addProperty(
- PropertySpec.builder(
- GeneratorUtils.getParameterName(it.name),
- type
+ eventClass.addProperty(
+ PropertySpec.builder(
+ parameter.name.toCamelCase().decapitalize(Locale.ENGLISH),
+ type
+ )
+ .initializer(parameter.name.toCamelCase().decapitalize(Locale.ENGLISH))
+ .build()
)
- .initializer(GeneratorUtils.getParameterName(it.name))
- .build()
- )
- }
+ }
- eventClass.addModifiers(KModifier.DATA)
- eventClass.primaryConstructor(constructorBuilder.build())
- eventClass.addKdoc(event.description)
- eventClass.superclass(ClassName("", CLASS_NAME))
- eventClass.addAnnotation(
- AnnotationSpec.builder(
- ClassName(
- Generator.COLLAR_ANNOTATION_PACKAGE,
- Generator.COLLAR_ANNOTATION_EVENT_NAME
+ if (event.parameters.isNullOrEmpty().not()) {
+ eventClass.addModifiers(KModifier.DATA)
+ }
+ eventClass.primaryConstructor(constructorBuilder.build())
+ event.description?.takeIf { it.isNotBlank() }?.let { eventClass.addKdoc(it) }
+ eventClass.superclass(ClassName("", CLASS_NAME))
+ eventClass.addAnnotation(
+ AnnotationSpec.builder(
+ ClassName(
+ Generator.COLLAR_ANNOTATION_PACKAGE,
+ Generator.COLLAR_ANNOTATION_EVENT_NAME
+ )
)
+ .addMember(ANNOTATION_FORMAT, event.name).build()
)
- .addMember(ANNOTATION_FORMAT, event.name).build()
- )
- addType(eventClass.build())
- }
- }.build()
+ addType(eventClass.build())
+ }
+ }.build()
+ }
}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/generators/Generator.kt b/generator/src/main/kotlin/co/infinum/collar/generator/generators/Generator.kt
index a26c290f..33148b6a 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/generators/Generator.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/generators/Generator.kt
@@ -18,7 +18,7 @@ internal interface Generator {
fun file(): FileSpec.Builder
- fun type(): TypeSpec
+ fun type(): TypeSpec?
fun write(fileSpec: FileSpec)
}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/generators/GeneratorUtils.kt b/generator/src/main/kotlin/co/infinum/collar/generator/generators/GeneratorUtils.kt
deleted file mode 100644
index d8e2e7e6..00000000
--- a/generator/src/main/kotlin/co/infinum/collar/generator/generators/GeneratorUtils.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package co.infinum.collar.generator.generators
-
-import co.infinum.collar.generator.extensions.hasDigit
-import co.infinum.collar.generator.extensions.toCamelCase
-import co.infinum.collar.generator.models.DataType
-import co.infinum.collar.generator.models.Parameter
-import co.infinum.collar.generator.models.Property
-import com.squareup.kotlinpoet.ClassName
-import com.squareup.kotlinpoet.TypeName
-import java.util.Locale
-
-internal class GeneratorUtils private constructor() {
-
- companion object {
-
- fun getClassName(parameter: Parameter): TypeName {
- val dataTypeEnum = parameter.type
-
- if (parameter.values?.isNotEmpty() == true) {
- return ClassName("kotlin", "String")
- }
-
- return when (dataTypeEnum) {
- DataType.TEXT.toString() -> ClassName("kotlin", "String")
- DataType.NUMBER.toString() -> ClassName("kotlin", "Long")
- DataType.DECIMAL.toString() -> ClassName("kotlin", "Double")
- DataType.BOOLEAN.toString() -> ClassName("kotlin", "Boolean")
- else -> throw NotImplementedError("$dataTypeEnum is not supported")
- }
- }
-
- fun getClassName(property: Property): TypeName {
- return ClassName("kotlin", "String")
- }
-
- fun getParameterName(parameter: String) = parameter.toCamelCase().decapitalize()
-
- fun getParameterEnumName(parameter: String) = parameter.toCamelCase() + "Enum"
-
- fun getParameterValueEnumName(value: String): String {
- var parameterValueEnumName = value.toUpperCase(Locale.ENGLISH).replace(" ", "_").replace(".", "_")
- if (value.hasDigit()) {
- parameterValueEnumName = "NUMBER_$parameterValueEnumName"
- }
- return parameterValueEnumName
- }
- }
-}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/generators/ScreensGenerator.kt b/generator/src/main/kotlin/co/infinum/collar/generator/generators/ScreensGenerator.kt
index 17b0e99b..a44ad2f3 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/generators/ScreensGenerator.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/generators/ScreensGenerator.kt
@@ -7,30 +7,36 @@ import com.squareup.kotlinpoet.TypeSpec
import java.util.Locale
internal class ScreensGenerator(
- private val items: List,
+ private val items: List?,
outputPath: String,
packageName: String
) : CommonGenerator(outputPath, packageName, CLASS_NAME) {
companion object {
- const val CLASS_NAME = "AnalyticsScreens"
+ const val CLASS_NAME = "TrackingPlanScreens"
}
- override fun type(): TypeSpec =
- TypeSpec.objectBuilder(CLASS_NAME)
- .apply {
- items.forEach {
- addProperty(
- PropertySpec.builder(
- it.name.toUpperCase(Locale.ENGLISH).replace(" ", "_"),
- String::class,
- KModifier.CONST
+ override fun type(): TypeSpec? =
+ items?.takeIf { it.isNotEmpty() }?.let {
+ TypeSpec.objectBuilder(CLASS_NAME)
+ .apply {
+ it.forEach { screen ->
+ addProperty(
+ PropertySpec.builder(
+ screen.name.toUpperCase(Locale.ENGLISH).replace(" ", "_"),
+ String::class,
+ KModifier.CONST
+ )
+ .initializer("%S", screen.name)
+ .apply {
+ screen.description
+ ?.takeIf { it.isNotBlank() }
+ ?.let { addKdoc(it) }
+ }
+ .build()
)
- .initializer("%S", it.name)
- .addKdoc(it.description)
- .build()
- )
+ }
}
- }
- .build()
+ .build()
+ }
}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/generators/UserPropertiesGenerator.kt b/generator/src/main/kotlin/co/infinum/collar/generator/generators/UserPropertiesGenerator.kt
index eb32da93..9b211678 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/generators/UserPropertiesGenerator.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/generators/UserPropertiesGenerator.kt
@@ -1,6 +1,7 @@
package co.infinum.collar.generator.generators
import co.infinum.collar.generator.extensions.toCamelCase
+import co.infinum.collar.generator.extensions.toEnumValue
import co.infinum.collar.generator.generators.Generator.Companion.COLLAR_ANNOTATION_PACKAGE
import co.infinum.collar.generator.generators.Generator.Companion.COLLAR_ANNOTATION_PROPERTY_NAME
import co.infinum.collar.generator.generators.Generator.Companion.COLLAR_ANNOTATION_USER_PROPERTIES
@@ -13,82 +14,90 @@ import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
internal class UserPropertiesGenerator(
- private val userProperties: List,
+ private val items: List?,
outputPath: String,
packageName: String
) : CommonGenerator(outputPath, packageName, CLASS_NAME) {
companion object {
- const val CLASS_NAME = "AnalyticsUserProperties"
+ const val CLASS_NAME = "TrackingPlanUserProperties"
const val PROPERTY_PARAMETER_NAME = "value"
}
@Suppress("LongMethod", "NestedBlockDepth")
- override fun type(): TypeSpec =
- TypeSpec.classBuilder(CLASS_NAME)
- .addAnnotation(ClassName(COLLAR_ANNOTATION_PACKAGE, COLLAR_ANNOTATION_USER_PROPERTIES))
- .addModifiers(KModifier.SEALED)
- .apply {
- userProperties.forEach { userProperty ->
- val name = userProperty.name.toCamelCase()
- val propertyNameClassName = ClassName(COLLAR_ANNOTATION_PACKAGE, COLLAR_ANNOTATION_PROPERTY_NAME)
- val propertyNameAnnotation = AnnotationSpec.builder(propertyNameClassName)
- .addMember(ANNOTATION_FORMAT, userProperty.name).build()
- val userPropertyClass = TypeSpec.classBuilder(name).apply {
- addModifiers(KModifier.DATA)
- primaryConstructor(
- FunSpec.constructorBuilder()
- .addParameter(
- PROPERTY_PARAMETER_NAME,
- GeneratorUtils.getClassName(userProperty)
- )
- .build()
+ override fun type(): TypeSpec? =
+ items?.takeIf { it.isNotEmpty() }?.let {
+ TypeSpec.classBuilder(CLASS_NAME)
+ .addAnnotation(ClassName(COLLAR_ANNOTATION_PACKAGE, COLLAR_ANNOTATION_USER_PROPERTIES))
+ .addModifiers(KModifier.SEALED)
+ .apply {
+ it.forEach { userProperty ->
+ val name = userProperty.name.toCamelCase()
+ val propertyNameClassName = ClassName(
+ COLLAR_ANNOTATION_PACKAGE,
+ COLLAR_ANNOTATION_PROPERTY_NAME
)
- addProperty(
- PropertySpec.builder(
- PROPERTY_PARAMETER_NAME,
- GeneratorUtils.getClassName(userProperty)
- )
- .initializer(PROPERTY_PARAMETER_NAME)
- .build()
- )
-
- addKdoc(userProperty.description)
- superclass(ClassName("", CLASS_NAME))
- addAnnotation(propertyNameAnnotation)
- }
- if (userProperty.values?.isNotEmpty() == true) {
- val enumBuilder = TypeSpec.enumBuilder(GeneratorUtils.getParameterEnumName(userProperty.name))
- .primaryConstructor(
+ val propertyNameAnnotation = AnnotationSpec.builder(propertyNameClassName)
+ .addMember(ANNOTATION_FORMAT, userProperty.name).build()
+ val userPropertyClass = TypeSpec.classBuilder(name).apply {
+ addModifiers(KModifier.DATA)
+ primaryConstructor(
FunSpec.constructorBuilder()
- .addParameter("value", String::class)
- .build()
- )
- .addProperty(
- PropertySpec.builder("value", String::class)
- .initializer("value")
+ .addParameter(
+ PROPERTY_PARAMETER_NAME,
+ ClassName("kotlin", "String")
+ )
.build()
)
- .addFunction(
- FunSpec.builder("toString")
- .addModifiers(KModifier.OVERRIDE)
- .addStatement("return value")
+ addProperty(
+ PropertySpec.builder(
+ PROPERTY_PARAMETER_NAME,
+ ClassName("kotlin", "String")
+ )
+ .initializer(PROPERTY_PARAMETER_NAME)
.build()
)
- userProperty.values.forEach { value ->
- enumBuilder.addEnumConstant(
- GeneratorUtils.getParameterValueEnumName(value), TypeSpec.anonymousClassBuilder()
- .addSuperclassConstructorParameter("%S", value)
- .build()
+ userProperty.description?.takeIf { it.isNotBlank() }?.let { addKdoc(it) }
+ superclass(ClassName("", CLASS_NAME))
+ addAnnotation(propertyNameAnnotation)
+ }
+ if (userProperty.values?.isNotEmpty() == true) {
+ val enumBuilder = TypeSpec.enumBuilder(
+ userProperty.name.toCamelCase()
)
+ .primaryConstructor(
+ FunSpec.constructorBuilder()
+ .addParameter("value", String::class, KModifier.PRIVATE)
+ .build()
+ )
+ .addProperty(
+ PropertySpec.builder("value", String::class)
+ .initializer("value")
+ .build()
+ )
+ .addFunction(
+ FunSpec.builder("toString")
+ .addModifiers(KModifier.OVERRIDE)
+ .addStatement("return value")
+ .build()
+ )
+
+ userProperty.values.forEach { value ->
+ enumBuilder.addEnumConstant(
+ value.toEnumValue(),
+ TypeSpec.anonymousClassBuilder()
+ .addSuperclassConstructorParameter("%S", value)
+ .build()
+ )
+ }
+
+ userPropertyClass.addType(enumBuilder.build())
}
- userPropertyClass.addType(enumBuilder.build())
+ addType(userPropertyClass.build())
}
-
- addType(userPropertyClass.build())
}
- }
- .build()
+ .build()
+ }
}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/models/AnalyticsModel.kt b/generator/src/main/kotlin/co/infinum/collar/generator/models/AnalyticsModel.kt
index f8f54e38..ebd8f8ad 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/models/AnalyticsModel.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/models/AnalyticsModel.kt
@@ -1,7 +1,17 @@
package co.infinum.collar.generator.models
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
internal data class AnalyticsModel(
- val events: List,
- val screens: List,
- val userProperties: List
+
+ @SerialName("screens")
+ val screens: List? = null,
+
+ @SerialName("events")
+ val events: List? = null,
+
+ @SerialName("userProperties")
+ val properties: List? = null
)
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/models/DataType.kt b/generator/src/main/kotlin/co/infinum/collar/generator/models/DataType.kt
index a76b0a5a..0c0dc39d 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/models/DataType.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/models/DataType.kt
@@ -1,12 +1,16 @@
package co.infinum.collar.generator.models
-internal enum class DataType {
- TEXT,
- NUMBER,
- DECIMAL,
- BOOLEAN;
-
- override fun toString(): String {
- return super.toString().toLowerCase()
+import com.squareup.kotlinpoet.ClassName
+import java.util.Locale
+
+internal enum class DataType(val className: ClassName) {
+ TEXT(ClassName("kotlin", "String")),
+ NUMBER(ClassName("kotlin", "Long")),
+ DECIMAL(ClassName("kotlin", "Double")),
+ BOOLEAN(ClassName("kotlin", "Boolean"));
+
+ companion object {
+
+ operator fun invoke(name: String) = values().firstOrNull { it.toString().toLowerCase(Locale.ENGLISH) == name }
}
}
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/models/Event.kt b/generator/src/main/kotlin/co/infinum/collar/generator/models/Event.kt
index 5c6b19a4..eec1080a 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/models/Event.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/models/Event.kt
@@ -1,7 +1,17 @@
package co.infinum.collar.generator.models
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
internal data class Event(
+
+ @SerialName("description")
+ val description: String? = null,
+
+ @SerialName("name")
val name: String,
- val description: String,
- val parameters: List
+
+ @SerialName("properties")
+ val parameters: List? = null
)
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/models/Parameter.kt b/generator/src/main/kotlin/co/infinum/collar/generator/models/Parameter.kt
index 493356d1..fae43183 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/models/Parameter.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/models/Parameter.kt
@@ -1,8 +1,20 @@
package co.infinum.collar.generator.models
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
internal data class Parameter(
+
+ @SerialName("description")
+ val description: String? = null,
+
+ @SerialName("name")
val name: String,
- val description: String,
+
+ @SerialName("type")
val type: String,
- val values: List?
+
+ @SerialName("values")
+ val values: List? = null
)
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/models/Property.kt b/generator/src/main/kotlin/co/infinum/collar/generator/models/Property.kt
index 4153c84b..bf0529b2 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/models/Property.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/models/Property.kt
@@ -1,7 +1,17 @@
package co.infinum.collar.generator.models
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
internal data class Property(
+
+ @SerialName("description")
+ val description: String? = null,
+
+ @SerialName("name")
val name: String,
- val description: String,
- val values: List?
+
+ @SerialName("values")
+ val values: List? = null
)
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/models/Screen.kt b/generator/src/main/kotlin/co/infinum/collar/generator/models/Screen.kt
index 54c23794..210a9f48 100644
--- a/generator/src/main/kotlin/co/infinum/collar/generator/models/Screen.kt
+++ b/generator/src/main/kotlin/co/infinum/collar/generator/models/Screen.kt
@@ -1,6 +1,14 @@
package co.infinum.collar.generator.models
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
internal data class Screen(
- val name: String,
- val description: String
+
+ @SerialName("description")
+ val description: String? = null,
+
+ @SerialName("name")
+ val name: String
)
diff --git a/generator/src/main/kotlin/co/infinum/collar/generator/providers/MoshiProvider.kt b/generator/src/main/kotlin/co/infinum/collar/generator/providers/MoshiProvider.kt
deleted file mode 100644
index e4bff045..00000000
--- a/generator/src/main/kotlin/co/infinum/collar/generator/providers/MoshiProvider.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package co.infinum.collar.generator.providers
-
-import com.squareup.moshi.Moshi
-import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
-
-internal object MoshiProvider {
-
- fun provide(): Lazy = lazy {
- Moshi.Builder()
- .add(KotlinJsonAdapterFactory())
- .build()
- }
-}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d53029d2..20ba0f3c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
diff --git a/in_app_notification.jpg b/in_app_notification.jpg
new file mode 100644
index 00000000..76b39f02
Binary files /dev/null and b/in_app_notification.jpg differ
diff --git a/notification.jpg b/notification.jpg
index 9bfd005d..27b18d1c 100644
Binary files a/notification.jpg and b/notification.jpg differ
diff --git a/plugin/build.gradle b/plugin/build.gradle
index ff46fd1c..672c73e4 100644
--- a/plugin/build.gradle
+++ b/plugin/build.gradle
@@ -13,6 +13,7 @@ compileTestKotlin {
dependencies {
implementation packages.kotlin.core
+ implementation packages.kotlin.reflect
implementation packages.collar.generator
compileOnly gradleApi()
compileOnly packages.gradle
diff --git a/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarExtension.kt b/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarExtension.kt
index 059fd9de..1220bfe2 100644
--- a/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarExtension.kt
+++ b/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarExtension.kt
@@ -5,29 +5,19 @@ internal open class CollarExtension {
companion object {
const val NAME = "collar"
- private const val DEFAULT_VERSION = "1.1.5"
- private const val DEFAULT_FILEPATH = ""
- private const val DEFAULT_VARIANT = "main"
+ const val DEFAULT_VERSION = "1.1.6"
+ private const val DEFAULT_FILENAME = ""
private const val DEFAULT_PACKAGE_NAME = ""
}
open var version = DEFAULT_VERSION
- open var filePath = DEFAULT_FILEPATH
- open var variant = DEFAULT_VARIANT
+ open var fileName = DEFAULT_FILENAME
open var packageName = DEFAULT_PACKAGE_NAME
}
internal fun CollarExtension.validate(): List {
val errors = mutableListOf()
- if (filePath.isBlank()) {
- errors.add("filePath must be specified")
- }
-
- if (variant.isBlank()) {
- errors.add("variant must be specified")
- }
-
if (packageName.isBlank()) {
errors.add("packageName must be specified")
}
diff --git a/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarPlugin.kt b/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarPlugin.kt
index 0ab43f8b..88e85744 100644
--- a/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarPlugin.kt
+++ b/plugin/src/main/kotlin/co/infinum/collar/plugin/CollarPlugin.kt
@@ -1,6 +1,7 @@
package co.infinum.collar.plugin
import co.infinum.collar.plugin.tasks.GenerateTask
+import com.android.build.gradle.AppExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.net.URI
@@ -40,10 +41,19 @@ internal class CollarPlugin : Plugin {
private fun addTasks(project: Project) {
with(project) {
- tasks.create(GenerateTask.NAME, GenerateTask::class.java).run {
- group = GenerateTask.GROUP
- description = GenerateTask.DESCRIPTION
+ tasks.register(GenerateTask.NAME, GenerateTask::class.java) { task ->
+ task.group = GenerateTask.GROUP
+ task.description = GenerateTask.DESCRIPTION
+ task.setSource(projectDir)
+ task.include {
+ it.name == (extensions.findByName(CollarExtension.NAME) as CollarExtension).fileName
+ }
}
+ .run {
+ extensions.findByType(AppExtension::class.java)?.applicationVariants?.all { variant ->
+ variant.registerJavaGeneratingTask(get(), get().outputDirectory)
+ }
+ }
}
}
}
diff --git a/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/GenerateTask.kt b/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/GenerateTask.kt
index 18a4b802..99e9bb74 100644
--- a/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/GenerateTask.kt
+++ b/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/GenerateTask.kt
@@ -2,23 +2,47 @@ package co.infinum.collar.plugin.tasks
import co.infinum.collar.generator.CollarGenerator
import co.infinum.collar.plugin.CollarExtension
-import co.infinum.collar.plugin.tasks.shared.BaseTask
+import co.infinum.collar.plugin.tasks.shared.BaseSourceTask
import co.infinum.collar.plugin.validate
+import com.android.builder.model.AndroidProject.FD_GENERATED
+import org.gradle.api.file.FileTree
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction
+import java.io.File
-internal open class GenerateTask : BaseTask() {
+@CacheableTask
+internal open class GenerateTask : BaseSourceTask() {
companion object {
const val GROUP = "collar"
const val NAME = "generate"
const val DESCRIPTION = "Generates Kotlin files for screen names, events and user properties."
-
- private const val TEMPLATE_OUTPUT_PATH = "%s/src/%s/kotlin"
- private const val TEMPLATE_FILE_PATH = "%s/%s"
}
+ @Suppress("unused") // Required to invalidate the task on version updates.
+ @Input
+ val pluginVersion = CollarExtension.DEFAULT_VERSION
+
+ @get:OutputDirectory
+ var outputDirectory: File = File(
+ project.buildDir,
+ "$FD_GENERATED${File.separatorChar}${CollarExtension.NAME}${File.separatorChar}trackingPlan"
+ )
+
private val collarGenerator = CollarGenerator()
+ @InputFiles
+ @SkipWhenEmpty
+ @PathSensitive(PathSensitivity.ABSOLUTE)
+ override fun getSource(): FileTree =
+ super.getSource()
+
@TaskAction
fun doOnRun() {
(project.extensions.findByName(CollarExtension.NAME) as CollarExtension).run {
@@ -26,25 +50,23 @@ internal open class GenerateTask : BaseTask() {
when {
errors.isNotEmpty() -> showError(errors.joinToString(System.lineSeparator()))
else -> {
- val outputPath = String.format(TEMPLATE_OUTPUT_PATH, project.projectDir, variant)
- val filePath = String.format(TEMPLATE_FILE_PATH, project.projectDir, filePath)
- generateTrackingPlan(filePath, outputPath, packageName)
+ generateTrackingPlan(packageName)
}
}
}
}
@Suppress("TooGenericExceptionCaught")
- private fun generateTrackingPlan(filePath: String, outputPath: String, packageName: String) {
+ private fun generateTrackingPlan(packageName: String) {
try {
- println("Tracking plan file path: $filePath")
- if (collarGenerator.generate(filePath, outputPath, packageName)) {
- println("Tracking plan classes generated on path: $outputPath")
+ println("Tracking plan file path: ${source.first().absolutePath}")
+ if (collarGenerator.generate(source.first().absolutePath, outputDirectory.absolutePath, packageName)) {
+ println("Tracking plan classes generated on path: ${outputDirectory.absolutePath}")
} else {
showError("Task generate failed")
}
- } catch (e: Exception) {
- showError(e.stackTrace.toString())
+ } catch (throwable: Throwable) {
+ throwable.printStackTrace()
}
}
}
diff --git a/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/shared/BaseTask.kt b/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/shared/BaseSourceTask.kt
similarity index 69%
rename from plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/shared/BaseTask.kt
rename to plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/shared/BaseSourceTask.kt
index e8a0ccc7..bc23aa9c 100644
--- a/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/shared/BaseTask.kt
+++ b/plugin/src/main/kotlin/co/infinum/collar/plugin/tasks/shared/BaseSourceTask.kt
@@ -1,9 +1,9 @@
package co.infinum.collar.plugin.tasks.shared
-import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskExecutionException
-internal abstract class BaseTask : DefaultTask() {
+internal abstract class BaseSourceTask : SourceTask() {
internal fun showError(message: String): Nothing = throw TaskExecutionException(this, Exception(message))
}
diff --git a/processor/build.gradle b/processor/build.gradle
index 265acb0e..3c919b6e 100644
--- a/processor/build.gradle
+++ b/processor/build.gradle
@@ -7,6 +7,7 @@ dependencies {
implementation packages.kotlin.core
implementation packages.kotlin.poet_metadata
implementation packages.kotlin.poet
+ implementation packages.kotlin.reflect
compileOnly packages.incap.core
kapt packages.incap.processor
diff --git a/processor/src/main/kotlin/co/infinum/collar/processor/shared/Constants.kt b/processor/src/main/kotlin/co/infinum/collar/processor/shared/Constants.kt
index b82f7c4d..04be31e3 100644
--- a/processor/src/main/kotlin/co/infinum/collar/processor/shared/Constants.kt
+++ b/processor/src/main/kotlin/co/infinum/collar/processor/shared/Constants.kt
@@ -4,11 +4,11 @@ import com.squareup.kotlinpoet.ClassName
internal object Constants {
- val CLASS_COMPONENT_ACTIVITY = ClassName("androidx.core.app", "ComponentActivity")
- val CLASS_ACTIVITY = ClassName("android.app", "Activity")
val CLASS_FRAGMENT = ClassName("android.app", "Fragment")
val CLASS_SUPPORT_FRAGMENT = ClassName("android.support.v4.app", "Fragment")
- val CLASS_ANDROIDX_FRAGMENT = ClassName("androidx.fragment.app", "Fragment")
+ private val CLASS_COMPONENT_ACTIVITY = ClassName("androidx.core.app", "ComponentActivity")
+ private val CLASS_ACTIVITY = ClassName("android.app", "Activity")
+ private val CLASS_ANDROIDX_FRAGMENT = ClassName("androidx.fragment.app", "Fragment")
val SUPPORTED_SCREEN_NAME_CLASSES = listOf(
CLASS_COMPONENT_ACTIVITY,
diff --git a/processor/src/main/kotlin/co/infinum/collar/processor/specs/ScreenNameSpec.kt b/processor/src/main/kotlin/co/infinum/collar/processor/specs/ScreenNameSpec.kt
index 0baa1b1d..8d68c750 100644
--- a/processor/src/main/kotlin/co/infinum/collar/processor/specs/ScreenNameSpec.kt
+++ b/processor/src/main/kotlin/co/infinum/collar/processor/specs/ScreenNameSpec.kt
@@ -2,13 +2,9 @@ package co.infinum.collar.processor.specs
import co.infinum.collar.processor.extensions.applyIf
import co.infinum.collar.processor.models.ScreenHolder
-import co.infinum.collar.processor.shared.Constants.CLASS_ACTIVITY
-import co.infinum.collar.processor.shared.Constants.CLASS_ANDROIDX_FRAGMENT
-import co.infinum.collar.processor.shared.Constants.CLASS_COMPONENT_ACTIVITY
import co.infinum.collar.processor.shared.Constants.CLASS_FRAGMENT
import co.infinum.collar.processor.shared.Constants.CLASS_SUPPORT_FRAGMENT
import com.squareup.kotlinpoet.AnnotationSpec
-import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
@@ -23,8 +19,7 @@ internal class ScreenNameSpec private constructor(
private const val SIMPLE_NAME = "ScreenNames"
private const val FUNCTION_NAME = "trackScreen"
private const val PARAMETER_NAME = "this"
- private const val STATEMENT_ACTIVITY = "is %T -> %T.%L(%L, %S)"
- private const val STATEMENT_FRAGMENT = "is %T -> activity?.let { %T.%L(it, %S) }"
+ private const val STATEMENT = "is %T -> %T.%L(%S)"
}
internal open class Builder(
@@ -61,22 +56,17 @@ internal class ScreenNameSpec private constructor(
.receiver(keyClass)
.applyIf(holders.isNotEmpty()) {
beginControlFlow(CONTROL_FLOW_WHEN, parameterName())
- addCode(screens(keyClass, holders))
+ addCode(screens(holders))
endControlFlow()
}
.build()
}
- private fun screens(keyClass: ClassName, holders: List): CodeBlock =
+ private fun screens(holders: List): CodeBlock =
CodeBlock.builder()
.apply {
holders.forEach {
- when (keyClass) {
- CLASS_COMPONENT_ACTIVITY -> addActivityStatement(this, it)
- CLASS_ACTIVITY -> addActivityStatement(this, it)
- CLASS_ANDROIDX_FRAGMENT -> addFragmentStatement(this, it)
- else -> addFragmentStatement(this, it)
- }
+ addStatement(this, it)
}
}
.build()
@@ -93,19 +83,9 @@ internal class ScreenNameSpec private constructor(
.addMember(CodeBlock.of("%S", "DEPRECATION"))
.build()
- private fun addActivityStatement(builder: CodeBlock.Builder, holder: ScreenHolder) =
- builder.addStatement(
- STATEMENT_ACTIVITY,
- holder.className,
- CLASS_COLLAR,
- functionName(),
- parameterName(),
- holder.screenName
- )
-
- private fun addFragmentStatement(builder: CodeBlock.Builder, holder: ScreenHolder) =
+ private fun addStatement(builder: CodeBlock.Builder, holder: ScreenHolder) =
builder.addStatement(
- STATEMENT_FRAGMENT,
+ STATEMENT,
holder.className,
CLASS_COLLAR,
functionName(),
diff --git a/redacted_notification.jpg b/redacted_notification.jpg
new file mode 100644
index 00000000..a73c7d1e
Binary files /dev/null and b/redacted_notification.jpg differ
diff --git a/redacted_ui.jpg b/redacted_ui.jpg
new file mode 100644
index 00000000..a8023807
Binary files /dev/null and b/redacted_ui.jpg differ
diff --git a/sample/build.gradle b/sample/build.gradle
index d7af1b9a..a3db41bf 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -23,8 +23,8 @@ android {
applicationId "co.infinum.collar.sample"
minSdkVersion buildConfig.minSdk
targetSdkVersion buildConfig.targetSdk
- versionCode 10105
- versionName "1.1.5"
+ versionCode 10106
+ versionName "1.1.6"
buildTypes {
debug {
@@ -55,7 +55,7 @@ android {
test.java.srcDirs += 'src/test/kotlin'
}
- viewBinding.enabled = true
+ buildFeatures.viewBinding = true
}
dependencies {
@@ -67,7 +67,7 @@ dependencies {
}
collar {
- version "1.1.5"
- filePath = "example.json"
- packageName = "co.infinum.collar.sample.analytics.generated"
-}
\ No newline at end of file
+ version "1.1.6"
+ fileName = "example.json"
+ packageName = "co.infinum.collar.sample.analytics.trackingplan"
+}
diff --git a/sample/example.json b/sample/example.json
index 29964379..fbe9162b 100644
--- a/sample/example.json
+++ b/sample/example.json
@@ -1,83 +1,181 @@
{
"events": [
{
- "name": "loginUser",
- "description": "User clicked on login button",
- "parameters": [
+ "name": "user_log_in",
+ "description": "User logged into the app",
+ "properties": [
{
- "name": "languageType",
- "description": "Type of login clicked",
+ "name": "type",
+ "description": "Type of log in",
"type": "text",
"values": [
- "kotlin",
- "java"
+ "facebook",
+ "google",
+ "in app"
+ ]
+ },
+ {
+ "name": "source",
+ "description": "Source of the log in",
+ "type": "text",
+ "values": [
+ "onboarding",
+ "settings"
]
}
]
},
{
- "name": "checkTypes",
- "description": "We need to check types here",
- "parameters": [
+ "name": "analytics_consent",
+ "description": "User opts in or opts out for analytics",
+ "properties": [
{
- "name": "checkBool",
- "description": "checki if bool is ok",
- "type": "boolean"
+ "name": "type",
+ "description": "Information if a user gave consent",
+ "type": "text",
+ "values": [
+ "opt in",
+ "opt out"
+ ]
},
{
- "name": "checkText",
- "description": "check if text is ok",
- "type": "text"
+ "name": "source",
+ "description": "Source where a user gave or declined analytics consent",
+ "type": "text",
+ "values": [
+ "onboarding",
+ "settings"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "philips_network_join",
+ "description": "User successfully completed connection or failed to connect to the Philips network",
+ "properties": [
+ {
+ "name": "status",
+ "description": "Status of joining to the Philips network",
+ "type": "text",
+ "values": [
+ "success",
+ "fail"
+ ]
},
{
- "name": "checkNumber",
- "description": "check if number is ok",
- "type": "number"
+ "name": "source",
+ "description": "Part of the app from which a user joined Philips network",
+ "type": "text",
+ "values": [
+ "onboarding",
+ "home"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "machine_phone_pair",
+ "description": "User successfully completed pairing or failed to pair phone with the coffee machine",
+ "properties": [
+ {
+ "name": "status",
+ "description": "Status of phone pairing to the Coffee machine",
+ "type": "text",
+ "values": [
+ "success",
+ "fail"
+ ]
},
{
- "name": "checkDecimal",
- "description": "check if decimal is ok",
- "type": "decimal"
+ "name": "source",
+ "description": "Source of the pairing",
+ "type": "text",
+ "values": [
+ "onboarding",
+ "home"
+ ]
}
]
- }
- ],
- "userProperties": [
+ },
{
- "name": "price",
- "description": "user's price"
+ "name": "machine_wifi_connect",
+ "description": "User successfully completed connection or failed connect Coffee machine to the local WiFi network",
+ "properties": [
+ {
+ "name": "status",
+ "description": "Status of the Coffee machine connecting to the WiFi network",
+ "type": "text",
+ "values": [
+ "success",
+ "fail"
+ ]
+ },
+ {
+ "name": "source",
+ "description": "Source of the machine connecting to the WiFi network",
+ "type": "text",
+ "values": [
+ "onboarding",
+ "home"
+ ]
+ }
+ ]
},
{
- "name": "genderGo",
- "values": [
- "male",
- "female",
- "unknown"
- ],
- "description": "..."
+ "name": "machine_turn_on",
+ "description": "User turned on the Coffee machine from the app"
},
{
- "name": "userType",
- "values": [
- "corporate",
- "retail",
- "unknown"
- ],
- "description": "..."
+ "name": "machine_turn_off",
+ "description": "User turned off the Coffee machine from the app"
+ },
+ {
+ "name": "machine_remove",
+ "description": "User removed Coffee machine from the app"
},
{
- "name": "userId",
- "description": "..."
+ "name": "alexa_connect",
+ "description": "User connects or fails to connect Alexa",
+ "properties": [
+ {
+ "name": "status",
+ "description": "Status of connecting Alexa",
+ "type": "text",
+ "values": [
+ "success",
+ "fail"
+ ]
+ },
+ {
+ "name": "source",
+ "description": "Source of connecting Alexa",
+ "type": "text",
+ "values": [
+ "onboarding",
+ "settings"
+ ]
+ }
+ ]
}
],
- "screens": [
+ "userProperties": [
{
- "name": "Main Screen",
- "description": "Main screen example"
+ "name": "machine_model",
+ "description": "Model of the Coffee machine (e.g. Philips 3200 LatteGo)"
},
{
- "name": "Child Screen",
- "description": "child screen example"
+ "name": "analytics_consent",
+ "values": [
+ "opt in",
+ "opt out"
+ ],
+ "description": "User segmentation by the analytics consent (opt in/opt out). Should be set and updated in two locations, onboarding and settings"
+ }
+ ],
+ "screens": [
+ {
+ "name": "home",
+ "description": "Home screen viewed"
}
]
}
\ No newline at end of file
diff --git a/sample/src/main/java/co/infinum/collar/sample/SampleApplication.java b/sample/src/main/java/co/infinum/collar/sample/SampleApplication.java
index 802bbc64..85afa45a 100644
--- a/sample/src/main/java/co/infinum/collar/sample/SampleApplication.java
+++ b/sample/src/main/java/co/infinum/collar/sample/SampleApplication.java
@@ -5,10 +5,14 @@
import org.jetbrains.annotations.NotNull;
+import java.util.Set;
+
+import androidx.collection.ArraySet;
import co.infinum.collar.Collar;
import co.infinum.collar.Event;
import co.infinum.collar.Property;
import co.infinum.collar.Screen;
+import co.infinum.collar.ui.Configuration;
import co.infinum.collar.ui.LiveCollector;
public class SampleApplication extends Application {
@@ -21,7 +25,9 @@ public void onCreate() {
}
private void attachCollar() {
- Collar.attach(new LiveCollector(true, true) {
+ final Set redactedWords = new ArraySet<>(1);
+ redactedWords.add("Java");
+ Collar.attach(new LiveCollector(new Configuration(true, true, redactedWords)) {
@Override
public void onScreen(@NotNull Screen screen) {
diff --git a/sample/src/main/kotlin/co/infinum/collar/sample/KotlinMainActivity.kt b/sample/src/main/kotlin/co/infinum/collar/sample/KotlinMainActivity.kt
index 13eaf771..dadb120a 100644
--- a/sample/src/main/kotlin/co/infinum/collar/sample/KotlinMainActivity.kt
+++ b/sample/src/main/kotlin/co/infinum/collar/sample/KotlinMainActivity.kt
@@ -4,10 +4,11 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import co.infinum.collar.annotations.ScreenName
+import co.infinum.collar.sample.analytics.trackingplan.TrackingPlanScreens
import co.infinum.collar.sample.databinding.ActivityMainKotlinBinding
import co.infinum.collar.trackScreen
-@ScreenName(value = KotlinScreenNames.MAIN_SCREEN, enabled = true)
+@ScreenName(value = TrackingPlanScreens.HOME, enabled = true)
class KotlinMainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/ui-no-op/build.gradle b/ui-no-op/build.gradle
index 6ee72066..22b36dfc 100644
--- a/ui-no-op/build.gradle
+++ b/ui-no-op/build.gradle
@@ -28,7 +28,7 @@ android {
test.java.srcDirs += 'src/test/kotlin'
}
- viewBinding.enabled = true
+ buildFeatures.viewBinding = true
}
dependencies {
diff --git a/ui-no-op/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt b/ui-no-op/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt
index b2e102ba..2a8db921 100644
--- a/ui-no-op/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt
+++ b/ui-no-op/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt
@@ -2,11 +2,20 @@ package co.infinum.collar.ui
import android.content.Intent
+/**
+ * No operation singleton object for UI entry point with convenience methods
+ */
object CollarUi {
+ /**
+ * No operation stub that creates an empty Intent.
+ */
@JvmStatic
fun launchIntent() = Intent()
+ /**
+ * No operation stub that does nothing.
+ */
@JvmStatic
fun show() = Unit
}
diff --git a/ui-no-op/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt b/ui-no-op/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt
index ea7b4f0d..75b903a4 100644
--- a/ui-no-op/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt
+++ b/ui-no-op/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt
@@ -6,18 +6,40 @@ import co.infinum.collar.Event
import co.infinum.collar.Property
import co.infinum.collar.Screen
+/**
+ * No operation stub that does nothing.
+ * Parameters are unused.
+ *
+ * @param showSystemNotifications is false by default.
+ * @param showInAppNotifications is false by default.
+ */
@Suppress("UNUSED_PARAMETER")
open class LiveCollector(
showSystemNotifications: Boolean = false,
showInAppNotifications: Boolean = false
) : Collector {
+ /**
+ * No operation stub that does nothing.
+ *
+ * @param screen is unused.
+ */
@CallSuper
override fun onScreen(screen: Screen) = Unit
+ /**
+ * No operation stub that does nothing.
+ *
+ * @param event is unused.
+ */
@CallSuper
override fun onEvent(event: Event) = Unit
+ /**
+ * No operation stub that does nothing.
+ *
+ * @param property is unused.
+ */
@CallSuper
override fun onProperty(property: Property) = Unit
}
diff --git a/ui.jpg b/ui.jpg
index e1f33831..9f17d7b1 100644
Binary files a/ui.jpg and b/ui.jpg differ
diff --git a/ui/build.gradle b/ui/build.gradle
index 1f4e6db7..018620f5 100644
--- a/ui/build.gradle
+++ b/ui/build.gradle
@@ -46,7 +46,7 @@ android {
it.java.srcDirs += "src/$it.name/kotlin"
}
- viewBinding.enabled = true
+ buildFeatures.viewBinding = true
}
dependencies {
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt b/ui/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt
index 7ee1629a..5f8849b8 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/CollarUi.kt
@@ -2,11 +2,22 @@ package co.infinum.collar.ui
import co.infinum.collar.ui.presentation.Presentation
+/**
+ * Singleton object for UI entry point with convenience methods
+ */
object CollarUi {
+ /**
+ * Creates and prepares an Intent for launching UI.
+ * This intent starts CollarActivity and applies Intent.FLAG_ACTIVITY_NEW_TASK flag.
+ */
@JvmStatic
fun launchIntent() = Presentation.launchIntent()
+ /**
+ * Directly shows CollarActivity by previously prepared Intent.
+ * This intent applies Intent.FLAG_ACTIVITY_NEW_TASK flag.
+ */
@JvmStatic
fun show() = Presentation.show()
}
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/Configuration.kt b/ui/src/main/kotlin/co/infinum/collar/ui/Configuration.kt
new file mode 100644
index 00000000..0668a06b
--- /dev/null
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/Configuration.kt
@@ -0,0 +1,10 @@
+package co.infinum.collar.ui
+
+data class Configuration(
+
+ val showSystemNotifications: Boolean = true,
+
+ val showInAppNotifications: Boolean = true,
+
+ val redactedKeywords: Set = setOf()
+)
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt b/ui/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt
index 8a65d28e..732f3fe0 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/LiveCollector.kt
@@ -10,22 +10,29 @@ import co.infinum.collar.ui.data.models.local.EntityType
import co.infinum.collar.ui.data.models.local.SettingsEntity
import co.infinum.collar.ui.domain.repositories.EntityRepository
import co.infinum.collar.ui.domain.repositories.SettingsRepository
+import co.infinum.collar.ui.extensions.redact
import co.infinum.collar.ui.presentation.BundleMapper
import co.infinum.collar.ui.presentation.Presentation
import co.infinum.collar.ui.presentation.notifications.inapp.InAppNotificationProvider
import co.infinum.collar.ui.presentation.notifications.system.SystemNotificationProvider
-open class LiveCollector(
- showSystemNotifications: Boolean = true,
- showInAppNotifications: Boolean = true
+/**
+ * Implementation of Collector interface providing UI for collected screen name, analytics event or user property.
+ *
+ * @param showSystemNotifications is true by default.
+ * @param showInAppNotifications is true by default.
+ * @constructor Default values are provided.
+ */
+open class LiveCollector constructor(
+ private val configuration: Configuration = Configuration()
) : Collector {
private val systemNotificationProvider: SystemNotificationProvider = Presentation.systemNotification()
private val inAppNotificationProvider: InAppNotificationProvider = Presentation.inAppNotification()
private var settings = SettingsEntity(
- showSystemNotifications = showSystemNotifications,
- showInAppNotifications = showInAppNotifications
+ showSystemNotifications = configuration.showSystemNotifications,
+ showInAppNotifications = configuration.showInAppNotifications
)
init {
@@ -40,11 +47,16 @@ open class LiveCollector(
}
}
+ /**
+ * Invoked when a new screen is emitted.
+ *
+ * @param screen wrapper class.
+ */
@CallSuper
override fun onScreen(screen: Screen) {
val entity = CollarEntity(
type = EntityType.SCREEN,
- name = screen.name
+ name = screen.name.redact(configuration.redactedKeywords)
)
EntityRepository.saveScreen(entity)
if (settings.showSystemNotifications) {
@@ -55,12 +67,17 @@ open class LiveCollector(
}
}
+ /**
+ * Invoked when a new analytics event is emitted.
+ *
+ * @param event wrapper class.
+ */
@CallSuper
override fun onEvent(event: Event) {
val entity = CollarEntity(
type = EntityType.EVENT,
- name = event.name,
- parameters = event.params?.let { BundleMapper.toMap(it) }
+ name = event.name.redact(configuration.redactedKeywords),
+ parameters = event.params?.let { BundleMapper.toMap(it, configuration.redactedKeywords) }
)
EntityRepository.saveEvent(entity)
if (settings.showSystemNotifications) {
@@ -71,12 +88,17 @@ open class LiveCollector(
}
}
+ /**
+ * Invoked when a new user property is emitted.
+ *
+ * @param property wrapper class.
+ */
@CallSuper
override fun onProperty(property: Property) {
val entity = CollarEntity(
type = EntityType.PROPERTY,
- name = property.name,
- value = property.value
+ name = property.name.redact(configuration.redactedKeywords),
+ value = property.value?.redact(configuration.redactedKeywords)
)
EntityRepository.saveProperty(entity)
if (settings.showSystemNotifications) {
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/domain/Domain.kt b/ui/src/main/kotlin/co/infinum/collar/ui/domain/Domain.kt
index 908902ff..28852959 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/domain/Domain.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/domain/Domain.kt
@@ -3,7 +3,7 @@ package co.infinum.collar.ui.domain
import android.content.Context
import co.infinum.collar.ui.data.Data
-object Domain {
+internal object Domain {
fun initialise(context: Context) = Data.initialise(context)
}
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/extensions/String.kt b/ui/src/main/kotlin/co/infinum/collar/ui/extensions/String.kt
new file mode 100644
index 00000000..19a5c35e
--- /dev/null
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/extensions/String.kt
@@ -0,0 +1,6 @@
+package co.infinum.collar.ui.extensions
+
+fun String.redact(keywords: Set): String =
+ this.replace(
+ Regex(keywords.joinToString("|") { it })
+ ) { result -> "".padEnd(result.value.length, '•') }
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/BundleMapper.kt b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/BundleMapper.kt
index cd9568cb..077a18b1 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/BundleMapper.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/BundleMapper.kt
@@ -3,11 +3,12 @@ package co.infinum.collar.ui.presentation
import android.os.Bundle
import android.util.Log
import co.infinum.collar.ui.CollarUi
+import co.infinum.collar.ui.extensions.redact
@Suppress("ComplexMethod")
internal object BundleMapper {
- fun toMap(bundle: Bundle): String {
+ fun toMap(bundle: Bundle, redactedKeywords: Set): String {
val map = mutableMapOf()
val keys: Set = bundle.keySet()
@@ -29,7 +30,7 @@ internal object BundleMapper {
is Short -> bundle.getShort(key).toString()
is CharSequence -> bundle.getCharSequence(key).toString()
is Bundle -> toMap(bundle.getBundle(key)
- ?: Bundle.EMPTY)
+ ?: Bundle.EMPTY, redactedKeywords)
else -> {
Log.w(
CollarUi.javaClass.simpleName,
@@ -38,7 +39,7 @@ internal object BundleMapper {
""
}
}
- }.orEmpty()
+ }.orEmpty().redact(redactedKeywords)
}
return map.toList().joinToString("\n") { "${it.first} = ${it.second}" }
}
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/CollarActivity.kt b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/CollarActivity.kt
index 2f054659..a85d0bb7 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/CollarActivity.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/CollarActivity.kt
@@ -12,7 +12,6 @@ import androidx.core.app.ShareCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import co.infinum.collar.ui.R
import co.infinum.collar.ui.data.models.local.CollarEntity
@@ -167,9 +166,9 @@ internal class CollarActivity : AppCompatActivity() {
detailDialog = MaterialAlertDialogBuilder(this)
.setIcon(
when (entity.type) {
- EntityType.SCREEN -> R.drawable.collar_ic_screen_detail
- EntityType.EVENT -> R.drawable.collar_ic_event_detail
- EntityType.PROPERTY -> R.drawable.collar_ic_property_detail
+ EntityType.SCREEN -> R.drawable.collar_ic_screen
+ EntityType.EVENT -> R.drawable.collar_ic_event
+ EntityType.PROPERTY -> R.drawable.collar_ic_property
else -> 0
}
)
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/InAppNotificationProvider.kt b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/InAppNotificationProvider.kt
index 0cd6a7fe..d3e63c68 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/InAppNotificationProvider.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/InAppNotificationProvider.kt
@@ -4,6 +4,7 @@ import android.app.Activity
import android.app.Application
import android.content.Context
import android.view.View
+import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.app.ShareCompat
import co.infinum.collar.ui.R
@@ -33,8 +34,8 @@ internal class InAppNotificationProvider(
override fun showScreen(entity: CollarEntity) {
buildNotification(
callbacks.currentActivity,
- R.drawable.collar_shape_background_screen,
- R.drawable.collar_ic_screen,
+ R.color.collar_color_screen,
+ R.drawable.collar_ic_screen_white,
entity
)
}
@@ -42,8 +43,8 @@ internal class InAppNotificationProvider(
override fun showEvent(entity: CollarEntity) {
buildNotification(
callbacks.currentActivity,
- R.drawable.collar_shape_background_event,
- R.drawable.collar_ic_event,
+ R.color.collar_color_event,
+ R.drawable.collar_ic_event_white,
entity
)
}
@@ -51,21 +52,21 @@ internal class InAppNotificationProvider(
override fun showProperty(entity: CollarEntity) {
buildNotification(
callbacks.currentActivity,
- R.drawable.collar_shape_background_property,
- R.drawable.collar_ic_property,
+ R.color.collar_color_property,
+ R.drawable.collar_ic_property_white,
entity
)
}
private fun buildNotification(
activity: Activity?,
- @DrawableRes background: Int,
+ @ColorRes backgroundTint: Int,
@DrawableRes icon: Int,
entity: CollarEntity
) {
CollarSnackbar.make(
activity?.findViewById(android.R.id.content),
- background,
+ backgroundTint,
icon,
entity.name,
entity.parameters,
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbar.kt b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbar.kt
index da654501..c1ad9359 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbar.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbar.kt
@@ -3,6 +3,7 @@ package co.infinum.collar.ui.presentation.notifications.inapp.snackbar
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
@@ -25,7 +26,7 @@ internal class CollarSnackbar(
@Suppress("LongParameterList")
fun make(
parentLayout: FrameLayout?,
- @DrawableRes background: Int,
+ @ColorRes backgroundTint: Int,
@DrawableRes icon: Int,
title: String?,
message: String?,
@@ -35,7 +36,7 @@ internal class CollarSnackbar(
parentLayout?.let {
CollarSnackbarView(it.context)
.apply {
- setIconBackgroundResource(background)
+ setIconBackgroundTint(backgroundTint)
setIconResource(icon)
setTitle(title)
setValue(message)
diff --git a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbarView.kt b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbarView.kt
index 16c7a671..8ca40e67 100644
--- a/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbarView.kt
+++ b/ui/src/main/kotlin/co/infinum/collar/ui/presentation/notifications/inapp/snackbar/CollarSnackbarView.kt
@@ -1,10 +1,13 @@
package co.infinum.collar.ui.presentation.notifications.inapp.snackbar
import android.content.Context
+import android.content.res.ColorStateList
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
+import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
import co.infinum.collar.ui.CollarUi
import co.infinum.collar.ui.databinding.CollarViewSnackbarBinding
import com.google.android.material.snackbar.ContentViewCallback
@@ -45,8 +48,8 @@ internal class CollarSnackbarView @JvmOverloads constructor(
}
}
- fun setIconBackgroundResource(@DrawableRes drawableResId: Int) {
- viewBinding.iconView.setBackgroundResource(drawableResId)
+ fun setIconBackgroundTint(@ColorRes colorResId: Int) {
+ viewBinding.iconView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(context, colorResId))
}
fun setIconResource(@DrawableRes drawableResId: Int) {
diff --git a/ui/src/main/res/drawable-night/collar_ic_clear.xml b/ui/src/main/res/drawable-night/collar_ic_clear.xml
deleted file mode 100644
index a6187c8f..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_clear.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/ui/src/main/res/drawable-night/collar_ic_event_menu.xml b/ui/src/main/res/drawable-night/collar_ic_event_menu.xml
deleted file mode 100644
index fda59c6b..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_event_menu.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_filter.xml b/ui/src/main/res/drawable-night/collar_ic_filter.xml
deleted file mode 100644
index e4fc8b68..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_filter.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_logo.xml b/ui/src/main/res/drawable-night/collar_ic_logo.xml
deleted file mode 100644
index a4871dd3..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_logo.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_notifications_inapp.xml b/ui/src/main/res/drawable-night/collar_ic_notifications_inapp.xml
deleted file mode 100644
index 8e67d434..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_notifications_inapp.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_notifications_system.xml b/ui/src/main/res/drawable-night/collar_ic_notifications_system.xml
deleted file mode 100644
index 02f99be9..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_notifications_system.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/ui/src/main/res/drawable-night/collar_ic_property_detail.xml b/ui/src/main/res/drawable-night/collar_ic_property_detail.xml
deleted file mode 100644
index 0d1e755c..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_property_detail.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_screen_detail.xml b/ui/src/main/res/drawable-night/collar_ic_screen_detail.xml
deleted file mode 100644
index 3b85cd49..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_screen_detail.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_search.xml b/ui/src/main/res/drawable-night/collar_ic_search.xml
deleted file mode 100644
index eb90d030..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_search.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_settings.xml b/ui/src/main/res/drawable-night/collar_ic_settings.xml
deleted file mode 100644
index 9e65421f..00000000
--- a/ui/src/main/res/drawable-night/collar_ic_settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable/collar_decoration_dot.xml b/ui/src/main/res/drawable/collar_decoration_dot.xml
index a944c14a..06403dfb 100644
--- a/ui/src/main/res/drawable/collar_decoration_dot.xml
+++ b/ui/src/main/res/drawable/collar_decoration_dot.xml
@@ -2,10 +2,9 @@
-
+
-
+
\ No newline at end of file
diff --git a/ui/src/main/res/drawable/collar_ic_event.xml b/ui/src/main/res/drawable/collar_ic_event.xml
index c1940817..97550394 100644
--- a/ui/src/main/res/drawable/collar_ic_event.xml
+++ b/ui/src/main/res/drawable/collar_ic_event.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_event_detail.xml b/ui/src/main/res/drawable/collar_ic_event_detail.xml
deleted file mode 100644
index 1589aa3b..00000000
--- a/ui/src/main/res/drawable/collar_ic_event_detail.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable/collar_ic_event_menu.xml b/ui/src/main/res/drawable/collar_ic_event_menu.xml
deleted file mode 100644
index 1589aa3b..00000000
--- a/ui/src/main/res/drawable/collar_ic_event_menu.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_event_detail.xml b/ui/src/main/res/drawable/collar_ic_event_white.xml
similarity index 92%
rename from ui/src/main/res/drawable-night/collar_ic_event_detail.xml
rename to ui/src/main/res/drawable/collar_ic_event_white.xml
index fda59c6b..e70924ef 100644
--- a/ui/src/main/res/drawable-night/collar_ic_event_detail.xml
+++ b/ui/src/main/res/drawable/collar_ic_event_white.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_filter.xml b/ui/src/main/res/drawable/collar_ic_filter.xml
index 129f4a87..c70623cd 100644
--- a/ui/src/main/res/drawable/collar_ic_filter.xml
+++ b/ui/src/main/res/drawable/collar_ic_filter.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_logo.xml b/ui/src/main/res/drawable/collar_ic_logo.xml
index 9eac82c0..193416e2 100644
--- a/ui/src/main/res/drawable/collar_ic_logo.xml
+++ b/ui/src/main/res/drawable/collar_ic_logo.xml
@@ -4,7 +4,7 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_notification.xml b/ui/src/main/res/drawable/collar_ic_notification.xml
index f52f4def..4792268d 100644
--- a/ui/src/main/res/drawable/collar_ic_notification.xml
+++ b/ui/src/main/res/drawable/collar_ic_notification.xml
@@ -5,8 +5,5 @@
android:viewportHeight="24">
+ android:fillColor="@android:color/white"/>
diff --git a/ui/src/main/res/drawable/collar_ic_notifications_inapp.xml b/ui/src/main/res/drawable/collar_ic_notifications_inapp.xml
index 08ddaa40..61e63bc4 100644
--- a/ui/src/main/res/drawable/collar_ic_notifications_inapp.xml
+++ b/ui/src/main/res/drawable/collar_ic_notifications_inapp.xml
@@ -3,16 +3,10 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
-
-
+
+
diff --git a/ui/src/main/res/drawable/collar_ic_notifications_system.xml b/ui/src/main/res/drawable/collar_ic_notifications_system.xml
index 76d029b6..905eac6b 100644
--- a/ui/src/main/res/drawable/collar_ic_notifications_system.xml
+++ b/ui/src/main/res/drawable/collar_ic_notifications_system.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
\ No newline at end of file
diff --git a/ui/src/main/res/drawable/collar_ic_property.xml b/ui/src/main/res/drawable/collar_ic_property.xml
index 654160fb..8e2d92a4 100644
--- a/ui/src/main/res/drawable/collar_ic_property.xml
+++ b/ui/src/main/res/drawable/collar_ic_property.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_property_detail.xml b/ui/src/main/res/drawable/collar_ic_property_detail.xml
deleted file mode 100644
index 07185e65..00000000
--- a/ui/src/main/res/drawable/collar_ic_property_detail.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable/collar_ic_property_menu.xml b/ui/src/main/res/drawable/collar_ic_property_menu.xml
deleted file mode 100644
index 07185e65..00000000
--- a/ui/src/main/res/drawable/collar_ic_property_menu.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_property_menu.xml b/ui/src/main/res/drawable/collar_ic_property_white.xml
similarity index 91%
rename from ui/src/main/res/drawable-night/collar_ic_property_menu.xml
rename to ui/src/main/res/drawable/collar_ic_property_white.xml
index 0d1e755c..d58e56fc 100644
--- a/ui/src/main/res/drawable-night/collar_ic_property_menu.xml
+++ b/ui/src/main/res/drawable/collar_ic_property_white.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_screen.xml b/ui/src/main/res/drawable/collar_ic_screen.xml
index 125538b1..c3895b0a 100644
--- a/ui/src/main/res/drawable/collar_ic_screen.xml
+++ b/ui/src/main/res/drawable/collar_ic_screen.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_screen_detail.xml b/ui/src/main/res/drawable/collar_ic_screen_detail.xml
deleted file mode 100644
index 15e9e55b..00000000
--- a/ui/src/main/res/drawable/collar_ic_screen_detail.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable/collar_ic_screen_menu.xml b/ui/src/main/res/drawable/collar_ic_screen_menu.xml
deleted file mode 100644
index 15e9e55b..00000000
--- a/ui/src/main/res/drawable/collar_ic_screen_menu.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/ui/src/main/res/drawable-night/collar_ic_screen_menu.xml b/ui/src/main/res/drawable/collar_ic_screen_white.xml
similarity index 86%
rename from ui/src/main/res/drawable-night/collar_ic_screen_menu.xml
rename to ui/src/main/res/drawable/collar_ic_screen_white.xml
index 3b85cd49..d32a6551 100644
--- a/ui/src/main/res/drawable-night/collar_ic_screen_menu.xml
+++ b/ui/src/main/res/drawable/collar_ic_screen_white.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_search.xml b/ui/src/main/res/drawable/collar_ic_search.xml
index 2145a68a..d22246e2 100644
--- a/ui/src/main/res/drawable/collar_ic_search.xml
+++ b/ui/src/main/res/drawable/collar_ic_search.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_ic_settings.xml b/ui/src/main/res/drawable/collar_ic_settings.xml
index 597cbed7..fac13892 100644
--- a/ui/src/main/res/drawable/collar_ic_settings.xml
+++ b/ui/src/main/res/drawable/collar_ic_settings.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/ui/src/main/res/drawable/collar_shape_background_icon.xml b/ui/src/main/res/drawable/collar_shape_background_item_line.xml
similarity index 78%
rename from ui/src/main/res/drawable/collar_shape_background_icon.xml
rename to ui/src/main/res/drawable/collar_shape_background_item_line.xml
index ff78e77b..6995bfd3 100644
--- a/ui/src/main/res/drawable/collar_shape_background_icon.xml
+++ b/ui/src/main/res/drawable/collar_shape_background_item_line.xml
@@ -2,7 +2,7 @@
-
+
-
+
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ui/src/main/res/drawable/collar_shape_background_screen.xml b/ui/src/main/res/drawable/collar_shape_background_screen.xml
deleted file mode 100644
index e078e315..00000000
--- a/ui/src/main/res/drawable/collar_shape_background_screen.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ui/src/main/res/drawable/collar_shape_background_unknown.xml b/ui/src/main/res/drawable/collar_shape_background_unknown.xml
deleted file mode 100644
index fdfe11e3..00000000
--- a/ui/src/main/res/drawable/collar_shape_background_unknown.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ui/src/main/res/layout/collar_activity_collar.xml b/ui/src/main/res/layout/collar_activity_collar.xml
index 50635181..4b51fcf8 100644
--- a/ui/src/main/res/layout/collar_activity_collar.xml
+++ b/ui/src/main/res/layout/collar_activity_collar.xml
@@ -28,6 +28,8 @@
android:scrollbars="vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
-
+
\ No newline at end of file
diff --git a/ui/src/main/res/layout/collar_item_event.xml b/ui/src/main/res/layout/collar_item_event.xml
index 00d5b8b8..d3b707e8 100644
--- a/ui/src/main/res/layout/collar_item_event.xml
+++ b/ui/src/main/res/layout/collar_item_event.xml
@@ -15,7 +15,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
android:gravity="center_horizontal"
android:maxWidth="56dp"
@@ -30,14 +29,13 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
- android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp">
+ android:layout_marginStart="16dp">
+ android:background="@drawable/collar_shape_background_item_line" />
+ android:src="@drawable/collar_ic_event_white" />
diff --git a/ui/src/main/res/layout/collar_item_property.xml b/ui/src/main/res/layout/collar_item_property.xml
index ae533974..bcd042bc 100644
--- a/ui/src/main/res/layout/collar_item_property.xml
+++ b/ui/src/main/res/layout/collar_item_property.xml
@@ -15,14 +15,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
android:gravity="center_horizontal"
android:maxWidth="56dp"
- android:layout_marginBottom="8dp"
- android:minHeight="48dp"
android:maxLength="8"
android:maxLines="1"
+ android:minHeight="48dp"
android:visibility="invisible"
tools:text="20:00:00" />
@@ -37,7 +36,7 @@
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_gravity="center"
- android:background="@drawable/collar_shape_background_icon" />
+ android:background="@drawable/collar_shape_background_item_line" />
+ android:src="@drawable/collar_ic_property_white" />
diff --git a/ui/src/main/res/layout/collar_item_screen.xml b/ui/src/main/res/layout/collar_item_screen.xml
index 9b197ac8..86a55830 100644
--- a/ui/src/main/res/layout/collar_item_screen.xml
+++ b/ui/src/main/res/layout/collar_item_screen.xml
@@ -15,14 +15,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
android:gravity="center_horizontal"
android:maxWidth="56dp"
- android:layout_marginBottom="8dp"
- android:minHeight="48dp"
android:maxLength="8"
android:maxLines="1"
+ android:minHeight="48dp"
android:visibility="invisible"
tools:text="20:00:00" />
@@ -30,14 +29,13 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
- android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp">
+ android:layout_marginStart="16dp">
+ android:background="@drawable/collar_shape_background_item_line" />
+ android:src="@drawable/collar_ic_screen_white" />
diff --git a/ui/src/main/res/layout/collar_view_snackbar.xml b/ui/src/main/res/layout/collar_view_snackbar.xml
index 0a2d420d..cc44af63 100644
--- a/ui/src/main/res/layout/collar_view_snackbar.xml
+++ b/ui/src/main/res/layout/collar_view_snackbar.xml
@@ -18,16 +18,15 @@
android:layout_gravity="top"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
- android:background="@drawable/collar_shape_background_screen"
+ android:background="@drawable/collar_shape_background_item_type"
android:scaleType="centerInside"
- android:src="@drawable/collar_ic_screen" />
+ android:src="@drawable/collar_ic_screen_white" />
@@ -68,7 +67,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp"
android:minWidth="48dp"
android:text="@string/collar_share"
android:textColor="@color/collar_color_primary"
diff --git a/ui/src/main/res/menu/collar_menu.xml b/ui/src/main/res/menu/collar_menu.xml
index 429f3de8..987e11fb 100644
--- a/ui/src/main/res/menu/collar_menu.xml
+++ b/ui/src/main/res/menu/collar_menu.xml
@@ -21,17 +21,17 @@
diff --git a/ui/src/main/res/values-night/colors.xml b/ui/src/main/res/values-night/colors.xml
index dc35c02b..efbbbdd2 100644
--- a/ui/src/main/res/values-night/colors.xml
+++ b/ui/src/main/res/values-night/colors.xml
@@ -1,20 +1,8 @@
- #ffa000
#121212
- #121212
- #121212
- #005fff
- #F44336
- #000000
- #000000
- #ffffff
- #ffffff
- #000000
- #00ffa0
- #005fff
- #ffa000
- #F44336
#353535
+
+ @color/collar_color_primary
\ No newline at end of file
diff --git a/ui/src/main/res/values-night/themes.xml b/ui/src/main/res/values-night/themes.xml
deleted file mode 100644
index 439810f3..00000000
--- a/ui/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ui/src/main/res/values/colors.xml b/ui/src/main/res/values/colors.xml
index 84ad68ba..e9f22032 100644
--- a/ui/src/main/res/values/colors.xml
+++ b/ui/src/main/res/values/colors.xml
@@ -2,19 +2,17 @@
#ffa000
#c67100
- #ffffff
- #ffffff
- #005fff
+ #049ce5
+
#F44336
- #ffffff
- #000000
- #000000
- #000000
- #ffffff
+ #FFF7F8
#00ffa0
#005fff
#ffa000
#F44336
+
#323232
+
+ @android:color/black
\ No newline at end of file
diff --git a/ui/src/main/res/values/themes.xml b/ui/src/main/res/values/themes.xml
index 1f1f8582..2ac560ee 100644
--- a/ui/src/main/res/values/themes.xml
+++ b/ui/src/main/res/values/themes.xml
@@ -5,15 +5,6 @@
- @color/collar_color_primary
- @color/collar_color_primary_variant
- @color/collar_color_secondary
- - @color/collar_color_background
- - @color/collar_color_surface
- - @color/collar_color_error
- - @color/collar_color_on_primary
- - @color/collar_color_on_secondary
- - @color/collar_color_on_background
- - @color/collar_color_on_surface
- - @color/collar_color_on_error
- - @color/collar_color_on_secondary