Skip to content

Commit

Permalink
feat: event tracking, identify customer (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shahroz16 committed Oct 15, 2021
1 parent c7d795c commit 5181268
Show file tree
Hide file tree
Showing 33 changed files with 1,028 additions and 53 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Expand Up @@ -48,6 +48,9 @@ dependencies {
implementation Dependencies.androidxCoreKtx
implementation Dependencies.materialComponents

implementation Dependencies.coroutinesCore
implementation Dependencies.coroutinesAndroid

androidTestImplementation Dependencies.junit4
testImplementation Dependencies.junit4
testImplementation Dependencies.androidxTestJunit
Expand Down
14 changes: 10 additions & 4 deletions app/src/main/AndroidManifest.xml
@@ -1,17 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.customer.example">

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

<application
android:name=".MainApplication"
android:allowBackup="true"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name=".MainApplication"
android:theme="@style/Theme.App">
<activity android:name="io.customer.example.MainActivity"
android:exported="false">
android:theme="@style/Theme.App"
tools:targetApi="m">
<activity
android:name="io.customer.example.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
85 changes: 84 additions & 1 deletion app/src/main/java/io/customer/example/MainActivity.kt
@@ -1,13 +1,96 @@
package io.customer.example

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import io.customer.base.comunication.Action
import io.customer.base.data.ErrorResult
import io.customer.base.data.Success
import io.customer.sdk.CustomerIO
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)
// there are two ways to identify customer

// 1st way
// makeSynchronousRequest()

// 2nd way
// makeAsynchronousRequest()

// log events
makeEventsRequests()
}

private val outputCallback = Action.Callback<Unit> { result ->
when (result) {
is ErrorResult -> Log.v("ErrorResult", result.error.getDisplayMessage())
is Success -> Log.v("Success", "Success")
}
}

data class Fol(val a: String, val c: Int)

private fun makeEventsRequests() {
CustomerIO.instance().track(
name = "string event",
attributes = mapOf(
"value" to "string test",
"target" to 1
)
).enqueue(outputCallback)
CustomerIO.instance().track(
name = "int event",
attributes = mapOf("value" to 1)
).enqueue(outputCallback)
CustomerIO.instance().track(
name = "long event",
attributes = mapOf("value" to 1L)
).enqueue(outputCallback)
CustomerIO.instance().track(
name = "array event",
attributes = mapOf("value" to listOf("1", "2"))
).enqueue(outputCallback)
CustomerIO.instance().track(
name = "date event",
attributes = mapOf("value" to Date())
).enqueue(outputCallback)
CustomerIO.instance().track(
name = "timestamp event",
attributes = mapOf("value" to Date().time)
).enqueue(outputCallback)
CustomerIO.instance().track(
name = "custom class event",
attributes = mapOf("value" to Fol(a = "aa", c = 1))
)
}

private fun makeAsynchronousRequest() {
CustomerIO.instance()
.identify(
identifier = "identifier",
attributes = mapOf("email" to "testemail@email.com")
).enqueue(outputCallback)
}

private fun makeSynchronousRequest() {
CoroutineScope(Dispatchers.IO).launch {
when (
val result =
CustomerIO.instance()
.identify("sample@email.com@email.com", mapOf("speed" to "slow"))
.execute()
) {
is ErrorResult -> Log.v("ErrorResult", result.error.cause.toString())
is Success -> Log.v("Success", "Success")
}
}
}
}
13 changes: 12 additions & 1 deletion app/src/main/java/io/customer/example/MainApplication.kt
@@ -1,5 +1,16 @@
package io.customer.example

import android.app.Application
import io.customer.sdk.CustomerIO

class MainApplication : Application()
class MainApplication : Application() {

override fun onCreate() {
super.onCreate()
CustomerIO.Builder(
siteId = "YOUR-SITE-ID",
apiKey = "YOUR-API-KEY",
appContext = this
).build()
}
}
43 changes: 43 additions & 0 deletions base/src/main/java/io/customer/base/comunication/Action.kt
@@ -0,0 +1,43 @@
package io.customer.base.comunication

import androidx.annotation.WorkerThread
import io.customer.base.data.Result

/**
*
* [Action] can be used by following ways:
* [execute]: Action may be executed synchronously or asynchronously with [enqueue]
*
*/
interface Action<T : Any> {
/**
* Synchronously send the request and return its response. Only call this from a background thread.
*/
@WorkerThread
public fun execute(): Result<T>

/**
* Asynchronously send the request and notify callback of its response.
*/
fun enqueue(callback: Callback<T>)

/**
* Executes the call asynchronously, on a background thread. Safe to call from the main
* thread.
*
* To get notified of the result and handle errors, use enqueue(callback) instead.
*/
fun enqueue(): Unit = enqueue {}

/**
* Cancels the execution of the call, if cancellation is supported for the operation.
*
* Note that calls can not be cancelled when running them with [execute].
*/
fun cancel()

fun interface Callback<T : Any> {
fun onResult(result: Result<T>)
}
}

2 changes: 1 addition & 1 deletion base/src/main/java/io/customer/base/data/Result.kt
Expand Up @@ -11,7 +11,7 @@ sealed class Result<T> {
}
}

data class Success<T>(val data: T, val responseModified: Boolean = true) : Result<T>() {
data class Success<T>(val data: T) : Result<T>() {
override fun get(): T = data
}

Expand Down
19 changes: 15 additions & 4 deletions base/src/main/java/io/customer/base/error/ErrorDetail.kt
@@ -1,6 +1,17 @@
package io.customer.base.error

public open class ErrorDetail(
public val message: String? = null,
public val cause: Throwable = Throwable()
)
open class ErrorDetail(
val message: String? = null,
val statusCode: StatusCode = StatusCode.Unknown,
val cause: Throwable = Throwable()
) {
fun getDisplayMessage(): String {
if (message != null) {
return message
}
if (statusCode != StatusCode.Unknown) {
return statusCode.getMessage()
}
return cause.message ?: statusCode.getMessage()
}
}
81 changes: 81 additions & 0 deletions base/src/main/java/io/customer/base/error/StatusCode.kt
@@ -0,0 +1,81 @@
package io.customer.base.error

enum class StatusCode(val code: Int) {
UnIdentifiedUser(1),

Continue(100),
SwitchingProtocols(101),
Processing(102),

OK(200),
Created(201),
Accepted(202),
NonAuthoritativeInformation(203),
NoContent(204),
ResetContent(205),
PartialContent(206),
MultiStatus(207),
AlreadyReported(208),
IMUsed(226),

MultipleChoices(300),
MovedPermanently(301),
Found(302),
SeeOther(303),
NotModified(304),
UseProxy(305),
TemporaryRedirect(307),
PermanentRedirect(308),

BadRequest(400),
Unauthorized(401),
PaymentRequired(402),
Forbidden(403),
NotFound(404),
MethodNotAllowed(405),
NotAcceptable(406),
ProxyAuthenticationRequired(407),
RequestTimeout(408),
Conflict(409),
Gone(410),
LengthRequired(411),
PreconditionFailed(412),
PayloadTooLarge(413),
URITooLong(414),
UnsupportedMediaType(415),
RangeNotSatisfiable(416),
ExpectationFailed(417),
IAmATeapot(418),
MisdirectedRequest(421),
UnprocessableEntity(422),
Locked(423),
FailedDependency(424),
UpgradeRequired(426),
PreconditionRequired(428),
TooManyRequests(429),
RequestHeaderFieldsTooLarge(431),
UnavailableForLegalReasons(451),

InternalServerError(500),
NotImplemented(501),
BadGateway(502),
ServiceUnavailable(503),
GatewayTimeout(504),
HTTPVersionNotSupported(505),
VariantAlsoNegotiates(506),
InsufficientStorage(507),
LoopDetected(508),
NotExtended(510),
NetworkAuthenticationRequired(511),

Unknown(0);

// Custom description for the Error to describe the error that happened.
fun getMessage(): String {
return when (this) {
Unauthorized -> "HTTP request responded with 401. Configure the SDK with valid credentials."
UnIdentifiedUser -> "Customer has not yet been identified."
else -> this.name
}
}
}
Expand Up @@ -28,6 +28,7 @@ object Dependencies {
const val timber = "com.jakewharton.timber:timber:${Versions.TIMBER}"
const val robolectric = "org.robolectric:robolectric:${Versions.ROBOLECTRIC}"
const val retrofit = "com.squareup.retrofit2:retrofit:${Versions.RETROFIT}"
const val retrofitMoshiConverter = "com.squareup.retrofit2:converter-moshi:${Versions.RETROFIT}"
const val okhttp = "com.squareup.okhttp3:okhttp:${Versions.OKHTTP}"
const val okhttpLoggingInterceptor = "com.squareup.okhttp3:logging-interceptor:${Versions.OKHTTP}"

Expand Down
5 changes: 1 addition & 4 deletions sdk/build.gradle
Expand Up @@ -46,15 +46,12 @@ android {
dependencies {
api project(":base")

implementation Dependencies.retrofit

implementation Dependencies.coroutinesCore
implementation Dependencies.coroutinesAndroid
implementation Dependencies.retrofit
implementation Dependencies.retrofitMoshiConverter
implementation Dependencies.okhttpLoggingInterceptor

testImplementation Dependencies.androidxTestJunit
androidTestImplementation Dependencies.junit4

}

Expand Up @@ -2,7 +2,7 @@ package io.customer.sdk

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

Expand Down
7 changes: 7 additions & 0 deletions sdk/src/main/AndroidManifest.xml
@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.customer.sdk">

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

<application
android:hardwareAccelerated="true"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="n" />
</manifest>

0 comments on commit 5181268

Please sign in to comment.