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

feat: integrate Clickstream Android SDK #6

Merged
merged 3 commits into from
Jan 8, 2024
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ We look forward to seeing the insightful ways you leverage Clickstream for enhan
## Android SDK Example
Android example app **Joomia** shopping app is forked from https://github.com/joelkanyi/Joomia. To get started with the Android example, refer to the [Android Example README](android/README.md).

You can refer this [PR](https://github.com/aws-samples/clickstream-sdk-samples/pull/6/files) to learn how to integrate Clickstream Android SDK.

More references:

[Clickstream Android SDK Repositry](https://github.com/awslabs/clickstream-android)
Expand Down
2 changes: 2 additions & 0 deletions android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Joomia
A fake shopping app built with Jetpack Compose consuming [FAKE STORE API](https://fakestoreapi.com/)

And this app is already integrated with Clickstream Android SDK. You can find more detail about the [Clickstream Android SDK](https://github.com/awslabs/clickstream-android).

## Note:
- The API has some endpoints that does nothing i.e adding item to cart, registering a user and many more. So some screens have UI implementation but no logic
- Contributions are welcomed, because the app is far away from being good
Expand Down
1 change: 1 addition & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,5 @@ dependencies {
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"

implementation 'software.aws.solution:clickstream:0.10.0'
}
16 changes: 16 additions & 0 deletions android/app/src/main/java/com/kanyideveloper/joomia/JoomiaApp.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
package com.kanyideveloper.joomia

import android.app.Application
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import com.amplifyframework.AmplifyException
import dagger.hilt.android.HiltAndroidApp
import software.aws.solution.clickstream.ClickstreamAnalytics
import timber.log.Timber

@HiltAndroidApp
class JoomiaApp : Application() {
@OptIn(ExperimentalComposeUiApi::class)
override fun onCreate() {
super.onCreate()
initTimber()
try {
Modifier.semantics { testTagsAsResourceId = true; testTagsAsResourceId = true }
ClickstreamAnalytics.init(applicationContext)
ClickstreamAnalytics.getClickStreamConfiguration()
.withLogEvents(true)
Timber.i("MyApp", "Initialized ClickstreamAnalytics")
} catch (error: AmplifyException) {
Timber.e("MyApp", "Could not initialize ClickstreamAnalytics", error)
}
}

private fun initTimber() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.kanyideveloper.joomia.feature_auth.data.remote.request.LoginRequest
import com.kanyideveloper.joomia.feature_auth.domain.repository.LoginRepository
import kotlinx.coroutines.flow.first
import retrofit2.HttpException
import software.aws.solution.clickstream.ClickstreamAnalytics
import software.aws.solution.clickstream.ClickstreamUserAttribute
import timber.log.Timber
import java.io.IOException
import kotlin.random.Random
Expand All @@ -24,6 +26,15 @@ class LoginRepositoryImpl(
val gender = if (Random.nextBoolean()) "women" else "men"
it.avatar = avatarUrl + gender + "/" + Random.nextInt(0, 99) + ".jpg"
dataPreferences.saveUserdata(it)
ClickstreamAnalytics.setUserId(it.id.toString())
val userAttribute = ClickstreamUserAttribute.builder()
.add("user_name", it.name.firstname + " " + it.name.lastname)
.add("email", it.email)
.add("phone", it.phone)
.add("gender", gender)
.build()
ClickstreamAnalytics.addUserAttributes(userAttribute)
ClickstreamAnalytics.recordEvent("login")

val randomLoginRequest = LoginRequest(
username = it.username.trim(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import com.kanyideveloper.joomia.feature_auth.domain.use_case.LoginUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import software.aws.solution.clickstream.ClickstreamAnalytics
import software.aws.solution.clickstream.ClickstreamUserAttribute
import javax.inject.Inject

@HiltViewModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.util.rangeTo
import androidx.hilt.navigation.compose.hiltViewModel
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
Expand All @@ -32,7 +33,11 @@ import com.kanyideveloper.joomia.core.util.LoadingAnimation
import com.kanyideveloper.joomia.core.util.UiEvents
import com.kanyideveloper.joomia.feature_cart.domain.model.CartProduct
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.flow.collectLatest
import software.aws.solution.clickstream.ClickstreamAnalytics
import software.aws.solution.clickstream.ClickstreamEvent
import software.aws.solution.clickstream.ClickstreamItem

@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Destination
Expand All @@ -45,6 +50,7 @@ fun CartScreen(
val scaffoldState = rememberScaffoldState()

LaunchedEffect(key1 = true) {
ClickstreamAnalytics.recordEvent("view_cart")
viewModel.eventFlow.collectLatest { event ->
when (event) {
is UiEvents.SnackbarEvent -> {
Expand Down Expand Up @@ -196,6 +202,22 @@ private fun CheckoutComponent(state: CartItemsState) {
testTag = "check_out_button"; testTagsAsResourceId = true
},
onClick = {
val items = arrayOfNulls<ClickstreamItem>(state.cartItems.size)
for (i in 0 until state.cartItems.size) {
val itemProduct = ClickstreamItem.builder()
.add(ClickstreamAnalytics.Item.ITEM_ID, state.cartItems[i].id)
.add(ClickstreamAnalytics.Item.ITEM_NAME, state.cartItems[i].name)
.add(ClickstreamAnalytics.Item.PRICE, state.cartItems[i].price)
.add(ClickstreamAnalytics.Item.QUANTITY, state.cartItems[i].quantity)
.add(ClickstreamAnalytics.Item.ITEM_CATEGORY, state.cartItems[i].category)
.build()
items[i] = itemProduct
}
val event = ClickstreamEvent.builder()
.name("check_out")
.setItems(items)
.build()
ClickstreamAnalytics.recordEvent(event)
},
shape = RoundedCornerShape(8)
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kanyideveloper.joomia.feature_products.presentation.home

import android.annotation.SuppressLint
import android.util.Log
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
Expand All @@ -16,7 +17,12 @@ import androidx.compose.material.MaterialTheme.typography
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddShoppingCart
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.Center
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
Expand All @@ -25,9 +31,11 @@ import androidx.compose.ui.Alignment.Companion.End
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.semantics
Expand Down Expand Up @@ -58,6 +66,10 @@ import com.kanyideveloper.joomia.feature_profile.domain.model.getDisplayName
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.flow.collectLatest
import software.aws.solution.clickstream.ClickstreamAnalytics
import software.aws.solution.clickstream.ClickstreamEvent
import software.aws.solution.clickstream.ClickstreamItem
import timber.log.Timber

@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@OptIn(ExperimentalComposeUiApi::class)
Expand All @@ -72,6 +84,7 @@ fun HomeScreen(
val productsState = viewModel.productsState.value
val categories = viewModel.categoriesState.value
LaunchedEffect(key1 = true, block = {
ClickstreamAnalytics.recordEvent("view_home")
viewModel.getProfile()
})
val user = viewModel.profileState.value
Expand Down Expand Up @@ -133,6 +146,20 @@ private fun HomeScreenContent(
// Actual product items list
items(productsState.products) { product ->
LaunchedEffect(product.id) {
val itemProduct = ClickstreamItem.builder()
.add(ClickstreamAnalytics.Item.ITEM_ID, product.id)
.add(ClickstreamAnalytics.Item.ITEM_NAME, product.title)
.add(ClickstreamAnalytics.Item.ITEM_CATEGORY, product.category)
.add(ClickstreamAnalytics.Item.PRICE, product.price)
.add("rating", product.rating.rate)
.add("description", product.description.take(200))
.build()
val event = ClickstreamEvent.builder()
.name("product_exposure")
.add("item_id", product.id)
.setItems(arrayOf(itemProduct))
.build()
ClickstreamAnalytics.recordEvent(event)
}
ProductItem(
product = product,
Expand Down Expand Up @@ -250,6 +277,21 @@ private fun ProductItem(

OutlinedButton(
onClick = {
val itemProduct = ClickstreamItem.builder()
.add(ClickstreamAnalytics.Item.ITEM_ID, product.id)
.add(ClickstreamAnalytics.Item.ITEM_NAME, product.title)
.add(ClickstreamAnalytics.Item.ITEM_CATEGORY, product.category)
.add(ClickstreamAnalytics.Item.PRICE, product.price)
.add("rating", product.rating.rate)
.add("description", product.description.take(200))
.build()
val event = ClickstreamEvent.builder()
.name("add_to_cart")
.add("item_id", product.id)
.add("page_name", "home_screen")
.setItems(arrayOf(itemProduct))
.build()
ClickstreamAnalytics.recordEvent(event)
},
modifier = Modifier
.size(40.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ import com.kanyideveloper.joomia.feature_wish_list.domain.model.Wishlist
import com.kanyideveloper.joomia.feature_wish_list.presentation.wishlist.WishlistViewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import software.aws.solution.clickstream.ClickstreamAnalytics
import software.aws.solution.clickstream.ClickstreamEvent
import software.aws.solution.clickstream.ClickstreamItem

@OptIn(ExperimentalComposeUiApi::class)
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
Expand Down Expand Up @@ -143,6 +146,20 @@ fun DetailsScreenContent(
) {
Column {
LaunchedEffect(product.id) {
val itemProduct = ClickstreamItem.builder()
.add(ClickstreamAnalytics.Item.ITEM_ID, product.id)
.add(ClickstreamAnalytics.Item.ITEM_NAME, product.title)
.add(ClickstreamAnalytics.Item.ITEM_CATEGORY, product.category)
.add(ClickstreamAnalytics.Item.PRICE, product.price)
.add("rating", product.rating.rate)
.add("description", product.description.take(200))
.build()
val event = ClickstreamEvent.builder()
.name("item_view")
.add("item_id", product.id)
.setItems(arrayOf(itemProduct))
.build()
ClickstreamAnalytics.recordEvent(event)
}
Box(modifier = modifier.weight(1f), contentAlignment = Alignment.Center) {
Image(
Expand Down Expand Up @@ -250,6 +267,21 @@ fun DetailsScreenContent(
testTag = "add_to_cart_button"; testTagsAsResourceId = true
},
onClick = {
val itemProduct = ClickstreamItem.builder()
.add(ClickstreamAnalytics.Item.ITEM_ID, product.id)
.add(ClickstreamAnalytics.Item.ITEM_NAME, product.title)
.add(ClickstreamAnalytics.Item.ITEM_CATEGORY, product.category)
.add(ClickstreamAnalytics.Item.PRICE, product.price)
.add("rating", product.rating.rate)
.add("description", product.description.take(200))
.build()
val event = ClickstreamEvent.builder()
.name("add_to_cart")
.add("product_id", product.id)
.add("page_name", "product_detail_screen")
.setItems(arrayOf(itemProduct))
.build()
ClickstreamAnalytics.recordEvent(event)
},
colors = ButtonDefaults.buttonColors(
contentColor = Color.Black,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import com.kanyideveloper.joomia.feature_profile.domain.model.getDisplayName
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.flow.collectLatest
import software.aws.solution.clickstream.ClickstreamAnalytics
import java.util.*

@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
Expand All @@ -63,6 +64,7 @@ fun AccountScreen(
val scaffoldState = rememberScaffoldState()

LaunchedEffect(key1 = true) {
ClickstreamAnalytics.recordEvent("view_account")
viewModel.eventFlow.collectLatest { event ->
when (event) {
is UiEvents.SnackbarEvent -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import software.aws.solution.clickstream.ClickstreamAnalytics
import timber.log.Timber
import javax.inject.Inject

Expand Down Expand Up @@ -51,6 +52,7 @@ class ProfileViewModel @Inject constructor(
Timber.d("Result: ${result.message}")
when (result) {
is Resource.Success -> {
ClickstreamAnalytics.setUserId(null)
_eventFlow.emit(
UiEvents.NavigateEvent(route = AuthDashboardScreenDestination.route)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.kanyideveloper.joomia.feature_wish_list.domain.model.Wishlist
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.flow.collectLatest
import software.aws.solution.clickstream.ClickstreamAnalytics

@Destination
@Composable
Expand All @@ -49,6 +50,7 @@ fun WishlistScreen(
val scaffoldState = rememberScaffoldState()

LaunchedEffect(key1 = true) {
ClickstreamAnalytics.recordEvent("view_wishlist")
viewModel.eventFlow.collectLatest { event ->
when (event) {
is UiEvents.SnackbarEvent -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import software.aws.solution.clickstream.ClickstreamAnalytics
import software.aws.solution.clickstream.ClickstreamEvent
import software.aws.solution.clickstream.ClickstreamItem
import javax.inject.Inject

@HiltViewModel
Expand All @@ -27,6 +30,20 @@ class WishlistViewModel @Inject constructor(
viewModelScope.launch {
repository.insertToWishlist(wishlist)
}
val itemProduct = ClickstreamItem.builder()
.add(ClickstreamAnalytics.Item.ITEM_ID, wishlist.id)
.add(ClickstreamAnalytics.Item.ITEM_NAME, wishlist.title)
.add(ClickstreamAnalytics.Item.ITEM_CATEGORY, wishlist.category)
.add(ClickstreamAnalytics.Item.PRICE, wishlist.price)
.add("rating", wishlist.rating.rate)
.add("description", wishlist.description.take(200))
.build()
val event = ClickstreamEvent.builder()
.name("add_to_wishlist")
.add("item_id", wishlist.id)
.setItems(arrayOf(itemProduct))
.build()
ClickstreamAnalytics.recordEvent(event)
}

fun inWishlist(id: Int): LiveData<Boolean> {
Expand All @@ -40,6 +57,20 @@ class WishlistViewModel @Inject constructor(
UiEvents.SnackbarEvent(message = "Item removed from wishlist")
)
}
val itemProduct = ClickstreamItem.builder()
.add(ClickstreamAnalytics.Item.ITEM_ID, wishlist.id)
.add(ClickstreamAnalytics.Item.ITEM_NAME, wishlist.title)
.add(ClickstreamAnalytics.Item.ITEM_CATEGORY, wishlist.category)
.add(ClickstreamAnalytics.Item.PRICE, wishlist.price)
.add("rating", wishlist.rating.rate)
.add("description", wishlist.description.take( 200))
.build()
val event = ClickstreamEvent.builder()
.name("remove_from_wishlist")
.add("item_id", wishlist.id)
.setItems(arrayOf(itemProduct))
.build()
ClickstreamAnalytics.recordEvent(event)
}

fun deleteAllWishlist() {
Expand Down
Loading