Skip to content

Commit 807002b

Browse files
Merge branch 'development'
2 parents eb743b5 + 60db96a commit 807002b

File tree

25 files changed

+402
-49
lines changed

25 files changed

+402
-49
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ repositories {
2525
2626
dependencies {
2727
28-
def version = '1.0.10'
28+
def version = '1.0.11'
2929
30-
// add the basic analytics interface library
30+
// add the basic analytics interface library - incl. LoggerDispatcher
3131
compile "com.sofakingforever.analytics:analytics:version@aar"
3232
3333
// then add the kits you need
@@ -46,12 +46,15 @@ Initiate analytics and send events
4646

4747
```kotlin
4848
// init analytics
49-
analytics = Analytics(this, AnswersDispatcherImpl(), FirebaseDispatcherImpl())
49+
analytics = Analytics(context = context,
50+
FirebaseDispatcherImpl(init = true),
51+
MixPanelDispatcherImpl(init = true, projectToken = "TOKEN"),
52+
AnswersDispatcherImpl(init = true))
5053

5154
// send event
5255
analytics.track(SimpleEvent())
5356

54-
// declare event - will be sent to both Answers and Firebase
57+
// declare event - will be sent to both Firebase, MixPanel and Answers
5558
class SimpleEvent : CustomEvent {
5659
override fun getEventName(kit: AnalyticsKit): String = "Simple Event"
5760

analytics/build.gradle

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,32 @@ android {
3737
}
3838
}
3939

40+
// testOptions {
41+
// unitTests.returnDefaultValues = true
42+
// }
4043
}
4144

4245
dependencies {
46+
4347
implementation fileTree(dir: 'libs', include: ['*.jar'])
4448

49+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
50+
4551
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
4652
testImplementation 'junit:junit:4.12'
53+
54+
4755
androidTestImplementation 'com.android.support.test:runner:1.0.2'
4856
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
49-
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
50-
57+
// required if you want to use Mockito for unit tests
58+
testImplementation 'org.mockito:mockito-core:2.7.22'
59+
// required if you want to use Mockito for Android tests
60+
androidTestImplementation 'org.mockito:mockito-android:2.7.22'
61+
62+
// testCompile 'org.powermock:powermock:1.6.5'
63+
// testCompile 'org.powermock:powermock-module-junit4:1.6.5'
64+
//
65+
// testCompile 'org.powermock:powermock-api-mockito:1.6.5'
5166

5267

5368
}

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@ package com.sofakingforever.analytics
33
import android.content.Context
44
import android.util.Log
55
import com.sofakingforever.analytics.events.base.Event
6+
import com.sofakingforever.analytics.exceptions.EventNotTrackedException
67

78
/**
89
* The *Analytics* class is in charge of tracking any *Event* implementation.
910
*
10-
* @property context any context from the app
11+
* @param context any context from the app
1112
* @property dispatchers list of *AnalyticsDispatchers* to trigger for every event
1213
*
1314
* @constructor create an instance of the *Analytics* class
1415
*/
1516
class Analytics(context: Context, private vararg val dispatchers: AnalyticsDispatcher) {
1617

18+
19+
private val enabledKitMap: EnabledMap<AnalyticsKit> = EnabledMap()
20+
private val enabledDispatcherMap: EnabledMap<String> = EnabledMap()
21+
1722
val settings: AnalyticsSettings = AnalyticsSettings()
1823

1924
init {
2025
dispatchers.forEach { dispatcher ->
2126
if (dispatcher.init) {
22-
dispatcher.initDispatcher(context)
27+
dispatcher.initDispatcher(context.applicationContext)
2328
}
2429
}
2530
}
@@ -32,20 +37,31 @@ class Analytics(context: Context, private vararg val dispatchers: AnalyticsDispa
3237
if (settings.isAnalyticsEnabled.not()) return
3338

3439
events.forEach {
35-
3640
dispatchers.forEach { dispatcher ->
3741

42+
if (enabledKitMap.isDisabled(dispatcher.kit)) {
43+
return
44+
}
45+
if (enabledDispatcherMap.isDisabled(dispatcher.dispatcherName)) {
46+
return
47+
}
3848
try {
3949
dispatcher.track(it)
4050
} catch (e: Exception) {
41-
Log.e("Analytics", "${dispatcher.kit.name} dispatcher couldn't fire \"${it.javaClass.name}\" event", e)
42-
settings.exceptionHandler?.onException(e)
51+
settings.exceptionHandler?.onException(EventNotTrackedException(dispatcher, it, e))
4352
}
4453
}
4554

4655

4756
}
4857
}
4958

59+
fun setKitEnabled(kit: AnalyticsKit, enabled: Boolean) {
60+
enabledKitMap[kit] = enabled
61+
}
62+
63+
fun setDispatcherEnabled(dispatcherName: String, enabled: Boolean) {
64+
enabledDispatcherMap[dispatcherName] = enabled
65+
}
5066

5167
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface AnalyticsDispatcher {
2121

2222
val kit: AnalyticsKit
2323

24+
val dispatcherName : String
2425
/**
2526
* Should call the analytics library's initiation methods
2627
*/
@@ -33,7 +34,7 @@ interface AnalyticsDispatcher {
3334

3435
fun trackInviteEvent(inviteEvent: InviteEvent)
3536

36-
fun setUserProperty(property : SetUserProperty)
37+
fun setUserProperty(property: SetUserProperty)
3738

3839
/**
3940
* This method is called from the parent @Analytics class for each event.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.sofakingforever.analytics
2+
3+
class EnabledMap<Key> : LinkedHashMap<Key, Boolean>() {
4+
5+
fun isDisabled(something : Key) : Boolean = this[something] == false
6+
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.sofakingforever.analytics.exceptions
2+
3+
import com.sofakingforever.analytics.AnalyticsDispatcher
4+
import com.sofakingforever.analytics.events.base.Event
5+
6+
class EventNotTrackedException(message: String?, cause: Throwable?) : RuntimeException(message, cause) {
7+
8+
constructor(dispatcher: AnalyticsDispatcher, event: Event, t: Throwable) : this("${dispatcher.dispatcherName} dispatcher couldn't fire \"${event.javaClass.name}\" event", t)
9+
10+
}

analytics/src/main/java/com/sofakingforever/analytics/kits/logger/LoggerDispatcherImpl.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ import com.sofakingforever.analytics.events.SetUserProperty
1717
*/
1818
class LoggerDispatcherImpl(override val init: Boolean) : AnalyticsDispatcher {
1919

20-
20+
override val dispatcherName: String = "LoggerDispatcher"
2121

2222
constructor() : this(true)
2323

24-
private val tag = "LoggerDispatcher"
24+
25+
private val tag = dispatcherName
2526

2627
override val kit: AnalyticsKit = LoggerKit.instance
2728

@@ -40,7 +41,8 @@ class LoggerDispatcherImpl(override val init: Boolean) : AnalyticsDispatcher {
4041
override fun trackInviteEvent(inviteEvent: InviteEvent) {
4142
Log.d(tag, "Tracking inviteEvent ${inviteEvent.packageName}")
4243
}
44+
4345
override fun setUserProperty(property: SetUserProperty) {
44-
// used only for certain analytics
46+
Log.d(tag, "Tracking user property ${property.key} = ${property.value}")
4547
}
4648
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.sofakingforever.library
2+
3+
import android.content.Context
4+
import android.util.Log
5+
import com.sofakingforever.analytics.Analytics
6+
import com.sofakingforever.analytics.AnalyticsSettings
7+
import com.sofakingforever.analytics.exceptions.EventNotTrackedException
8+
import com.sofakingforever.analytics.exceptions.UnsupportedEventException
9+
import com.sofakingforever.library.dispatcher.TestKit
10+
import com.sofakingforever.library.dispatcher.TestableDispatcher
11+
import com.sofakingforever.library.events.InitDispatcherEvent
12+
import com.sofakingforever.library.events.TestContentViewEvent
13+
import com.sofakingforever.library.events.TestCustomEvent
14+
import com.sofakingforever.library.events.UnsupportedEvent
15+
import org.junit.Test
16+
import org.mockito.Mockito
17+
import org.mockito.Mockito.mock
18+
19+
class AnalyticsUnitTest {
20+
21+
private val contextMock = mock(Context::class.java)
22+
23+
private lateinit var analytics: Analytics
24+
25+
private val dispatcher = TestableDispatcher()
26+
private var raisedException: Exception? = null
27+
28+
init {
29+
Mockito.`when`(contextMock.applicationContext).thenReturn(contextMock)
30+
}
31+
32+
@Test
33+
fun testAnalytics() {
34+
35+
analytics = Analytics(contextMock, dispatcher).apply {
36+
37+
this.settings.exceptionHandler = object : AnalyticsSettings.ExceptionHandler {
38+
override fun onException(e: Exception) {
39+
raisedException = e
40+
}
41+
42+
}
43+
}
44+
45+
// track some events
46+
trackTestEvents()
47+
48+
// assert events
49+
assertEvents()
50+
51+
// assert no exceptions were raised
52+
assert(raisedException == null)
53+
54+
// track an unsupported event
55+
analytics.track(UnsupportedEvent())
56+
57+
// assert UnsupportedEventException was raised
58+
assert(raisedException != null)
59+
assert(raisedException is EventNotTrackedException)
60+
assert((raisedException as EventNotTrackedException).cause is UnsupportedEventException)
61+
62+
}
63+
64+
65+
private fun trackTestEvents() {
66+
67+
analytics.track(TestCustomEvent(1))
68+
69+
analytics.setDispatcherEnabled(TestableDispatcher.DispatcherName, false)
70+
71+
analytics.track(TestCustomEvent(2))
72+
73+
analytics.setDispatcherEnabled(TestableDispatcher.DispatcherName, true)
74+
75+
analytics.track(TestCustomEvent(3))
76+
77+
analytics.setKitEnabled(TestKit.instance, false)
78+
79+
analytics.track(TestCustomEvent(4))
80+
81+
analytics.setKitEnabled(TestKit.instance, true)
82+
83+
analytics.track(TestCustomEvent(5))
84+
85+
analytics.track(TestContentViewEvent(1))
86+
87+
}
88+
89+
90+
private fun assertEvents() {
91+
92+
93+
val eventList = dispatcher.eventList
94+
95+
// expect to find 3 custom events , 1 contentview and 1 init event
96+
assert(eventList.size == 5)
97+
assert(eventList[0] is InitDispatcherEvent)
98+
assert((eventList[1] as TestCustomEvent).number == 1)
99+
assert((eventList[2] as TestCustomEvent).number == 3)
100+
assert((eventList[3] as TestCustomEvent).number == 5)
101+
assert((eventList[4] as TestContentViewEvent).number == 1)
102+
}
103+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.sofakingforever.library
2+
3+
import android.app.Application
4+
import com.sofakingforever.library.dispatcher.TestableDispatcher
5+
import com.sofakingforever.library.events.InitDispatcherEvent
6+
import com.sofakingforever.library.events.TestCustomEvent
7+
import org.junit.Test
8+
9+
10+
class DispatcherTest {
11+
12+
private lateinit var raisedException: Exception
13+
14+
@Test
15+
fun testDispatcher() {
16+
17+
val dispatcher = TestableDispatcher()
18+
19+
dispatcher.initDispatcher(Application())
20+
21+
dispatcher.track(TestCustomEvent(1))
22+
23+
dispatcher.track(TestCustomEvent(2))
24+
25+
dispatcher.track(TestCustomEvent(3))
26+
27+
dispatcher.track(TestCustomEvent(4))
28+
29+
dispatcher.track(TestCustomEvent(5))
30+
31+
val eventList = dispatcher.eventList
32+
33+
// expect to find 5 events + init event
34+
assert(eventList.size == 6)
35+
assert(eventList[0] is InitDispatcherEvent)
36+
assert((eventList[1] as TestCustomEvent).number == 1)
37+
38+
// that's enough
39+
40+
}
41+
}

analytics/src/test/java/com/sofakingforever/library/ExampleUnitTest.java

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.sofakingforever.library.dispatcher
2+
3+
import com.sofakingforever.analytics.AnalyticsKit
4+
5+
class TestKit private constructor() : AnalyticsKit {
6+
override val name: String = "Test Kit"
7+
8+
private object Holder {
9+
val INSTANCE = TestKit()
10+
}
11+
12+
companion object {
13+
val instance: TestKit by lazy { Holder.INSTANCE }
14+
}
15+
16+
17+
}

0 commit comments

Comments
 (0)