{"id":13610079,"url":"https://github.com/infinum/android-collar","last_synced_at":"2025-04-14T19:34:07.581Z","repository":{"id":40409362,"uuid":"242731077","full_name":"infinum/android-collar","owner":"infinum","description":"Gradle plugin which collects all analytics screen names, events and user properties for Android projects.","archived":false,"fork":false,"pushed_at":"2025-03-14T08:28:45.000Z","size":1746,"stargazers_count":26,"open_issues_count":4,"forks_count":3,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-11T17:10:55.939Z","etag":null,"topics":["analytics","android","android-development","android-library","firebase","gradle","gradle-plugin","kotlin","kotlin-android","kotlin-library","maven","open-source"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/infinum.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-24T12:30:13.000Z","updated_at":"2024-05-10T06:26:30.000Z","dependencies_parsed_at":"2024-08-01T19:43:58.240Z","dependency_job_id":"9f529d55-5280-45b5-a706-4f7426cbee4a","html_url":"https://github.com/infinum/android-collar","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fandroid-collar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fandroid-collar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fandroid-collar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fandroid-collar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infinum","download_url":"https://codeload.github.com/infinum/android-collar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248946407,"owners_count":21187502,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analytics","android","android-development","android-library","firebase","gradle","gradle-plugin","kotlin","kotlin-android","kotlin-library","maven","open-source"],"created_at":"2024-08-01T19:01:41.013Z","updated_at":"2025-04-14T19:34:07.538Z","avatar_url":"https://github.com/infinum.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"![Download](https://img.shields.io/maven-central/v/com.infinum.collar/collar-plugin) ![Validate Gradle Wrapper](https://github.com/infinum/android-collar/workflows/Validate%20Gradle%20Wrapper/badge.svg) ![Code analysis](https://github.com/infinum/android-collar/workflows/Code%20analysis/badge.svg)\n\n### \u003cimg align=\"left\" src=\"logo.svg\" width=\"48\"\u003e\n# Collar Android plugin\n\nGradle plugin which collects all analytics screen names, events and user properties for Android projects.  \nThis plugin has been written in Kotlin but works both inside Kotlin and Java projects.\n\n**IMPORTANT:** Collar does **NOT** send out analytics data to remote services. This is left for the developer to solve in their own codebase, with Collar being simply a reflection of the current state of analytics data.\n\nThe project is organized in the following modules:\n\n- `annotations` - contains all annotations necessary for the plugin to work\n- `core` - plugin implementation core, depends on the `annotations` module\n- `processor` - annotation processor generating and transforming classes for plugin, depends on the `annotations` module\n- `plugin` - the Gradle plugin that adds all necessary dependencies to the project\n- `ui` - contains a single screen UI that provides visual tracking of sent events\n- `ui-no-op` - contains a stub for easy release implementation of UI package\n- `generator` - contains a generator code for provided tracking plan\n- `lint` - contains custom Lint checks\n- `sample` - a sample app for testing the Gradle plugin\n\n## Usage\n\nTo include plugin to your project, you have to add buildscript dependencies in your project level `build.gradle` or `build.gradle.kts`:\n\n**Groovy**\n```gradle\nbuildscript {\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n        classpath \"com.infinum.collar:collar-plugin:1.4.0\"\n    }\n}\n```\n**KotlinDSL**\n```kotlin\nbuildscript {\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n        classpath(\"com.infinum.collar:collar-plugin:1.4.0\")\n    }\n}\n```\n\nThen apply the plugin in your app `build.gradle` or `build.gradle.kts` :\n\n**Groovy**\n```gradle\napply plugin: \"com.infinum.collar.plugin\"\n```\n**KotlinDSL**\n```kotlin\nplugins {\n    ...\n    \n    id(\"com.infinum.collar.plugin\")\n}\n```\n\nNow you can sync your project.\n\n\n### Getting started\n\nCreate or inject an instance of _Collar_ in your Application class and attach a _Collector_:\n\n\n```kotlin\nCollar.attach(object : Collector {\n  \n    override fun onScreen(screen: Screen) =\n        analyticsProvider.sendScreenName(screenName = screen.name)\n\n    override fun onEvent(event: Event) =\n        analyticsProvider.sendEvent(eventName = event.name, eventParameters = event.params ?: mapOf\u003cString, *\u003e())\n\n    override fun onProperty(property: Property) =\n        analyticsProvider.sendProperty(property.name, property.value)\n})\n```\n\n_analyticsProvider_ is your own implementation of an analytics delegate class.\n\n#### Screen names\n\nScreen names can be annotated on top of **Activities** or **Fragments**. No other views are eligible as screen name destination holders.\n\n\n```kotlin\n@ScreenName(AnalyticsKeys.ScreenName.BRAND_DETAILS)\nclass BrandFragment : Fragment(R.layout.fragment_brand) {\n\n    ...\n\n    override fun onResume() = super.onResume().run { trackScreen() }\n\n}\n```\n\nActual screen name annotation values are maintained and provided per project implementing this plugin.  \nIf a screen name annotation value is not provided then class name will be used as annotation value.  \nA convenience extension method will be generated by annotation processor _trackScreen_ which must be called explicitly and preferably in _onResume_ lifecycle callback.  \nHowever, you can always use _Collar_ methods explicitly besides the extension one to track your screen names.\n```kotlin\nCollar.trackScreen(screen)\n```\n\n#### Events\n\nCreate a model container **sealed** class with arbitrary name where you see fit in your project and annotate as illustrated below.  \nNested classes represent the events you want to track.   \nClass name will be processed into lowercase snake_case appropriate for most analytics dependencies, as well as the containing variable names.  \nVariable values will be propagated as event parameter values.  \nYou can override event name for specific nested class then use _EventName_ annotation with a respective new event name.  \nAccordingly, you can override event parameter name using _EventParameterName_ annotation too.\n\n\n```kotlin\n@AnalyticsEvents\nsealed class AnalyticsEvent {\n\n    data class LanguageSelection(\n        val selectedLanguage: String,\n        val exhibitorCount: Int\n    ) : AnalyticsEvent()\n    \n    class Login : AnalyticsEvent()\n    \n    @EventName(\"login_guest\")\n    class Guest : AnalyticsEvent()\n    \n    data class ShortcutLink(\n        @EventParameterName(\"URL\")\n        val url: String\n    ) : AnalyticsEvent()\n}\n```\nYou can have multiple annotated sealed classes declared like this through your project.  \nA convenience extension method will be generated by annotation processor _trackEvent_ which must be called explicitly with the instance of your previously defined event class.  \nHowever, you can always use _Collar_ methods explicitly besides the extension one to track your analytics events in the same way.\n```kotlin\nCollar.trackEvent(event)\n```\n\n```kotlin\n@ScreenName(AnalyticsKeys.ScreenName.BRAND_DETAILS)\nclass BrandFragment : Fragment(R.layout.fragment_brand) {\n\n    ...\n\n    override fun onResume() = super.onResume().run { trackScreen() }\n    \n    ...\n    \n    private fun onFavor(exhibitor: Exhibitor) {\n        viewModel.favor(exhibitor)\n        \n        ...\n        \n        trackEvent(AnalyticsEvent.ExhibitorFavored(exhibitorId = exhibitor.id, exhibitorName = exhibitor.name))\n    }\n\n}\n```\n#### Properties\n\nCreate a model container **sealed** class with arbitrary name where you see fit in your project and annotate as illustrated below.  \nNested classes represent the user properties you want to track.   \nClass name will be processed into lowercase snake_case appropriate for most analytics dependencies, as well as the containing variable names.  \nVariable value will be propagated as user property value.  \nYou can override user property name for specific nested class then use _PropertyName_ annotation with a respective new property name.  \nA property nested class can have only 1 declared variable as value. \n\n\n```kotlin\n@UserProperties\nsealed class UserProperty {\n\n    @PropertyName(value = \"user_type_corporate\")\n    data class UserType1(\n        val value: String\n    ) : UserProperty()\n    \n    data class UserTypeRetail(\n        val someCoolValue: String\n    ) : UserProperty()\n}\n```\nYou can have multiple annotated sealed classes declared like this through your project.  \nA convenience extension method will be generated by annotation processor _trackProperty_ which must be called explicitly with the instance of your previously defined property class.  \nHowever, you can always use _Collar_ methods explicitly besides the extension one to track your user properties in the same way.\n```kotlin\nCollar.trackProperty(property)\n```\n\n```kotlin\n@ScreenName(AnalyticsKeys.ScreenName.BRAND_DETAILS)\nclass BrandFragment : Fragment(R.layout.fragment_brand) {\n    \n    ...\n\n    override fun onCreate() = super.onCreate().run {\n        trackProperty(UserProperty.UserTypeRetail(\"retail\"))\n    }\n\n    ...\n\n    override fun onResume() = super.onResume().run { trackScreen() }\n    \n    ...\n    \n    private fun onFavor(exhibitor: Exhibitor) {\n        viewModel.favor(exhibitor)\n        \n        ...\n        \n        trackEvent(AnalyticsEvent.ExhibitorFavored(exhibitorId = exhibitor.id, exhibitorName = exhibitor.name))\n    }\n\n}\n```\n## Options\n\n### Annotation processor\n\n_Collar_ plugin has been tuned per default for the most often used Firebase Analytics but additional options can be passed to annotation processor for fine tuning:\n\n```gradle\njavaCompileOptions {\n    annotationProcessorOptions {\n        argument \"events_count\", \"100\"\n        argument \"event_parameters_count\", \"10\"\n        argument \"event_name_length\", \"20\"\n        argument \"event_name_regex\", \"^[a-zA-Z0-9_]*$\"\n        argument \"properties_count\", \"17\"\n        argument \"property_name_regex\", \"^[a-zA-Z0-9_]*$\"\n        argument \"reserved_prefixes\", \"ga_,fb_\"\n        argument \"reserved_properties\", \"Age,Country\"\n    }\n}\n```\n\n## Lint checks\n\n_Collar_ plugin provides it's own custom _Lint_ checks. These can be disabled, suppressed or fixed like any other Lint registered issues.    \nChecks:\n- `MissingScreenNameAnnotation` - All Activities and Fragments require a valid screen name annotation on the class.  You must annotate an Activity or Fragment with @ScreenName with a valid value parameter or set enabled parameter to false.\n\n## Debug UI\n\n![UI](ui.png)![ui-dark](ui-dark.png)\n\nA separate _ui_ and _ui-no-op_ packages are provided if you want to visually track what has been sent through Collar.  \nYou can search, filter and clear all sent analytics.  \n\nIn your app `build.gradle` or `build.gradle.kts` add:  \n**Groovy**\n```gradle\ndebugImplementation \"com.infinum.collar:collar-ui:1.4.0\"\nreleaseImplementation \"com.infinum.collar:collar-ui-no-op:1.4.0\"\n```\n**KotlinDSL**\n```kotlin\ndebugImplementation(\"com.infinum.collar:collar-ui:1.4.0\")\nreleaseImplementation(\"com.infinum.collar:collar-ui-no-op:1.4.0\")\n```\n\nIn order to start tracking with UI you must use _LiveCollector_ as in this example:\n```kotlin\nval configuration = Configuration(\n  analyticsCollectionEnabled = true, \n  showSystemNotifications = true, \n  showInAppNotifications = true, \n  redactedKeywords = redactedWords\n)\nCollar.attach(object : LiveCollector(configuration) {\n   \n    override fun onScreen(screen: Screen) =\n        super.onScreen(screen).run {\n            analyticsProvider.sendScreenName(screenName = screen.name)\n        }\n\n    override fun onEvent(event: Event) =\n        super.onEvent(event).run {\n            analyticsProvider.sendEvent(eventName = event.name, eventParameters = event.params ?: mapOf\u003cString, *\u003e())\n        }\n\n    override fun onProperty(property: Property) =\n        super.onProperty(property).run {\n            analyticsProvider.sendProperty(property.name, property.value)\n        }\n})\n```\n_LiveCollector_ constructor has a _Configuration_ parameter that consists of the following members. \nThe first parameter (*setAnalyticsCollectionEnabled*) defines the default state of analytics collection. If set to *false*, a warning message will appear in the Collar UI.\nIf you set the second parameter (*showSystemNotification*) as *true*, a notification will show once analytics are gathered and clicking on it will open a dedicated screen.  \nThe third parameter (*showInAppNotification*) with value *true* will show a Snackbar-ish popup once analytics are gathered inside the currently running Activity.  \nThese parameters are default values per collector session but can be changed via _CollarActivity_ menu and will remain valid until the next session.  \nOtherwise 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:\n\n```kotlin\nstartActivity(\n    CollarUi.launchIntent().apply {\n        addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)\n    }\n)\n```\nAlternatively, you can use a dedicated method with default Intent setup:\n```kotlin\nCollarUi.show()\n```\nThe final *Configuration* parameter is a set of keywords to redact if found in screen names, analytics events names and parameters and user properties names or values.\n\n![Notification](notification.jpg) ![In app notification](in_app_notification.jpg)\n\n### Redaction\nIn 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.  \nThis set of keywords is provided to _LiveCollector_ via _Configuration_.  \n\n![Redacted notification](redacted_notification.jpg)![UI](redacted_ui.jpg)\n\n## Tasks\n### Generate\n\nGradle plugin supports code generation from a JSON formatted file.  \nYou will need to specify `fileName` and `packageName` in  `collar` plugin extension.  \nFor example:\n\n```\ncollar {\n    fileName = \"example.json\"\n    packageName = \"com.infinum.collar.sample.analytics.generated\"\n}\n```\nJSON file has to be formatted in the same way as it is in `sample` project module.  \nIf you don't want to use this task simply don't specify data parameters in plugin extension.  \nUsing this file is just a temporary solution and fetching the tracking plan will be implemented soon in future releases.\n\nTo run the task you can:\n\n- Open `gradle` panel on right side, find `collar` task group and run `generate` task \n- Type `./gradlew generate` in terminal\n\n`generate` Gradle task will create classes prepared for the _Collar_ annotation processor.\n\n## License\n\n```\nCopyright 2020 Infinum\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n## Credits\nMaintained and sponsored by [Infinum](http://www.infinum.com).\n\n\u003cp align=\"center\"\u003e\n  \u003ca href='https://infinum.com'\u003e\n    \u003cpicture\u003e\n        \u003csource srcset=\"https://assets.infinum.com/brand/logo/static/white.svg\" media=\"(prefers-color-scheme: dark)\"\u003e\n        \u003cimg src=\"https://assets.infinum.com/brand/logo/static/default.svg\"\u003e\n    \u003c/picture\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinum%2Fandroid-collar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfinum%2Fandroid-collar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinum%2Fandroid-collar/lists"}