-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(SDK-4171): Make Analytics manager testable #689
base: develop
Are you sure you want to change the base?
Changes from all commits
4935e17
a786cf1
bd05894
8bd0c51
31947ce
b5a8471
6b6b32d
4ff1d69
736629e
3035182
678dc9e
be704b3
a357dd0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,48 +34,41 @@ | |
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
|
||
import kotlin.jvm.functions.Function0; | ||
|
||
public class AnalyticsManager extends BaseAnalyticsManager { | ||
|
||
private final CTLockManager ctLockManager; | ||
|
||
private final HashMap<String, Integer> installReferrerMap = new HashMap<>(8); | ||
|
||
private final BaseEventQueueManager baseEventQueueManager; | ||
|
||
private final BaseCallbackManager callbackManager; | ||
|
||
private final CleverTapInstanceConfig config; | ||
|
||
private final Context context; | ||
|
||
private final ControllerManager controllerManager; | ||
|
||
private final CoreMetaData coreMetaData; | ||
|
||
private final DeviceInfo deviceInfo; | ||
|
||
private final ValidationResultStack validationResultStack; | ||
|
||
private final Validator validator; | ||
|
||
private final InAppResponse inAppResponse; | ||
|
||
private final HashMap<String, Object> notificationIdTagMap = new HashMap<>(); | ||
|
||
private final Function0<Long> currentTimeProvider; | ||
private final Object notificationMapLock = new Object(); | ||
|
||
private final HashMap<String, Object> notificationIdTagMap = new HashMap<>(); | ||
private final HashMap<String, Object> notificationViewedIdTagMap = new HashMap<>(); | ||
|
||
AnalyticsManager(Context context, | ||
CleverTapInstanceConfig config, | ||
BaseEventQueueManager baseEventQueueManager, | ||
Validator validator, | ||
ValidationResultStack validationResultStack, | ||
CoreMetaData coreMetaData, | ||
DeviceInfo deviceInfo, | ||
BaseCallbackManager callbackManager, ControllerManager controllerManager, | ||
final CTLockManager ctLockManager, | ||
InAppResponse inAppResponse) { | ||
AnalyticsManager( | ||
Context context, | ||
CleverTapInstanceConfig config, | ||
BaseEventQueueManager baseEventQueueManager, | ||
Validator validator, | ||
ValidationResultStack validationResultStack, | ||
CoreMetaData coreMetaData, | ||
DeviceInfo deviceInfo, | ||
BaseCallbackManager callbackManager, ControllerManager controllerManager, | ||
final CTLockManager ctLockManager, | ||
InAppResponse inAppResponse, | ||
Function0<Long> currentTimeProvider | ||
) { | ||
this.context = context; | ||
this.config = config; | ||
this.baseEventQueueManager = baseEventQueueManager; | ||
|
@@ -87,6 +80,7 @@ public class AnalyticsManager extends BaseAnalyticsManager { | |
this.ctLockManager = ctLockManager; | ||
this.controllerManager = controllerManager; | ||
this.inAppResponse = inAppResponse; | ||
this.currentTimeProvider = currentTimeProvider; | ||
} | ||
|
||
@Override | ||
|
@@ -464,8 +458,7 @@ public void pushNotificationClickedEvent(final Bundle extras) { | |
} | ||
|
||
boolean shouldProcess = (accountId == null && config.isDefaultInstance()) | ||
|| config.getAccountId() | ||
.equals(accountId); | ||
|| config.getAccountId().equals(accountId); | ||
|
||
if (!shouldProcess) { | ||
config.getLogger().debug(config.getAccountId(), | ||
|
@@ -474,60 +467,12 @@ public void pushNotificationClickedEvent(final Bundle extras) { | |
} | ||
|
||
if (extras.containsKey(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_KEY)) { | ||
Task<Void> task = CTExecutorFactory.executors(config).postAsyncSafelyTask(); | ||
task.execute("testInappNotification",new Callable<Void>() { | ||
@Override | ||
public Void call() { | ||
try { | ||
String inappPreviewPayloadType = extras.getString(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_TYPE_KEY); | ||
String inappPreviewString = extras.getString(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_KEY); | ||
JSONObject inappPreviewPayload = new JSONObject(inappPreviewString); | ||
|
||
JSONArray inappNotifs = new JSONArray(); | ||
if (Constants.INAPP_IMAGE_INTERSTITIAL_TYPE.equals(inappPreviewPayloadType) | ||
|| Constants.INAPP_ADVANCED_BUILDER_TYPE.equals(inappPreviewPayloadType)) { | ||
inappNotifs.put(getHalfInterstitialInApp(inappPreviewPayload)); | ||
} else { | ||
inappNotifs.put(inappPreviewPayload); | ||
} | ||
|
||
JSONObject inAppResponseJson = new JSONObject(); | ||
inAppResponseJson.put(Constants.INAPP_JSON_RESPONSE_KEY, inappNotifs); | ||
|
||
inAppResponse.processResponse(inAppResponseJson, null, context); | ||
} catch (Throwable t) { | ||
Logger.v("Failed to display inapp notification from push notification payload", t); | ||
} | ||
return null; | ||
} | ||
}); | ||
handleInAppPreview(extras); | ||
return; | ||
} | ||
|
||
if (extras.containsKey(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY)) { | ||
Task<Void> task = CTExecutorFactory.executors(config).postAsyncSafelyTask(); | ||
task.execute("testInboxNotification",new Callable<Void>() { | ||
@Override | ||
public Void call() { | ||
try { | ||
Logger.v("Received inbox via push payload: " + extras | ||
.getString(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY)); | ||
JSONObject r = new JSONObject(); | ||
JSONArray inboxNotifs = new JSONArray(); | ||
r.put(Constants.INBOX_JSON_RESPONSE_KEY, inboxNotifs); | ||
JSONObject testPushObject = new JSONObject( | ||
extras.getString(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY)); | ||
testPushObject.put("_id", String.valueOf(System.currentTimeMillis() / 1000)); | ||
inboxNotifs.put(testPushObject); | ||
|
||
CleverTapResponse cleverTapResponse = new InboxResponse(config, ctLockManager, callbackManager, controllerManager); | ||
cleverTapResponse.processResponse(r, null, context); | ||
} catch (Throwable t) { | ||
Logger.v("Failed to process inbox message from push notification payload", t); | ||
} | ||
return null; | ||
} | ||
}); | ||
handleInboxPreview(extras); | ||
return; | ||
} | ||
|
||
|
@@ -538,8 +483,7 @@ public Void call() { | |
|
||
if (!extras.containsKey(Constants.NOTIFICATION_ID_TAG) || (extras.getString(Constants.NOTIFICATION_ID_TAG) == null)) { | ||
config.getLogger().debug(config.getAccountId(), | ||
"Push notification ID Tag is null, not processing Notification Clicked event for: " + extras | ||
.toString()); | ||
"Push notification ID Tag is null, not processing Notification Clicked event for: " + extras); | ||
return; | ||
} | ||
|
||
|
@@ -552,26 +496,12 @@ public Void call() { | |
return; | ||
} | ||
|
||
JSONObject event = new JSONObject(); | ||
JSONObject notif = new JSONObject(); | ||
try { | ||
for (String x : extras.keySet()) { | ||
if (!x.startsWith(Constants.WZRK_PREFIX)) { | ||
continue; | ||
} | ||
Object value = extras.get(x); | ||
notif.put(x, value); | ||
} | ||
// convert bundle to json | ||
JSONObject event = AnalyticsManagerBundler.INSTANCE.notificationViewedJson(extras); | ||
|
||
event.put("evtName", Constants.NOTIFICATION_CLICKED_EVENT_NAME); | ||
event.put("evtData", notif); | ||
baseEventQueueManager.queueEvent(context, event, Constants.RAISED_EVENT); | ||
|
||
try { | ||
coreMetaData.setWzrkParams(getWzrkFields(extras)); | ||
} catch (Throwable t) { | ||
// no-op | ||
} | ||
coreMetaData.setWzrkParams(AnalyticsManagerBundler.INSTANCE.wzrkBundleToJson(extras)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} catch (Throwable t) { | ||
// We won't get here | ||
} | ||
|
@@ -583,6 +513,62 @@ public Void call() { | |
} | ||
} | ||
|
||
private void handleInboxPreview(Bundle extras) { | ||
Task<Void> task = CTExecutorFactory.executors(config).postAsyncSafelyTask(); | ||
task.execute("testInboxNotification",new Callable<Void>() { | ||
@Override | ||
public Void call() { | ||
try { | ||
Logger.v("Received inbox via push payload: " + extras | ||
.getString(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY)); | ||
JSONObject r = new JSONObject(); | ||
JSONArray inboxNotifs = new JSONArray(); | ||
r.put(Constants.INBOX_JSON_RESPONSE_KEY, inboxNotifs); | ||
JSONObject testPushObject = new JSONObject( | ||
extras.getString(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY)); | ||
testPushObject.put("_id", String.valueOf(System.currentTimeMillis() / 1000)); | ||
inboxNotifs.put(testPushObject); | ||
|
||
CleverTapResponse cleverTapResponse = new InboxResponse(config, ctLockManager, callbackManager, controllerManager); | ||
cleverTapResponse.processResponse(r, null, context); | ||
} catch (Throwable t) { | ||
Logger.v("Failed to process inbox message from push notification payload", t); | ||
} | ||
return null; | ||
} | ||
}); | ||
} | ||
|
||
private void handleInAppPreview(Bundle extras) { | ||
Task<Void> task = CTExecutorFactory.executors(config).postAsyncSafelyTask(); | ||
task.execute("testInappNotification",new Callable<Void>() { | ||
@Override | ||
public Void call() { | ||
try { | ||
String inappPreviewPayloadType = extras.getString(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_TYPE_KEY); | ||
String inappPreviewString = extras.getString(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_KEY); | ||
JSONObject inappPreviewPayload = new JSONObject(inappPreviewString); | ||
|
||
JSONArray inappNotifs = new JSONArray(); | ||
if (Constants.INAPP_IMAGE_INTERSTITIAL_TYPE.equals(inappPreviewPayloadType) | ||
|| Constants.INAPP_ADVANCED_BUILDER_TYPE.equals(inappPreviewPayloadType)) { | ||
inappNotifs.put(getHalfInterstitialInApp(inappPreviewPayload)); | ||
} else { | ||
inappNotifs.put(inappPreviewPayload); | ||
} | ||
|
||
JSONObject inAppResponseJson = new JSONObject(); | ||
inAppResponseJson.put(Constants.INAPP_JSON_RESPONSE_KEY, inappNotifs); | ||
|
||
inAppResponse.processResponse(inAppResponseJson, null, context); | ||
} catch (Throwable t) { | ||
Logger.v("Failed to display inapp notification from push notification payload", t); | ||
} | ||
return null; | ||
} | ||
}); | ||
} | ||
|
||
private JSONObject getHalfInterstitialInApp(final JSONObject inapp) throws JSONException { | ||
String inAppConfig = inapp.optString(Constants.INAPP_IMAGE_INTERSTITIAL_CONFIG); | ||
String htmlContent = wrapImageInterstitialContent(inAppConfig); | ||
|
@@ -650,33 +636,26 @@ public void pushNotificationViewedEvent(Bundle extras) { | |
return; | ||
} | ||
|
||
if (!extras.containsKey(Constants.NOTIFICATION_ID_TAG) || (extras.getString(Constants.NOTIFICATION_ID_TAG) | ||
== null)) { | ||
if (!extras.containsKey(Constants.NOTIFICATION_ID_TAG) | ||
|| (extras.getString(Constants.NOTIFICATION_ID_TAG) == null)) { | ||
config.getLogger().debug(config.getAccountId(), | ||
"Push notification ID Tag is null, not processing Notification Viewed event for: " + extras | ||
.toString()); | ||
"Push notification ID Tag is null, not processing Notification Viewed event for: " + extras); | ||
return; | ||
} | ||
|
||
// Check for dupe notification views; if same notficationdId within specified time interval (2 secs) don't process | ||
boolean isDuplicate = checkDuplicateNotificationIds(extras, notificationViewedIdTagMap, | ||
boolean isDuplicate = checkDuplicateNotificationIds(extras, | ||
notificationViewedIdTagMap, | ||
Constants.NOTIFICATION_VIEWED_ID_TAG_INTERVAL); | ||
if (isDuplicate) { | ||
config.getLogger().debug(config.getAccountId(), | ||
"Already processed Notification Viewed event for " + extras.toString() + ", dropping duplicate."); | ||
"Already processed Notification Viewed event for " + extras + ", dropping duplicate."); | ||
return; | ||
} | ||
|
||
config.getLogger().debug("Recording Notification Viewed event for notification: " + extras.toString()); | ||
config.getLogger().debug("Recording Notification Viewed event for notification: " + extras); | ||
|
||
JSONObject event = new JSONObject(); | ||
try { | ||
JSONObject notif = getWzrkFields(extras); | ||
event.put("evtName", Constants.NOTIFICATION_VIEWED_EVENT_NAME); | ||
event.put("evtData", notif); | ||
} catch (Throwable ignored) { | ||
//no-op | ||
} | ||
JSONObject event = AnalyticsManagerBundler.INSTANCE.notificationViewedJson(extras); | ||
baseEventQueueManager.queueEvent(context, event, Constants.NV_EVENT); | ||
} | ||
|
||
|
@@ -1170,7 +1149,7 @@ private boolean checkDuplicateNotificationIds(Bundle extras, HashMap<String, Obj | |
boolean isDupe = false; | ||
try { | ||
String notificationIdTag = extras.getString(Constants.NOTIFICATION_ID_TAG); | ||
long now = System.currentTimeMillis(); | ||
long now = currentTimeProvider.invoke(); | ||
if (notificationTagMap.containsKey(notificationIdTag)) { | ||
long timestamp; | ||
// noinspection ConstantConditions | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.clevertap.android.sdk | ||
|
||
import android.os.Bundle | ||
import org.json.JSONException | ||
import org.json.JSONObject | ||
|
||
object AnalyticsManagerBundler { | ||
|
||
@Throws(JSONException::class) | ||
fun wzrkBundleToJson(root: Bundle): JSONObject { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can add |
||
val fields = JSONObject() | ||
for (s in root.keySet()) { | ||
val o = root[s] | ||
if (o is Bundle) { | ||
val wzrkFields = wzrkBundleToJson(o) | ||
val keys = wzrkFields.keys() | ||
while (keys.hasNext()) { | ||
val k = keys.next() | ||
fields.put(k, wzrkFields[k]) | ||
} | ||
} else if (s.startsWith(Constants.WZRK_PREFIX)) { | ||
fields.put(s, root[s]) | ||
} | ||
} | ||
|
||
return fields | ||
} | ||
|
||
fun notificationViewedJson(root: Bundle): JSONObject { | ||
val event = JSONObject() | ||
try { | ||
val notif = wzrkBundleToJson(root) | ||
event.put("evtName", Constants.NOTIFICATION_VIEWED_EVENT_NAME) | ||
event.put("evtData", notif) | ||
} catch (ignored: Throwable) { | ||
//no-op | ||
} | ||
return event | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use the
com.clevertap.android.sdk.utils.Clock
interface which looks like it was created for this purpose. Or usejava.time.Clock