Last active
October 23, 2018 21:21
-
-
Save frel/79eda166117f70a7579753a3c670b677 to your computer and use it in GitHub Desktop.
This is an example of how log events can be defined using android resources files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources xmlns:tools="http://schemas.android.com/tools"> | |
<!-- Log events --> | |
<integer name="event_item_apply" tools:keep="@integer/item_apply">0</integer> | |
<integer name="event_item_download" tools:keep="@integer/event_item_download">1</integer> | |
<integer name="event_item_share" tools:keep="@integer/event_item_share">2</integer> | |
<integer name="event_item_purchase" tools:keep="@integer/event_item_purchase">3</integer> | |
<!-- Parameters --> | |
<integer name="param_uuid" tools:keep="@integer/param_uuid">0</integer> | |
<integer name="param_ctype" tools:keep="@integer/param_ctype">1</integer> | |
<integer name="param_title" tools:keep="@integer/param_title">2</integer> | |
</resources> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.subgarden.eventlogging | |
import android.os.Bundle | |
import android.support.annotation.IntegerRes | |
import android.support.v7.app.AppCompatActivity | |
import android.util.Log | |
import java.util.* | |
class MainActivity : AppCompatActivity() { | |
companion object { | |
const val TAG = "MainActivity" | |
// This would be part of the application and global state | |
private lateinit var eventNameMap: Map<String, Int> | |
private lateinit var eventIdMap: Map<Int, String> | |
private lateinit var paramNameMap: Map<String, Int> | |
private lateinit var paramIdMap: Map<Int, String> | |
} | |
interface LogHandler { | |
fun logEvent(eventId: Int, parameters: Map<Int, String>) | |
} | |
class AmplitudeLogHandler : LogHandler { | |
override fun logEvent(eventId: Int, parameters: Map<Int, String>) { | |
Log.d(TAG, "Logging ${eventIdMap[eventId]}($eventId)") | |
parameters.forEach { (key, value) -> | |
Log.d(TAG, "Parameter: ${paramIdMap[key]}($key)=$value") | |
} | |
} | |
} | |
private val logHandlers: MutableList<LogHandler> = mutableListOf() | |
private val amplitudeLogHandler = AmplitudeLogHandler() | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
// This could be moved to an annotation processor. | |
initLogEvents() | |
initLogParams() | |
logHandlers.add(amplitudeLogHandler) | |
// Debug all event KVP | |
eventNameMap.forEach { (key, value) -> | |
Log.d(TAG, "$key=$value") | |
} | |
// Log a download event | |
logEvent(R.integer.event_item_download, | |
R.integer.param_uuid to UUID.randomUUID().toString(), | |
R.integer.param_ctype to "wallpaper", | |
R.integer.param_title to "This is a title") | |
} | |
fun logEvent(@IntegerRes eventId: Int, vararg parameters: Pair<Int, String>) { | |
// Throws IllegalStateException if eventId <= 0 | |
val eventValue = resources.getInteger(eventId) | |
// Unnecessary sanity check if we have some compile time validation | |
require(eventValue > 0) | |
val parametersMap = parameters.map { resources.getInteger(it.first) to it.second}.toMap() | |
logEventInternal(eventValue, parametersMap) | |
} | |
private fun logEventInternal(eventId: Int, parameters: Map<Int, String>) { | |
logHandlers.forEach { handler -> | |
handler.logEvent(eventId, parameters) | |
} | |
} | |
/* This would run at startup of the application, if not code generation is used instead. */ | |
private fun initLogEvents() { | |
initXmlIntegerResources("event_") { nameMap, idMap -> | |
eventNameMap = nameMap | |
eventIdMap = idMap | |
} | |
} | |
private fun initLogParams() { | |
initXmlIntegerResources("param_") { nameMap, idMap -> | |
paramNameMap = nameMap | |
paramIdMap = idMap | |
} | |
} | |
private fun initXmlIntegerResources(namePrefix: String, block: (Map<String, Int>, Map<Int, String>) -> Unit) { | |
val idMap = hashMapOf<Int, String>() | |
val nameMap = hashMapOf<String, Int>() | |
R.integer::class.java.declaredFields | |
.filter { it.name.startsWith(namePrefix) } | |
.forEach { field -> | |
val resourceId = resources.getIdentifier(field.name, "integer", packageName) | |
val value = resources.getInteger(resourceId) | |
if (value in idMap) { | |
throw IllegalStateException( | |
"All resources must have unique values! " + | |
"Cannot assign value $value to '${field.name}'. " + | |
"Already assigned to '${idMap[value]}'.") | |
} | |
idMap[value] = field.name | |
nameMap[field.name] = value | |
} | |
block(nameMap.toMap(), idMap.toMap()) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Log output: | |
event_item_download=1 | |
event_item_share=2 | |
event_item_purchase=3 | |
event_item_apply=0 | |
Logging event_item_download(1) | |
Parameter: param_uuid(0)=6bb68142-6e2e-4fa0-b24b-da655a038cde | |
Parameter: param_ctype(1)=wallpaper | |
Parameter: param_title(2)=This is a title |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment