Giter Site home page Giter Site logo

customerio-android's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

customerio-android's Issues

ANR io.customer.sdk.util.AndroidSimpleTimer.timerDone Input dispatching timed out

SDK version: v3.1.1

Environment: Production

Are logs available?

main (blocked):tid=1 systid=32402 | waiting to lock <0x0844ef67> (ic.b) held by thread 95
       at io.customer.sdk.util.AndroidSimpleTimer.timerDone(AndroidSimpleTimer.java:73)
       at io.customer.sdk.util.AndroidSimpleTimer.access$timerDone(AndroidSimpleTimer.java:21)
       at io.customer.sdk.util.AndroidSimpleTimer$scheduleAndCancelPrevious$1$newTimer$1$1.onFinish(SimpleTimer.kt:47)
       at android.os.CountDownTimer$1.handleMessage(CountDownTimer.java:142)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8741)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
DefaultDispatcher-worker-11 (blocked):tid=95 systid=3863 | waiting to lock <0x06f58114> (ic.a$a) held by thread 1
       at android.os.CountDownTimer.cancel(CountDownTimer.java)
       at io.customer.sdk.util.AndroidSimpleTimer.unsafeCancel(SimpleTimer.kt:97)
       at io.customer.sdk.util.AndroidSimpleTimer.cancel(SimpleTimer.kt:83)
       at io.customer.sdk.queue.QueueImpl.run(Queue.kt:106)
       at io.customer.sdk.queue.QueueImpl$runAsync$1.invokeSuspend(Queue.kt:124)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
       at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.java:570)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

Describe the bug
This randomly happens on different device brands and OS versions ranging from SDK 28 to 33.

To Reproduce

Expected behavior
The library should not be causing an ANR.

Screenshots

Additional context

In-App Message created from a non-UI thread on Android

Version(s) of SDK bug appears: 3.0.0-beta.1

Current Behavior

I have tested in-app messages by sending some test to my phone :
The message is displayed in my test, but I got a dangerous error message on the Logcat

Screenshot 2022-09-19 at 16 37 36

It seems that the Banner (or Dialog) is created from a non-UI Thread

Steps to Reproduce

Phone Model :
OnePlus 5T - Android 10
Huawei P20 - Android 10
OnePlus 6T - Android 11

App Target :

Send an in-app Example Banner or Example Welcome

Screenshot 2022-09-19 at 16 40 38

    const val targetSdkVersion = 32
    const val compileSdkVersion = 32

Expected Behavior

The Overlay should be created from UI Thread

Fatal Exception: NPE onMessageReceive

SDK version: 3.1.0
Environment: Production

Are logs available?
Fatal Exception: java.lang.NullPointerException: null cannot be cast to non-null type io.customer.messagingpush.MessagingPushModuleConfig at java.util.Objects.requireNonNull(Objects.java:228) at io.customer.messagingpush.di.DiGraphMessagingPushKt.getModuleConfig(DiGraphMessagingPush.kt:23) at io.customer.messagingpush.CustomerIOPushNotificationHandler.getModuleConfig(CustomerIOPushNotificationHandler.kt:62) at io.customer.messagingpush.CustomerIOPushNotificationHandler.handleNotification(CustomerIOPushNotificationHandler.kt:198) at io.customer.messagingpush.CustomerIOPushNotificationHandler.handleMessage(CustomerIOPushNotificationHandler.kt:105) at io.customer.messagingpush.CustomerIOFirebaseMessagingService$Companion.handleMessageReceived(CustomerIOFirebaseMessagingService.kt:56) at io.customer.messagingpush.CustomerIOFirebaseMessagingService$Companion.onMessageReceived(CustomerIOFirebaseMessagingService.kt:31) at io.customer.messagingpush.CustomerIOFirebaseMessagingService$Companion.onMessageReceived$default(CustomerIOFirebaseMessagingService.kt:27) at io.customer.messagingpush.CustomerIOFirebaseMessagingService$Companion.onMessageReceived(CustomerIOFirebaseMessagingService.kt:8) at cz.ai.aschool.movies.fcm.FirebaseMessagingServiceMooveez.onMessageReceived(FirebaseMessagingServiceMooveez.java:99)

Describe the bug
After upgrade from customer io 2.1.1 to 3.1.0 Firebase Push Notification causes crash of our app.
I can provide you e-mails and times when crash occured.
Code in our class FirebaseMessagingServiceMooveez only bypasses RemoteMessage received.

Make customer focused SDK documentation

Write the documentation for the SDK. Documentation to give to our customer's engineering team to implement and use the SDK.

Why are we doing this for our customers?

To help our customers succeed, we can provide to them documentation on how to get up and running quick and easy with our mobile SDK. Get the SDK into their app and help them start using it, quick.

How do we test that this change is successful?

This is a docs change, only. No QA needed.

This is done when we have provided the docs team with everything that they need to publish our SDK documentation for customers to use.

How are we building this?

  • Write markdown document on Getting started. How customer can get the SDK into their app and start using the SDK in their app.
  • High-level overview of the functions that the SDK offers and how to use them.
  • Testing guide. How to write automated tests with the CIO SDK installed in their app.
  • Do we include push notification focused documentation? We get customers often ask us questions about how (technically) to get push enabled in their account. Having developer focused docs could help our customers and tech support teams. We give code samples, step-by-step instructions, and debugging steps docs for FCM and APN.

Initialization of SDK with CIO configuration

In order for our customers to use the SDK, they must give us some information in order for us to be able to send data to their CIO workspace. This task is to get this information and save it on the device. With this task, we are going to cater the authentication process along with the storage of authentication token, also create an interface for the customer to interact and configure the SDK accordingly.

Why are we doing this for our customers?

This task is required for our customers to use the SDK. The SDK cannot do any work successfully without this task.

How do we test that this change is successful?

Not much to test here. If future tasks such as identify a customer works after this task, we have tested it successfully.

How are we building this?

  • Create a public interfacing class, ideally singleton to configure the SDK with the required options.
CIO.configure(siteId: String, apiKey: String)

Save the siteId and apiKey to shared preferences to be reused and add the region entity to be configured.

  • Create a queue task to test credentials valid. In development mode, throw an IllegalArgumentException(). If not in development, ignore the request so no siteid or apikey get saved.

  • Create a queue task to find account region. Once response comes back from that, save the country code.

Crash on Android 5-7 because of dependency on java.util.Base64 in Gist upgraded in 3.3.0

SDK version: 3.3.0 - 3.4.2

Environment: Production

Are logs available?
Error fetching messages: canceled due to java.lang.NoClassDefFoundError: Failed resolution of: Ljava/util/Base64;
2023-05-06 17:05:04.673 22480-22563 AndroidRuntime cz.ai.aschool.movies E FATAL EXCEPTION: OkHttp Dispatcher
Process: cz.ai.aschool.movies, PID: 22480
java.lang.NoClassDefFoundError: Failed resolution of: Ljava/util/Base64;
at build.gist.data.listeners.Queue$gistQueueService$2.invoke$lambda-2(Queue.kt:35)
at build.gist.data.listeners.Queue$gistQueueService$2.$r8$lambda$PAgpBkRd0H90geyu2KeSV3WZpLE(Queue.kt)
at build.gist.data.listeners.Queue$gistQueueService$2$$ExternalSyntheticLambda0.intercept(D8$$SyntheticClass)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.ClassNotFoundException: Didn't find class "java.util.Base64" on path: DexPathList[[zip file "/data/app/cz.ai.aschool.movies-2/base.apk"],nativeLibraryDirectories=[/data/app/cz.ai.aschool.movies-2/lib/x86, /data/app/cz.ai.aschool.movies-2/base.apk!/lib/x86, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at build.gist.data.listeners.Queue$gistQueueService$2.invoke$lambda-2(Queue.kt:35) 
at build.gist.data.listeners.Queue$gistQueueService$2.$r8$lambda$PAgpBkRd0H90geyu2KeSV3WZpLE(Queue.kt) 
at build.gist.data.listeners.Queue$gistQueueService$2$$ExternalSyntheticLambda0.intercept(D8$$SyntheticClass) 
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) 
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) 
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
at java.lang.Thread.run(Thread.java:818) 
Suppressed: java.lang.ClassNotFoundException: java.util.Base64
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 10 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
2023-05-06 17:05:08.756 22480-22554 Gist cz.ai.aschool.movies E Error fetching messages: canceled due to java.lang.NoClassDefFoundError: Failed resolution of: Ljava/util/Base64;

Describe the bug
After updating to version 3.3.2 I have found, that my app starts crashing. Last working cio SKD version is 3.2.0.
I suspect Gist dependency update as a root cause of the problem, as java.util.Base64 is available from Android 8.
But cio declares support for Android 5 so this is why i consider it to be a bug.

To Reproduce
Run your SDK on Android 5-7

Expected behavior

Screenshots

Additional context

Crash inapp message

SDK version: 3.6.3

**Environment:**Production

Are logs available?
Fatal Exception: java.util.ConcurrentModificationException:
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at io.customer.messaginginapp.gist.presentation.GistSdk.handleGistError$messaginginapp_release(GistSdk.kt:228)
at io.customer.messaginginapp.gist.presentation.GistView.error(GistView.kt:138)
at io.customer.messaginginapp.gist.presentation.engine.EngineWebView$setupTimeout$1.run(EngineWebView.kt:98)
at java.util.TimerThread.mainLoop(Timer.java:562)
at java.util.TimerThread.run(Timer.java:512)

Describe the bug
Crash but user without any inapp message (not deployed on our automations)

To Reproduce
no idea

Expected behavior
no cash

🔵 Don’t always restart the app whenever a push is opened

Is your feature request related to a problem? Please describe.
If users open a push with the app open, the app restarts. That means discarding whatever state and progress users had in the app, and potentially re-running unnecessary app start logic. We don’t see a reason for having this default behavior and we believe the app should be the one in control of what happens with the navigation stack when a push notification is opened. We’d love to hear from your end if there’s any reason for the existing behavior, but it’s currently not bringing any benefits for us.

Describe the solution you'd like
The root cause is here:

private val notificationIntentFlags: Int = Intent.FLAG_ACTIVITY_NEW_TASK or
    Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP

So the easiest solution is to simply drop these flags.

Describe alternatives you've considered
In order to keep the current behavior for existing clients, you could introduce a way to configure this so clients can opt out of these flags.

Additional context
Right now the iOS SDK doesn’t restart the iOS app whenever a push is opened, so this change would make the SDK behavior more consistent across platforms.

Delete push notification device token

In order to send a push notification to a mobile device, you need to:

  1. Setup the push notification backend (integrate APN/FCM with your code or CIO)
  2. Capture the unique token for each device that you want to send a push notification to.

This issue has to do with item 2 above.

Note: This task is assumed that you can add/update device token in the SDK already.

Why are we doing this for our customers?

Today, our customers delete push notification device tokens from CIO via (1) 3rd party services like Segment.io or (2) they write custom code in their apps to send the token to CIO via our API. This issue is to do item (2) for our customers. Deleting a push notification device token from CIO will now just be 1 line of code:

Customers.deviceToken = null

...and that's it!

Our SDK can also help our customers by handling various use case scenarios for them. Example, if someone logs out of an account in your app and logs into a different account. Our SDK should have a default behavior of deleting the device token from the original account and then add the same device token to the newly logged in user. Our customers would need to handle all of these scenarios manually (causing possible bugs to their workspace).

How do we test that this change is successful?

There are some use cases that this issue needs to be able to handle.

  • Change users, transfer device token
  1. Follow one of the above 2 flows so that you are logged into an account in the mobile app and you have confirmed the device token is added to person in CIO workspace.
  2. In the UI of the app, logout of your user account.
  3. Login to a different account in the app. Different from what you just logged out of.
  4. Open CIO workspace. You should see a device token removed from the first user account you were logged in as and a device token (the same token that used to be used on the previous user account) in this new account you just logged in with.

How are we building this?

  • Modify property setter to Customers singleton. The API for the customer should be:
Customers.deviceToken = null
  • Add a function to Customers singleton. The API for the customer should be:
Customers.deleteDeviceToken()

This is a convenient function that matches our API. Does the same thing as Customers.deviceToken = null.

  • Create tasks in the queue system to delete device tokens when this function is called.

  • When Customers.updateX() functions are called to logout the customer, make a queue task to delete the device token from the person in CIO. Do not delete the token from persistent storage, however. The device token is shared with all people who log into accounts in the mobile app on the same 1 device.

Add an argument that allows customer to not automatically delete device token from account: Customers.updateX(...., deleteDeviceToken = true). Note the default value.

  • Modify Customers.deviceToken = "XXX" so when it's called with a non-null value, create a queue task to delete the previously set device token if there is a device token already saved in persistent storage.

Notes:

  1. Make sure Java API is similarly named to our API
Customers.setDeviceToken(null)
Customers.deleteDeviceToken()

In App Message Not Appearing

Version(s) of SDK bug appears:

implementation 'io.customer.android:tracking:3.0.0-beta.2'
implementation 'io.customer.android:messaging-push-fcm:3.0.0-beta.2'
implementation 'io.customer.android:messaging-in-app:3.0.0-beta.2'

Current Behavior

In App Message doesn't appear. However I can see following logs:

 I/Gist: Current gist route set to: Home
 I/Gist: Messages timer started
 I/Gist: Fetching user messages
 I/Gist: No messages found for user
 I/Gist: Fetching user messages
 I/Gist: No messages found for user
 I/Gist: Fetching user messages
 I/Gist: No messages found for user

SDK Initialisation Steps:

Following is in Application class:

CustomerIO.Builder(
        siteId = "xxxxx",
        apiKey = "xxxxx",
        appContext = this
    )
        .addCustomerIOModule(ModuleMessagingPushFCM())
        .addCustomerIOModule(ModuleMessagingInApp("xxxxxxxxx"))
        .autoTrackScreenViews(true)
        .setRequestTimeout(8000L)
        .build()

Following code is called on App Launch (HomeViewModel):

CustomerIO.instance().identify(
        identifier = profile.userId?.toString()!!,
        attributes = mapOf("email" to (profile.email ?: ""), "firstName" to profile.firstName!!)
    )

HomeActivity has label "Home" in manifest

Expected Behavior

In App Message Should Appear On Home Screen

push_enabled is not updated after permission is granted

SDK version: 3.6.4

Environment: Development and Production

Are logs available? I don’t think it’s relevant

Describe the bug

When users grant push notifications permission, CIO doesn’t update and still has push_enabled as false for that particular device. It only gets updated after the app is killed and opened again. This issue is also present on iOS.

Looking at here I kinda see why this is the case since we simply make that call once and that’s it, and it seems it’s similar on iOS as well.

To Reproduce

  • Create a new user and observe push_enabled is false
  • Grant notification permissions and notice push_enabled is still false
  • Kill and open the app, observe now push_enabled is updated to true

Expected behavior

Either the SDK should automatically notice the permission was granted so it can update itself right away (I don’t think there’s a good way to achieve that, though, since what Accompanist currently does for that is to simply check the permission in the resume lifecycle event, for instance), or it should provide an API that we can call to update this as soon as the permission is granted. Is there currently an API for this that I’m missing? Do I simply need to call identify again or should I simply restart CIO if the permission is granted?

I also think the documentation should highlight this case as it’s quite critical for us that this is updated as soon as our users grant permission. Maybe here you could say that if an identified user grants permission, there’s still something we need to do to make sure this is immediately reflected on the customerio side?

Crash with EngineWebView

SDK version:
3.6.6

Environment: Development or Production
Production

Are logs available?

Fatal Exception: android.util.AndroidRuntimeException: android.util.AndroidRuntimeException: android.webkit.WebViewFactory$MissingWebViewPackageException: Failed to load WebView provider: No WebView installed
       at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:382)
       at android.webkit.WebView.getFactory(WebView.java:2599)
       at android.webkit.WebView.ensureProviderCreated(WebView.java:2593)
       at android.webkit.WebView.setOverScrollMode(WebView.java:2661)
       at android.view.View.<init>(View.java:5648)
       at android.view.View.<init>(View.java:5823)
       at android.view.ViewGroup.<init>(ViewGroup.java:707)
       at android.widget.AbsoluteLayout.<init>(AbsoluteLayout.java:59)
       at android.webkit.WebView.<init>(WebView.java:426)
       at android.webkit.WebView.<init>(WebView.java:368)
       at android.webkit.WebView.<init>(WebView.java:350)
       at android.webkit.WebView.<init>(WebView.java:337)
       at android.webkit.WebView.<init>(WebView.java:327)
       at io.customer.messaginginapp.gist.presentation.engine.EngineWebView.<init>(EngineWebView.kt:28)
       at io.customer.messaginginapp.gist.presentation.engine.EngineWebView.<init>(EngineWebView.kt:20)
       at io.customer.messaginginapp.gist.presentation.GistView.<init>(GistView.kt:27)
       at io.customer.messaginginapp.gist.presentation.GistSdk.init$lambda$0(GistSdk.kt:106)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8757)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Caused by android.util.AndroidRuntimeException: android.webkit.WebViewFactory$MissingWebViewPackageException: Failed to load WebView provider: No WebView installed
       at android.webkit.WebViewFactory.getProviderClass(WebViewFactory.java:554)
       at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:353)
       at android.webkit.WebView.getFactory(WebView.java:2599)
       at android.webkit.WebView.ensureProviderCreated(WebView.java:2593)
       at android.webkit.WebView.setOverScrollMode(WebView.java:2661)
       at android.view.View.<init>(View.java:5648)
       at android.view.View.<init>(View.java:5823)
       at android.view.ViewGroup.<init>(ViewGroup.java:707)
       at android.widget.AbsoluteLayout.<init>(AbsoluteLayout.java:59)
       at android.webkit.WebView.<init>(WebView.java:426)
       at android.webkit.WebView.<init>(WebView.java:368)
       at android.webkit.WebView.<init>(WebView.java:350)
       at android.webkit.WebView.<init>(WebView.java:337)
       at android.webkit.WebView.<init>(WebView.java:327)
       at io.customer.messaginginapp.gist.presentation.engine.EngineWebView.<init>(EngineWebView.kt:28)
       at io.customer.messaginginapp.gist.presentation.engine.EngineWebView.<init>(EngineWebView.kt:20)
       at io.customer.messaginginapp.gist.presentation.GistView.<init>(GistView.kt:27)
       at io.customer.messaginginapp.gist.presentation.GistSdk.init$lambda$0(GistSdk.kt:106)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8757)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Caused by android.webkit.WebViewFactory$MissingWebViewPackageException: Failed to load WebView provider: No WebView installed
       at android.webkit.WebViewFactory.getWebViewContextAndSetProvider(WebViewFactory.java:447)
       at android.webkit.WebViewFactory.getProviderClass(WebViewFactory.java:512)
       at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:353)
       at android.webkit.WebView.getFactory(WebView.java:2599)
       at android.webkit.WebView.ensureProviderCreated(WebView.java:2593)
       at android.webkit.WebView.setOverScrollMode(WebView.java:2661)
       at android.view.View.<init>(View.java:5648)
       at android.view.View.<init>(View.java:5823)
       at android.view.ViewGroup.<init>(ViewGroup.java:707)
       at android.widget.AbsoluteLayout.<init>(AbsoluteLayout.java:59)
       at android.webkit.WebView.<init>(WebView.java:426)
       at android.webkit.WebView.<init>(WebView.java:368)
       at android.webkit.WebView.<init>(WebView.java:350)
       at android.webkit.WebView.<init>(WebView.java:337)
       at android.webkit.WebView.<init>(WebView.java:327)
       at io.customer.messaginginapp.gist.presentation.engine.EngineWebView.<init>(EngineWebView.kt:28)
       at io.customer.messaginginapp.gist.presentation.engine.EngineWebView.<init>(EngineWebView.kt:20)
       at io.customer.messaginginapp.gist.presentation.GistView.<init>(GistView.kt:27)
       at io.customer.messaginginapp.gist.presentation.GistSdk.init$lambda$0(GistSdk.kt:106)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8757)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Describe the bug

It seam something related with the in app message.

To Reproduce
No clear path. I'm getting this exception on Firebase.

Expected behavior
No crash.

Send rich push notifications (media, deep links) to device

We have the goal of allowing customers to show rich push notifications on a device on day 1 of signing up for CIO. Let's have the SDK to the heavy-lifting for you to showing rich push notifications for you.

What is rich push? These are push notifications that contain:

  • images
  • when push touched, a deep link is opened to take you to a destination in your app
  • optionally action buttons on the push notification that open a deep link when pressed

Rich push can do more then this but this task is focused only on the 2-3 items above.

This task is only for the FCM service. No other Android push notification service planned for this specific task at this time.

Why are we doing this for our customers?

Today, if a customer wants to send rich push notifications to their device, it requires custom code in the mobile app. We want to add feature to the SDK to give the ability to display rich push notifications with little work on the engineer's end. We require some setup from the engineering team but after setup, CIO can take care of the rest.

How do customers send rich push from CIO after this work is complete?

Customers will be using the existing CIO custom payload feature they are already using in the CIO editor. We will provide a JSON template to modify to be able to send push notifications that the mobile SDK can interpret.

FCM's custom payload follows a strict format. Here are some of the rules:

  • The notification key is reserved by the FCM Android and iOS SDK to display a push notification for you. This is very similar to what we will be doing as well - you provide information to us and the CIO SDK will display a push notification for you.
  • The data key is used to provide custom data to the app. This allows you to send data that the FCM Android and iOS SDK ignores and passes to your app to process. This is what we must use to pass data to the CIO SDK. data is an object with string values, only.

FYI: If a custom payload does not have data in the custom payload, there is a chance that the SDK will not be given the FCM custom payload at all to process.

This means that the custom payload will look like this:

{
    "data": {
        "CIO-title": "title of push notification",
        "CIO-body": "message of push",
        "CIO-image": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fpawsindoorssouthend.com%2Fwp-content%2Fuploads%2F2020%2F04%2Ftips-for-choosing-doggy-day-care.jpg",
        "CIO-action": "remote-habits://deep?message=hello&message2=world",
        "CIO-button1-title": "Shop",
        "CIO-button1-action": "remote-habits://deep?message=go&message2=shopping!",
        "CIO-button2-title": "Open",
        "CIO-button2-action": "remote-habits://deep?message=action&message2=opened",
        "CIO-channel-id": "channel-id-push"
    }
}

The SDK will see the CIO- prefix and handle it for you.

How do we test that this change is successful?

This is a feature add task so QA is required.

  • Send a test push notification in CIO to a device using the custom payload above in this issue. Please, feel free to remove pieces of the JSON (example: do not use CIO-image to not display an image).
  • You should see the push show up on the device. It should include a title, description, and an image.
  • Touch the push notification. The Remote Habits app should indicate that a deep link was pressed.

How are we building this?

Tip: See prototype code for help

  • Include the Firebase messaging SDK as a dependency of the SDK. This is to (1) enforce that the version of FCM SDK used by the app and by the SDK are compatible and (2) our SDK can be aware of objects like RemoteMessage in the FCM SDK so it can parse it.
  • Create a function Messaging.onMessageReceived(message: RemoteMessage): MessagingResult? that is provided the FCM message. Our SDK will not provide it's own FirebaseMessagingService subclass but instead will have this function get called when a message is received by FCM.
    • This function will return an object is the SDK determines that the remote message from FCM has anything to do with CIO. If the SDK sees a data key with the prefix CIO- then an object will be returned. This object will have fields in it such as: didDisplayNotification saying if a push notification was presented on the device by the SDK. This will help the engineers determine what they should do next with the push notification if the push contains extra data it needs to process.
  • Compose a push notification to be presented on the device. Do this by enabling features depending on the JSON payload (example: if CIO-image is missing, don't show an image).
    • For images, make sure the bitmap created is memory efficient and will not crash when low on memory.
  • Show push notification on the device with the channel ID provided in the custom payload or, show by a default one that the SDK creates called "Marketing".
  • Create a BroadcastReceiver that will get called when the push notification is touched or the action buttons are touched.
    • Include this broadcast receiver in the SDK manifest. It will then get merged into the customer's mobile app.
  • If a deep link is attached to the action button or to the push itself, launch that deep link in the BroadcastReceiver.

Always register token after identify.

There might be a case where registerToken might get called before identify

We need to come up with a way, where we fix the sequence, either in Background queue or come up with an automated way of fetching token again after identify.

Identify a customer

Add to our mobile SDK the ability to identify a person. This is very similar to adding/updating a customer with the CIO API, but with different API unique to the mobile SDK. This task is useful for customers who have mobile apps with user accounts and they want to identify the customers in CIO workspaces.

Why are we doing this for our customers?

  1. Not require our customers to manually write code in their mobile app to add/update a customer in CIO via our API.
  2. Go a step further and create a developer friendly API in the SDK to make updating an existing customer more convenient.
  • If CIO makes changes to workspaces (like recent change of being able to use email as identifier), the SDK can adapt to make our customers not need to make any code changes in their apps. Or, at least, make it minimal changes.

How do we test that this change is successful?

  • Adding new customer
  1. Login to mobile app with an email address not associated with a Person in CIO workspace.
  2. Wait 30 seconds for CIO network operations to succeed.
  3. Check CIO workspace > People. You should see a new person added.
  • Updating new customer
  1. Login to mobile app with an email address already associated with a Person in CIO workspace.
  2. Perform some operation that updates the customer - example: have an attribute that keeps track of the Date last logged into app.
  3. Wait 30 seconds for CIO network operations to succeed.
  4. Check CIO workspace > People > select Person that is already created in workspace. You should see the existing Person's user attributes updated.

How are we building this?

  • Add function to the Customers singleton. The customer API will be:
Customers.identify(id: String, attributes: Map<String, String | Int | Bool>? = null)

Add client-side validation for the ID. Make sure it's 150 characters or less in length. In development mode, throw IllegalArgument exception if given ID that's too long. Not in development mode, ignore the request.

Save the identifier in persistent storage (shared prefs) to give the SDK a state.

Note: It may be better to use Pair data type or something else. This is open for discussion.

  • Create a queue task to add/update a customer with this given identifier and optional attributes.

  • Add functions to the Customers singleton. The customer API will be:

Customers.update(attributes: Map<String, String | Int | Bool>)

This will create a new task to the queue to update a customer.

Note: In development mode: throw exception if a customer has not yet been identified. Non-development, ignore the function call.

Deeplinks do not open using SDK on android

SDK version: 3.1.1

Describe the issue
I had an integration with Customer.IO without SDK, but i want to monitor events like opened and delivery, so I add the Customer IO SDK for android following the documentation, but my deeplinks was stop working, when I click in notification it redirect me for main activity, if the deeplink is a web page, it opens the page on browser but stay in background, with app in main activity.

image

OBS: The notification is delivery and monitory correct, only problem is the handle of deeplinks.

Send push notification device token to CIO

In order to send a push notification to a mobile device from CIO, you need to:

  1. Setup the push notification backend service (APN/FCM) with your mobile app. FCM and APN has steps that they require to setup your app.
  2. FCM and APN then give the mobile app a device token. CIO needs this device token.

This task here has to do with item 2 above.

Note: Deleting device tokens is not in the scope of this task. That's in another task

Why are we doing this for our customers?

Today, our customers send push notification device tokens to CIO via (1) 3rd party services like Segment or (2) they write custom code in their apps to send the token to CIO via our API. This task is to do item (2) for our customers. Sending a push notification device token to CIO will now just be 1 line of code:

Customers.deviceToken = "XXXXXXXXXXXXX"

...and that's it!

Along with sending a device token, this task will also set the platform and last_used properties automatically.

How do we test that this change is successful?

This is a feature add task which means we require QA testing on this task.

  • First app open
  1. Re-install the mobile app so it's a fresh new install.
  2. You open mobile app. Wait 30 seconds or so as long as you have an Internet connection. This gives FCM time to give us a device token before we try to login.
  3. Login to user account in the app.
  4. Login to CIO workspace, navigate to person that your mobile app is currently logged in as. You will see an Android device token added for that person.
  • Offline capable
  1. Have the mobile app closed and in airplane mode.
  2. Open mobile app. Wait 30 seconds or so for FCM to give us a device token.
  3. Login to user account in app.
  4. Open CIO workspace for person should not have a device token registered.
  5. Turn off airplane mode on device so it has Internet.
  6. Close app and then open it up again. This is required to "manually" trigger a SDK push of data to the API.
  7. Open CIO workspace for person should have a device token registered.
  • Change users, transfer device token

This use case is covered by a different issue, not in this task's scope.

How are we building this?

  • Add property setter to Customers singleton. The API for the customer should be:
Customers.deviceToken = "XXXXXXXXXXXXX"

For now, we do not allow null to be passed in.

There is no client side validation to make sure token is valid. We accept anything.

  • Whatever string value is provided to us via the singleton, we capture to persistent storage (shared prefs) in our case.

  • Create tasks in the queue system to add/update token.

In this task, we add device object to request:

"device": {
    "id": "<token>",
    "platform": "android",
    "last_used": Date.now
}
  • When Customer.updateX() functions are called to login a new customer, make a queue task to add/update device token to person if a device token already exists for the device.

  • When the initialization code is called, add to queue a new update device token task so we can update last_used.

Notes:

  1. Make sure Java API is similarly named to our API
Customers.setDeviceToken("XXXX")

CustomerIOPushNotificationCallback isn’t called if the app is in the background and the notification doesn’t have a deep link

SDK version: 3.6.4

Environment: Development and Production

Are logs available?

There’s something interesting in the logs: while the app is in the background (open or closed), when I send a notification with deep link, I see this:

Received message with empty deliveryId
Handling push message. Bundle: …

But when the notification doesn’t have a deep link, I only see the first message, even though the notification shows up as usual:

Received message with empty deliveryId

So it’s like this is never being called for some reason?

Describe the bug

CustomerIOPushNotificationCallback isn’t being called if we send a notification without a deep link while the app is in the background (open or closed). It works fine if the app is in the foreground, or if the notification has a deep link.

To Reproduce

  • Make sure the app is not in the foreground (either kill it or keep it open but not in the foreground)
  • Send a notification message without a deep link
  • Observe CustomerIOPushNotificationCallback won’t be called

Expected behavior

CustomerIOPushNotificationCallback should always be called.

Additional context

I’ve reproduced this on a Pixel 4a running Android 13 so far.

Support for merge customers endpoint

Is your feature request related to a problem? Please describe.
Hello!

We're trying to merge duplicated users in our App.
According to the documentation you provide, this can be done via web management, or calling the /api/v1/merge_customers endpoint.
I see there is no function to merge users in customer.io sdk for Android, nor iOS.

Are you planning to include a function to merge two users in the sdk?

Thanks in advance!

Describe the solution you'd like
An idea to user as starting point could be

Android (CustomerIO.kt)

fun merge(identifier: String, newIdentifier: String) {
    ... // call to /api/v1/merge_customers
}

iOS (CustomerIO.swift)

    func merge(
        identifier: String,
        newIdentifier: String
    )

Describe alternatives you've considered

Additional context
Having it in a newer version of the library would be awesome!

Ability to set notification icon/color

The notification icon is currently set to the application icon inside CustomerIOPushNotificationHandler. I'd like a way to set a different notification icon as for the majority of cases, the application icon will have a shape background which will be masked on Lollipop+ devices (Display a white circle/square).

See screenshot:
Screen Shot 2022-04-10 at 9 44 42 PM

Why are we doing this for our customers?

--

How do we test that this change is successful?

Send test notification when a custom notification icon has been set.

How are we building this?

Currently setting the application icon when building the notification:

https://github.com/customerio/customerio-android/blob/1.1.0-alpha.2/messagingpush/src/main/java/io/customer/messagingpush/CustomerIOPushNotificationHandler.kt#L118

FCM has some documentation that sets defaults when the icon does not exists. Unfortunately because the call to setSmallIcon these defaults do not seem to take.

<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
     See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
     notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorAccent" />

I'm not sure if I'd prefer to set ApplicationManifest metadata to set the notification, or if a programmatic method similar to CustomerIOFirebaseMessagingService.onMessageReceived in order to achieve this.

Some of the device ID is not registering a profile on UAT environment.

SDK version: 3.5.2

Are logs available?
[CIO]: identify profile d7061a7254bbf5e8
D/[CIO]: identify profile d7061a7254bbf5e8, {reason=-, device_model=HD1901, app_version=20.2.0, cio_sdk_version=30, timezone=Asia/Kolkata, created_at=1687502615, device_region=in, language=en, device_os=Android, unsubscribed=false, subscribe_source=Mobile_App, push_enabled=true, id=d7061a7254bbf5e8, email=}
I/[CIO]: adding queue task IdentifyProfile
D/[CIO]: added queue task data {"identifier":"d7061a7254bbf5e8","attributes":{"reason":"-","device_model":"HD1901","app_version":"20.2.0","cio_sdk_version":30,"timezone":"Asia/Kolkata","created_at":1687502615,"device_region":"in","language":"en","device_os":"Android","unsubscribed":"false","subscribe_source":"Mobile_App","push_enabled":"true","id":"d7061a7254bbf5e8","email":""}}
D/[CIO]: processing queue status QueueStatus(siteId=06f9c82b54a139b44d51, numTasksInQueue=1)
I/[CIO]: queue timer: scheduled to run queue in 30.0 seconds seconds
D/[CIO]: storing identifier on device storage d7061a7254bbf5e8
D/[CIO]: first time identified or changing identified profile
D/[CIO]: automatically registering device token to newly identified profile
I/[CIO]: registering device token fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8, attributes: {device_os=30, device_model=HD1901, device_manufacturer=OnePlus, app_version=3.3.46, cio_sdk_version=3.5.1, device_locale=en-IN, push_enabled=true}
D/[CIO]: storing device token to device storage fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8
I/[CIO]: adding queue task RegisterDeviceToken
D/[CIO]: added queue task data {"profileIdentified":"d7061a7254bbf5e8","device":{"id":"fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8","platform":"android","lastUsed":1687502615,"attributes":{"device_os":30,"device_model":"HD1901","device_manufacturer":"OnePlus","app_version":"3.3.46","cio_sdk_version":"3.5.1","device_locale":"en-IN","push_enabled":true}}}
D/[CIO]: processing queue status QueueStatus(siteId=06f9c82b54a139b44d51, numTasksInQueue=2)
I/[CIO]: queue timer: scheduled to run queue in 30.0 seconds seconds
I/[CIO]: registering device token fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8, attributes: {device_os=30, device_model=HD1901, device_manufacturer=OnePlus, app_version=3.3.46, cio_sdk_version=3.5.1, device_locale=en-IN, push_enabled=true}
D/[CIO]: storing device token to device storage fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8
I/[CIO]: adding queue task RegisterDeviceToken
D/[CIO]: added queue task data {"profileIdentified":"d7061a7254bbf5e8","device":{"id":"fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8","platform":"android","lastUsed":1687502615,"attributes":{"device_os":30,"device_model":"HD1901","device_manufacturer":"OnePlus","app_version":"3.3.46","cio_sdk_version":"3.5.1","device_locale":"en-IN","push_enabled":true}}}
D/[CIO]: processing queue status QueueStatus(siteId=06f9c82b54a139b44d51, numTasksInQueue=3)
I/[CIO]: queue timer: scheduled to run queue in 30.0 seconds seconds
D/[CIO]: Timer czvaaoihae making a timer for 30.0 seconds
D/[CIO]: Timer czvaaoihae making a timer for 30.0 seconds
I/System.out: 2131296975permission was granted
V/FA: Activity resumed, time: 268653351
D/DecorView: onWindowFocusChangedFromViewRoot hasFocus: true, DecorView@7711e71[NewHomeActivity]
D/ViewRootImpl[NewHomeActivity]: windowFocusChanged hasFocus=true inTouchMode=true
V/FA: Inactivity, disconnecting from the service
D/[CIO]: Timer czvaaoihae timer is done! It's been reset
I/[CIO]: queue timer: now running queue
D/[CIO]: Timer czvaaoihae timer is being cancelled
D/[CIO]: queue starting to run tasks...
D/[CIO]: queue querying next task. criteria: QueueQueryCriteria(excludeGroups=[])
D/[CIO]: queue tasks left to run: 3 out of 3
D/[CIO]: queue next task to run: 8ee6b040-1db1-49f2-8175-3383905e8e4d, IdentifyProfile, {"identifier":"d7061a7254bbf5e8","attributes":{"reason":"-","device_model":"HD1901","app_version":"20.2.0","cio_sdk_version":30,"timezone":"Asia/Kolkata","created_at":1687502615,"device_region":"in","language":"en","device_os":"Android","unsubscribed":"false","subscribe_source":"Mobile_App","push_enabled":"true","id":"d7061a7254bbf5e8","email":""}}, QueueTaskRunResults(totalRuns=0)
D/[CIO]: queue task 8ee6b040-1db1-49f2-8175-3383905e8e4d ran successfully
D/[CIO]: queue deleting task 8ee6b040-1db1-49f2-8175-3383905e8e4d
D/[CIO]: queue querying next task. criteria: QueueQueryCriteria(excludeGroups=[])
D/[CIO]: queue tasks left to run: 2 out of 3
D/[CIO]: queue next task to run: 4f3b4f3c-b0c3-40b3-8907-4f229427e1a8, RegisterDeviceToken, {"profileIdentified":"d7061a7254bbf5e8","device":{"id":"fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8","platform":"android","lastUsed":1687502615,"attributes":{"device_os":30,"device_model":"HD1901","device_manufacturer":"OnePlus","app_version":"3.3.46","cio_sdk_version":"3.5.1","device_locale":"en-IN","push_enabled":true}}}, QueueTaskRunResults(totalRuns=0)
D/[CIO]: queue task 4f3b4f3c-b0c3-40b3-8907-4f229427e1a8 ran successfully
D/[CIO]: queue deleting task 4f3b4f3c-b0c3-40b3-8907-4f229427e1a8
D/[CIO]: queue querying next task. criteria: QueueQueryCriteria(excludeGroups=[])
D/[CIO]: queue tasks left to run: 1 out of 3
D/[CIO]: queue next task to run: ed7a4097-beec-4a19-8f22-a299c9030e68, RegisterDeviceToken, {"profileIdentified":"d7061a7254bbf5e8","device":{"id":"fSX-oAfVT46HdIRA1CnsiC:APA91bHunTrg0iJi47nRCs8am3eAs_L3UBkEcaqWcHWkp2FqJRvy_Dm_bcq0PMrb0EoaB1i4xCjkvd61IWvsVtomMPU8e_YYwQPSGitsSzjGZhdt4uiS52l3e2ZKMVfTrneNcRcGTlI8","platform":"android","lastUsed":1687502615,"attributes":{"device_os":30,"device_model":"HD1901","device_manufacturer":"OnePlus","app_version":"3.3.46","cio_sdk_version":"3.5.1","device_locale":"en-IN","push_enabled":true}}}, QueueTaskRunResults(totalRuns=0)
D/[CIO]: queue task ed7a4097-beec-4a19-8f22-a299c9030e68 ran successfully
D/[CIO]: queue deleting task ed7a4097-beec-4a19-8f22-a299c9030e68
D/[CIO]: queue done running tasks
D/[CIO]: resetting queue tasks query criteria

Describe the issue
Some of the device id is not creating the profile on UAT environment but the same device id is working fine on Production environment.

Screenshots

Additional context

Design the API of the SDK

Don't write any code here. Get out some paper and pencil and design the API for the customer for how they will interact with the SDK.

Why are we doing this for our customers?

When a customer has the SDK in their mobile app, any change that we make to the SDK might require that our customers have their engineers tweak their mobile app code to work with our SDK. We want to avoid this as much as possible. To prepare for that, let's design many of our SDK's functions we plan to implement before we get into writing code. That way we know the customer facing API will not change after we begin writing code.

Note: No matter how much we prepare there is always the chance that we introduce changes to the SDK that requires our customers making a change. This task will, however, help to avoid it.

How do we test that this change is successful?

This task is done by product and engineering team feedback.

How are we building this?

  • Make a markdown file giving the Java and Kotlin SDK APIs for function calls we plan to introduce.
  • Handle use case of users logging in and logging out of customer accounts in mobile app. How does identify and other function calls handle in this scenario?
  • How does client-side validation work? Example: verifying an email address is valid in an email-based workflow while disabling validation in non-email based workflow.
  • How does the developer write automated tests on their code-base while using our SDK?
  • How to setup FCM push notification integration. We require that you install the FCM SDK yourself, create your own FirebaseMessagingService subclass, then call functions on our SDK.
    • How to create Android notification channels. Maybe we provide a handy way to create them within the SDK?

FCM Token isn't being associated with the person

Version(s) of SDK bug appears: 1.0.1

Hello,

First of all, great work releasing the native SDKs for mobile platforms.
I start to integrate your SDK, all it's working well the track of the events, the identity of the user, but when I go to the user data in Customer.io the device list is always empty and the FCM Token isn't associated.

I'm calling CustomerIOFirebaseMessagingService.onNewToken(token) in the onNewToken() function from the FirebaseMessagingService, and when I test send a static push notification everything works fine, the only problem is the association between the user and the token in the Customer.io platform.
Can you help me understand if missing something?

Update dependencies, plugins, gradle version and publishing process of artifacts

There are a lot of dependencies being used along with the plugins that need to be updated also,
Android Studio naming convention has changed and according to the Google docs current stable version is Arctic Fox | 2020.3.1

Why are we doing this for our customers?

This is internal cleanup of SDK dependencies, plugins, build process, and dependencies management.

Why is this needed?

  • For a multi-module SDK that we are planning to become, having a single source of truth for versions, dependencies and common configuration is very important.
  • In order to test the under development SDK, we need to support SNAPSHOT builds, which can be overridden and used to test.
  • Currently, we have to manually go to Nexus manager and release the builds, which needs to get automated.

How do we test that this change is successful?

Developers to test it and verify.

How are we building this?

  • Update Dependencies being used
  • Remove unused dependencies
  • Update AGP version
  • Update SDK Build Tools version
  • Update minSDK=21 (for now, it's a random number based on preliminary survey result but subject to change)
  • Add BuildSrc support and move all dependencies in there.
  • Add support of SNAPSHOT and its workflow
  • Use Nexus plugin to automate releasing of artifacts.

Rich push notification not tracking opened on targetSDK > 30

SDK version:
3.2.0-alpha2

Environment:
Development and Production

Are logs available?

Describe the bug
The rich notifications are never seen as opened by the android SDK if target SDK is greater than 30

To Reproduce
Try rich push with an application with target SDK set to 33

Additional context
The problem is situated in io.customer.messagingpush.CustomerIOPushNotificationHandler#createIntentFromLink. If the targetSDK is higher than 30, io.customer.messagingpush.util.DeepLinkUtilImpl#queryDeepLinksForHostApp is called and this creates an intent with ACTION_VIEW and not a CustomerIOPushReceiver.ACTION intent

Note that this notifications also restart applications using android:launchMode="singleTask" which probably would defeat the tracking anyway because the CustomerIO SDK may not be initialized before the broadcastReceiver is executed.

Disabling push notifications

SDK version: 3.8.2

Are logs available?
No

Describe the issue
How to disable push notifications in Android? I didn't find anything related on the official site.

v3.1.0 MessagingPushModuleConfig can not create object due to private constructor

SDK version: 3.1.0

Environment: Production

Are logs available?

This is compile time issues because MessagingPushModuleConfig we can't use it due to private constructor
Describe the bug

For reference of this link https://www.customer.io/docs/sdk/android/migrate-upgrade/ below code snippet attached to the documented site.
ModuleMessagingPushFCM( config = MessagingPushModuleConfig( notificationCallback = this, redirectDeepLinksToOtherApps = false, ) )
In this code block MessagingPushModuleConfig required 3 param but documentation mentions 2 param that is first bug also MessagingPushModuleConfig object can't able to create due to private constructor or way of code snippet wrote that is wrong.
To Reproduce

This is compile time issue so it is easily reproduced if you can write code like mention is above link it is easily reproduce the issue.

Crash when rich push with image is received while the app is closed

SDK version: 3.1.1

Environment: Development or Production

Are logs available?
Yes but there are no logs from CIO, only the stacktrace:

FATAL EXCEPTION: Firebase-Messaging-Intent-Handle
Process: de.climatelabs.planetwild.debug, PID: 20818
java.lang.IllegalStateException: CustomerIO.Builder::build() must be called before obtaining CustomerIO instance
  at io.customer.sdk.CustomerIO$Companion.instance(CustomerIO.kt:119)
  at io.customer.messagingpush.CustomerIOFirebaseMessagingService$Companion.getDiGraph(CustomerIOFirebaseMessagingService.kt:14)
  at io.customer.messagingpush.CustomerIOFirebaseMessagingService$Companion.handleMessageReceived(CustomerIOFirebaseMessagingService.kt:56)
  at io.customer.messagingpush.CustomerIOFirebaseMessagingService$Companion.handleMessageReceived$default(CustomerIOFirebaseMessagingService.kt:51)
  at io.customer.messagingpush.CustomerIOFirebaseMessagingService.onMessageReceived(CustomerIOFirebaseMessagingService.kt:65)
  at com.google.firebase.messaging.FirebaseMessagingService.dispatchMessage(com.google.firebase:firebase-messaging@@22.0.0:13)
  at com.google.firebase.messaging.FirebaseMessagingService.passMessageIntentToSdk(com.google.firebase:firebase-messaging@@22.0.0:8)
  at com.google.firebase.messaging.FirebaseMessagingService.handleMessageIntent(com.google.firebase:firebase-messaging@@22.0.0:3)
  at com.google.firebase.messaging.FirebaseMessagingService.handleIntent(com.google.firebase:firebase-messaging@@22.0.0:3)
  at com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0$EnhancedIntentService(com.google.firebase:firebase-messaging@@22.0.0:1)
  at com.google.firebase.messaging.EnhancedIntentService$$Lambda$0.run(Unknown Source:6)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
  at com.google.android.gms.common.util.concurrent.zza.run(com.google.android.gms:play-services-basement@@18.1.0:2)
  at java.lang.Thread.run(Thread.java:1012)

Describe the bug
App process crashes and notification isn't rendered when we send a rich push with an image while the app process is dead.

To Reproduce

  • Make sure the app process is dead. This means manually closing the app and making sure no other notification is received after that. If a notification is received, the app needs to be open and closed again to ensure the process is dead so the issue can be reproduced.
  • Send a rich push containing an image.
  • Observe the notification isn't received and the error message above shows up in the logs.

Expected behavior
Notification is rendered and process doesn't crash.

Screenshots
N/A

Additional context
The error message may make it seem like we're not properly setting up the SDK, but we are. Everything is working and we're able to receive notifications without images while the app process is dead (and everything works fine while the app is open). But as soon as we add images, this happens, so I believe there's an issue on the SDK side.

I've found this issue on a Pixel 4 running Android 13 but I also tried on an emulator running Android 11 and I was also able to reproduce it there, so it doesn't really seem to be device or Android version specific.

Track when push notification is `opened`

When push notifications are touched on the device, we want to track that a push notification sent by CIO was touched.

Note: This task does not involve other push metrics like delivered

Note: This task depends on rich push integration.

Why are we doing this for our customers?

CIO gives you the ability today to track when a push notification is opened (aka clicked). This task in the SDK will do all the work for the customer so they don't have to write the code to interact with our API themselves.

The API for how the customer reports push notifications is not yet decided.

How do we test that this change is successful?

This task is a feature change so it needs QA testing.

  • Send a push notification from CIO to a device. Using the "Test notification" feature may not work because the SDK requires the push contains some extra payload data used for tracking.
  • Touch the push notification shown to the user on the device.
  • Check CIO for the person's activity. You should see a push opened metric reported for the person.

How are we building this?

  • From the rich push feature implementation, the SDK knows when a push notification was clicked or an action button was clicked on a notification.
  • Parse the incoming CIO push notification FCM data payload for the CIO-Delivery- values and add those to the Intent that goes to the BroadcastReceiver.
  • Create a queue background task to report push metrics opened event to the API.
  • When the BroadcastReciever is opened because of a push notification being touched, get the values from the Intent and create a new queue background task to do the reporting.
  • Allow SDK to be customized to turn this automatic push metrics recording off.

Fatal Exception: java.io.FileNotFoundException on registerDeviceToken

SDK version: v3.1.0

Environment: Production

Are logs available?

Fatal Exception: java.io.FileNotFoundException: /data/user/0/ng.com.fairmoney.fairmoney/files/io.customer/24485baaaacb896e63da/queue/tasks/1152fc26-8470-4294-9100-138b237eed53.json: open failed: ENOENT (No such file or directory)
       at libcore.io.IoBridge.open(IoBridge.java:574)
       at java.io.FileInputStream.<init>(FileInputStream.java:160)
       at kotlin.io.FilesKt__FileReadWriteKt.readText$default(FileReadWrite.kt:125)
       at kotlin.io.FilesKt__FileReadWriteKt.readText$default(FileReadWrite.kt:125)
       at io.customer.sdk.data.store.FileStorage.get(FileStorage.kt:62)
       at io.customer.sdk.queue.QueueStorageImpl.get(QueueStorage.kt:103)
       at io.customer.sdk.queue.QueueRunRequestImpl.runTasks(QueueRunRequest.kt:37)
       at io.customer.sdk.queue.QueueRunRequestImpl.runTasks$default(QueueRunRequest.kt:26)
       at io.customer.sdk.queue.QueueRunRequestImpl.run(QueueRunRequest.kt:23)
       at io.customer.sdk.queue.QueueImpl.run(Queue.kt:114)
       at io.customer.sdk.queue.QueueImpl$runAsync$1.invokeSuspend(Queue.kt:124)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
       at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Caused by android.system.ErrnoException: open failed: ENOENT (No such file or directory)
       at libcore.io.Linux.open(Linux.java)
       at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
       at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274)
       at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
       at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:8523)
       at libcore.io.IoBridge.open(IoBridge.java:560)
       at java.io.FileInputStream.<init>(FileInputStream.java:160)
       at kotlin.io.FilesKt__FileReadWriteKt.readText$default(FileReadWrite.kt:125)
       at kotlin.io.FilesKt__FileReadWriteKt.readText$default(FileReadWrite.kt:125)
       at io.customer.sdk.data.store.FileStorage.get(FileStorage.kt:62)
       at io.customer.sdk.queue.QueueStorageImpl.get(QueueStorage.kt:103)
       at io.customer.sdk.queue.QueueRunRequestImpl.runTasks(QueueRunRequest.kt:37)
       at io.customer.sdk.queue.QueueRunRequestImpl.runTasks$default(QueueRunRequest.kt:26)
       at io.customer.sdk.queue.QueueRunRequestImpl.run(QueueRunRequest.kt:23)
       at io.customer.sdk.queue.QueueImpl.run(Queue.kt:114)
       at io.customer.sdk.queue.QueueImpl$runAsync$1.invokeSuspend(Queue.kt:124)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
       at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

Describe the bug
This randomly happens and is currently affecting a lot of our users across several device brands

To Reproduce

Expected behavior
Device token is registered successfully without crashes

Screenshots

Additional context

Fatal Exception: com.squareup.moshi.JsonEncodingException: Use JsonReader.setLenient(true) to accept malformed JSON at path $

Version(s) of SDK bug appears: 2.0.1
Android versions: 9-12
Kotlin version: 1.7.10

Current Behavior

The app is crashed in first 5 seconds.

Steps to Reproduce

Initialise customer io sdk with this code:

 CustomerIO.Builder(
        siteId = application.getString(R.string.customer_io_site_id),
        apiKey = application.getString(R.string.customer_io_api_key),
        appContext = application,
        region = Region.EU
  ).autoTrackScreenViews(false).build()

Expected Behavior

The app shouldn't be crashed.

Any other info?

Stack trace:

Fatal Exception: com.squareup.moshi.JsonEncodingException: Use JsonReader.setLenient(true) to accept malformed JSON at path $
       at com.squareup.moshi.JsonReader.syntaxError(JsonReader.java:243)
       at com.squareup.moshi.JsonUtf8Reader.checkLenient(JsonUtf8Reader.java:1152)
       at com.squareup.moshi.JsonUtf8Reader.doPeek(JsonUtf8Reader.java:326)
       at com.squareup.moshi.JsonUtf8Reader.peek(JsonUtf8Reader.java:206)
       at com.squareup.moshi.JsonAdapter.fromJson(JsonAdapter.java:71)
       at io.customer.sdk.queue.QueueStorageImpl.getInventory(QueueStorage.kt:172)
       at io.customer.sdk.queue.QueueStorageImpl.deleteExpired(QueueStorage.kt:131)
       at io.customer.sdk.queue.QueueImpl.deleteExpiredTasks(Queue.kt:215)
       at io.customer.sdk.repository.CleanupRepositoryImpl.cleanup(CleanupRepository.kt:17)
       at io.customer.sdk.CustomerIO$postInitialize$1.invokeSuspend(CustomerIO.kt:232)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
       at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

Error on install CustomerIO tracking and messaging-push-fcm: Record requires ASM8

Target SDK: 31
Min SDK: 21
Kotlin Version: 1.6.10
Gradle Wrapper: https://services.gradle.org/distributions/gradle-7.3.3-all.zip
Gradle Android: com.android.tools.build:gradle:7.0.4
JVM: 11

Error Log on Build:

Execution failed for task ':app:transformClassesWithRealmTransformerForHomolog'.
> Could not resolve all files for configuration ':app:homologRuntimeClasspath'.
   > Failed to transform moshi-1.14.0.jar (com.squareup.moshi:moshi:1.14.0) to match attributes {artifactType=android-asm-instrumented-jars, asm-transformed-variant=homolog, org.gradle.category=library, org.gradle.dependency.bundling=external, org.gradle.jvm.environment=standard-jvm, org.gradle.jvm.version=8, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-runtime, org.jetbrains.kotlin.platform.type=jvm}.
      > Execution failed for AsmClassesTransform: /home/thiago/.gradle/caches/transforms-3/8d8b915a258fe961ebeefbe46c277915/transformed/jetified-moshi-1.14.0.jar.
         > Record requires ASM8

I have been add the CustomerIO libs, but when i try build my project this error hapens, i have tryed upgrade ASM to version 8, add moshi to ignorelist in Jetifier, and even disable Jetifier, but nothing works.

I have a long time in this bug, and nothing of i view in web works.

Can you help me?

Track an Event

We need the ability to track a customer event.

Why are we doing this for our customers?

CIO gives you the ability today to track an event, we need to cater that in the Android SDK.

How do we test that this change is successful?

Upon tracking any event through SDK it should be reflected on Portal as well, also integration test.

How are we building this?

After the customer is identified, store the identifier in preferences. Provide a method in the CustomerIo class to track an event, also able to stop identifying.

  • Store identifier in the prefrences.
  • Create method to track an event CustomerIO.track()
  • Create method to stop identifying events CustomerIO. identifyStop()

In-App Message action iOS/Android Deep Link reopens the app

Version(s) of SDK bug appears: 3.0.0

Current Behavior

We use iOS/Android Deep Link action in the In-App message, but when the app is opened, and we get the in-app message, after pressing Yes! button which triggers the action, the app then reopens again.

Steps to Reproduce

Screen Shot 2022-10-14 at 1 11 01 PM

Screen Shot 2022-10-14 at 1 17 32 PM

Expected Behavior

The app shouldn't reopen because it is already in foreground.

Any other info?

Setup tests for the SDK

In order to move the SDK to the beta phase, we need to have unit tests written for all the components of the SDK.

Why are we doing this for our customers?

Tests help to make us and customers feel more confident our SDK is stable and this would give customers guidance into how to test our SDK in their apps if needed.

How do we test that this change is successful?

We have a unit test coverage reaching every basic module and component of the SDK.

How are we building this?

We Will be adding unit tests for the following for now

  • CustomerIO (singleton class)
  • Identity (Repository, API)
  • Tracking (Repository, API)
  • Push (Repository, API)
  • Push/Rich notifications (Repository, API)
  • Track metrics
  • Network layer

The automated release is failing 🚨

🚨 The automated release from the alpha branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you can benefit from your bug fixes and new features again.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can fix this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the alpha branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here are some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Unfortunately this error doesn't have any additional information. Feel free to kindly ask the author of the @semantic-release/exec plugin to add more helpful information.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Add networking logic to perform HTTP calls to API

Technical requirement in order for the SDK to perform requests to the CIO API.

Why are we doing this for our customers?

Today, our customers need to write custom code in their apps to perform requests to the CIO API to add data to their workspace. This task will setup the technical code in order for us to perform the API calls on your behalf.

How do we test that this change is successful?

The end goal is to perform a successful HTTP request to the CIO API in an Android app via the SDK. This includes:

How are we building this?

Not 100% sure. The end goal is keeping the SDK slim so not using retrofit or volley is a great start.

My current idea is to use kotlin coroutines. Google gave a nice code snippet in it's docs:

sealed class Result<out R> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
}

class LoginRepository(private val responseParser: LoginResponseParser) {
    private const val loginUrl = "https://example.com/login"

    // Function that makes the network request, blocking the current thread
    fun makeLoginRequest(
        jsonBody: String
    ): Result<LoginResponse> {
        val url = URL(loginUrl)
        (url.openConnection() as? HttpURLConnection)?.run {
            requestMethod = "POST"
            setRequestProperty("Content-Type", "application/json; utf-8")
            setRequestProperty("Accept", "application/json")
            doOutput = true
            outputStream.write(jsonBody.toByteArray())
            return Result.Success(responseParser.parse(inputStream))
        }
        return Result.Error(Exception("Cannot open HttpURLConnection"))
    }
}
  • Decide how we will do HTTP networking calls. Implement that code in the SDK.

  • Add User-Agent header for all requests. Value set to Customer.io Android SDK vX.X.X, Y Android app vX.X.X where Y is the name of the customer's Android app. Thinking you can specify an optional app identifier in the configuration?

  • Create a common module to be shared among different modules and possible extension methods for networking, as well as common request/response models

I am having a bit of a hard time understanding why would we need to `pause` making any HTTP requests?

I am having a bit of a hard time understanding why would we need to pause making any HTTP requests?

Shouldn't be either rate-limiting from the start so this issue never arise? if we throttle our requests when making from BQ, we wont get to the point that it starts impacting our APIs performance? Thoughts?

Maybe we can take this to the squad as well for their input on this. ?

Originally posted by @Shahroz16 in #85 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.