Skip to content

Commit

Permalink
feat: sdk initialization re-architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
Shahroz16 committed Oct 13, 2022
1 parent f72280b commit 9e21960
Show file tree
Hide file tree
Showing 33 changed files with 614 additions and 190 deletions.
15 changes: 7 additions & 8 deletions common-test/src/main/java/io/customer/commontest/BaseTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import io.customer.sdk.data.model.Region
import io.customer.sdk.data.store.Client
import io.customer.sdk.data.store.DeviceStore
import io.customer.sdk.di.CustomerIOComponent
import io.customer.sdk.di.CustomerIOSharedComponent
import io.customer.sdk.di.CustomerIOStaticComponent
import io.customer.sdk.module.CustomerIOModuleConfig
import io.customer.sdk.util.*
import okhttp3.ResponseBody.Companion.toResponseBody
Expand Down Expand Up @@ -39,7 +39,8 @@ abstract class BaseTest {
protected lateinit var deviceStore: DeviceStore
protected lateinit var dispatchersProviderStub: DispatchersProviderStub

protected lateinit var sharedDI: CustomerIOSharedComponent
protected lateinit var staticDIComponent: CustomerIOStaticComponent

protected lateinit var di: CustomerIOComponent
protected val jsonAdapter: JsonAdapter
get() = di.jsonAdapter
Expand All @@ -64,7 +65,6 @@ abstract class BaseTest {
backgroundQueueTaskExpiredSeconds: Double = Seconds.fromDays(3).value,
logLevel: CioLogLevel = CioLogLevel.DEBUG,
trackingApiUrl: String? = null,
targetSdkVersion: Int = android.os.Build.VERSION_CODES.R,
configurations: Map<String, CustomerIOModuleConfig> = emptyMap()
) = CustomerIOConfig(
client = client,
Expand All @@ -79,7 +79,6 @@ abstract class BaseTest {
backgroundQueueTaskExpiredSeconds = backgroundQueueTaskExpiredSeconds,
logLevel = logLevel,
trackingApiUrl = trackingApiUrl,
targetSdkVersion = targetSdkVersion,
configurations = configurations
)

Expand All @@ -98,21 +97,21 @@ abstract class BaseTest {
throw RuntimeException("server didnt' start ${cioConfig.trackingApiUrl}")
}

sharedDI = CustomerIOSharedComponent()
staticDIComponent = CustomerIOStaticComponent()
di = CustomerIOComponent(
sharedComponent = sharedDI,
staticComponent = staticDIComponent,
sdkConfig = cioConfig,
context = application
)
di.fileStorage.deleteAllSdkFiles()
di.sharedPreferenceRepository.clearAll()
di.sitePreferenceRepository.clearAll()

dateUtilStub = DateUtilStub().also {
di.overrideDependency(DateUtil::class.java, it)
}
deviceStore = DeviceStoreStub().getDeviceStore(cioConfig)
dispatchersProviderStub = DispatchersProviderStub().also {
sharedDI.overrideDependency(DispatchersProvider::class.java, it)
staticDIComponent.overrideDependency(DispatchersProvider::class.java, it)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ internal class InAppMessagesProviderTest : BaseTest() {
var wasOnErrorCalled = false

gistInAppMessagesProvider.subscribeToEvents(
onMessageShown = { deliveryID ->
onMessageShown = {
wasOnMessageShownCalled = true
},
onAction = { _: String?, _: String, _: String ->
Expand Down
4 changes: 4 additions & 0 deletions messagingpush/api/messagingpush.api
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ public final class io/customer/messagingpush/CustomerIOFirebaseMessagingService
}

public final class io/customer/messagingpush/CustomerIOFirebaseMessagingService$Companion {
public final fun onMessageReceived (Landroid/content/Context;Lcom/google/firebase/messaging/RemoteMessage;)Z
public final fun onMessageReceived (Landroid/content/Context;Lcom/google/firebase/messaging/RemoteMessage;Z)Z
public final fun onMessageReceived (Lcom/google/firebase/messaging/RemoteMessage;)Z
public final fun onMessageReceived (Lcom/google/firebase/messaging/RemoteMessage;Z)Z
public static synthetic fun onMessageReceived$default (Lio/customer/messagingpush/CustomerIOFirebaseMessagingService$Companion;Landroid/content/Context;Lcom/google/firebase/messaging/RemoteMessage;ZILjava/lang/Object;)Z
public static synthetic fun onMessageReceived$default (Lio/customer/messagingpush/CustomerIOFirebaseMessagingService$Companion;Lcom/google/firebase/messaging/RemoteMessage;ZILjava/lang/Object;)Z
public final fun onNewToken (Landroid/content/Context;Ljava/lang/String;)V
public final fun onNewToken (Ljava/lang/String;)V
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package io.customer.messagingpush

import android.content.Context
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.customer.sdk.CustomerIO
import io.customer.sdk.di.CustomerIOComponent

class CustomerIOFirebaseMessagingService : FirebaseMessagingService() {

companion object {
private const val TAG = "FirebaseMessaging:"

private val diGraph: CustomerIOComponent
get() = CustomerIO.instance().diGraph

/**
* Handles receiving an incoming push notification.
*
Expand All @@ -24,11 +19,59 @@ class CustomerIOFirebaseMessagingService : FirebaseMessagingService() {
* @return Boolean indicating whether this will be handled by CustomerIo
*/
@JvmOverloads
fun onMessageReceived(
context: Context,
remoteMessage: RemoteMessage,
handleNotificationTrigger: Boolean = true
): Boolean {
return handleMessageReceived(context, remoteMessage, handleNotificationTrigger)
}

/**
* Handles receiving an incoming push notification.
*
* Call this from a custom [FirebaseMessagingService] to pass push messages to
* CustomerIO SDK for tracking and rendering
* @param remoteMessage Remote message received from Firebase in
* [FirebaseMessagingService.onMessageReceived]
* @param handleNotificationTrigger indicating if the local notification should be triggered
* @return Boolean indicating whether this will be handled by CustomerIO
*/
@Deprecated(
"This method is deprecated and will be removed in future releases. Use the one with context",
ReplaceWith("onMessageReceived(context = applicationContext, remoteMessage = remoteMessage, handleNotificationTrigger = handleNotificationTrigger)")
)
@JvmOverloads
fun onMessageReceived(
remoteMessage: RemoteMessage,
handleNotificationTrigger: Boolean = true
): Boolean {
return handleMessageReceived(remoteMessage, handleNotificationTrigger)
val diGraph = getInstanceOrNull()?.diGraph ?: return false
return handleMessageReceived(diGraph.context, remoteMessage, handleNotificationTrigger)
}

// Only to be used by deprecated methods relying on DI graphs from CustomerIO instance, should be removed with deprecated methods
private fun getInstanceOrNull(): CustomerIO? {
return try {
CustomerIO.instance()
} catch (e: Exception) {
null
}
}

/**
* Handles new or refreshed token
* Call this from [FirebaseMessagingService] to register the new device token
*
* @param token new or refreshed token
*/
@Deprecated(
"This method is deprecated and will be removed in future releases. Use the one with context",
ReplaceWith("onNewToken(context = applicationContext, token = token)")
)
fun onNewToken(token: String) {
val diGraph = getInstanceOrNull()?.diGraph ?: return
handleNewToken(diGraph.context, token)
}

/**
Expand All @@ -37,31 +80,36 @@ class CustomerIOFirebaseMessagingService : FirebaseMessagingService() {
*
* @param token new or refreshed token
*/
@JvmOverloads
fun onNewToken(
context: Context,
token: String
) {
handleNewToken(token)
handleNewToken(context = context, token = token)
}

private fun handleNewToken(token: String) {
CustomerIO.instanceOrNull()?.registerDeviceToken(deviceToken = token)
private fun handleNewToken(context: Context, token: String) {
CustomerIO.instanceOrNull(context)?.registerDeviceToken(deviceToken = token)
}

private fun handleMessageReceived(
context: Context,
remoteMessage: RemoteMessage,
handleNotificationTrigger: Boolean = true
): Boolean {
// if CustomerIO instance isn't initialized, we can't handle the notification
if (CustomerIO.instanceOrNull(context) == null) {
return false
}
val handler = CustomerIOPushNotificationHandler(remoteMessage = remoteMessage)
return handler.handleMessage(diGraph.context, handleNotificationTrigger)
return handler.handleMessage(context, handleNotificationTrigger)
}
}

override fun onNewToken(token: String) {
handleNewToken(token)
handleNewToken(context = this, token = token)
}

override fun onMessageReceived(remoteMessage: RemoteMessage) {
handleMessageReceived(remoteMessage)
handleMessageReceived(this, remoteMessage)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,25 @@ import io.customer.messagingpush.util.DeepLinkUtil
import io.customer.messagingpush.util.PushTrackingUtil.Companion.DELIVERY_ID_KEY
import io.customer.messagingpush.util.PushTrackingUtil.Companion.DELIVERY_TOKEN_KEY
import io.customer.sdk.CustomerIO
import io.customer.sdk.CustomerIOConfig
import io.customer.sdk.CustomerIOShared
import io.customer.sdk.data.request.MetricEvent
import io.customer.sdk.di.CustomerIOComponent
import io.customer.sdk.di.CustomerIOStaticComponent
import io.customer.sdk.util.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.net.URL
import kotlin.math.abs

internal class CustomerIOPushNotificationHandler(private val remoteMessage: RemoteMessage) {
/**
* Class to handle PushNotification.
*
* Make sure [CustomerIO] instance is always initialized before using this class.
*/
internal class CustomerIOPushNotificationHandler(
private val remoteMessage: RemoteMessage
) {

companion object {
const val DEEP_LINK_KEY = "link"
Expand All @@ -52,11 +60,11 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo
private val diGraph: CustomerIOComponent
get() = CustomerIO.instance().diGraph

private val logger: Logger
get() = diGraph.logger
private val staticGraph: CustomerIOStaticComponent
get() = CustomerIOShared.instance().diStaticGraph

private val sdkConfig: CustomerIOConfig
get() = diGraph.sdkConfig
private val logger: Logger
get() = staticGraph.logger

private val moduleConfig: MessagingPushModuleConfig
get() = diGraph.moduleConfig
Expand Down Expand Up @@ -90,7 +98,7 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo
val deliveryToken = bundle.getString(DELIVERY_TOKEN_KEY)

if (deliveryId != null && deliveryToken != null) {
CustomerIO.instance().trackMetric(
CustomerIO.instanceOrNull(context)?.trackMetric(
deliveryID = deliveryId,
deviceToken = deliveryToken,
event = MetricEvent.delivered
Expand Down Expand Up @@ -223,7 +231,7 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo
PendingIntent.FLAG_UPDATE_CURRENT
}

if (sdkConfig.targetSdkVersion > Build.VERSION_CODES.R) {
if (context.applicationInfo.targetSdkVersion > Build.VERSION_CODES.R) {
val taskStackBuilder = moduleConfig.notificationCallback?.createTaskStackFromPayload(
context,
payload
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.customer.messagingpush.di.deepLinkUtil
import io.customer.messagingpush.di.moduleConfig
import io.customer.messagingpush.util.DeepLinkUtil
import io.customer.sdk.CustomerIO
import io.customer.sdk.CustomerIOShared
import io.customer.sdk.data.request.MetricEvent
import io.customer.sdk.di.CustomerIOComponent
import io.customer.sdk.util.Logger
Expand All @@ -22,23 +23,17 @@ internal class CustomerIOPushReceiver : BroadcastReceiver() {
const val PUSH_PAYLOAD_KEY = "CIO-Push-Payload"
}

private val diGraph: CustomerIOComponent
get() = CustomerIO.instance().diGraph

private val logger: Logger
get() = diGraph.logger

private val moduleConfig: MessagingPushModuleConfig
get() = diGraph.moduleConfig

private val deepLinkUtil: DeepLinkUtil
get() = diGraph.deepLinkUtil
get() = CustomerIOShared.instance().diStaticGraph.logger

override fun onReceive(context: Context?, intent: Intent?) {
if (context == null || intent == null) {
return
}

val diGraph: CustomerIOComponent? = CustomerIO.instanceOrNull(context)?.diGraph
val moduleConfig: MessagingPushModuleConfig? = diGraph?.moduleConfig

// Dismiss the notification
val requestCode = intent.getIntExtra(NOTIFICATION_REQUEST_CODE, 0)
val mNotificationManager =
Expand All @@ -49,7 +44,7 @@ internal class CustomerIOPushReceiver : BroadcastReceiver() {
val deliveryId = payload?.cioDeliveryId
val deliveryToken = payload?.cioDeliveryToken

if (deliveryId != null && deliveryToken != null && moduleConfig.autoTrackPushEvents) {
if (deliveryId != null && deliveryToken != null && moduleConfig?.autoTrackPushEvents != false) {
CustomerIO.instance().trackMetric(deliveryId, MetricEvent.opened, deliveryToken)
}

Expand All @@ -60,25 +55,30 @@ internal class CustomerIOPushReceiver : BroadcastReceiver() {

private fun handleDeepLink(context: Context, payload: CustomerIOParsedPushPayload) {
// check if host app overrides the handling of deeplink
val diGraph: CustomerIOComponent? = CustomerIO.instanceOrNull(context)?.diGraph
val moduleConfig: MessagingPushModuleConfig? = diGraph?.moduleConfig

val deepLinkUtil: DeepLinkUtil? = diGraph?.deepLinkUtil

val taskStackBuilder =
moduleConfig.notificationCallback?.createTaskStackFromPayload(context, payload)
moduleConfig?.notificationCallback?.createTaskStackFromPayload(context, payload)
if (taskStackBuilder != null) {
taskStackBuilder.startActivities()
return
}

val deepLinkIntent = deepLinkUtil.createDeepLinkHostAppIntent(
val deepLinkIntent = deepLinkUtil?.createDeepLinkHostAppIntent(
// check if the deep links are handled within the host app
context,
payload.deepLink
) ?: payload.deepLink?.let { link ->
// check if the deep links can be opened outside the host app
deepLinkUtil.createDeepLinkExternalIntent(
deepLinkUtil?.createDeepLinkExternalIntent(
context = context,
link = link,
startingFromService = true
)
} ?: deepLinkUtil.createDefaultHostAppIntent(
} ?: deepLinkUtil?.createDefaultHostAppIntent(
context = context,
contentActionLink = null
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ internal val CustomerIOComponent.fcmTokenProvider: DeviceTokenProvider
get() = override() ?: FCMTokenProviderImpl(logger = logger, context = context)

internal val CustomerIOComponent.moduleConfig: MessagingPushModuleConfig
get() = override() ?: sdkConfig.configurations[
get() = override() ?: sdkConfig.configurations.getOrElse(
ModuleMessagingPushFCM.MODULE_NAME
] as MessagingPushModuleConfig
) {
MessagingPushModuleConfig.default()
} as MessagingPushModuleConfig

internal val CustomerIOComponent.deepLinkUtil: DeepLinkUtil
get() = override() ?: DeepLinkUtilImpl(logger, moduleConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ else resources?.getIdentifier(name, "drawable", packageName)?.takeUnless { id ->
internal fun Context.getColorOrNull(@ColorRes id: Int): Int? = try {
ContextCompat.getColor(this, id)
} catch (ex: Resources.NotFoundException) {
CustomerIOShared.instance().diGraph.logger.error("Invalid resource $id, ${ex.message}")
CustomerIOShared.instance().diStaticGraph.logger.error("Invalid resource $id, ${ex.message}")
null
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import io.customer.sdk.CustomerIOShared
internal fun String.toColorOrNull(): Int? = try {
Color.parseColor(this)
} catch (ex: IllegalArgumentException) {
CustomerIOShared.instance().diGraph.logger.error("Invalid color string $this, ${ex.message}")
CustomerIOShared.instance().diStaticGraph.logger.error("Invalid color string $this, ${ex.message}")
null
}
Loading

0 comments on commit 9e21960

Please sign in to comment.