Skip to content

Commit

Permalink
feat: migrate to new signal names
Browse files Browse the repository at this point in the history
  • Loading branch information
kkostov committed Oct 4, 2024
1 parent da74f99 commit e934093
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 46 deletions.
74 changes: 51 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,9 @@ This package allows you to send signals to [TelemetryDeck](https://telemetrydeck

## Installation

The TelemetryDeck is distributed using [jitpack](https://jitpack.io/), so you'll need to add the jitpack dependency to your `settings.gradle` file:
### Dependencies

```groovy
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' } // <-- add this line
}
}
```

After that is done, add the following to your `build.gradle` file, under `dependencies`:
The TelemetryDeck SDK for Kotlin is available from Maven Central and can be used as a dependency directly in `build.gradle` file:

```groovy
dependencies {
Expand All @@ -33,14 +22,16 @@ If needed, update your `gradle.settings` to reference Kotlin version compatible
id "org.jetbrains.kotlin.android" version "1.9.25" apply false
```

## Permission for internet access
### Permission for internet access

Sending signals requires access to the internet so the following permission should be added to the app's `AndroidManifest.xml`

```xml
<uses-permission android:name="android.permission.INTERNET" />
```

## Getting Started

### Using the application manifest

The TelemetryDeck can be initialized automatically by adding the application key to the `application` section of the app's `AndroidManifest.xml`:
Expand Down Expand Up @@ -92,6 +83,36 @@ To enqueue a signal to be sent by TelemetryDeck at a later time
TelemetryDeck.signal("appLaunchedRegularly")
```

### Environment Parameters

By default, TelemetryDeck will include the following environment parameters for each outgoing signal


| Signal name | Provider |
|------------------------------------------------|--------------------------------|
| `TelemetryDeck.Session.started` | `SessionAppProvider` |
| `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.brand` | `EnvironmentParameterProvider` |
| `TelemetryDeck.AppInfo.buildNumber` | `EnvironmentParameterProvider` |
| `TelemetryDeck.AppInfo.version` | `EnvironmentParameterProvider` |
| `TelemetryDeck.AppInfo.versionAndBuildNumber` | `EnvironmentParameterProvider` |
| `TelemetryDeck.RunContext.locale` | `EnvironmentParameterProvider` |
| `TelemetryDeck.RunContext.targetEnvironment` | `EnvironmentParameterProvider` |
| `TelemetryDeck.SDK.name` | `EnvironmentParameterProvider` |
| `TelemetryDeck.SDK.version` | `EnvironmentParameterProvider` |
| `TelemetryDeck.SDK.nameAndVersion` | `EnvironmentParameterProvider` |

See [Custom Telemetry](#custom-telemetry) on how to implement your own parameter enrichment.

## Custom Telemetry

Another way to send signals is to register a custom `TelemetryDeckProvider`.
Expand Down Expand Up @@ -186,14 +207,22 @@ val builder = TelemetryDeck.Builder()

Please note that the logger implementation should be thread safe as it may be invoked in different queues and contexts.

### Migrating providers to 3.0+


## Requirements

- Android API 21 or later
- Kotlin 1.9.25 or later


## Migrating providers to 3.0+

If you had TelemetryDeck SDK for Kotlin 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](https://telemetrydeck.com/docs/articles/grand-rename/).

To upgrade, please perform the following changes depending on how you use TelemetryDeck SDK.

#### Using the application manifest
### If you're using the application manifest

* Adapt the manifest of your app and rename all keys from `com.telemetrydeck.sdk.*` to `com.telemetrydeck.*` for example:

Expand All @@ -211,7 +240,7 @@ After:
* 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 to `TelemetryDeck.signal()`.

#### Programmatic Usage
### Programmatic Usage

* In your app sourcecode, rename all uses of `TelemetryManager` to `TelemetryDeck`.
* If you were using `send()` to send signals, no further changes are needed!
Expand All @@ -225,7 +254,11 @@ After:
| `EnvironmentMetadataProvider` | `EnvironmentParameterProvider` |


#### Custom Telemetry
> [!TIP]
> You can rename all deprecated classes in your project using the Code Cleanup function in IntelliJ/Android Studio.

### Custom Telemetry


Your custom providers must replace `TelemetryProvider` with `TelemetryDeckProvider`.
Expand All @@ -240,8 +273,3 @@ You now have access to the entire `TelemetryDeckClient` interface:
* To access the signal cache, use `client.signalCache`



## Requirements

- Android API 21 or later
- Kotlin 1.9.25 or later
1 change: 0 additions & 1 deletion lib/src/main/java/com/telemetrydeck/sdk/TelemetryDeck.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.app.Application
import android.content.Context
import android.content.pm.ApplicationInfo
import com.telemetrydeck.sdk.providers.EnvironmentParameterProvider
import com.telemetrydeck.sdk.providers.SessionActivityProvider
import com.telemetrydeck.sdk.providers.SessionAppProvider
import java.lang.ref.WeakReference
import java.net.URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import com.telemetrydeck.sdk.BuildConfig
import com.telemetrydeck.sdk.ManifestMetadataReader
import com.telemetrydeck.sdk.TelemetryDeckClient
import com.telemetrydeck.sdk.TelemetryDeckProvider
import com.telemetrydeck.sdk.signals.AppInfo
import com.telemetrydeck.sdk.signals.Device
import com.telemetrydeck.sdk.signals.RunContext
import com.telemetrydeck.sdk.signals.SDK
import java.lang.ref.WeakReference
import java.util.Locale

Expand All @@ -20,55 +24,79 @@ class EnvironmentParameterProvider : TelemetryDeckProvider {
private var enabled: Boolean = true
private var manager: WeakReference<TelemetryDeckClient>? = null
private var metadata = mutableMapOf<String, String>()
// The approach from the SwiftSDK is not compatible here as we need to evaluate for platform capabilities
// In case of Kotlin Multiplatform, a per-platform value can be provided
// For now, we're defaulting to "Android"
private val platform: String = "Android"
private val os: String = "Android"
private val sdkName: String = "KotlinSDK"

override fun register(ctx: Application?, client: TelemetryDeckClient) {
this.manager = WeakReference(client)

if (ctx != null) {
val appVersion = ManifestMetadataReader.getAppVersion(ctx)
if (!appVersion.isNullOrEmpty()) {
metadata["appVersion"] = appVersion
metadata[AppInfo.Version.signalName] = appVersion
}
ManifestMetadataReader.getBuildNumber(ctx)?.let { buildNumber ->
metadata["buildNumber"] = buildNumber.toString()
metadata[AppInfo.BuildNumber.signalName] = buildNumber.toString()
metadata[AppInfo.VersionAndBuildNumber.signalName] = "$appVersion (build $buildNumber)"
}
} else {
this.manager?.get()?.debugLogger?.error("EnvironmentParameterProvider requires a context but received null. Signals will contain incomplete metadata.")
}



if (android.os.Build.VERSION.RELEASE.isNullOrEmpty()) {
this.manager?.get()?.debugLogger?.error(
"EnvironmentMetadataProvider found no platform version information (android.os.Build.VERSION.RELEASE). Signal payloads will not be enriched."
)
} else {
// Device metadata
metadata[Device.Platform.signalName] = platform
val release = android.os.Build.VERSION.RELEASE
val sdkVersion = android.os.Build.VERSION.SDK_INT
metadata["systemVersion"] = "Android SDK: $sdkVersion ($release)"
metadata[Device.SystemVersion.signalName] = "$platform $release (SDK: $sdkVersion)"

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
val versionInfo = VersionInfo.getInstance(release)
metadata["majorSystemVersion"] = versionInfo.major.toString()
metadata["majorMinorSystemVersion"] = "${versionInfo.major}.${versionInfo.minor}"
metadata[Device.SystemMajorVersion.signalName] = "${versionInfo.major}"
metadata[Device.SystemMajorMinorVersion.signalName] = "${versionInfo.major}.${versionInfo.minor}"
} else {
val versionInfo = release.split(".")
metadata["majorSystemVersion"] = versionInfo.elementAtOrNull(0) ?: "0"
metadata["majorMinorSystemVersion"] = "${versionInfo.elementAtOrNull(0) ?: "0"}.${versionInfo.elementAtOrNull(1) ?: "0"}"
val major = versionInfo.elementAtOrNull(0) ?: "0"
val minor = versionInfo.elementAtOrNull(1) ?: "0"
metadata[Device.SystemMajorVersion.signalName] = major
metadata[Device.SystemMajorMinorVersion.signalName] = "$major.$minor"
}
}

metadata["locale"] = Locale.getDefault().displayName
if (android.os.Build.BRAND != null) {
metadata["brand"] = android.os.Build.BRAND
metadata[Device.Brand.signalName] = android.os.Build.BRAND
}
if (android.os.Build.DEVICE != null) {
metadata["targetEnvironment"] = android.os.Build.DEVICE
metadata[RunContext.TargetEnvironment.signalName] = android.os.Build.DEVICE
}
if (android.os.Build.MODEL != null && android.os.Build.PRODUCT != null) {
metadata["modelName"] = "${android.os.Build.MODEL} (${android.os.Build.PRODUCT})"
metadata[Device.ModelName.signalName] = "${android.os.Build.MODEL} (${android.os.Build.PRODUCT})"
}
metadata["architecture"] = System.getProperty("os.arch") ?: ""
metadata["operatingSystem"] = "Android"
metadata["telemetryClientVersion"] = BuildConfig.LIBRARY_PACKAGE_NAME
metadata[Device.Architecture.signalName] = System.getProperty("os.arch") ?: ""
metadata[Device.OperatingSystem.signalName] = os


// SDK Metadata
metadata[SDK.Name.signalName] = sdkName
// TODO: create a build property to pass the maven coordinates of the library
metadata[SDK.Version.signalName] = BuildConfig.LIBRARY_PACKAGE_NAME
metadata[SDK.NameAndVersion.signalName] = "$sdkName ${BuildConfig.LIBRARY_PACKAGE_NAME}"


// RunContext Metadata
metadata[RunContext.Locale.signalName] = Locale.getDefault().displayName


this.enabled = true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import android.app.Application
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import com.telemetrydeck.sdk.SignalType
import com.telemetrydeck.sdk.TelemetryDeckClient
import com.telemetrydeck.sdk.TelemetryDeckProvider
import com.telemetrydeck.sdk.signals.Session
import java.lang.ref.WeakReference

/**
Expand All @@ -28,7 +28,7 @@ class SessionAppProvider: TelemetryDeckProvider, DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
if (manager?.get()?.configuration?.sendNewSessionBeganSignal == true) {
manager?.get()?.signal(
SignalType.NewSessionBegan
Session.Started.signalName
)
}
}
Expand Down
8 changes: 8 additions & 0 deletions lib/src/main/java/com/telemetrydeck/sdk/signals/AppInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.telemetrydeck.sdk.signals

internal enum class AppInfo(val signalName: String) {
BuildNumber("TelemetryDeck.AppInfo.buildNumber"),
Version("TelemetryDeck.AppInfo.version"),
VersionAndBuildNumber("TelemetryDeck.AppInfo.versionAndBuildNumber"),
}

19 changes: 19 additions & 0 deletions lib/src/main/java/com/telemetrydeck/sdk/signals/Device.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.telemetrydeck.sdk.signals


// TODO: add more device parameters from the Swift SDK:
//"TelemetryDeck.Device.orientation": Self.orientation,
//"TelemetryDeck.Device.screenResolutionHeight": Self.screenResolutionHeight,
//"TelemetryDeck.Device.screenResolutionWidth": Self.screenResolutionWidth,
//"TelemetryDeck.Device.timeZone": Self.timeZone,

internal enum class Device(val signalName: String) {
Architecture("TelemetryDeck.Device.architecture"),
ModelName("TelemetryDeck.Device.modelName"),
OperatingSystem("TelemetryDeck.Device.operatingSystem"),
Platform("TelemetryDeck.Device.platform"),
SystemMajorMinorVersion("TelemetryDeck.Device.systemMajorMinorVersion"),
SystemMajorVersion("TelemetryDeck.Device.systemMajorVersion"),
SystemVersion("TelemetryDeck.Device.systemVersion"),
Brand("TelemetryDeck.Device.brand"),
}
14 changes: 14 additions & 0 deletions lib/src/main/java/com/telemetrydeck/sdk/signals/RunContext.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.telemetrydeck.sdk.signals


//"TelemetryDeck.RunContext.isAppStore": "\(Self.isAppStore)",
//"TelemetryDeck.RunContext.isDebug": "\(Self.isDebug)",
//"TelemetryDeck.RunContext.isSimulator": "\(Self.isSimulator)",
//"TelemetryDeck.RunContext.isTestFlight": "\(Self.isTestFlight)",
//"TelemetryDeck.RunContext.language": Self.appLanguage,
//"TelemetryDeck.RunContext.targetEnvironment": Self.targetEnvironment,

internal enum class RunContext(val signalName: String) {
Locale("TelemetryDeck.RunContext.locale"),
TargetEnvironment("TelemetryDeck.RunContext.targetEnvironment"),
}
7 changes: 7 additions & 0 deletions lib/src/main/java/com/telemetrydeck/sdk/signals/SDK.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.telemetrydeck.sdk.signals

internal enum class SDK(val signalName: String) {
Name("TelemetryDeck.SDK.name"),
Version("TelemetryDeck.SDK.version"),
NameAndVersion("TelemetryDeck.SDK.nameAndVersion"),
}
5 changes: 5 additions & 0 deletions lib/src/main/java/com/telemetrydeck/sdk/signals/Session.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.telemetrydeck.sdk.signals

internal enum class Session(val signalName: String) {
Started("TelemetryDeck.Session.started"),
}
4 changes: 2 additions & 2 deletions lib/src/test/java/com/telemetrydeck/sdk/TelemetryDeckTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ class TelemetryDeckTests {
.build(null)
sut.signal("type")

Assert.assertEquals(4, sut.providers.count())
Assert.assertTrue(sut.providers[3] is TestTelemetryDeckProvider)
Assert.assertEquals(3, sut.providers.count())
Assert.assertTrue(sut.providers.last() is TestTelemetryDeckProvider)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class EnvironmentParameterProviderTest {
val queuedSignal = manager.cache?.empty()?.first()

Assert.assertNotNull(queuedSignal)
Assert.assertEquals(queuedSignal?.payload?.contains("telemetryClientVersion:com.telemetrydeck.sdk"), true)
Assert.assertEquals(queuedSignal?.payload?.contains("TelemetryDeck.SDK.version:com.telemetrydeck.sdk"), true)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.telemetrydeck.sdk.providers

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.LifecycleOwner
import com.telemetrydeck.sdk.SignalType
import com.telemetrydeck.sdk.TelemetryDeck
import com.telemetrydeck.sdk.signals.Session
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -40,7 +40,7 @@ class SessionAppProviderTest {
sut.onStart(lifecycleOwner)

Assert.assertEquals(1, manager.cache?.count())
Assert.assertEquals(SignalType.NewSessionBegan.type, manager.cache?.empty()?.get(0)?.type)
Assert.assertEquals(Session.Started.signalName, manager.cache?.empty()?.get(0)?.type)
}

@Test
Expand All @@ -53,7 +53,7 @@ class SessionAppProviderTest {
sut.onStart(lifecycleOwner)

Assert.assertEquals(1, manager.cache?.count())
Assert.assertEquals(SignalType.NewSessionBegan.type, manager.cache?.empty()?.get(0)?.type)
Assert.assertEquals(Session.Started.signalName, manager.cache?.empty()?.get(0)?.type)
}

@Test
Expand Down

0 comments on commit e934093

Please sign in to comment.