Skip to content

Commit

Permalink
feat: add identity module and file storage (#8)
Browse files Browse the repository at this point in the history
* feat: add identity module and file storage
  • Loading branch information
qingzhuozhen authored Mar 18, 2022
1 parent 4a4ff66 commit 8b7bada
Show file tree
Hide file tree
Showing 27 changed files with 736 additions and 18 deletions.
5 changes: 4 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,13 @@ android {
dependencies {
implementation project(':common')
implementation project(':common-android')
implementation project(':id')
implementation project(':core')
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'

coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'

testImplementation 'io.mockk:mockk:1.10.6'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
Expand All @@ -55,4 +58,4 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2'
testImplementation 'org.robolectric:robolectric:4.7.3'
testImplementation 'androidx.test:core:1.4.0'
}
}
6 changes: 6 additions & 0 deletions android/src/main/java/com/amplitude/android/Amplitude.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ package com.amplitude.android

import com.amplitude.core.Amplitude
import com.amplitude.core.platform.plugins.AmplitudeDestination
import com.amplitude.core.utilities.AnalyticsIdentityListener
import com.amplitude.id.FileIdentityStorageProvider
import com.amplitude.id.IdentityConfiguration
import com.amplitude.id.IdentityContainer

open class Amplitude(
configuration: Configuration
): Amplitude(configuration) {

override fun build() {
idContainer = IdentityContainer.getInstance(IdentityConfiguration(instanceName = configuration.instanceName, apiKey = configuration.apiKey, identityStorageProvider = FileIdentityStorageProvider()))
idContainer.identityManager.addIdentityListener(AnalyticsIdentityListener(store))
add(AmplitudeDestination())
}
}
6 changes: 4 additions & 2 deletions android/src/main/java/com/amplitude/android/Configuration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import com.amplitude.core.StorageProvider
import com.amplitude.android.utilities.AndroidLoggerProvider
import com.amplitude.android.utilities.AndroidStorageProvider
import com.amplitude.core.events.BaseEvent
import com.amplitude.core.utilities.FileStorageProvider

class Configuration(
apiKey: String,
context: Context,
flushQueueSize: Int = FLUSH_QUEUE_SIZE,
flushIntervalMillis: Int = FLUSH_INTERVAL_MILLIS,
instanceName: String = DEFAULT_INSTANCE,
optOut: Boolean = false,
storageProvider: StorageProvider = AndroidStorageProvider(),
storageProvider: StorageProvider = FileStorageProvider(),
loggerProvider: LoggerProvider = AndroidLoggerProvider(),
minIdLength: Int? = null,
callback: ((BaseEvent) -> Unit)? = null,
useAdvertisingIdForDeviceId: Boolean = false,
useAppSetIdForDeviceId: Boolean = false,
enableCoppaControl: Boolean = false
) : Configuration(apiKey, flushQueueSize, flushIntervalMillis, optOut, storageProvider, loggerProvider, minIdLength, callback)
) : Configuration(apiKey, flushQueueSize, flushIntervalMillis, instanceName, optOut, storageProvider, loggerProvider, minIdLength, callback)
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@ import com.amplitude.core.Amplitude
import com.amplitude.core.Storage
import com.amplitude.core.StorageProvider
import com.amplitude.core.events.BaseEvent
import com.amplitude.core.utilities.FileStorageProvider

class AndroidStorage(
val amplitude: Amplitude
) : Storage {
override fun write(event: BaseEvent) {
override suspend fun writeEvent(event: BaseEvent) {
TODO("Not yet implemented")
}

override fun rollover() {
override suspend fun write(key: Storage.Constants, value: String) {
TODO("Not yet implemented")
}

override suspend fun rollover() {
TODO("Not yet implemented")
}

override fun read(key: Storage.Constants): String? {
TODO("Not yet implemented")
}

Expand Down
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
implementation project(":common")
implementation project(":common-jvm")
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(':id')
// MAIN DEPS
compileOnly 'org.json:json:20211205'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
Expand Down
30 changes: 28 additions & 2 deletions core/src/main/java/com/amplitude/core/Amplitude.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import com.amplitude.core.events.BaseEvent
import com.amplitude.core.events.Identify
import com.amplitude.core.events.Revenue
import com.amplitude.core.events.RevenueEvent
import com.amplitude.core.platform.ObservePlugin
import com.amplitude.core.platform.Plugin
import com.amplitude.core.platform.Timeline
import com.amplitude.core.platform.plugins.AmplitudeDestination
import com.amplitude.core.platform.plugins.ContextPlugin
import com.amplitude.core.utilities.AnalyticsIdentityListener
import com.amplitude.id.IMIdentityStorageProvider
import com.amplitude.id.IdentityConfiguration
import com.amplitude.id.IdentityContainer
import kotlinx.coroutines.*
import java.util.concurrent.Executors

Expand All @@ -24,6 +29,7 @@ open class Amplitude internal constructor(
internal val timeline: Timeline
val storage: Storage
val logger: Logger
protected lateinit var idContainer: IdentityContainer

init {
require(configuration.isValid()) { "invalid configuration" }
Expand All @@ -39,6 +45,8 @@ open class Amplitude internal constructor(
constructor(configuration: Configuration) : this(configuration, State())

open fun build() {
idContainer = IdentityContainer.getInstance(IdentityConfiguration(instanceName = configuration.instanceName, apiKey = configuration.apiKey, identityStorageProvider = IMIdentityStorageProvider()))
idContainer.identityManager.addIdentityListener(AnalyticsIdentityListener(store))
add(ContextPlugin())
add(AmplitudeDestination())

Expand All @@ -61,7 +69,11 @@ open class Amplitude internal constructor(
}

fun identify(userId: String) {
this.idContainer.identityManager.editIdentity().setUserId(userId).commit()
}

fun setDeviceId(deviceId: String) {
this.idContainer.identityManager.editIdentity().setDeviceId(deviceId).commit()
}

fun groupIdentify(identify: Identify) {
Expand Down Expand Up @@ -102,12 +114,26 @@ open class Amplitude internal constructor(
}

fun add(plugin: Plugin) : Amplitude {
this.timeline.add(plugin)
when (plugin) {
is ObservePlugin -> {
this.store.add(plugin)
}
else -> {
this.timeline.add(plugin)
}
}
return this
}

fun remove(plugin: Plugin): Amplitude {
this.timeline.remove(plugin)
when (plugin) {
is ObservePlugin -> {
this.store.remove(plugin)
}
else -> {
this.timeline.remove(plugin)
}
}
return this
}

Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/com/amplitude/core/Configuration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open class Configuration(
val apiKey: String,
val flushQueueSize: Int = FLUSH_QUEUE_SIZE,
val flushIntervalMillis: Int = FLUSH_INTERVAL_MILLIS,
val instanceName: String = DEFAULT_INSTANCE,
val optOut: Boolean = false,
val storageProvider: StorageProvider = InMemoryStorageProvider(),
val loggerProvider: LoggerProvider = ConsoleLoggerProvider(),
Expand All @@ -18,6 +19,7 @@ open class Configuration(
companion object {
const val FLUSH_QUEUE_SIZE = 30
const val FLUSH_INTERVAL_MILLIS = 30 * 1000 // 30s
const val DEFAULT_INSTANCE = "\$default_instance"
}

fun isValid(): Boolean {
Expand Down
28 changes: 27 additions & 1 deletion core/src/main/java/com/amplitude/core/State.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
package com.amplitude.core

import com.amplitude.core.platform.ObservePlugin

class State {
var userId: String? = null
set(value: String?) {
userId = value
plugins.forEach { plugin ->
plugin.onUserIdChanged(value)
}
}

var deviceId: String? = null
set(value: String?) {
deviceId = value
plugins.forEach { plugin ->
plugin.onDeviceIdChanged(value)
}
}

val plugins: MutableList<ObservePlugin> = mutableListOf()

fun add(plugin: ObservePlugin) = synchronized(plugins) {
plugins.add(plugin)
}

}
fun remove(plugin: ObservePlugin) = synchronized(plugins) {
plugins.removeAll { it === plugin }
}
}
17 changes: 15 additions & 2 deletions core/src/main/java/com/amplitude/core/Storage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,22 @@ package com.amplitude.core
import com.amplitude.core.events.BaseEvent

interface Storage {
fun write(event: BaseEvent)

fun rollover()
enum class Constants(val rawVal: String) {
LAST_EVENT_ID("last_event_id"),
PREVIOUS_SESSION_ID("previous_session_id"),
LAST_EVENT_TIME("last_event_time"),
OPT_OUT("opt_out"),
Events("events")
}

suspend fun writeEvent(event: BaseEvent)

suspend fun write(key: Constants, value: String)

suspend fun rollover()

fun read(key: Constants): String?

fun getEvents(): List<String>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import java.util.concurrent.atomic.AtomicInteger

internal class EventPipeline(
private val amplitude: Amplitude

) {

private val writeChannel: Channel<WriteQueueMessage>
Expand Down Expand Up @@ -72,7 +71,7 @@ internal class EventPipeline(
// write to storage
val triggerFlush = (message.type == WriteQueueMessageType.FLUSH)
if (!triggerFlush && message.event != null) try {
storage.write(message.event)
storage.writeEvent(message.event)
} catch (e: Exception) {
e.printStackTrace()
}
Expand Down Expand Up @@ -148,4 +147,4 @@ enum class WriteQueueMessageType {
data class WriteQueueMessage(
val type: WriteQueueMessageType,
val event: BaseEvent?
)
)
11 changes: 11 additions & 0 deletions core/src/main/java/com/amplitude/core/platform/Plugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,14 @@ abstract class DestinationPlugin: EventPlugin {
}
}

abstract class ObservePlugin: Plugin {
override val type: Plugin.Type = Plugin.Type.Observe

abstract fun onUserIdChanged(userId: String?)

abstract fun onDeviceIdChanged(deviceId: String?)

final override fun execute(event: BaseEvent): BaseEvent? {
return null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.amplitude.core.utilities

import com.amplitude.core.State
import com.amplitude.id.Identity
import com.amplitude.id.IdentityListener
import com.amplitude.id.IdentityUpdateType

class AnalyticsIdentityListener(private val state: State) : IdentityListener {

override fun onUserIdChange(userId: String?) {
state.userId = userId
}

override fun onDeviceIdChange(deviceId: String?) {
state.deviceId = deviceId
}

override fun onIdentityChanged(identity: Identity, updateType: IdentityUpdateType) {
if (updateType == IdentityUpdateType.Initialized) {
state.userId = identity.userId
state.deviceId = identity.deviceId
// TODO("update device id based on configuration")
}
}
}
Loading

0 comments on commit 8b7bada

Please sign in to comment.