Skip to content

Commit

Permalink
fix!: android 12 deep link fix
Browse files Browse the repository at this point in the history
  • Loading branch information
mrehan27 authored Aug 10, 2022
1 parent 1036c80 commit fd7ae28
Show file tree
Hide file tree
Showing 27 changed files with 723 additions and 148 deletions.
35 changes: 34 additions & 1 deletion common-test/src/main/java/io/customer/commontest/BaseTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.customer.sdk.CustomerIOConfig
import io.customer.sdk.data.model.Region
import io.customer.sdk.data.store.DeviceStore
import io.customer.sdk.di.CustomerIOComponent
import io.customer.sdk.module.CustomerIOModuleConfig
import io.customer.sdk.util.CioLogLevel
import io.customer.sdk.util.DateUtil
import io.customer.sdk.util.DispatchersProvider
Expand Down Expand Up @@ -51,9 +52,41 @@ abstract class BaseTest {
protected lateinit var mockWebServer: MockWebServer
protected lateinit var dateUtilStub: DateUtilStub

protected fun createConfig(
siteId: String = this.siteId,
apiKey: String = "xyz",
region: Region = Region.EU,
timeout: Long = 100,
autoTrackScreenViews: Boolean = true,
autoTrackDeviceAttributes: Boolean = true,
backgroundQueueMinNumberOfTasks: Int = 10,
backgroundQueueSecondsDelay: Double = 30.0,
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(
siteId = siteId,
apiKey = apiKey,
region = region,
timeout = timeout,
autoTrackScreenViews = autoTrackScreenViews,
autoTrackDeviceAttributes = autoTrackDeviceAttributes,
backgroundQueueMinNumberOfTasks = backgroundQueueMinNumberOfTasks,
backgroundQueueSecondsDelay = backgroundQueueSecondsDelay,
backgroundQueueTaskExpiredSeconds = backgroundQueueTaskExpiredSeconds,
logLevel = logLevel,
trackingApiUrl = trackingApiUrl,
targetSdkVersion = targetSdkVersion,
configurations = configurations
)

protected open fun setupConfig() = createConfig()

@Before
open fun setup() {
cioConfig = CustomerIOConfig(siteId, "xyz", Region.EU, 100, null, true, true, 10, 30.0, Seconds.fromDays(3).value, CioLogLevel.DEBUG, null)
cioConfig = setupConfig()

// Initialize the mock web server before constructing DI graph as dependencies may require information such as hostname.
mockWebServer = MockWebServer().apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.customer.messaginginapp

import io.customer.sdk.module.CustomerIOModuleConfig

/**
* In app messaging module configurations that can be used to customize app
* experience based on the provided configurations
*/
class MessagingInAppModuleConfig : CustomerIOModuleConfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@ import android.app.Application
import io.customer.messaginginapp.di.gistProvider
import io.customer.messaginginapp.hook.ModuleInAppHookProvider
import io.customer.sdk.CustomerIO
import io.customer.sdk.CustomerIOModule
import io.customer.sdk.data.request.MetricEvent
import io.customer.sdk.di.CustomerIOComponent
import io.customer.sdk.hooks.HookModule
import io.customer.sdk.hooks.HooksManager
import io.customer.sdk.module.CustomerIOModule
import io.customer.sdk.repository.TrackRepository

class ModuleMessagingInApp internal constructor(
override val moduleConfig: MessagingInAppModuleConfig = MessagingInAppModuleConfig(),
private val overrideDiGraph: CustomerIOComponent?,
private val organizationId: String
) : CustomerIOModule {
) : CustomerIOModule<MessagingInAppModuleConfig> {

constructor(organizationId: String) : this(
@JvmOverloads
constructor(
organizationId: String,
config: MessagingInAppModuleConfig = MessagingInAppModuleConfig()
) : this(
moduleConfig = config,
overrideDiGraph = null,
organizationId = organizationId
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.customer.messagingpush

import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
Expand All @@ -10,14 +9,21 @@ import android.graphics.BitmapFactory
import android.media.RingtoneManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.customer.messagingpush.data.model.CustomerIOParsedPushPayload
import io.customer.messagingpush.di.deepLinkUtil
import io.customer.messagingpush.di.moduleConfig
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.data.request.MetricEvent
import io.customer.sdk.util.PushTrackingUtilImpl.Companion.DELIVERY_ID_KEY
import io.customer.sdk.util.PushTrackingUtilImpl.Companion.DELIVERY_TOKEN_KEY
import io.customer.sdk.di.CustomerIOComponent
import io.customer.sdk.util.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
Expand All @@ -27,8 +33,6 @@ import kotlin.math.abs
internal class CustomerIOPushNotificationHandler(private val remoteMessage: RemoteMessage) {

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

const val DEEP_LINK_KEY = "link"
const val IMAGE_KEY = "image"
const val TITLE_KEY = "title"
Expand All @@ -37,6 +41,21 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo
const val NOTIFICATION_REQUEST_CODE = "requestCode"
}

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

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

private val sdkConfig: CustomerIOConfig
get() = diGraph.sdkConfig

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

private val deepLinkUtil: DeepLinkUtil
get() = diGraph.deepLinkUtil

private val bundle: Bundle by lazy {
Bundle().apply {
remoteMessage.data.forEach { entry ->
Expand All @@ -49,9 +68,10 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo
context: Context,
handleNotificationTrigger: Boolean = true
): Boolean {
logger.debug("Handling push message. Bundle: $bundle")
// Check if message contains a data payload.
if (bundle.isEmpty) {
Log.d(TAG, "Message data payload: $bundle")
logger.debug("Push message received is empty")
return false
}

Expand All @@ -74,29 +94,23 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo

// Check if message contains a notification payload.
if (handleNotificationTrigger) {
handleNotification(context)
handleNotification(context, deliveryId, deliveryToken)
}

return true
}

@SuppressLint("LaunchActivityFromNotification")
private fun handleNotification(
context: Context
context: Context,
deliveryId: String,
deliveryToken: String
) {
val applicationName = context.applicationInfo.loadLabel(context.packageManager).toString()

val requestCode = abs(System.currentTimeMillis().toInt())

bundle.putInt(NOTIFICATION_REQUEST_CODE, requestCode)

// In Android 12, you must specify the mutability of each PendingIntent
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}

val icon = context.applicationInfo.icon

// set title and body
Expand All @@ -112,6 +126,7 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setTicker(applicationName)
.setStyle(NotificationCompat.BigTextStyle().bigText(body))

try {
// check for image
Expand Down Expand Up @@ -139,22 +154,71 @@ internal class CustomerIOPushNotificationHandler(private val remoteMessage: Remo
}

// set pending intent
val pushContentIntent = Intent(CustomerIOPushReceiver.ACTION)
pushContentIntent.setClass(context, CustomerIOPushReceiver::class.java)

pushContentIntent.putExtras(bundle)
val notificationClickedIntent = PendingIntent.getBroadcast(
val payload = CustomerIOParsedPushPayload(
extras = Bundle(bundle),
deepLink = bundle.getString(DEEP_LINK_KEY),
cioDeliveryId = deliveryId,
cioDeliveryToken = deliveryToken,
title = title,
body = body
)
createIntentFromLink(
context,
requestCode,
pushContentIntent,
flags
)
notificationBuilder.setContentIntent(notificationClickedIntent)
payload
)?.let { pendingIntent ->
notificationBuilder.setContentIntent(pendingIntent)
}

val notification = notificationBuilder.build()
notificationManager.notify(requestCode, notification)
}

private fun createIntentFromLink(
context: Context,
requestCode: Int,
payload: CustomerIOParsedPushPayload
): PendingIntent? {
// In Android 12, you must specify the mutability of each PendingIntent
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}

if (sdkConfig.targetSdkVersion > Build.VERSION_CODES.R) {
val taskStackBuilder = moduleConfig.notificationCallback?.createTaskStackFromPayload(
context,
payload
) ?: kotlin.run {
val pushContentIntent: Intent? = deepLinkUtil.createDeepLinkHostAppIntent(
context,
payload.deepLink
) ?: deepLinkUtil.createDefaultHostAppIntent(context, payload.deepLink)
pushContentIntent?.putExtras(bundle)

return@run pushContentIntent?.let { intent ->
TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(intent)
}
}
}

return taskStackBuilder?.getPendingIntent(requestCode, flags)
} else {
val pushContentIntent = Intent(CustomerIOPushReceiver.ACTION)
pushContentIntent.setClass(context, CustomerIOPushReceiver::class.java)

pushContentIntent.putExtra(CustomerIOPushReceiver.PUSH_PAYLOAD_KEY, payload)
return PendingIntent.getBroadcast(
context,
requestCode,
pushContentIntent,
flags
)
}
}

private fun addImage(
imageUrl: String,
builder: NotificationCompat.Builder,
Expand Down
Loading

0 comments on commit fd7ae28

Please sign in to comment.