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

Manually logging OPENED_PUSH not showing on klaviyo's dashboard #78

Closed
3 tasks done
iballan opened this issue Jun 19, 2023 · 10 comments
Closed
3 tasks done

Manually logging OPENED_PUSH not showing on klaviyo's dashboard #78

iballan opened this issue Jun 19, 2023 · 10 comments
Assignees
Labels
bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request

Comments

@iballan
Copy link

iballan commented Jun 19, 2023

Description

We are trying to integrate Klaviyo's notification into project that already uses Firebase Messaging.
In FirebaseMessagingService's onMessageReceived we customize how the notification looks, so we are not able to use KlaviyoNotification to display notification.

So we extract the information from Klaviyo's notification and display.

    val isKlaviyoNotification = remoteMessage.isKlaviyoNotification
    val title = if (isKlaviyoNotification) remoteMessage.title else remoteMessage.notification?.title
    val body = if (isKlaviyoNotification) remoteMessage.body else remoteMessage.notification?.body
    val deepLinkUri = if (isKlaviyoNotification) remoteMessage.deepLink else remoteMessage.data[DEEP_LINK_KEY]?.toUri()

// ... displaying notification with custom intent

Then when notification clicked, if it is Klaviyo's notification we are trying to track it like this:

            if (isKlaviyo) {
                storage.getPushNotificationsToken()?.let { token ->
                    klaviyoManager.logOpenedPush(token, id.orEmpty())
                }
            }

Tracking method:

    fun logOpenedPush(pushToken: String, notificationId: String) {
        val event = Event(EventType.OPENED_PUSH)
            .setProperty(EventKey.PUSH_TOKEN, pushToken)
        if (notificationId.isNotEmpty()) {
            event.setProperty(EventKey.EVENT_ID, notificationId)
        }
        klaviyo.createEvent(event)
    }

I couldn't find any document related to loggin EventType.OPENED_PUSH manually except in the code of EventKey:

/**
 * All event property keys recognised by the Klaviyo APIs
 * Custom properties can be defined using the [CUSTOM] inner class
 */
sealed class EventKey(name: String) : Keyword(name) {
    object EVENT_ID : EventKey("\$event_id")
    object VALUE : EventKey("\$value")

    /**
     * For [EventType.OPENED_PUSH] events, append the device token as an event property
     */
    object PUSH_TOKEN : EventKey("push_token")

    class CUSTOM(propertyName: String) : EventKey(propertyName)
}

So I added push_token as shown above in my tracking opened push

Checklist

  • I have determined whether this bug is also reproducible in a vanilla Android project
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or pull request.

Expected behavior

I expected to see open rate on Klaviyo's dashboard in campaign report

Actual behavior

It shows only Delievered notifications number

unnamed

Steps to reproduce

Log OPENED_PUSH without using Klaviyo's handleNotification and newIntent

The Klaviyo Android SDK version information

1.1.0

Device Information

Emulator

Android Studio Version

Flamingo Patch 2 ( latest )

Android API Level

API Level 33

@iballan iballan added the bug Something isn't working label Jun 19, 2023
@evan-masseau
Copy link
Contributor

Thanks for this feedback, I'm going to take a look at adding better documentation and helper methods for custom display of notifications and logging an opened_push in diverse scenarios and get back to you!

A quick answer though is that your manual opened_push event here is missing tracking parameters that we use on the backend to identify the push notification and perform validation steps before we can attribute that event to the campaign or flow. In our built-in displayNotification method we use RemoteMessage.toIntent() to create an intent that contains all of that message's original payload as the intent's extras. Then in our handlePush(intent) method we attach those extras to the event. On our backend we parse that whole payload and do the aforementioned validations and attribution.

@evan-masseau evan-masseau self-assigned this Jun 19, 2023
@evan-masseau evan-masseau added documentation Improvements or additions to documentation enhancement New feature or request labels Jun 19, 2023
@iballan
Copy link
Author

iballan commented Jun 19, 2023

Hey @evan-masseau
Tried to use the SDK's way of handling the opened_push event, yet it is not working for me.
Opened same issue for iOS klaviyo/klaviyo-swift-sdk#90

@iballan
Copy link
Author

iballan commented Jun 21, 2023

@evan-masseau to give more context on how we are doing it, I did write more information:

We already have Firebase setup and working in our project:
So we already have overriden FirebaseMessagingService

class AppMessagingService : FirebaseMessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        val notification = remoteMessage.notification
        if (notification != null || remoteMessage.isKlaviyoNotification) {
            showNotification(remoteMessage)
        }
    }

    private fun showNotification(remoteMessage: RemoteMessage) {
        if (!hasPostNotificationPermission) return
        val isKlaviyoNotification = remoteMessage.isKlaviyoNotification
        val title = if (isKlaviyoNotification) remoteMessage.title else remoteMessage.notification?.title
        val body = if (isKlaviyoNotification) remoteMessage.body else remoteMessage.notification?.body
        val deepLinkUri = if (isKlaviyoNotification) remoteMessage.deepLink else remoteMessage.data[DEEP_LINK_KEY]?.toUri()
        // -- creating of the intent 
        val intent = Intent(this, AppActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        // more custom payloads we use for our firebase notification
        // then
        // displaying custom notification with NotificationCompat
   }

}

Then on app startup in the AppActivity -> onCreate:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Get custom payload from the intent
        val isKlavioy = intent.getStringExtra(IS_KLAVIYO)
        val deeplink = intent.extras?.getString(DEEP_LINK_KEY)
        // ... other custom payloads 
        viewModel.trackNotificationClicked(notificationId, ..other payloads, isKlaviyo)
        // Handle Deep link ( we do have our custom methods to handle deeplink )

The method that tracks the event is: trackNotificationClicked

    fun trackNotificationClicked(notificationId String?, ..other payloads, isKlaviyo: Boolean = false) {
            analyticsManager.trackNotificationClicked(id, ..payloads, isKlaviyo)
            if (isKlaviyo) {
                // here we want to log Klaviyo's notification to see it on Klaviyo's dashboard
               storage.getPushNotificationsToken()?.let { token ->
                    klaviyoManager.logOpenedPush(token, id.orEmpty())
                }
            }

logOpenedPush is exactly like this:

    fun logOpenedPush(pushToken: String, notificationId: String) {
        val event = Event(EventType.OPENED_PUSH)
            .setProperty(EventKey.PUSH_TOKEN, pushToken)
        if (notificationId.isNotEmpty()) {
            event.setProperty(EventKey.EVENT_ID, notificationId)
        }
        klaviyo.createEvent(event)
    }

We are not able to use Klaviyo's standard flow because we have Firebase Messaging also working in the project as I mentioned before

@evan-masseau
Copy link
Contributor

Thanks! That is helpful context. Still working on a few updates that should help you out with a custom workflow like this. Now that I see the code I can definitely see why your event isn't getting logged as a valid opened push event. There is tracking data in the remoteMessage that needs to get loaded into the notification intent's extras, and then that tracking data needs to be attached to the opened push event as properties. So when you create the intent here: val intent = Intent(this, AppActivity::class.java) you'd have to attach the extras. Then parse out those extras to attach as properties

But here's an alternative you could try too. Instead of parsing our message out to display with your own logic, your firebase service could look like this:

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)

        val notification = remoteMessage.notification

        if (remoteMessage.isKlaviyoNotification) {
            //Let Klaviyo SDK display notifications that originated from Klaviyo            
            KlaviyoNotification(remoteMessage).displayNotification(this)
        } else if (notification != null) {
            //Display notifications from elsewhere
            showNotification(remoteMessage)
        }
    }

and your intent handling like this

    override fun onCreate(savedInstanceState: Bundle?) {
        //...  

        // handlePush ignores an intent that isn't from a Klaviyo push notification
        Klaviyo.handlePush(intent)

        //... handle your deep links etc. Klaviyo's display method attaches the deep link to the intent's data property, e.g:
        intent?.data?.let {
            //deep link logic...
            navigateTo(it)
        }
    }

We identify a Klaviyo push from the presence of tracking data, keyed "_k". So if you don't want to send all intents through our .handlePush method you could do this:

//Detect if this intent has klaviyo tracking params
if (intent.getStringExtra("_k").isNotEmpty()) { 
    Klaviyo.handlePush(intent)
} 

Of course, if you want to stick to displaying the notification with custom logic, that is understandable too. I am going to look at writing a few utility functions to help

  • Attach extras to a notification's intent that Klaviyo needs to be able to track opens that you can use in your custom display.
  • Filter the intent so you know whether to call Klaviyo.handlePush with it (e.g. an Intent extension method that does that check for _k in the above snippet)

@iballan
Copy link
Author

iballan commented Jun 21, 2023

@evan-masseau
For the first part, so yeah we would like to use our custom showNotification methods where we have some custom logic in it too.

If this Klaviyo.handlePush(intent) method doesn't handle deeplink, I can do call it in the onCreate.
But would like to get some help on what to include in the intent when it is a Klaviyo's message ?

@evan-masseau
Copy link
Contributor

Hey again @iballan, I made this branch for you to try out:feature/custom-notification-display-helpers if you take a look at that new commit you'll see two new extension functions:

  • Intent.appendKlaviyoExtras(RemoteMessage) will append the extras to your notification intent as I referred to in my earlier comments
  • Intent.isKlaviyoIntent detects whether an intent contains the Klaviyo tracking parameters so you could check that in your new intent handler (Klaviyo.handlePush(intent) will also use that check now though).

I've tested this new method in a demo application to confirm that an opened push event is still created. Let us know how you go, hope this helps!

@evan-masseau
Copy link
Contributor

@iballan Hey thanks again for working with us on this. Here is the syntax for using a specific branch from your gradle file:

implementation 'com.github.klaviyo:klaviyo-android-sdk:feature~custom-notification-display-helpers-SNAPSHOT'

You can find this syntax and explore what branches can be targeted on our repo's jitpack page: https://jitpack.io/#klaviyo/klaviyo-android-sdk

@iballan
Copy link
Author

iballan commented Jun 30, 2023

Hey @evan-masseau,
Thank you for the follow up with the custom jetpack branch
I can confirm that It worked 💪

Screenshot 2023-06-30 at 11 58 10 AM

@evan-masseau
Copy link
Contributor

So glad to hear! Please reach out here if you ever have more questions, I'm going to close this issue then. We'll have a new public release soon that will include those extension methods.

@evan-masseau
Copy link
Contributor

Hey this change is officially released now! You can update to v1.1.1.
Thanks again for helping us with this, glad to see android opens are at least working!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

When branches are created from issues, their pull requests are automatically linked.

2 participants