io.adtrace.sdk.SdkClickHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of android-sdk Show documentation
Show all versions of android-sdk Show documentation
The AdTrace SDK for Android
package io.adtrace.sdk;
import org.json.JSONArray;
import org.json.JSONException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.adtrace.sdk.network.IActivityPackageSender;
import io.adtrace.sdk.scheduler.SingleThreadCachedScheduler;
import io.adtrace.sdk.scheduler.ThreadScheduler;
/**
* AdTrace android SDK (https://adtrace.io)
* Created by Nasser Amini (github.com/namini40) on April 2022.
* Notice: See LICENSE.txt for modification and distribution information
* Copyright © 2022.
*/
public class SdkClickHandler implements ISdkClickHandler {
/**
* Divisor for milliseconds -> seconds conversion.
*/
private static final double MILLISECONDS_TO_SECONDS_DIVISOR = 1000.0;
/**
* SdkClickHandler scheduled executor source.
*/
private static final String SCHEDULED_EXECUTOR_SOURCE = "SdkClickHandler";
/**
* Intent based referrer source name inside of sdk_click package.
*/
private static final String SOURCE_REFTAG = "reftag";
/**
* Install referrer service referrer source name inside of sdk_click package.
*/
private static final String SOURCE_INSTALL_REFERRER = "install_referrer";
/**
* Indicates whether SdkClickHandler is paused or not.
*/
private boolean paused;
/**
* AdTrace logger.
*/
private ILogger logger;
/**
* Backoff strategy.
*/
private BackoffStrategy backoffStrategy;
/**
* Sending queue.
*/
private List packageQueue;
/**
* Custom actions scheduled executor.
*/
private ThreadScheduler scheduler;
/**
* ActivityHandler instance.
*/
private WeakReference activityHandlerWeakRef;
private IActivityPackageSender activityPackageSender;
/**
* SdkClickHandler constructor.
*
* @param activityHandler ActivityHandler reference
* @param startsSending Is sending paused?
*/
public SdkClickHandler(final IActivityHandler activityHandler,
final boolean startsSending,
final IActivityPackageSender sdkClickHandlerActivityPackageSender)
{
init(activityHandler, startsSending, sdkClickHandlerActivityPackageSender);
this.
logger = AdTraceFactory.getLogger();
backoffStrategy = AdTraceFactory.getSdkClickBackoffStrategy();
scheduler = new SingleThreadCachedScheduler("SdkClickHandler");
}
/**
* {@inheritDoc}
*/
@Override
public void init(final IActivityHandler activityHandler,
final boolean startsSending,
final IActivityPackageSender sdkClickHandlerActivityPackageSender) {
paused = !startsSending;
packageQueue = new ArrayList();
activityHandlerWeakRef = new WeakReference(activityHandler);
activityPackageSender = sdkClickHandlerActivityPackageSender;
}
/**
* {@inheritDoc}
*/
@Override
public void pauseSending() {
paused = true;
}
/**
* {@inheritDoc}
*/
@Override
public void resumeSending() {
paused = false;
sendNextSdkClick();
}
/**
* {@inheritDoc}
*/
@Override
public void sendSdkClick(final ActivityPackage sdkClick) {
scheduler.submit(new Runnable() {
@Override
public void run() {
packageQueue.add(sdkClick);
logger.debug("Added sdk_click %d", packageQueue.size());
logger.verbose("%s", sdkClick.getExtendedString());
sendNextSdkClick();
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void sendReftagReferrers() {
scheduler.submit(new Runnable() {
@Override
public void run() {
IActivityHandler activityHandler = activityHandlerWeakRef.get();
SharedPreferencesManager sharedPreferencesManager = SharedPreferencesManager.getDefaultInstance(
activityHandler.getContext());
try {
JSONArray rawReferrerArray = sharedPreferencesManager.getRawReferrerArray();
boolean hasRawReferrersBeenChanged = false;
for (int i = 0; i < rawReferrerArray.length(); i++) {
JSONArray savedRawReferrer = rawReferrerArray.getJSONArray(i);
int savedRawReferrerState = savedRawReferrer.optInt(2, -1);
// Don't send the one already sending or sent.
if (savedRawReferrerState != 0) {
continue;
}
String savedRawReferrerString = savedRawReferrer.optString(0, null);
long savedClickTime = savedRawReferrer.optLong(1, -1);
// Mark install referrer as being sent.
savedRawReferrer.put(2, 1);
hasRawReferrersBeenChanged = true;
// Create sdk click
ActivityPackage sdkClickPackage = PackageFactory.buildReftagSdkClickPackage(
savedRawReferrerString,
savedClickTime,
activityHandler.getActivityState(),
activityHandler.getAdTraceConfig(),
activityHandler.getDeviceInfo(),
activityHandler.getSessionParameters());
// Send referrer sdk_click package.
sendSdkClick(sdkClickPackage);
}
if (hasRawReferrersBeenChanged) {
sharedPreferencesManager.saveRawReferrerArray(rawReferrerArray);
}
} catch (JSONException e) {
logger.error("Send saved raw referrers error (%s)", e.getMessage());
}
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void sendPreinstallPayload(final String preinstallPayload, final String preinstallLocation) {
scheduler.submit(new Runnable() {
@Override
public void run() {
IActivityHandler activityHandler = activityHandlerWeakRef.get();
if (activityHandler == null) {
return;
}
// Create sdk click
ActivityPackage sdkClickPackage = PackageFactory.buildPreinstallSdkClickPackage(
preinstallPayload,
preinstallLocation,
activityHandler.getActivityState(),
activityHandler.getAdTraceConfig(),
activityHandler.getDeviceInfo(),
activityHandler.getSessionParameters());
// Send preinstall info sdk_click package.
sendSdkClick(sdkClickPackage);
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void teardown() {
logger.verbose("SdkClickHandler teardown");
if (scheduler != null) {
scheduler.teardown();
}
if (packageQueue != null) {
packageQueue.clear();
}
if (activityHandlerWeakRef != null) {
activityHandlerWeakRef.clear();
}
logger = null;
packageQueue = null;
backoffStrategy = null;
scheduler = null;
}
/**
* Send next sdk_click package from the queue.
*/
private void sendNextSdkClick() {
scheduler.submit(new Runnable() {
@Override
public void run() {
sendNextSdkClickI();
}
});
}
/**
* Send next sdk_click package from the queue (runs within scheduled executor).
*/
private void sendNextSdkClickI() {
IActivityHandler activityHandler = activityHandlerWeakRef.get();
if (activityHandler.getActivityState() == null) {
return;
}
if (activityHandler.getActivityState().isGdprForgotten) {
return;
}
if (paused) {
return;
}
if (packageQueue.isEmpty()) {
return;
}
final ActivityPackage sdkClickPackage = packageQueue.remove(0);
int retries = sdkClickPackage.getRetries();
Runnable runnable = new Runnable() {
@Override
public void run() {
sendSdkClickI(sdkClickPackage);
sendNextSdkClick();
}
};
if (retries <= 0) {
runnable.run();
return;
}
long waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategy);
double waitTimeSeconds = waitTimeMilliSeconds / MILLISECONDS_TO_SECONDS_DIVISOR;
String secondsString = Util.SecondsDisplayFormat.format(waitTimeSeconds);
logger.verbose("Waiting for %s seconds before retrying sdk_click for the %d time", secondsString, retries);
scheduler.schedule(runnable, waitTimeMilliSeconds);
}
/**
* Send sdk_click package passed as the parameter (runs within scheduled executor).
*
* @param sdkClickPackage sdk_click package to be sent.
*/
private void sendSdkClickI(final ActivityPackage sdkClickPackage) {
IActivityHandler activityHandler = activityHandlerWeakRef.get();
String source = sdkClickPackage.getParameters().get("source");
boolean isReftag = source != null && source.equals(SOURCE_REFTAG);
String rawReferrerString = sdkClickPackage.getParameters().get("raw_referrer");
if (isReftag) {
// Check before sending if referrer was removed already.
JSONArray rawReferrer = SharedPreferencesManager.getDefaultInstance(activityHandler.getContext()).getRawReferrer(
rawReferrerString,
sdkClickPackage.getClickTimeInMilliseconds());
if (rawReferrer == null) {
return;
}
}
boolean isInstallReferrer = source != null && source.equals(SOURCE_INSTALL_REFERRER);
long clickTime = -1;
long installBegin = -1;
String installReferrer = null;
long clickTimeServer = -1;
long installBeginServer = -1;
String installVersion = null;
Boolean googlePlayInstant = null;
String referrerApi = null;
if (isInstallReferrer) {
// Check if install referrer information is saved to activity state.
// If yes, we have successfully sent it at earlier point and no need to do it again.
// If not, proceed with sending of sdk_click package for install referrer.
clickTime = sdkClickPackage.getClickTimeInSeconds();
installBegin = sdkClickPackage.getInstallBeginTimeInSeconds();
installReferrer = sdkClickPackage.getParameters().get("referrer");
clickTimeServer = sdkClickPackage.getClickTimeServerInSeconds();
installBeginServer = sdkClickPackage.getInstallBeginTimeServerInSeconds();
installVersion = sdkClickPackage.getInstallVersion();
googlePlayInstant = sdkClickPackage.getGooglePlayInstant();
referrerApi = sdkClickPackage.getParameters().get("referrer_api");
}
boolean isPreinstall = source != null && source.equals(Constants.PREINSTALL);
Map sendingParameters = generateSendingParametersI();
ResponseData responseData = activityPackageSender.sendActivityPackageSync(
sdkClickPackage,
sendingParameters);
if (!(responseData instanceof SdkClickResponseData)) {
return;
}
SdkClickResponseData sdkClickResponseData = (SdkClickResponseData)responseData;
if (sdkClickResponseData.willRetry) {
retrySendingI(sdkClickPackage);
return;
}
if (activityHandler == null) {
return;
}
if (sdkClickResponseData.trackingState == TrackingState.OPTED_OUT) {
activityHandler.gotOptOutResponse();
return;
}
if (isReftag) {
// Remove referrer from shared preferences after sdk_click is sent.
SharedPreferencesManager.getDefaultInstance(
activityHandler.getContext()).removeRawReferrer(
rawReferrerString,
sdkClickPackage.getClickTimeInMilliseconds());
}
if (isInstallReferrer) {
// After successfully sending install referrer, store sent values in activity state.
sdkClickResponseData.clickTime = clickTime;
sdkClickResponseData.installBegin = installBegin;
sdkClickResponseData.installReferrer = installReferrer;
sdkClickResponseData.clickTimeServer = clickTimeServer;
sdkClickResponseData.installBeginServer = installBeginServer;
sdkClickResponseData.installVersion = installVersion;
sdkClickResponseData.googlePlayInstant = googlePlayInstant;
sdkClickResponseData.referrerApi = referrerApi;
sdkClickResponseData.isInstallReferrer = true;
}
if (isPreinstall) {
String payloadLocation = sdkClickPackage.getParameters().get("found_location");
if (payloadLocation != null && !payloadLocation.isEmpty()) {
// update preinstall flag in shared preferences after sdk_click is sent.
SharedPreferencesManager sharedPreferencesManager
= SharedPreferencesManager.getDefaultInstance(activityHandler.getContext());
if (Constants.SYSTEM_INSTALLER_REFERRER.equalsIgnoreCase(payloadLocation)) {
sharedPreferencesManager.removePreinstallReferrer();
} else {
long currentStatus = sharedPreferencesManager.getPreinstallPayloadReadStatus();
long updatedStatus = PreinstallUtil.markAsRead(payloadLocation, currentStatus);
sharedPreferencesManager.setPreinstallPayloadReadStatus(updatedStatus);
}
}
}
activityHandler.finishedTrackingActivity(sdkClickResponseData);
}
private Map generateSendingParametersI() {
HashMap sendingParameters = new HashMap<>();
long now = System.currentTimeMillis();
String dateString = Util.dateFormatter.format(now);
PackageBuilder.addString(sendingParameters, "sent_at", dateString);
int queueSize = packageQueue.size() - 1;
if (queueSize > 0) {
PackageBuilder.addLong(sendingParameters, "queue_size", queueSize);
}
return sendingParameters;
}
/**
* Retry sending of the sdk_click package passed as the parameter (runs within scheduled executor).
*
* @param sdkClickPackage sdk_click package to be retried.
*/
private void retrySendingI(final ActivityPackage sdkClickPackage) {
int retries = sdkClickPackage.increaseRetries();
logger.error("Retrying sdk_click package for the %d time", retries);
sendSdkClick(sdkClickPackage);
}
/**
* Print error log messages (runs within scheduled executor).
*
* @param sdkClickPackage sdk_click package for which error occured.
* @param message Message content.
* @param throwable Throwable to read the reason of the error.
*/
private void logErrorMessageI(final ActivityPackage sdkClickPackage,
final String message,
final Throwable throwable) {
final String packageMessage = sdkClickPackage.getFailureMessage();
final String reasonString = Util.getReasonString(message, throwable);
final String finalMessage = Util.formatString("%s. (%s)", packageMessage, reasonString);
logger.error(finalMessage);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy