Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mt 1083 #191

Merged
merged 3 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mobile/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ android {
versionName "1.0"
buildConfigField 'String', 'TAG', "\"App\""
buildConfigField 'String', 'TEALIUM_INSTANCE', "\"main\""
buildConfigField 'String', 'IDENTITY_KEY', "\"identity\""
buildConfigField 'Boolean', 'AUTO_TRACKING', "true"
buildConfigField 'Boolean', 'AUTO_TRACKING_PUSH_ENABLED', "false"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.tealium.fragments

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.fragment.app.Fragment
import com.tealium.core.Tealium
import com.tealium.core.messaging.VisitorIdUpdatedListener
import com.tealium.mobile.BuildConfig
import com.tealium.mobile.databinding.FragmentVisitorServiceBinding
import com.tealium.visitorservice.visitorService
Expand All @@ -15,8 +19,20 @@ import kotlinx.coroutines.launch
class VisitorServiceFragment : Fragment() {

private lateinit var binding: FragmentVisitorServiceBinding
private var existingIdentity: String = ""
private val visitorIdListener = object : VisitorIdUpdatedListener {
override fun onVisitorIdUpdated(visitorId: String) {
activity?.runOnUiThread {
setVisitorId(visitorId)
}
}
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentVisitorServiceBinding.inflate(inflater, container, false)
return binding.root
}
Expand All @@ -26,11 +42,63 @@ class VisitorServiceFragment : Fragment() {
binding.fetchProfileButton.setOnClickListener {
onFetchProfile()
}

binding.btnIdentifyUser.setOnClickListener {
val identity = binding.editCurrentIdentity.text.toString()
if (identity.isNotBlank()) {
onSetIdentity(identity)
}
}

binding.editCurrentIdentity.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

override fun afterTextChanged(s: Editable?) {
binding.btnIdentifyUser.isEnabled = s.toString() != existingIdentity
}
})

setVisitorId(Tealium[BuildConfig.TEALIUM_INSTANCE]?.visitorId ?: "")
}

override fun onResume() {
super.onResume()

existingIdentity =
Tealium[BuildConfig.TEALIUM_INSTANCE]?.dataLayer?.getString(
BuildConfig.IDENTITY_KEY,
) ?: ""
binding.editCurrentIdentity.setText(existingIdentity)

Tealium[BuildConfig.TEALIUM_INSTANCE]?.events?.subscribe(visitorIdListener)
}

override fun onDestroy() {
super.onDestroy()

Tealium[BuildConfig.TEALIUM_INSTANCE]?.events?.unsubscribe(visitorIdListener)
}

@UiThread
private fun setVisitorId(visitorId: String) {
binding.txtVisitorIdPlaceholder.text = visitorId
}

private fun onFetchProfile() {
GlobalScope.launch {
Tealium[BuildConfig.TEALIUM_INSTANCE]?.visitorService?.requestVisitorProfile()
}
}

private fun onSetIdentity(identity: String) {
existingIdentity = identity
GlobalScope.launch {
Tealium[BuildConfig.TEALIUM_INSTANCE]?.dataLayer?.putString(
BuildConfig.IDENTITY_KEY,
identity
)
}
}
}
2 changes: 2 additions & 0 deletions mobile/src/main/java/com/tealium/mobile/TealiumHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ object TealiumHelper : ActivityDataCollector {
// autoTrackingBlocklistUrl = "https://tags.tiqcdn.com/dle/tealiummobile/android/autotracking-blocklist.json"
autoTrackingCollectorDelegate = TealiumHelper
// overrideConsentCategoriesKey = "my_consent_categories_key"

visitorIdentityKey = BuildConfig.IDENTITY_KEY
}

Tealium.create(BuildConfig.TEALIUM_INSTANCE, config) {
Expand Down
50 changes: 50 additions & 0 deletions mobile/src/main/res/layout/fragment_visitor_service.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,54 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<EditText
android:id="@+id/edit_current_identity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="64dp"
android:layout_marginEnd="16dp"
android:hint="@string/hint_identity"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fetchProfileButton" />


<Button
android:id="@+id/btn_identify_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="@string/btn_identify"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_current_identity" />

<TextView
android:id="@+id/txt_visitor_id_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/visitor_id_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.045"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_identify_user" />

<TextView
android:id="@+id/txt_visitor_id_placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/visitor_id_placeholder"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.485"
app:layout_constraintStart_toEndOf="@+id/txt_visitor_id_label"
app:layout_constraintTop_toBottomOf="@+id/btn_identify_user" />

</androidx.constraintlayout.widget.ConstraintLayout>
4 changes: 4 additions & 0 deletions mobile/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@
<string name="second_activity_text_view">Second Activity</string>
<string name="third_activity">Third Activity</string>
<string name="third_activity_text_view">Third Activity</string>
<string name="hint_identity">Identity</string>
<string name="btn_identify">Identify</string>
<string name="visitor_id_label">Visitor Id:</string>
<string name="visitor_id_placeholder"><![CDATA[<visitor id>]]></string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.tealium.core

import android.app.Application
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.tealium.core.persistence.DataLayer
import com.tealium.core.persistence.DatabaseHelper
import com.tealium.core.persistence.DefaultVisitorStorage
import com.tealium.core.persistence.PersistentStorage
import com.tealium.core.persistence.VisitorStorage
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test

class InstrumentedVisitorIdProviderTests {
@MockK
private lateinit var onVisitorIdUpdated: (String) -> Unit

private lateinit var dbHelper: DatabaseHelper
private lateinit var dataLayer: DataLayer
private lateinit var visitorStorage: VisitorStorage

private val visitorIdKey = "identity_key"

@Before
fun setUp() {
MockKAnnotations.init(this)

val context = ApplicationProvider.getApplicationContext<Context>()
val config = TealiumConfig(context as Application, "test", "test", Environment.DEV)
dbHelper = DatabaseHelper(config, null) // in-memory
dbHelper.onCreate(dbHelper.writableDatabase)
dbHelper.onUpgrade(dbHelper.writableDatabase,1, DatabaseHelper.DATABASE_VERSION)

every { onVisitorIdUpdated(any()) } just Runs

dataLayer = PersistentStorage(dbHelper, "visitors", eventRouter = mockk(relaxed = true))
visitorStorage = DefaultVisitorStorage(dbHelper)
}

@Test
fun initial_VisitorId_GetsGenerated() {
val visitorIdProvider =
VisitorIdProvider(null, visitorIdKey, visitorStorage, dataLayer, onVisitorIdUpdated)

assertNotNull(visitorIdProvider.currentVisitorId)
assertTrue(visitorIdProvider.currentVisitorId.isNotBlank())
verify(exactly = 1) {
onVisitorIdUpdated(any())
}
}

@Test
fun onDataUpdated_VisitorId_GetsLinkedToIdentity() {
val visitorIdProvider =
VisitorIdProvider(null, visitorIdKey, visitorStorage, dataLayer, onVisitorIdUpdated)
val originalVisitorId = visitorIdProvider.currentVisitorId

visitorIdProvider.onDataUpdated(visitorIdKey, "new_identity")
val newVisitorId = visitorIdProvider.currentVisitorId

assertNotNull(newVisitorId)
assertEquals(originalVisitorId, newVisitorId)
verify(exactly = 1) {
onVisitorIdUpdated(any())
}
}

@Test
fun onDataUpdated_VisitorId_GetsSetFromKnownIdentity() {
val visitorIdProvider =
VisitorIdProvider(null, visitorIdKey, visitorStorage, dataLayer, onVisitorIdUpdated)
val originalId = visitorIdProvider.currentVisitorId

// Identify original user
visitorIdProvider.onDataUpdated(visitorIdKey, "new_identity")
val knownId1 = visitorIdProvider.currentVisitorId

// Switch identity
visitorIdProvider.onDataUpdated(visitorIdKey, "another_identity")
val knownId2 = visitorIdProvider.currentVisitorId

// Switch Identity Back
visitorIdProvider.onDataUpdated(visitorIdKey, "new_identity")
val knownId1Reverted =visitorIdProvider .currentVisitorId

// Switch back again
visitorIdProvider.onDataUpdated(visitorIdKey, "another_identity")
val knownId2Reverted = visitorIdProvider.currentVisitorId

assertEquals(originalId, knownId1)
// Switch
assertNotEquals(knownId1, knownId2)
// Switch back
assertEquals(knownId1, knownId1Reverted)
// Switch back again
assertEquals(knownId2, knownId2Reverted)
verify(exactly = 4) {
onVisitorIdUpdated(any())
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,18 @@ class TealiumTests {
fun testVisitorIdIsGenerated() {
assertNotNull(tealium.visitorId)
assertEquals(32, tealium.visitorId.length)
assertEquals(tealium.visitorId, tealium.dataLayer.getString("tealium_visitor_id"))
}

@Test
fun testVisitorIdIsReset() {
fun testVisitorIdIsReset() = runBlocking {
val vid = tealium.visitorId
assertNotNull(vid)
assertEquals(32, tealium.visitorId.length)
assertEquals(tealium.visitorId, tealium.dataLayer.getString("tealium_visitor_id"))
val storedVid = tealium.dataLayer.getString("tealium_visitor_id")

val resetVid = tealium.resetVisitorId()
delay(100) // reset is async
val storedResetVid = tealium.dataLayer.getString("tealium_visitor_id")
assertNotEquals(vid, resetVid)
assertNotEquals(storedVid, storedResetVid)
Expand All @@ -140,6 +140,7 @@ class TealiumTests {
Environment.DEV
)
config.existingVisitorId = "testExistingVisitorId"
deleteInstanceStorage(config)
val test = Tealium.create("tester", config)

val vid = test.visitorId
Expand All @@ -149,21 +150,23 @@ class TealiumTests {
}

@Test
fun resetExistingVisitorId() {
fun resetExistingVisitorId() = runBlocking {
val config = TealiumConfig(
application,
"testAccount2",
"testProfile2",
Environment.DEV
)
config.existingVisitorId = "testExistingVisitorId"
deleteInstanceStorage(config)
val teal = Tealium.create("tester2", config)

val vid = teal.visitorId
val storedVid = teal.dataLayer.getString("tealium_visitor_id")
assertEquals("testExistingVisitorId", vid)

val resetVid = teal.resetVisitorId()
delay(100) // reset is async
val storedResetVid = teal.dataLayer.getString("tealium_visitor_id")

assertNotEquals(vid, resetVid)
Expand All @@ -179,6 +182,7 @@ class TealiumTests {
"testProfile2",
Environment.DEV
)
config.tealiumDirectory.deleteRecursively()
config.existingVisitorId = "testExistingVisitorId"
Tealium.create("tester2", config) {
val data = gatherTrackData()
Expand Down Expand Up @@ -365,6 +369,12 @@ class TealiumTests {
}
}
}

private fun deleteInstanceStorage(config: TealiumConfig) {
try {
config.tealiumDirectory.deleteRecursively()
} catch (ignored: Exception) { }
}
}

private class TestFactory(context: TealiumContext, payload: Any?) : Collector {
Expand Down
Loading