From ab97a3e6e92fe358bf8256ebccaee645f18fdf11 Mon Sep 17 00:00:00 2001 From: maureenorea-clores <93700127+maureenorea-clores@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:18:42 +0900 Subject: [PATCH] chore: merge current `v7.7.0` changes to `master` --- inappmessaging/USERGUIDE.md | 6 ++ .../runtime/EventTrackerHelper.kt | 3 +- .../mobile/inappmessaging/runtime/InApp.kt | 29 ++++--- .../inappmessaging/runtime/InAppMessaging.kt | 4 +- .../coroutine/MessageActionsCoroutine.kt | 5 +- .../data/customjson/ApplyClickableImage.kt | 35 +++++++++ .../runtime/data/customjson/CustomJson.kt | 8 ++ .../runtime/data/customjson/MessageMapper.kt | 3 +- .../data/repositories/CampaignRepository.kt | 25 +++--- .../runtime/data/responses/ping/Message.kt | 6 +- .../runtime/extensions/ActivityExtensions.kt | 10 ++- .../runtime/manager/DisplayManager.kt | 2 +- .../manager/MessageReadinessManager.kt | 11 ++- .../runtime/utils/EventMatchingUtil.kt | 8 +- .../runtime/utils/InAppLogger.kt | 6 ++ .../runtime/utils/Initializer.kt | 11 ++- .../utils/MessageEventReconciliationUtil.kt | 2 +- .../runtime/utils/WorkerUtils.kt | 2 +- .../runtime/view/InAppMessageBaseView.kt | 16 ++-- .../runtime/view/InAppMessageViewListener.kt | 4 +- .../runtime/view/InAppMessagingTooltipView.kt | 4 +- .../workmanager/workers/ConfigWorker.kt | 8 +- .../workers/DisplayMessageWorker.kt | 9 ++- .../workmanager/workers/ImpressionWorker.kt | 6 +- .../MessageEventReconciliationWorker.kt | 6 +- .../workmanager/workers/MessageMixerWorker.kt | 11 ++- .../coroutine/MessageActionsCoroutineSpec.kt | 5 ++ .../customjson/ApplyClickableImageSpec.kt | 77 +++++++++++++++++++ .../data/customjson/MessageMapperSpec.kt | 24 ++++++ .../view/InAppMessageViewListenerSpec.kt | 38 ++++----- test/src/main/AndroidManifest.xml | 8 ++ 31 files changed, 294 insertions(+), 98 deletions(-) create mode 100644 inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImage.kt create mode 100644 inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImageSpec.kt diff --git a/inappmessaging/USERGUIDE.md b/inappmessaging/USERGUIDE.md index 88832ebb..e5b457b0 100644 --- a/inappmessaging/USERGUIDE.md +++ b/inappmessaging/USERGUIDE.md @@ -496,6 +496,12 @@ All the events "launch the app event, login event, purchase successful event, cu
(click to expand) +#### 7.7.0 (In-Progress) +* Improvements: + - RMCCX-6876: Improved console logging. +* RMC SDK updates: + - RMCCX-7186: Supported Clickable Image through CustomJson. + #### 7.6.0 (2024-09-17) * Improvements: - SDKCF-6327: Updated compile and target SDK to API 34 (Android 14). diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/EventTrackerHelper.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/EventTrackerHelper.kt index d8b9cc1d..2f6de1b6 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/EventTrackerHelper.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/EventTrackerHelper.kt @@ -33,7 +33,8 @@ internal object EventTrackerHelper { com.rakuten.tech.mobile.analytics.RatTracker.event(eventName, serializableData).track() return true } catch (e: Exception) { - InAppLogger(TAG).warn("Could not send event: $e") + InAppLogger(TAG).warn("sendEvent - analytics.track() failed") + InAppLogger(TAG).debug("exception: $e") } } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InApp.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InApp.kt index 1d66265c..cb1b0877 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InApp.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InApp.kt @@ -57,13 +57,13 @@ internal class InApp( override var onPushPrimer: (() -> Unit)? = null override fun registerPreference(userInfoProvider: UserInfoProvider) { - InAppLogger(TAG).info("registerPreference - userInfoProvider: $userInfoProvider") + InAppLogger(TAG).info("registerPreference: $userInfoProvider") accountRepo.userInfoProvider = userInfoProvider } @SuppressWarnings("TooGenericExceptionCaught") override fun registerMessageDisplayActivity(activity: Activity) { - InAppLogger(TAG).info("registerMessageDisplayActivity - Activity: $activity") + InAppLogger(TAG).info("registerActivity: $activity") try { hostAppInfoRepo.registerActivity(activity) // Making worker thread to display message. @@ -71,6 +71,7 @@ internal class InApp( displayManager.displayMessage() } } catch (ex: Exception) { + InAppLogger(TAG).error("registerActivity - error: ${ex.message}") errorCallback?.let { it(InAppMessagingException("In-App Messaging register activity failed", ex)) } @@ -79,13 +80,14 @@ internal class InApp( @SuppressWarnings("FunctionMaxLength", "TooGenericExceptionCaught") override fun unregisterMessageDisplayActivity() { - InAppLogger(TAG).info("unregisterMessageDisplayActivity") + InAppLogger(TAG).info("unregisterActivity") try { if (configRepo.isConfigEnabled()) { displayManager.removeMessage(hostAppInfoRepo.getRegisteredActivity(), removeAll = true) } hostAppInfoRepo.registerActivity(null) } catch (ex: Exception) { + InAppLogger(TAG).warn("unregisterActivity - error: ${ex.message}") errorCallback?.let { it(InAppMessagingException("In-App Messaging unregister activity failed", ex)) } @@ -102,30 +104,32 @@ internal class InApp( val isSameUser = !accountRepo.updateUserInfo() val areCampaignsSynced = campaignRepo.lastSyncMillis != null && eventMatchingUtil.eventBuffer.isEmpty() - InAppLogger(TAG).debug( - "name: ${event.getEventName()}, attributes: ${event.getAttributeMap()}, " + - "isConfigEnabled: $isConfigEnabled, isSameUser: $isSameUser, synced: $areCampaignsSynced", + InAppLogger(TAG).info( + "logEvent: ${event.getEventName()} - isConfigEnabled: $isConfigEnabled," + + " isSameUser: $isSameUser, synced: $areCampaignsSynced", ) + InAppLogger(TAG).debug("attributes: ${event.getAttributeMap()}") if (!isConfigEnabled || !isSameUser || !areCampaignsSynced) { // To be processed later (flushed after sync) - InAppLogger(TAG).debug("Event added to buffer") + InAppLogger(TAG).debug("event added to buffer") eventMatchingUtil.addToEventBuffer(event) } if (!isSameUser) { // Sync campaigns, flush event buffer, then match events - InAppLogger(TAG).debug("There is a change in user, will perform onSessionUpdate") + InAppLogger(TAG).debug("there is a change in user, will perform onSessionUpdate") sessionManager.onSessionUpdate() return } if (areCampaignsSynced) { // Match event right away - InAppLogger(TAG).debug("Event ${event.getEventName()} will be processed") + InAppLogger(TAG).debug("event ${event.getEventName()} will be processed") eventsManager.onEventReceived(event) } } catch (ex: Exception) { + InAppLogger(TAG).error("logEvent - error: ${ex.message}") errorCallback?.let { it(InAppMessagingException("In-App Messaging log event failed", ex)) } @@ -133,17 +137,17 @@ internal class InApp( } override fun closeMessage(clearQueuedCampaigns: Boolean) { - InAppLogger(TAG).info("closeMessage - clearQueuedCampaigns: $clearQueuedCampaigns") + InAppLogger(TAG).info("closeMessage: $clearQueuedCampaigns") closeCampaign(clearQueuedCampaigns = clearQueuedCampaigns) } override fun closeTooltip(viewId: String) { - InAppLogger(TAG).info("closeTooltip - viewId: $viewId") + InAppLogger(TAG).info("closeTooltip: $viewId") closeCampaign(viewId = viewId) } override fun trackPushPrimer(permissions: Array, grantResults: IntArray) { - InAppLogger(TAG).info("trackPushPrimer - permissions: $permissions, grantResults: $grantResults") + InAppLogger(TAG).info("trackPushPrimer - perm: $permissions, res: $grantResults") if (!BuildVersionChecker.isAndroidTAndAbove()) { return } @@ -178,6 +182,7 @@ internal class InApp( removeMessage(viewId) } } catch (ex: Exception) { + InAppLogger(TAG).warn("closeCampaign - error: ${ex.message}") errorCallback?.let { it(InAppMessagingException("In-App Messaging close message failed", ex)) } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InAppMessaging.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InAppMessaging.kt index dbf5e58a..0075ad49 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InAppMessaging.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/InAppMessaging.kt @@ -133,10 +133,9 @@ abstract class InAppMessaging internal constructor() { configUrl: String? = null, enableTooltipFeature: Boolean? = false, ): Boolean { - InAppLogger(TAG).info("configure") return try { if (!shouldProcess(subscriptionKey)) { - InAppLogger(TAG).debug("Not processed since RMC is integrated") + InAppLogger(TAG).info("configure called but using RMC, skipping") return false } @@ -158,6 +157,7 @@ abstract class InAppMessaging internal constructor() { errorCallback?.let { it(InAppMessagingException("In-App Messaging configuration failed", ex)) } + InAppLogger(TAG).error("configure - error: ${ex.message}") false } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutine.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutine.kt index 44e62928..64b6dba9 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutine.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutine.kt @@ -100,7 +100,8 @@ internal class MessageActionsCoroutine( R.id.message_close_button, BACK_BUTTON -> ImpressionType.EXIT R.id.message_single_button, R.id.message_button_left -> ImpressionType.ACTION_ONE R.id.message_button_right -> ImpressionType.ACTION_TWO - R.id.slide_up, R.id.message_tooltip_image_view, R.id.message_tip -> ImpressionType.CLICK_CONTENT + R.id.slide_up, R.id.message_tooltip_image_view, R.id.message_tip, R.id.message_image_view -> + ImpressionType.CLICK_CONTENT else -> ImpressionType.INVALID } } @@ -165,7 +166,7 @@ internal class MessageActionsCoroutine( try { activityContext.startActivity(intent) } catch (e: ActivityNotFoundException) { - InAppLogger(TAG).debug(e.message) + InAppLogger(TAG).info("handleDeeplinkRedirection - error: ${e.message}") } } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImage.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImage.kt new file mode 100644 index 00000000..47126da6 --- /dev/null +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImage.kt @@ -0,0 +1,35 @@ +package com.rakuten.tech.mobile.inappmessaging.runtime.data.customjson + +import com.rakuten.tech.mobile.inappmessaging.runtime.data.enums.ButtonActionType +import com.rakuten.tech.mobile.inappmessaging.runtime.data.enums.InAppMessageType +import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.Content +import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.OnClickBehavior +import com.rakuten.tech.mobile.inappmessaging.runtime.data.ui.UiMessage + +internal fun UiMessage.applyCustomClickableImage(clickableImage: ClickableImage?, isPushPrimer: Boolean): UiMessage { + fun Int.campaignTypeCanBeClickable(): Boolean { + return this in listOf( + InAppMessageType.FULL.typeId, + InAppMessageType.MODAL.typeId, + ) + } + + @SuppressWarnings("ComplexCondition") + if (clickableImage == null || + clickableImage.url.isNullOrEmpty() || + imageUrl.isNullOrEmpty() || + !type.campaignTypeCanBeClickable() || + isPushPrimer + ) { + return this + } + + val newOnclick = OnClickBehavior(action = ButtonActionType.REDIRECT.typeId, uri = clickableImage.url) + return this.copy( + content = if (this.content == null) { + Content(onClick = newOnclick) + } else { + this.content.copy(onClick = newOnclick) + }, + ) +} diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/CustomJson.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/CustomJson.kt index eb6fcde2..62004350 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/CustomJson.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/CustomJson.kt @@ -2,6 +2,7 @@ package com.rakuten.tech.mobile.inappmessaging.runtime.data.customjson internal data class CustomJson( val pushPrimer: PushPrimer? = null, + val clickableImage: ClickableImage? = null, ) internal data class PushPrimer( @@ -10,3 +11,10 @@ internal data class PushPrimer( */ val button: Int? = null, ) + +internal data class ClickableImage( + /** + * Redirect URL or deeplink. + */ + val url: String? = null, +) diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapper.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapper.kt index 46aee3a7..ffe5fbfc 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapper.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapper.kt @@ -36,7 +36,8 @@ internal object MessageMapper : Mapper { uiModel } else { uiModel - .applyCustomPushPrimer(customJsonData.pushPrimer) // PushPrimer + .applyCustomPushPrimer(customJsonData.pushPrimer) + .applyCustomClickableImage(customJsonData.clickableImage, from.isPushPrimer) } } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/repositories/CampaignRepository.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/repositories/CampaignRepository.kt index e062d8cc..3e4c5074 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/repositories/CampaignRepository.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/repositories/CampaignRepository.kt @@ -57,7 +57,7 @@ internal abstract class CampaignRepository { } override fun syncWith(messageList: List, timestampMillis: Long, ignoreTooltips: Boolean) { - InAppLogger(TAG).debug("START - message size: ${messageList.size}") + InAppLogger(TAG).info("syncWith start") lastSyncMillis = timestampMillis loadCachedData() // ensure we're using latest cache data for syncing below val oldList = LinkedHashMap(messages) // copy @@ -68,7 +68,7 @@ internal abstract class CampaignRepository { messages[updatedCampaign.campaignId] = updatedCampaign } saveDataToCache() - InAppLogger(TAG).debug("END") + InAppLogger(TAG).info("syncWith end") } private fun List.filterMessages(ignoreTooltips: Boolean): List { @@ -99,13 +99,10 @@ internal abstract class CampaignRepository { } override fun optOutCampaign(id: String): Message? { - InAppLogger(TAG).debug("Campaign: $id}") + InAppLogger(TAG).debug("campaign: $id}") val localCampaign = messages[id] if (localCampaign == null) { - InAppLogger(TAG).debug( - "Campaign ($id) could not be updated -" + - "not found in the repository", - ) + InAppLogger(TAG).debug("campaign not found in repository") return null } val updatedCampaign = localCampaign.apply { isOptedOut = true } @@ -116,7 +113,7 @@ internal abstract class CampaignRepository { } override fun decrementImpressions(id: String): Message? { - InAppLogger(TAG).debug("Campaign: $id") + InAppLogger(TAG).debug("campaign: $id") val campaign = messages[id] ?: return null return updateImpressions( campaign, @@ -136,7 +133,7 @@ internal abstract class CampaignRepository { @SuppressWarnings("TooGenericExceptionCaught") private fun loadCachedData() { if (InAppMessaging.instance().isLocalCachingEnabled()) { - InAppLogger(TAG).debug("START") + InAppLogger(TAG).debug("start") messages.clear() try { val jsonObject = JSONObject(retrieveData()) @@ -146,9 +143,9 @@ internal abstract class CampaignRepository { ) } } catch (ex: Exception) { - InAppLogger(TAG).debug(ex.cause, "Invalid JSON format for $IAM_USER_CACHE data") + InAppLogger(TAG).debug(ex.cause, "invalid JSON format for $IAM_USER_CACHE data") } - InAppLogger(TAG).debug("END") + InAppLogger(TAG).debug("end") } } @@ -161,7 +158,8 @@ internal abstract class CampaignRepository { key = IAM_USER_CACHE, defValue = "", ) - InAppLogger(TAG).debug("Cache Read - file: $preferenceFile, data: $preferenceData") + InAppLogger(TAG).info("retrieveData - file: $preferenceFile") + InAppLogger(TAG).debug("retrieveData - data: $preferenceData") preferenceData }.orEmpty() } @@ -171,7 +169,8 @@ internal abstract class CampaignRepository { HostAppInfoRepository.instance().getContext()?.let { ctx -> val preferenceFile = InAppMessaging.getPreferencesFile() val preferenceData = Gson().toJson(messages) - InAppLogger(TAG).debug("Cache Write - file: $preferenceFile, data: $preferenceData") + InAppLogger(TAG).info("saveData - file: $preferenceFile") + InAppLogger(TAG).debug("saveData - data: $preferenceData") PreferencesUtil.putString( context = ctx, name = preferenceFile, diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/responses/ping/Message.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/responses/ping/Message.kt index 7a3eefdc..de2246a8 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/responses/ping/Message.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/responses/ping/Message.kt @@ -72,7 +72,8 @@ internal data class Message( tooltip = null } } catch (je: JsonParseException) { - InAppLogger(TAG).warn("Invalid format for tooltip config.", je) + InAppLogger(TAG).warn("getTooltipConfig - invalid tooltip format") + InAppLogger(TAG).debug("parse exception: $je") } } return tooltip @@ -86,7 +87,8 @@ internal data class Message( try { customJsonData = Gson().fromJson(customJson, CustomJson::class.java) } catch (je: JsonParseException) { - InAppLogger(TAG).warn("Invalid format/representation for CustomJson", je) + InAppLogger(TAG).warn("getCustomJsonData - invalid customJson format") + InAppLogger(TAG).debug("parse exception: $je") } } return customJsonData diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/extensions/ActivityExtensions.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/extensions/ActivityExtensions.kt index 454cf656..7c1a018e 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/extensions/ActivityExtensions.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/extensions/ActivityExtensions.kt @@ -2,11 +2,15 @@ package com.rakuten.tech.mobile.inappmessaging.runtime.extensions import android.Manifest import android.app.Activity +import android.content.ActivityNotFoundException import android.content.Intent import android.os.Build import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat import com.rakuten.tech.mobile.inappmessaging.runtime.InAppMessaging +import com.rakuten.tech.mobile.inappmessaging.runtime.utils.InAppLogger + +private const val TAG = "IAM_ActivityExt" /** * Prompts the push notification permission dialog if applicable. @@ -24,5 +28,9 @@ internal fun Activity.openAppNotifPermissionSettings() { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.putExtra("android.provider.extra.APP_PACKAGE", this.packageName) - this.startActivity(intent) + try { + this.startActivity(intent) + } catch (e: ActivityNotFoundException) { + InAppLogger(TAG).info("openAppNotifPermissionSettings - error: ${e.message}") + } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/DisplayManager.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/DisplayManager.kt index fdae9d29..6efc7011 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/DisplayManager.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/DisplayManager.kt @@ -165,7 +165,7 @@ internal interface DisplayManager { parent.removeView(inAppMessageBaseView) } - InAppLogger(TAG).debug("View removed") + InAppLogger(TAG).debug("view removed") } private fun removeTooltip(parent: ViewGroup, id: String?, activity: Activity) { diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/MessageReadinessManager.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/MessageReadinessManager.kt index b2091e6e..792703a7 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/MessageReadinessManager.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/manager/MessageReadinessManager.kt @@ -112,13 +112,13 @@ internal class MessageReadinessManager( for (messageId in queuedMessagesCopy) { val message = campaignRepo.messages[messageId] if (message == null) { - InAppLogger(TAG).debug("Queued campaign $messageId does not exist in the repository anymore") + InAppLogger(TAG).debug("queued campaign $messageId does not exist in the repository anymore") continue } // First, check if this message should be displayed. if (!shouldDisplayMessage(message)) { - InAppLogger(TAG).debug("Skipping message: ${message.campaignId}") + InAppLogger(TAG).info("campaign won't be displayed: ${message.campaignId}") // Skip to next message. continue } @@ -136,11 +136,10 @@ internal class MessageReadinessManager( @SuppressWarnings("LongMethod") private fun shouldPing(message: Message, result: MutableList) = if (message.isTest) { - InAppLogger(TAG).debug("Skipping test message: ${message.campaignId}") + InAppLogger(TAG).debug("skipping test message: ${message.campaignId}") result.add(message) false } else { - InAppLogger(TAG).debug("Check API START - campaignId: ${message.campaignId}") // Check message display permission with server. val displayPermissionResponse = getMessagePermission(message) // If server wants SDK to ping for updated messages, do a new ping request and break this loop. @@ -260,7 +259,7 @@ internal class MessageReadinessManager( handleResponse(response, call.clone()) } catch (e: Exception) { checkAndRetry(call.clone()) { - InAppLogger(DISP_TAG).error(e.message) + InAppLogger(DISP_TAG).error("executeDisplayRequest - error: ${e.message}") InAppMessaging.errorCallback?.let { it(InAppMessagingException("In-App Messaging display permission request failed", e)) } @@ -272,7 +271,7 @@ internal class MessageReadinessManager( response: Response, callClone: Call, ): DisplayPermissionResponse? { - InAppLogger(DISP_TAG).debug("Check API END - code: ${response.code()}, body: ${response.body()}") + InAppLogger(DISP_TAG).info("check API - code: ${response.code()}") return when { response.isSuccessful -> response.body() response.code() >= HttpURLConnection.HTTP_INTERNAL_ERROR -> checkAndRetry(callClone) { diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/EventMatchingUtil.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/EventMatchingUtil.kt index 2155b035..06aa82ca 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/EventMatchingUtil.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/EventMatchingUtil.kt @@ -88,7 +88,7 @@ internal abstract class EventMatchingUtil { val totalMatchedEvents = campaignEvents.count() + persistentEvents.count() if (!(totalMatchedEvents > 0 && totalMatchedEvents >= eventsToRemove.size)) { - InAppLogger(TAG).debug("Couldn't find set of events") + InAppLogger(TAG).debug("couldn't find set of events") return false } @@ -121,7 +121,7 @@ internal abstract class EventMatchingUtil { } val index = campaignEvents.indexOf(eventToRemove) if (index == -1) { - InAppLogger(TAG).debug("Couldn't find requested set of events") + InAppLogger(TAG).debug("couldn't find requested set of events") return true } campaignEvents.removeAt(index) @@ -136,7 +136,7 @@ internal abstract class EventMatchingUtil { override fun addToEventBuffer(event: Event) { synchronized(eventBuffer) { eventBuffer.add(event) - InAppLogger(TAG).debug("Buffer: " + eventBuffer.map { it.getEventName() }) + InAppLogger(TAG).debug("buffer: " + eventBuffer.map { it.getEventName() }) } } @@ -144,7 +144,7 @@ internal abstract class EventMatchingUtil { synchronized(eventBuffer) { eventBuffer.forEach { ev -> matchAndStore(ev) } eventBuffer.clear() - InAppLogger(TAG).debug("Buffer: []") + InAppLogger(TAG).debug("buffer: []") } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/InAppLogger.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/InAppLogger.kt index 38a5e678..0168eb1f 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/InAppLogger.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/InAppLogger.kt @@ -2,6 +2,12 @@ package com.rakuten.tech.mobile.inappmessaging.runtime.utils import com.rakuten.tech.mobile.sdkutils.logger.Logger +/** + * Error, warn, and info logs are logged regardless of build configuration as per standard: + * https://source.android.com/docs/core/tests/debug/understanding-logging#log-level-guidelines + * + * Debug logs are logged only if host app enables it through manifest metadata. + */ internal class InAppLogger(tag: String) : Logger(tag) { init { this.setDebug(isDebug) diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/Initializer.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/Initializer.kt index 56d877e8..1255b982 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/Initializer.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/Initializer.kt @@ -97,13 +97,18 @@ internal object Initializer { enableTooltipFeature: Boolean? = false, sharedUtil: PreferencesUtil = PreferencesUtil, ) { + val deviceId = getDeviceId(context, sharedUtil) + val (appVer, rmcVer) = Pair(getHostAppVersion(context), getRmcSdkVersion(context)) + val hostAppInfo = HostAppInfo( - packageName = getHostAppPackageName(context), deviceId = getDeviceId(context, sharedUtil), - version = getHostAppVersion(context), subscriptionKey = subscriptionKey, locale = getLocale(context), + packageName = getHostAppPackageName(context), deviceId = deviceId, + version = appVer, subscriptionKey = subscriptionKey, locale = getLocale(context), configUrl = configUrl, isTooltipFeatureEnabled = enableTooltipFeature, context = context, - rmcSdkVersion = getRmcSdkVersion(context), + rmcSdkVersion = rmcVer, ) + InAppLogger(TAG).info("configure - device: $deviceId, appVer: $appVer, rmcVer: $rmcVer") + // Store hostAppInfo in repository. HostAppInfoRepository.instance().addHostInfo(hostAppInfo) diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/MessageEventReconciliationUtil.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/MessageEventReconciliationUtil.kt index 2f50f00c..6f802d16 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/MessageEventReconciliationUtil.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/MessageEventReconciliationUtil.kt @@ -23,7 +23,7 @@ internal class MessageEventReconciliationUtil( val triggers = campaign.triggers if (triggers.isNullOrEmpty()) { - InAppLogger(TAG).debug("Campaign (${campaign.campaignId}) has no triggers.") + InAppLogger(TAG).debug("campaign (${campaign.campaignId}) has no triggers.") continue } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/WorkerUtils.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/WorkerUtils.kt index d527a6a6..7e61f51f 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/WorkerUtils.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/utils/WorkerUtils.kt @@ -16,7 +16,7 @@ internal object WorkerUtils { } fun logSilentRequestError(tag: String, code: Int, message: String?) { - InAppLogger(tag).debug("Response Code $code: ${message ?: "no error message"}") + InAppLogger(tag).debug("response Code $code: ${message ?: "no error message"}") } fun checkRetry(counter: Int, retryFunc: () -> ListenableWorker.Result): ListenableWorker.Result { diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageBaseView.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageBaseView.kt index b3c2fa5e..1337349f 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageBaseView.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageBaseView.kt @@ -21,6 +21,7 @@ import com.google.android.material.button.MaterialButton import com.rakuten.tech.mobile.inappmessaging.runtime.R import com.rakuten.tech.mobile.inappmessaging.runtime.data.ui.UiMessage import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.MessageButton +import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.OnClickBehavior import com.rakuten.tech.mobile.inappmessaging.runtime.utils.BuildVersionChecker import com.rakuten.tech.mobile.inappmessaging.runtime.utils.InAppLogger import com.rakuten.tech.mobile.inappmessaging.runtime.utils.ResourceUtils @@ -47,6 +48,7 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) var listener: InAppMessageViewListener? = null private set private var imageUrl: String? = null + private var imageClickBehavior: OnClickBehavior? = null private var headerColor = 0 private var messageBodyColor = 0 private var header: String? = null @@ -70,6 +72,7 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) this.messageBody = uiMessage.bodyText this.buttons = uiMessage.buttons this.imageUrl = uiMessage.imageUrl + this.imageClickBehavior = uiMessage.content?.onClick this.listener = InAppMessageViewListener(uiMessage) this.displayOptOut = uiMessage.displaySettings.isOptedOut this.isDismissable = uiMessage.shouldShowUpperCloseButton @@ -168,6 +171,9 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) this.visibility = GONE findViewById(R.id.message_image_view)?.let { imgView -> imgView.setOnTouchListener(this.listener) + if (!this.imageClickBehavior?.uri.isNullOrEmpty()) { + imgView.setOnClickListener(this.listener) + } try { val callback = object : Callback { override fun onSuccess() { @@ -176,7 +182,7 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) } override fun onError(e: Exception?) { - InAppLogger(TAG).debug(e?.cause, "Downloading image failed $imageUrl") + InAppLogger(TAG).debug(e?.cause, "downloading image failed $imageUrl") } } (picasso ?: Picasso.get()).load(this.imageUrl) @@ -186,7 +192,7 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) .centerInside() .into(imgView, callback) } catch (ex: Exception) { - InAppLogger(TAG).debug(ex, "Downloading image failed $imageUrl") + InAppLogger(TAG).debug(ex, "downloading image failed $imageUrl") } } } @@ -215,7 +221,7 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) Color.parseColor(button.buttonBackgroundColor) } catch (e: IllegalArgumentException) { // values are from backend - InAppLogger(TAG).error(e.message) + InAppLogger(TAG).error("setBgColor - error: ${e.message}") // set to default color Color.WHITE } @@ -229,7 +235,7 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) Color.parseColor(button.buttonTextColor) } catch (e: IllegalArgumentException) { // values are from backend - InAppLogger(TAG).error(e.message) + InAppLogger(TAG).error("setTextColor - error: ${e.message}") // set to default color Color.parseColor("#1D1D1D") } @@ -346,7 +352,7 @@ internal open class InAppMessageBaseView(context: Context, attrs: AttributeSet?) ResourceUtils.getResourceIdentifier(ctx, ctx.getString(strId), "font"), ) } catch (rex: Resources.NotFoundException) { - InAppLogger(TAG).debug(rex.cause, "Font file is not found. Will revert to default font.") + InAppLogger(TAG).debug(rex.cause, "font file is not found. Will revert to default font.") } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListener.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListener.kt index 7b5ea1a1..09f10798 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListener.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListener.kt @@ -43,7 +43,7 @@ internal class InAppMessageViewListener( /** * Callback When touch event occurred. Which will trigger to magnify message view content. */ - @SuppressLint("NewApi") + @SuppressLint("NewApi", "ClickableViewAccessibility") override fun onTouch(view: View, event: MotionEvent): Boolean { if (buildChecker.isAndroidQAndAbove()) { when (event.actionMasked) { @@ -52,7 +52,7 @@ internal class InAppMessageViewListener( MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> this.magnifier?.dismiss() else -> this.magnifier?.dismiss() } - return view.performClick() + // No need to performClick as it will be handled by system through setOnClickListener if it is clickable } return false } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessagingTooltipView.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessagingTooltipView.kt index ce8b7532..cac61bcf 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessagingTooltipView.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessagingTooltipView.kt @@ -134,7 +134,7 @@ internal class InAppMessagingTooltipView( } override fun onError(e: Exception?) { - InAppLogger(TAG).debug(e?.cause, "Downloading image failed $imageUrl") + InAppLogger(TAG).debug(e?.cause, "downloading image failed: $imageUrl") } } @@ -160,7 +160,7 @@ internal class InAppMessagingTooltipView( .centerInside() .into(view, callback) } catch (ex: Exception) { - InAppLogger(TAG).debug(ex, "Downloading image failed $imageUrl") + InAppLogger(TAG).debug(ex, "downloading image failed: $imageUrl") } } } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ConfigWorker.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ConfigWorker.kt index 4bb2b354..420ef4fa 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ConfigWorker.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ConfigWorker.kt @@ -53,7 +53,6 @@ internal class ConfigWorker( */ @SuppressWarnings("TooGenericExceptionCaught") override fun doWork(): Result { - InAppLogger(TAG).debug("Config API - START") // Terminate request if any of the following values are empty if (!isConfigValid()) { return Result.failure() @@ -63,7 +62,7 @@ internal class ConfigWorker( // Executing the API network call. onResponse(setupCall().execute()) } catch (e: Exception) { - InAppLogger(TAG).error(e.message) + InAppLogger(TAG).error("config - error: ${e.message}") // RETRY by default has exponential backoff baked in. Result.retry() } @@ -102,6 +101,7 @@ internal class ConfigWorker( if (response.isSuccessful && response.body() != null) { handleResponse(response) } else { + InAppLogger(TAG).error("config API - error: ${response.code()}") return when { response.code() == RetryDelayUtil.RETRY_ERROR_CODE -> handleRetry(response) response.code() >= HttpURLConnection.HTTP_INTERNAL_ERROR -> handleInternalError(response) @@ -135,8 +135,8 @@ internal class ConfigWorker( // Schedule a ping request to message mixer. Initial delay is 0 // reset current delay to initial ConfigScheduler.currDelay = RetryDelayUtil.INITIAL_BACKOFF_DELAY - InAppLogger(TAG).debug( - "Config API END - rollout: ${response.body()?.data?.rollOutPercentage}, " + + InAppLogger(TAG).info( + "config API success - rollout: ${response.body()?.data?.rollOutPercentage}, " + "enabled: ${configRepo.isConfigEnabled()}", ) if (configRepo.isConfigEnabled()) { diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/DisplayMessageWorker.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/DisplayMessageWorker.kt index 7f6f51ec..4ef98495 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/DisplayMessageWorker.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/DisplayMessageWorker.kt @@ -37,9 +37,9 @@ internal class DisplayMessageWorker( * This method starts displaying message runnable. */ override suspend fun doWork(): Result { - InAppLogger(TAG).debug("Display worker START, thread: ${Thread.currentThread().name}") + InAppLogger(TAG).debug("display worker start - thread: ${Thread.currentThread().name}") prepareNextMessage() - InAppLogger(TAG).debug("Display worker END") + InAppLogger(TAG).debug("display worker end") return Result.success() } @@ -78,7 +78,7 @@ internal class DisplayMessageWorker( } override fun onError(e: Exception?) { - InAppLogger(TAG).debug("Downloading image failed") + InAppLogger(TAG).error("downloading image failed for: ${message.campaignId}") } }, context = hostActivity, picasso = picasso, @@ -88,7 +88,7 @@ internal class DisplayMessageWorker( private fun displayMessage(message: Message, hostActivity: Activity, newWorker: Boolean = false) { if (!verifyContexts(message)) { // Message display aborted by the host app - InAppLogger(TAG).debug("Message display cancelled by the host app") + InAppLogger(TAG).info("verifyContext - campaign cancelled: ${message.campaignId}") // Remove message in queue messageReadinessManager.removeMessageFromQueue(message.campaignId) // Prepare next message @@ -96,6 +96,7 @@ internal class DisplayMessageWorker( return } + InAppLogger(TAG).info("will display campaign: ${message.campaignId}") // Display message on main thread handler.post(DisplayMessageRunnable(uiMessage = MessageMapper.mapFrom(message), hostActivity)) } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ImpressionWorker.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ImpressionWorker.kt index 0873f275..cd8066dc 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ImpressionWorker.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/ImpressionWorker.kt @@ -56,7 +56,7 @@ internal class ImpressionWorker( val impressionRequest = try { Gson().fromJson(impressionRequestJsonRequest, ImpressionRequest::class.java) } catch (e: JsonParseException) { - InAppLogger(TAG).error(e.message) + InAppLogger(TAG).error("impression - error: ${e.message}") return Result.failure() } @@ -65,13 +65,13 @@ internal class ImpressionWorker( // Execute Retrofit API call and handle response. onResponse(createReportImpressionCall(impressionEndpoint, impressionRequest).execute()) } catch (e: Exception) { - InAppLogger(TAG).debug(e.message) + InAppLogger(TAG).error("impression - error: ${e.message}") Result.retry() } } private fun onResponse(response: Response): Result { - InAppLogger(TAG).debug("Impression Response:%d", response.code()) + InAppLogger(TAG).info("impression API - code: ${response.code()}") return when { response.code() >= HttpURLConnection.HTTP_INTERNAL_ERROR -> diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageEventReconciliationWorker.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageEventReconciliationWorker.kt index 0ad69e12..5ab3bad0 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageEventReconciliationWorker.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageEventReconciliationWorker.kt @@ -45,7 +45,7 @@ internal class MessageEventReconciliationWorker( * Then add ready to display messages to repository. */ override fun doWork(): Result { - InAppLogger(TAG).debug("Reconcile messages and local events") + InAppLogger(TAG).debug("reconcile messages and local events") var startTime: Long = 0 if (BuildConfig.DEBUG) { startTime = getCurrentTimeMillis() @@ -61,7 +61,7 @@ internal class MessageEventReconciliationWorker( if (BuildConfig.DEBUG) { endTime = getCurrentTimeMillis() } - InAppLogger(TAG).debug("Time took to reconcile: %d milliseconds", endTime - startTime) + InAppLogger(TAG).debug("time took to reconcile: %d milliseconds", endTime - startTime) return Result.success() } @@ -70,7 +70,7 @@ internal class MessageEventReconciliationWorker( if (eventMatchingUtil.removeSetOfMatchedEvents(events, campaign)) { messageReadinessManager.addMessageToQueue(campaign.campaignId) InAppLogger(TAG).debug( - "Ready message - campaignId: ${campaign.campaignId}, " + + "ready message - campaignId: ${campaign.campaignId}, " + "header: ${campaign.messagePayload.header}", ) } diff --git a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageMixerWorker.kt b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageMixerWorker.kt index 99a55e17..4227e5d9 100644 --- a/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageMixerWorker.kt +++ b/inappmessaging/src/main/java/com/rakuten/tech/mobile/inappmessaging/runtime/workmanager/workers/MessageMixerWorker.kt @@ -67,14 +67,12 @@ internal class MessageMixerWorker( // Execute a thread blocking API network call, and handle response. onResponse(call.execute()) } catch (e: Exception) { - InAppLogger(TAG).error("Ping API END - error: ${e.message}") + InAppLogger(TAG).error("ping - error: ${e.message}") Result.retry() } } private fun setupCall(): Call { - InAppLogger(TAG).debug("Ping API START") - // Create a retrofit API. val serviceApi = RuntimeUtil.getRetrofit().create(MessageMixerRetrofitService::class.java) @@ -104,11 +102,11 @@ internal class MessageMixerWorker( */ @VisibleForTesting fun onResponse(response: Response): Result { - InAppLogger(TAG).debug("Ping API END - isSuccessful: ${response.isSuccessful}") if (response.isSuccessful) { serverErrorCounter.set(0) // reset server error counter response.body()?.let { handleResponse(it) } } else { + InAppLogger(TAG).error("ping API error - code: ${response.code()}") return when { response.code() == RetryDelayUtil.RETRY_ERROR_CODE -> handleRetry(response) response.code() >= HttpURLConnection.HTTP_INTERNAL_ERROR -> handleInternalError(response) @@ -134,6 +132,8 @@ internal class MessageMixerWorker( } private fun handleResponse(messageMixerResponse: MessageMixerResponse) { + InAppLogger(TAG).info("ping API success - campaigns: ${messageMixerResponse.data.size}") + // Parse all data in response. val parsedMessages = parsePingRespTestMessage(messageMixerResponse) @@ -153,7 +153,6 @@ internal class MessageMixerWorker( // Schedule next ping. scheduleNextPing(messageMixerResponse.nextPingMillis) - InAppLogger(TAG).debug("campaign size: %d", messageMixerResponse.data.size) } private fun retryPingRequest(): Result { @@ -171,7 +170,7 @@ internal class MessageMixerWorker( // reset current delay to initial MessageMixerPingScheduler.currDelay = RetryDelayUtil.INITIAL_BACKOFF_DELAY messageMixerScheduler.pingMessageMixerService(nextPingMillis) - InAppLogger(TAG).debug("Next ping scheduled in: %d", nextPingMillis) + InAppLogger(TAG).debug("next ping scheduled in: $nextPingMillis") } /** diff --git a/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutineSpec.kt b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutineSpec.kt index d5e5106d..3bf4663a 100644 --- a/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutineSpec.kt +++ b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/coroutine/MessageActionsCoroutineSpec.kt @@ -168,6 +168,11 @@ class MessageActionsCoroutineFuncSpec : BaseTest() { action.getOnClickBehaviorType(-2) shouldBeEqualTo ImpressionType.INVALID } + @Test + fun `should return click content for image resource`() { + action.getOnClickBehaviorType(R.id.message_image_view) shouldBeEqualTo ImpressionType.CLICK_CONTENT + } + @Test fun `should return null for action one with empty buttons`() { val message = message.copy(buttons = listOf()) diff --git a/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImageSpec.kt b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImageSpec.kt new file mode 100644 index 00000000..deb1413f --- /dev/null +++ b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/ApplyClickableImageSpec.kt @@ -0,0 +1,77 @@ +package com.rakuten.tech.mobile.inappmessaging.runtime.data.customjson + +import com.rakuten.tech.mobile.inappmessaging.runtime.data.enums.InAppMessageType +import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.Content +import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.OnClickBehavior +import com.rakuten.tech.mobile.inappmessaging.runtime.testhelpers.TestDataHelper +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test + +class ApplyClickableImageSpec { + private val message = MessageMapper.mapFrom(TestDataHelper.createDummyMessage()).copy( + content = null, + imageUrl = "http://test.com", + type = InAppMessageType.MODAL.typeId, + ) + + @Test + fun `should do nothing if clickableImage setting does not exist`() { + val uiMessage = message.applyCustomClickableImage(null, false) + uiMessage shouldBeEqualTo message + } + + @Test + fun `should do nothing if url attribute does not exist or empty`() { + var uiMessage = message.applyCustomClickableImage(ClickableImage(), false) + uiMessage shouldBeEqualTo message + + uiMessage = message.applyCustomClickableImage(ClickableImage(""), false) + uiMessage shouldBeEqualTo message + } + + @Test + fun `should do nothing if imageUrl does not exist or empty`() { + var uiMessage = message.copy(imageUrl = null) + .applyCustomClickableImage(ClickableImage("http://test.com"), false) + uiMessage.content?.onClick?.uri shouldBeEqualTo null + + uiMessage = message.copy(imageUrl = "") + .applyCustomClickableImage(ClickableImage("http://test.com"), false) + uiMessage.content?.onClick?.uri shouldBeEqualTo null + } + + @Test + fun `should do nothing if campaign layout isn't clickable`() { + val uiMessage = message.copy(type = InAppMessageType.SLIDE.typeId) + .applyCustomClickableImage(ClickableImage("http://test.com"), false) + + uiMessage.content?.onClick?.uri shouldBeEqualTo null + } + + @Test + fun `should do nothing if campaign is a PushPrimer`() { + val uiMessage = message.copy(type = InAppMessageType.MODAL.typeId) + .applyCustomClickableImage(ClickableImage("http://test.com"), true) + + uiMessage.content?.onClick?.uri shouldBeEqualTo null + } + + @Test + fun `should update content url to clickableImage url for null content data`() { + val uiMessage = message.copy(type = InAppMessageType.MODAL.typeId) + .applyCustomClickableImage(ClickableImage("http://test.com"), false) + + uiMessage.content?.onClick?.uri shouldBeEqualTo "http://test.com" + } + + @Test + fun `should update content url to clickableImage url for non-null content data`() { + val uiMessage = message.copy( + type = InAppMessageType.MODAL.typeId, + content = Content(onClick = OnClickBehavior(3)), + ) + .applyCustomClickableImage(ClickableImage("http://test.com"), false) + + uiMessage.content?.onClick?.uri shouldBeEqualTo "http://test.com" + } +} diff --git a/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapperSpec.kt b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapperSpec.kt index b5426880..31244417 100644 --- a/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapperSpec.kt +++ b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/data/customjson/MessageMapperSpec.kt @@ -5,8 +5,10 @@ import com.rakuten.tech.mobile.inappmessaging.runtime.RmcHelper import com.rakuten.tech.mobile.inappmessaging.runtime.data.enums.ButtonActionType import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.MessageButton import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.OnClickBehavior +import com.rakuten.tech.mobile.inappmessaging.runtime.data.responses.ping.Resource import com.rakuten.tech.mobile.inappmessaging.runtime.testhelpers.TestDataHelper import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldNotBeEqualTo import org.junit.After import org.junit.Before import org.junit.Test @@ -60,4 +62,26 @@ class MessageMapperSpec { uiMessage.buttons.size shouldBeEqualTo 1 uiMessage.buttons[0].buttonBehavior.action shouldBeEqualTo ButtonActionType.PUSH_PRIMER.typeId } + + @Test + fun `should apply custom ClickableImage setting`() { + val settings = TestDataHelper.message0Payload.messageSettings + val payload = TestDataHelper.message0Payload.copy( + resource = Resource(imageUrl = "http://test/image.png", cropType = 0), + messageSettings = settings.copy(controlSettings = settings.controlSettings.copy(content = null)), + ) + + val uiMessage = MessageMapper.mapFrom( + TestDataHelper.createDummyMessage( + messagePayload = payload, + customJson = JsonParser.parseString("""{"clickableImage": { "url": "http://test.com" }}""") + .asJsonObject, + ), + ) + + uiMessage.content shouldNotBeEqualTo null + uiMessage.content?.onClick shouldNotBeEqualTo null + uiMessage.content?.onClick?.action shouldBeEqualTo ButtonActionType.REDIRECT.typeId + uiMessage.content?.onClick?.uri shouldBeEqualTo "http://test.com" + } } diff --git a/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListenerSpec.kt b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListenerSpec.kt index 4adb58d6..56a11dc2 100644 --- a/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListenerSpec.kt +++ b/inappmessaging/src/test/java/com/rakuten/tech/mobile/inappmessaging/runtime/view/InAppMessageViewListenerSpec.kt @@ -138,78 +138,78 @@ class InAppMessageViewListenerOnTouchSpec : InAppMessageViewListenerSpec() { } @Test - fun `should return true on touch with action down and onClick listener`() { + fun `should return false on touch with action down and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_DOWN) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() } @Test - fun `should return true on touch with action move and onClick listener`() { + fun `should return false on touch with action move and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_MOVE) listener.magnifier = Mockito.mock(Magnifier::class.java) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() } @Test - fun `should return true on touch with action cancel and onClick listener`() { + fun `should return false on touch with action cancel and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_CANCEL) listener.magnifier = Mockito.mock(Magnifier::class.java) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() } @Test - fun `should return true on touch with action up and onClick listener`() { + fun `should return false on touch with action up and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_UP) listener.magnifier = Mockito.mock(Magnifier::class.java) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() } @Test - fun `should return true on touch with other action and onClick listener`() { + fun `should return false on touch with other action and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_OUTSIDE) listener.magnifier = Mockito.mock(Magnifier::class.java) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() } @Test - fun `should return true on touch with action move, null magnifier, and onClick listener`() { + fun `should return false on touch with action move, null magnifier, and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_MOVE) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() listener.magnifier.shouldBeNull() } @Test - fun `should return true on touch with action cancel, null magnifier, and onClick listener`() { + fun `should return false on touch with action cancel, null magnifier, and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_CANCEL) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() listener.magnifier.shouldBeNull() } @Test - fun `should return true on touch with action up, null magnifier, and onClick listener`() { + fun `should return false on touch with action up, null magnifier, and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_UP) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() listener.magnifier.shouldBeNull() } @Test - fun `should return true on touch with other action, null magnifier, and onClick listener`() { + fun `should return false on touch with other action, null magnifier, and onClick listener`() { val listener = setupListener(MotionEvent.ACTION_OUTSIDE) - listener.onTouch(mockView, mockMotionEvent).shouldBeTrue() + listener.onTouch(mockView, mockMotionEvent).shouldBeFalse() listener.magnifier.shouldBeNull() } @Test - fun `should return true on touch with action down`() { + fun `should return false on touch with action down`() { val listener = InAppMessageViewListener( MessageMapper.mapFrom( TestDataHelper.createDummyMessage(campaignId = "1", isTest = true), diff --git a/test/src/main/AndroidManifest.xml b/test/src/main/AndroidManifest.xml index 2490ebdc..48a28330 100644 --- a/test/src/main/AndroidManifest.xml +++ b/test/src/main/AndroidManifest.xml @@ -24,6 +24,14 @@ + + + + + + + +