This package allows you to send signals to TelemetryDeck from your Android applications. Sign up for a free account at telemetrydeck.com
- Installation
- Getting Started
- Sending Signals
- User Identifiers
- Default Parameters
- Default Prefix
- Navigation Signals
- Acquisition
- Session Tracking
- Custom Telemetry
- Purchase Completed
- Custom Logging
- Requirements
- Migrating providers to 5.0+
- Migrating providers to 3.0+
The Kotlin SDK for TelemetryDeck is available from Maven Central at the following coordinates:
// `build.gradle`
dependencies {
implementation 'com.telemetrydeck:kotlin-sdk:6.2.0'
}
// `build.gradle.kts`
dependencies {
implementation("com.telemetrydeck:kotlin-sdk:6.2.0")
}
If needed, update your gradle.settings
to reference Kotlin version compatible with 2.0.20, e.g.:
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
Sending signals requires access to the internet so the following permission should be added to the
app's AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
A quick way to start is by adding your App ID to the application
section of the app's
AndroidManifest.xml
:
<application>
...
<meta-data android:name="com.telemetrydeck.appID"
android:value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
</application>
Replace XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
with your TelemetryDeck App ID.
In addition, the following optional properties are supported:
com.telemetrydeck.showDebugLogs
com.telemetrydeck.apiBaseURL
com.telemetrydeck.sendNewSessionBeganSignal
com.telemetrydeck.testMode
com.telemetrydeck.defaultUser
For greater control you can set up and configure TelemetryDeck
using the provided builder:
val builder = TelemetryDeck.Builder()
.appID("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.showDebugLogs(true)
.defaultUser("Person")
TelemetryDeck.start(applicationContext, builder)
To send a signal immediately
TelemetryDeck.send("appLaunchedRegularly")
To enqueue a signal to be sent by TelemetryDeck at a later time
TelemetryDeck.signal("appLaunchedRegularly")
When TelemetryDeck
is started for the first time, it will create a user identifier for the user
that is specific to the app installation.
-
The identity is stored within the application's file folder on the user's device.
-
The identifier will be removed when a user uninstalls an app. The KotlinSDK will not "bridge" the user's identity between installations.
-
Users can reset the identifier at any time by using the "Clear Data" action in Settings of their device.
If you have a better user identifier available, such as an email address or a username, you can use
that instead, by setting defaultUser
(the identifier will be hashed before sending it) in
configuration, or by passing the value when sending signals.
If you need a more robust mechanism for keep track of the user's identity, you can replace the
default behaviour by providing your own implementation of TelemetryDeckIdentityProvider
:
val builder = TelemetryDeck.Builder()
.appID("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.showDebugLogs(true)
.defaultUser("Person")
.identityProvider(YourIdentityProvider())
TelemetryDeck.start(applicationContext, builder)
By default, Kotlin SDK for TelemetryDeck will include the following environment parameters for each outgoing signal
Parameter name | Provider | Description |
---|---|---|
TelemetryDeck.AppInfo.buildNumber |
EnvironmentParameterProvider |
|
TelemetryDeck.AppInfo.version |
EnvironmentParameterProvider |
|
TelemetryDeck.AppInfo.versionAndBuildNumber |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.architecture |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.modelName |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.operatingSystem |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.platform |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.systemMajorMinorVersion |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.systemMajorVersion |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.systemVersion |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.orientation |
PlatformContextProvider |
|
TelemetryDeck.Device.screenDensity |
PlatformContextProvider |
|
TelemetryDeck.Device.screenResolutionHeight |
PlatformContextProvider |
|
TelemetryDeck.Device.screenResolutionWidth |
PlatformContextProvider |
|
TelemetryDeck.Device.brand |
EnvironmentParameterProvider |
|
TelemetryDeck.Device.timeZone |
PlatformContextProvider |
|
TelemetryDeck.AppInfo.buildNumber |
EnvironmentParameterProvider |
|
TelemetryDeck.AppInfo.version |
EnvironmentParameterProvider |
|
TelemetryDeck.AppInfo.versionAndBuildNumber |
EnvironmentParameterProvider |
|
TelemetryDeck.SDK.name |
EnvironmentParameterProvider |
|
TelemetryDeck.SDK.version |
EnvironmentParameterProvider |
|
TelemetryDeck.SDK.nameAndVersion |
EnvironmentParameterProvider |
|
TelemetryDeck.SDK.buildType |
EnvironmentParameterProvider |
|
TelemetryDeck.RunContext.locale |
PlatformContextProvider |
|
TelemetryDeck.RunContext.targetEnvironment |
PlatformContextProvider |
|
TelemetryDeck.RunContext.isSideLoaded |
PlatformContextProvider |
|
TelemetryDeck.RunContext.sourceMarketplace |
PlatformContextProvider |
|
TelemetryDeck.Accessibility.isBoldTextEnabled |
AccessibilityProvider |
API 31 and above |
TelemetryDeck.Accessibility.fontWeightAdjustment |
AccessibilityProvider |
API 31 and above |
TelemetryDeck.Accessibility.isDarkerSystemColorsEnabled |
AccessibilityProvider |
|
TelemetryDeck.Accessibility.fontScale |
AccessibilityProvider |
Mapped to iOS size categories |
TelemetryDeck.Accessibility.isInvertColorsEnabled |
AccessibilityProvider |
|
TelemetryDeck.Accessibility.isReduceMotionEnabled |
AccessibilityProvider |
|
TelemetryDeck.Accessibility.isReduceTransparencyEnabled |
AccessibilityProvider |
|
TelemetryDeck.Accessibility.shouldDifferentiateWithoutColor |
AccessibilityProvider |
|
TelemetryDeck.UserPreference.layoutDirection |
AccessibilityProvider |
Possible values are "rightToLeft" or "leftToRight" |
TelemetryDeck.UserPreference.region |
AccessibilityProvider |
Current device region in ISO 3166-1 alpha-2 format |
TelemetryDeck.UserPreference.language |
AccessibilityProvider |
Current application language in ISO 639-1 format |
TelemetryDeck.UserPreference.colorScheme |
AccessibilityProvider |
"Dark" or "Light" |
TelemetryDeck.Session.started |
SessionTrackingSignalProvider |
|
TelemetryDeck.Acquisition.newInstallDetected |
SessionTrackingSignalProvider |
|
TelemetryDeck.Acquisition.firstSessionDate |
SessionTrackingSignalProvider |
|
TelemetryDeck.Retention.averageSessionSeconds |
SessionTrackingSignalProvider |
|
TelemetryDeck.Retention.distinctDaysUsed |
SessionTrackingSignalProvider |
|
TelemetryDeck.Retention.totalSessionsCount |
SessionTrackingSignalProvider |
|
TelemetryDeck.Retention.previousSessionSeconds |
SessionTrackingSignalProvider |
|
TelemetryDeck.Retention.distinctDaysUsedLastMonth |
SessionTrackingSignalProvider |
|
TelemetryDeck.Calendar.dayOfMonth |
CalendarParameterProvider |
|
TelemetryDeck.Calendar.dayOfWeek |
CalendarParameterProvider |
|
TelemetryDeck.Calendar.dayOfYear |
CalendarParameterProvider |
|
TelemetryDeck.Calendar.weekOfYear |
CalendarParameterProvider |
|
TelemetryDeck.Calendar.isWeekend |
CalendarParameterProvider |
|
TelemetryDeck.Calendar.monthOfYear |
CalendarParameterProvider |
|
TelemetryDeck.Calendar.quarterOfYear |
CalendarParameterProvider |
|
TelemetryDeck.Calendar.hourOfDay |
CalendarParameterProvider |
TelemetryDeck.Acquisition.newInstallDetected
We send this signal when a user starts the app for the first time on a given device.
- Session data is stored locally on device as part of the application's files.
- If the application is uninstalled or it's data cleared, the SDK will report a new installation event (we do not bridge session data of any kind between installations).
See Session Tracking on how sessions are tracked.
TelemetryDeck.Accessibility.fontScale
- the value is mapped to better align with size categories sent by other SDKs:
fontScale <= 0.8f -> XS
fontScale > 0.8f && fontScale < 0.9f -> S
fontScale >= 0.9f && fontScale < 1.0f -> M
fontScale == 1.0f -> L
fontScale >= 1.0f && fontScale < 1.3f -> XL
fontScale >= 1.3f && fontScale < 1.4f -> XXL
fontScale >= 1.4f && fontScale < 1.5f -> XXXL
fontScale >= 1.5f && fontScale < 1.6f -> AccessibilityM
fontScale >= 1.6f && fontScale < 1.7f -> AccessibilityL
fontScale >= 1.7f && fontScale < 1.8f -> AccessibilityXL
fontScale >= 1.8f && fontScale < 1.9f -> AccessibilityXXL
fontScale >= 1.9f && fontScale < 2.0f -> AccessibilityXXXL
fontScale >= 2.0f -> AccessibilityXXXL
See Custom Telemetry on how to implement your own parameter enrichment.
If there are parameters you would like to include with every outgoing signal, you can use
DefaultParameterProvider
instead of passing them with every call.
// create an instance of [DefaultParameterProvider] and pass the key value you wish to be appended to every signal
val provider = DefaultParameterProvider(mapOf("key" to "value"))
// add the provider when configuring an instance of TelemetryDeck
val builder = TelemetryDeck.Builder()
.appID("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.addProvider(provider)
If you find yourself prepending the same prefix for to your custom signals or parameters,
you can optionally configure TelemetryDeck
to do this for you by activating our
DefaultPrefixProvider
:
// create an instance of [DefaultPrefixProvider] with a signal or parameter prefix
val provider = DefaultPrefixProvider("MyApp.", "MyApp.Params.")
// add the provider when configuring an instance of TelemetryDeck
val builder = TelemetryDeck.Builder()
.appID("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.addProvider(provider)
You can make use of Navigation Signals to better understand how your users a moving through the app.
// track a navigation event e.g. when the user is moving from one screen to another:
TelemetryDeck.navigate(sourcePath = "/onboarding", destinationPath = "/home")
// let TelemetryDeck take care of tracking the user's route by calling navigate when the path changes
TelemetryDeck.navigate("/onboarding")
TelemetryDeck.navigate("/home")
The following helper methods are available
/**
* Send a `TelemetryDeck.Acquisition.userAcquired` signal with the provided channel.
*/
fun acquiredUser(channel: String, ...)
/**
* Send a `TelemetryDeck.Acquisition.leadStarted` signal with the provided leadId.
*/
fun leadStarted(leadId: String, ...)
/**
* Send a `TelemetryDeck.Acquisition.leadConverted` signal with the provided leadId.
*/
fun leadConverted(leadId: String, ...)
The SDK uses session tracking detect when the app is launched for the first time (
TelemetryDeck.Acquisition.newInstallDetected
) and enrich signals with default parameters regarding
user retention like session duration, days used, number of sessions etc.
-
Session state is stored within the application's file folder on the user's device.
-
The session state will be removed when a user uninstalls an app. The SDK does not "bridge" state between installations.
-
Users can reset the session state at any time by using the "Clear Data" action in Settings of their device.
The SDK tracks sessions by means of a session manager. The default session manager is
SessionTrackingSignalProvider
and it is enabled by default.
Here are some concepts on which the SessionTrackingSignalProvider
is based:
This refers to the time during which the app is actively being used by the user. It's the period when the app is in the foreground and interacting with the user.
A session typically begins when the user opens the app or resumes interaction after a period of inactivity.
Note: If sendNewSessionBeganSignal
is set to true
, the TelemetryDeck.Session.started
is send
for every start of a new session.
A completed session is defined as the time between two subsequent session starts. Essentially, it's the duration from when the app becomes active until it becomes active again after a period of inactivity or closure.
Example:
-
First Session Start The user opens the app at 10:00. A new session is started. The user actively uses the app until 10:15, then minimizes it or switches to another app.
-
Second Session Start The user returns to the app at 10:30. A new session is started again. The user uses the app until 10:45, then closes it.
-
Third Session Start The user opens the app again at 11:00. A new session is started for the third time. The user uses the app until 11:10.
Results
Completed Session | Start Time | End Time | Duration |
---|---|---|---|
First | 10:00 | 10:30 | 30 min |
Second | 10:30 | 11:00 | 30 min |
The third session will not be counted until the next time the user opens the app.
In some situations, you may want to control the session identifier.
Session IDs are UUIDs which the SDK would generate automatically but you can provide your own as follows:
-
Retrieve the current session ID using
TelemetryDeck.sessionID
. If the value isnull
, the session manager is disabled or no session has been started yet. -
Start/end a session on demand by calling
TelemetryDeck.newSession()
and optionally passing a custom sessionID. -
Instruct the SDK to start with a custom sessionID using the builder:
val builder = TelemetryDeck.Builder()
.appID("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.sessionID(UUID.fromString("00000000-0000-0000-0000-000000000000"))
- Session tracking is optional and can be deactivated by disabling the session manager:
val builder = TelemetryDeck.Builder()
.appID("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.sessionManager(null)
- You can provide your own logic for session tracking by adopting
TelemetryDeckSessionManagerProvider
and setting it as the session manager.
The SDK offers convenience methods to facilitate tracking the duration of specific objects or events.
Once started, a duration signal will be tracked internally by the SDK and upon completion, it will send the signal while also adding a TelemetryDeck.Signal.durationInSeconds
parameter.
// start tracking, without sending a signal
TelemetryDeck.startDurationSignal("wizard_step1")
// end tracking, sends the signal including the total duration (excluding background time)
TelemetryDeck.stopAndSendDurationSignal("wizard_step1")
Duration signals are provided by the DurationSignalTrackerProvider
which is always enabled.
By default, the KotlinSDK will append the following parameters to all outgoing signals:
TelemetryDeck.Calendar.dayOfMonth
: The day-of-month (1..31) component of the date.TelemetryDeck.Calendar.dayOfWeek
: The ISO 8601 number of the given day of the week. Monday is 1, Sunday is 7.TelemetryDeck.Calendar.dayOfYear
: The 1-based day-of-year component of the date.TelemetryDeck.Calendar.weekOfYear
: The week number within the current year as defined bygetFirstDayOfWeek()
andgetMinimalDaysInFirstWeek()
.TelemetryDeck.Calendar.isWeekend
:true
if the day of the week is Saturday or Sunday,false
otherwise.TelemetryDeck.Calendar.monthOfYear
: The number-of-the-month (1..12) component of the date.TelemetryDeck.Calendar.quarterOfYear
: The the quarter-of-year (1..4). For API 26 and earlier, it's the number of the month divided by 3.TelemetryDeck.Calendar.hourOfDay
: The hour-of-day (0..23) time component of this time value.
The SDK offers a facility method TelemetryDeck.purchaseCompleted()
that can be invoked in response to a user purchase.
With information from the marketplace where the user made a purchase, you can inform TelemetryDeck as follows:
TelemetryDeck.purchaseCompleted(
event = PurchaseEvent.PAID_PURCHASE,
countryCode = "BE",
productID = "product1",
purchaseType = PurchaseType.ONE_TIME_PURCHASE,
priceAmountMicros = 7990000,
currencyCode = "EUR",
offerID = "offer1"
)
Depending on the specified PurchaseEvent
, one of the following signals will be sent:
TelemetryDeck.Purchase.completed
TelemetryDeck.Purchase.freeTrialStarted
TelemetryDeck.Purchase.convertedFromTrial
The floatValue
of the signal will be set to the provided purchase amount, converted USD
.
The following parameters are also included with the signal:
Parameter | Description |
---|---|
TelemetryDeck.Purchase.type |
subscription or one-time-purchase |
TelemetryDeck.Purchase.countryCode |
The country code of the marketplace where the purchase was made |
TelemetryDeck.Purchase.currencyCode |
The ISO 4217 currency code (e.g., "EUR" for Euro) used for the purchase. |
TelemetryDeck.Purchase.priceMicros |
The price of the product in micro-units of the currency |
TelemetryDeck.Purchase.productID |
The unique identifier of the purchased product |
TelemetryDeck.Purchase.offerID |
The specific offer identifier for subscription products or customized pricing |
When integrating with Google Play Billing Library, you can adopt the TelemetryDeck SDK with Google Play Services in order to let us determine the exact purchase parameters.
- Add the following package as a dependency to your app:
// `build.gradle.kts`
dependencies {
implementation("com.telemetrydeck:kotlin-sdk-google-services:6.2.0")
}
- You can now use the
purchaseCompleted
function optimized for Google Play Billing:
fun purchaseHandlerInYourApp(
billingConfig: BillingConfig,
purchase: Purchase,
productDetails: ProductDetails
) {
TelemetryDeck.purchaseCompleted(
billingConfig = billingConfig,
purchase = purchase,
productDetails = productDetails
)
}
By default, this method assumes the purchase is a PAID_PURCHASE
. To determine if the user is converting or starting a trial, you will have to implement your own server-side validation and inspect the paymentState
of a subscription.
To make it easier to get started, the TelemetryDeck SDK offers a helper method which attempts to guess the purchase origin based on locally available data, so you could:
TelemetryDeck.purchaseCompleted(
billingConfig = billingConfig,
purchase = purchase,
productDetails = productDetails,
purchaseOrigin = purchase.toTelemetryDeckPurchaseEvent(setOf("TRIAL_SKU"))
)
Note that this approach is not exact and comes with a certain number of limitations, please check the doc notes on com.telemetrydeck.sdk.googleservices.toTelemetryDeckPurchaseEvent
for more details.
Another way to send signals is to implement a custom TelemetryDeckProvider
.
A provider uses the TelemetryDeck client in order to queue or send signals based on environment or
other triggers.
To create a provider, implement the TelemetryDeckProvider
interface:
class CustomProvider : TelemetryDeckProvider {
override fun register(ctx: Context?, client: TelemetryDeckClient) {
// configure and start the provider
// you may retain a WeakReference to client
}
override fun stop() {
// deactivate the provider, perform cleanup work
}
}
Setup and start the provider during the register
method.
Tips:
- Do not retain a strong reference to the application context or TelemetryDeckClient instance.
- You can use
WeakReference<TelemetryDeckClient>
if you need to be able to call the TelemetryDeck at a later time.
To use your custom provider, register it by calling addProvider
using the
TelemetryDeck.Builder
:
val builder = TelemetryDeck.Builder()
// ...
.addProvider(CustomProvider()) // <-- Your custom provider
Every time the SDK is about to send signals to our servers, the enrich
method of every provider
will be invoked to give you the opportunity to append additional parameters.
In the implementation of your custom TelemetryDeckProvider
, you can override the enrich
method:
override fun enrich(
signalType: String,
clientUser: String?,
additionalPayload: Map<String, String>
): Map<String, String> {
// retrieve the payload of signal
val signalPayload = additionalPayload.toMutableMap()
// add additional attributes of your choice
val today = LocalDateTime.now().dayOfWeek
if (today == DayOfWeek.MONDAY) {
signalPayload["isMonday"] = "yes"
}
// return the enriched payload
return signalPayload
}
We use providers internally to provide lifecycle and environment integration out of the box. Feel free to examine how they work and inspire your own implementations.
You can also completely disable or override the default providers with your own.
SessionAppProvider
- Emits signals for application and activity lifecycle events. This provider is tasked with resetting the sessionID whensendNewSessionBeganSignal
is enabled.SessionActivityProvider
- Emits signals for application and activity lifecycle events. This provider is not enabled by default.EnvironmentParameterProvider
- Adds environment and device information to outgoing Signals. This provider overrides theenrich
method in order to append additional metadata for all signals before sending them.PlatformContextProvider
- Adds environment and device information which may change over time like the current timezone and screen metrics.AccessibilityProvider
- Adds parameters describing the currently active accessibility options.SessionTrackingSignalProvider
- Reports when a new app installation has been detected.
For a complete list, check the com.telemetrydeck.sdk.providers
package.
// Append a custom provider
val builder = TelemetryDeck.Builder()
// ...
.addProvider(CustomProvider())
// Replace all default providers
val builder = TelemetryDeck.Builder()
// ...
.providers(listOf(CustomProvider(), AnotherProvider()))
By default, TelemetryDeck SDK uses a simple println
to output internal diagnostic messages when
showDebugLogs
is set to true
in configuration.
If your platform has custom logging needs, you can adopt the DebugLogger
interface and provide it
to the TelemetryDeck
builder:
val builder = TelemetryDeck.Builder()
// ...
.logger(CustomLogger())
Please note that the logger implementation should be thread safe as it may be invoked in different queues and contexts.
- Android API 21 or later
- Kotlin 2.0.20
- Gradle 6.8.3–8.8*
- AGP 7.1.3–8.5
- The provider interface
TelemetryDeckProvider
has changed to accept aContext
instance instead of anApplication
. - The deprecated fallback provider callbacks are no longer used and the functionality has been removed.
- Providers can now optionally override the
transform
method in order to modify any component of the signal.
If you had Kotlin SDK for TelemetryDeck added to your app, you will notice that TelemetryManager
and related classes have been deprecated.
You can read more about the motivation behind these
changes here.
To upgrade, please perform the following changes depending on how you use TelemetryDeck SDK.
- Adapt the manifest of your app and rename all keys from
com.telemetrydeck.sdk.*
tocom.telemetrydeck.*
for example:
Before:
<meta-data android:name="com.telemetrydeck.sdk.appID"
android:value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
After:
<meta-data android:name="com.telemetrydeck.appID"
android:value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
- In your app sourcecode, rename all uses of
TelemetryManager
toTelemetryDeck
. - If you were using
send()
to send signals, no further changes are needed! - If you were using
queue()
to send signals, you will need to rename the method toTelemetryDeck.signal()
.
- In your app sourcecode, rename all uses of
TelemetryManager
toTelemetryDeck
. - If you were using
send()
to send signals, no further changes are needed! - If you were using
queue()
to send signals, you will need to rename the method toTelemetryDeck.signal()
. - If you had a custom provider configuration, please replace the corresponding providers as follows:
Provider (old name) | Provider (new, 3.0+) |
---|---|
AppLifecycleTelemetryProvider |
SessionAppProvider , SessionActivityProvider |
SessionProvider |
SessionAppProvider |
EnvironmentMetadataProvider |
EnvironmentParameterProvider , PlatformContextProvider |
Tip
You can rename all deprecated classes in your project using the Code Cleanup function in IntelliJ/Android Studio.
Warning
Do not mix usage of TelemetryManager
and TelemetryDeck
. Once you're ready to migrate, adapt
all uses at the same time.
Your custom providers must replace TelemetryProvider
with TelemetryDeckProvider
.
To adopt the new interface:
- Adapt the signature of the
register
method toregister(ctx: Context?, client: TelemetryDeckSignalProcessor)
The TelemetryDeckSignalProcessor
interface offers a subset of the TelemetryDeck
client API which
gives you access to:
- To access the logger, use can use
client.debugLogger
- To access the signal cache, use
client.signalCache