Skip to content

Commit 9622fc6

Browse files
Merge branch 'development'
2 parents 68ddeda + c3f3753 commit 9622fc6

File tree

14 files changed

+180
-56
lines changed

14 files changed

+180
-56
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Initiate analytics and send events
4646

4747
```kotlin
4848
// init analytics
49-
analytics = Analytics(context = context,
49+
analytics = Analytics(settings = AnalyticsSettings(context),
5050
FirebaseDispatcherImpl(init = true),
5151
MixPanelDispatcherImpl(init = true, projectToken = "TOKEN"),
5252
AnswersDispatcherImpl(init = true))

analytics/build.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ dependencies {
5959
// required if you want to use Mockito for Android tests
6060
androidTestImplementation 'org.mockito:mockito-android:2.7.22'
6161

62+
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
63+
64+
65+
6266
// testCompile 'org.powermock:powermock:1.6.5'
6367
// testCompile 'org.powermock:powermock-module-junit4:1.6.5'
6468
//
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package com.sofakingforever.analytics
22

3-
import android.content.Context
4-
import android.util.Log
53
import com.sofakingforever.analytics.events.base.Event
64
import com.sofakingforever.analytics.exceptions.EventNotTrackedException
5+
import com.sofakingforever.analytics.version.VersionChecker
76

87
/**
98
* The *Analytics* class is in charge of tracking any *Event* implementation.
@@ -13,55 +12,74 @@ import com.sofakingforever.analytics.exceptions.EventNotTrackedException
1312
*
1413
* @constructor create an instance of the *Analytics* class
1514
*/
16-
class Analytics(context: Context, private vararg val dispatchers: AnalyticsDispatcher) {
15+
class Analytics(val settings: AnalyticsSettings, private vararg val dispatchers: AnalyticsDispatcher) {
1716

17+
var exceptionHandler: ExceptionHandler? = null
1818

19-
private val enabledKitMap: EnabledMap<AnalyticsKit> = EnabledMap()
20-
private val enabledDispatcherMap: EnabledMap<String> = EnabledMap()
19+
init {
2120

22-
val settings: AnalyticsSettings = AnalyticsSettings()
21+
// check for new library version if enabled
22+
if (settings.checkForUpdates) VersionChecker.onCheckVersion()
2323

24-
init {
24+
// init all dispatchers
2525
dispatchers.forEach { dispatcher ->
2626
if (dispatcher.init) {
27-
dispatcher.initDispatcher(context.applicationContext)
27+
dispatcher.initDispatcher(settings.context.applicationContext)
2828
}
2929
}
30+
3031
}
3132

33+
3234
/**
3335
* Call this to track one or more *Events*
3436
*/
3537
fun track(vararg events: Event) {
3638

3739
if (settings.isAnalyticsEnabled.not()) return
3840

39-
events.forEach {
41+
42+
events.forEach { event ->
43+
4044
dispatchers.forEach { dispatcher ->
4145

42-
if (enabledKitMap.isDisabled(dispatcher.kit)) {
43-
return
44-
}
45-
if (enabledDispatcherMap.isDisabled(dispatcher.dispatcherName)) {
46-
return
47-
}
46+
if (settings.enabledKits.isDisabled(dispatcher.kit)) return
47+
48+
if (settings.enabledDispatchers.isDisabled(dispatcher.dispatcherName)) return
49+
4850
try {
49-
dispatcher.track(it)
51+
dispatcher.track(event)
5052
} catch (e: Exception) {
51-
settings.exceptionHandler?.onException(EventNotTrackedException(dispatcher, it, e))
53+
exceptionHandler?.onException(EventNotTrackedException(dispatcher, event, e))
5254
}
5355
}
5456

5557

5658
}
5759
}
5860

61+
/**
62+
* Set Kit as enabled or disabled for future event dispatches
63+
*/
5964
fun setKitEnabled(kit: AnalyticsKit, enabled: Boolean) {
60-
enabledKitMap[kit] = enabled
65+
settings.enabledKits[kit] = enabled
6166
}
6267

68+
/**
69+
* Set Dispatcher as enabled or disabled for future event dispatches
70+
*/
6371
fun setDispatcherEnabled(dispatcherName: String, enabled: Boolean) {
64-
enabledDispatcherMap[dispatcherName] = enabled
72+
settings.enabledDispatchers[dispatcherName] = enabled
73+
}
74+
75+
/**
76+
* Just an exception callback to log/monitor exceptions,
77+
* thrown by the *Analytics* class or any of its dispatchers.
78+
*/
79+
interface ExceptionHandler {
80+
81+
fun onException(e: Exception)
82+
6583
}
6684

6785
}

analytics/src/main/java/com/sofakingforever/analytics/AnalyticsDispatcher.kt

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ interface AnalyticsDispatcher {
2222
val kit: AnalyticsKit
2323

2424
val dispatcherName : String
25+
2526
/**
2627
* Should call the analytics library's initiation methods
2728
*/
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
package com.sofakingforever.analytics
22

3+
import android.content.Context
4+
35
/**
46
* Holds some things for the Analytics class
57
* @property isAnalyticsEnabled - no events will be sent if this is set to *false*
8+
* @property checkForUpdates - should the library check for updates
69
* @property exceptionHandler - implementation of @ExceptionHandler
710
*/
8-
class AnalyticsSettings {
11+
class AnalyticsSettings(val context: Context) {
912

1013
@Volatile
1114
var isAnalyticsEnabled = true
15+
var checkForUpdates = true
1216

13-
var exceptionHandler: ExceptionHandler? = null
17+
val enabledKits: ServiceEnabledMap<AnalyticsKit> = ServiceEnabledMap()
18+
val enabledDispatchers: ServiceEnabledMap<String> = ServiceEnabledMap()
1419

15-
/**
16-
* Just an exception callback to log/monitor exceptions,
17-
* thrown by the *Analytics* class or any of its dispatchers.
18-
*/
19-
interface ExceptionHandler {
20+
class ServiceEnabledMap<Key> : LinkedHashMap<Key, Boolean>() {
2021

21-
fun onException(e: Exception)
22+
fun isDisabled(key: Key): Boolean = this[key] == false
2223

2324
}
25+
}
2426

25-
26-
}

analytics/src/main/java/com/sofakingforever/analytics/EnabledMap.kt

-7
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.sofakingforever.analytics.exceptions
2+
3+
import java.net.MalformedURLException
4+
5+
class IllegalVersionFormatException(version : String) : MalformedURLException("Version '$version' is illegal") {
6+
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.sofakingforever.analytics.version
2+
3+
import com.sofakingforever.analytics.exceptions.IllegalVersionFormatException
4+
5+
class Version : Comparable<Version> {
6+
7+
private val version: String
8+
9+
@Throws(IllegalVersionFormatException::class) constructor(version: String) {
10+
if (!version.contains(".")) throw IllegalVersionFormatException(version)
11+
this.version = version
12+
}
13+
14+
override fun toString(): String {
15+
return version
16+
}
17+
18+
override fun compareTo(that: Version): Int {
19+
20+
val levels1 = this.version.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
21+
val levels2 = that.version.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
22+
23+
val length = Math.max(levels1.size, levels2.size)
24+
25+
for (i in 0 until length) {
26+
val v1 = if (i < levels1.size) Integer.parseInt(levels1[i]) else 0
27+
val v2 = if (i < levels2.size) Integer.parseInt(levels2[i]) else 0
28+
val compare = v1.compareTo(v2)
29+
if (compare != 0) {
30+
return compare
31+
}
32+
}
33+
34+
return 0
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.sofakingforever.analytics.version
2+
3+
import android.util.Log
4+
import com.sofakingforever.analytics.exceptions.IllegalVersionFormatException
5+
import com.sofakingforever.library.BuildConfig
6+
import okhttp3.*
7+
import java.io.IOException
8+
9+
object VersionChecker {
10+
11+
fun onCheckVersion() {
12+
13+
val currentVersion = Version(BuildConfig.VERSION_NAME)
14+
15+
val client = OkHttpClient.Builder()
16+
.followRedirects(true)
17+
.build()
18+
19+
val request = Request.Builder()
20+
.url("https://bintray.com/sofakingforever/analytics/kotlin-analytics/_latestVersion")
21+
.get()
22+
.build()
23+
24+
client.newCall(request).enqueue(object : Callback {
25+
26+
override fun onFailure(call: Call?, e: IOException?) {
27+
// ignore failure, no need to fill logcat with bullshit
28+
}
29+
30+
override fun onResponse(call: Call?, response: Response?) {
31+
// try to finish up
32+
response?.url().also { url ->
33+
34+
try {
35+
val latestVersion = Version(extractVersion(url))
36+
37+
if (currentVersion < latestVersion) {
38+
// user should update
39+
Log.w("Analytics", "Latest kotlin-analytics version is $latestVersion > $currentVersion")
40+
}
41+
} catch (e: IllegalVersionFormatException) {
42+
// ignore failure, no need to fill logcat with bullshit
43+
}
44+
45+
46+
}
47+
}
48+
49+
})
50+
}
51+
52+
fun extractVersion(it: HttpUrl?) : String = it?.encodedPathSegments()?.last() ?: ""
53+
54+
private fun Response?.url(): HttpUrl? {
55+
return this?.networkResponse()?.request()?.url()
56+
}
57+
58+
}

analytics/src/test/java/com/sofakingforever/library/AnalyticsUnitTest.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.sofakingforever.library
22

33
import android.content.Context
4-
import android.util.Log
54
import com.sofakingforever.analytics.Analytics
6-
import com.sofakingforever.analytics.AnalyticsSettings
75
import com.sofakingforever.analytics.exceptions.EventNotTrackedException
86
import com.sofakingforever.analytics.exceptions.UnsupportedEventException
97
import com.sofakingforever.library.dispatcher.TestKit
@@ -34,7 +32,7 @@ class AnalyticsUnitTest {
3432

3533
analytics = Analytics(contextMock, dispatcher).apply {
3634

37-
this.settings.exceptionHandler = object : AnalyticsSettings.ExceptionHandler {
35+
this.settings.exceptionHandler = object : Analytics.ExceptionHandler {
3836
override fun onException(e: Exception) {
3937
raisedException = e
4038
}

app/build.gradle

+9-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,16 @@ dependencies {
2929
testImplementation 'junit:junit:4.12'
3030
androidTestImplementation 'com.android.support.test:runner:1.0.2'
3131
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
32+
3233
implementation project(path: ':analytics')
3334
implementation project(path: ':kit-answers')
3435
implementation project(path: ':kit-firebase')
35-
implementation project(':kit-flurry')
36-
implementation project(':kit-mixpanel')
36+
implementation project(path: ':kit-flurry')
37+
implementation project(path: ':kit-mixpanel')
38+
39+
implementation('com.crashlytics.sdk.android:answers:1.4.2@aar') {
40+
transitive = true
41+
}
42+
43+
3744
}

app/src/main/java/com/sofakingforever/example/App.kt

+11-12
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import com.sofakingforever.analytics.Analytics
66
import com.sofakingforever.analytics.AnalyticsSettings
77
import com.sofakingforever.analytics.kits.answers.AnswersDispatcherImpl
88
import com.sofakingforever.analytics.kits.firebase.FirebaseDispatcherImpl
9-
import com.sofakingforever.example.custom.CustomDispatcher
109
import com.sofakingforever.analytics.kits.logger.LoggerDispatcherImpl
1110
import com.sofakingforever.analytics.kits.mixpanel.MixPanelDispatcherImpl
11+
import com.sofakingforever.example.custom.CustomDispatcher
1212

1313
class App : Application() {
1414

@@ -18,8 +18,14 @@ class App : Application() {
1818
super.onCreate()
1919

2020

21+
// set an analytics enabled / disabled via SharedPrefs, Database, or anything else
22+
val settings = AnalyticsSettings(this).also {
23+
it.isAnalyticsEnabled = true
24+
25+
}
26+
2127
// init analytics property. this is in charge of tracking all events
22-
analytics = Analytics(this,
28+
analytics = Analytics(settings,
2329
CustomDispatcher(init = true),
2430
LoggerDispatcherImpl(init = true),
2531
FirebaseDispatcherImpl(init = true),
@@ -29,29 +35,22 @@ class App : Application() {
2935

3036
// if you're using crashlytics, or any other fabric kit in addition to Answers
3137
// AnswersDispatcherImpl(init = true, Answers(), Crashlytics())
32-
)
33-
34-
35-
analytics.settings.apply {
36-
37-
// set analytics enabled / disabled via SharedPrefs, Database, or anything else
38-
isAnalyticsEnabled = true
38+
).also {
3939

4040
// set an exception handler
4141
// either way, the analytics util won't crash your app
42-
exceptionHandler = object : AnalyticsSettings.ExceptionHandler {
42+
it.exceptionHandler = object : Analytics.ExceptionHandler {
4343
override fun onException(e: Exception) {
4444

4545
// this is the exception, log it, send it or ignore it.
46-
4746
Log.w("Analytics", "Analytics Exception Raised")
4847

4948
e.printStackTrace()
5049
}
5150

5251
}
53-
5452
}
5553

54+
5655
}
5756
}

app/src/main/java/com/sofakingforever/example/events/EventPerKit.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.sofakingforever.example.events
22

3-
import com.sofakingforever.analytics.events.CustomEvent
43
import com.sofakingforever.analytics.AnalyticsKit
4+
import com.sofakingforever.analytics.events.CustomEvent
5+
import com.sofakingforever.analytics.kits.answers.AnswersKit
6+
import com.sofakingforever.analytics.kits.firebase.FirebaseKit
57

68
class EventPerKit(private val whatever: Boolean) : CustomEvent {
79
override fun getEventName(kit: AnalyticsKit): String = when (kit) {

0 commit comments

Comments
 (0)