All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.adtrace.sdk.PackageHandler Maven / Gradle / Ivy

There is a newer version: 2.6.1
Show newest version


package io.adtrace.sdk;

import static io.adtrace.sdk.Constants.CALLBACK_PARAMETERS;
import static io.adtrace.sdk.Constants.PARTNER_PARAMETERS;

import android.content.Context;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

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 PackageHandler implements IPackageHandler,
        IActivityPackageSender.ResponseDataCallbackSubscriber
{
    private static final String PACKAGE_QUEUE_FILENAME = "AdTraceIoPackageQueue";
    private static final String PACKAGE_QUEUE_NAME = "Package queue";

    private ThreadScheduler scheduler;
    private IActivityPackageSender activityPackageSender;
    private WeakReference activityHandlerWeakRef;
    private List packageQueue;
    private AtomicBoolean isSending;
    private boolean paused;
    private Context context;
    private ILogger logger;
    private BackoffStrategy backoffStrategy;
    private BackoffStrategy backoffStrategyForInstallSession;

    @Override
    public void teardown() {
        logger.verbose("PackageHandler teardown");
        if (scheduler != null) {
            scheduler.teardown();
        }
        if (activityHandlerWeakRef != null) {
            activityHandlerWeakRef.clear();
        }
        if (packageQueue != null) {
            packageQueue.clear();
        }
        scheduler = null;
        activityHandlerWeakRef = null;
        packageQueue = null;
        isSending = null;
        context = null;
        logger = null;
        backoffStrategy = null;
    }

    static void deleteState(Context context) {
        deletePackageQueue(context);
    }

    public PackageHandler(IActivityHandler activityHandler,
                          Context context,
                          boolean startsSending,
                          IActivityPackageSender packageHandlerActivityPackageSender)
    {
        this.scheduler = new SingleThreadCachedScheduler("PackageHandler");
        this.logger = AdTraceFactory.getLogger();
        this.backoffStrategy = AdTraceFactory.getPackageHandlerBackoffStrategy();
        this.backoffStrategyForInstallSession = AdTraceFactory.getInstallSessionBackoffStrategy();


        init(activityHandler, context, startsSending, packageHandlerActivityPackageSender);

        scheduler.submit(new Runnable() {
            @Override
            public void run() {
                initI();
            }
        });
    }

    @Override
    public void init(IActivityHandler activityHandler,
                     Context context,
                     boolean startsSending,
                     IActivityPackageSender packageHandlerActivityPackageSender)
    {
        this.activityHandlerWeakRef = new WeakReference(activityHandler);
        this.context = context;
        this.paused = !startsSending;
        this.activityPackageSender = packageHandlerActivityPackageSender;
    }

    // add a package to the queue
    @Override
    public void addPackage(final ActivityPackage activityPackage) {
        scheduler.submit(new Runnable() {
            @Override
            public void run() {
                addI(activityPackage);
            }
        });
    }

    // try to send the oldest package
    @Override
    public void sendFirstPackage() {
        scheduler.submit(new Runnable() {
            @Override
            public void run() {
                sendFirstI();
            }
        });
    }

    @Override
    public void onResponseDataCallback(final ResponseData responseData) {
        logger.debug("Got response in PackageHandler");
        IActivityHandler activityHandler = activityHandlerWeakRef.get();
        if (activityHandler != null &&
                responseData.trackingState == TrackingState.OPTED_OUT) {
            activityHandler.gotOptOutResponse();
        }

        if (!responseData.willRetry) {
            scheduler.submit(new Runnable() {
                @Override
                public void run() {
                    sendNextI();
                }
            });

            if (activityHandler != null) {
                activityHandler.finishedTrackingActivity(responseData);
            }
            return;
        }

        if (activityHandler != null) {
            activityHandler.finishedTrackingActivity(responseData);
        }

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                logger.verbose("Package handler can send");
                isSending.set(false);

                // Try to send the same package after sleeping
                sendFirstPackage();
            }
        };

        if (responseData.activityPackage == null) {
            runnable.run();
            return;
        }

        int retries = responseData.activityPackage.increaseRetries();
        long waitTimeMilliSeconds;

        SharedPreferencesManager sharedPreferencesManager = SharedPreferencesManager.getDefaultInstance(context);

        if (responseData.activityPackage.getActivityKind() ==
                ActivityKind.SESSION && !sharedPreferencesManager.getInstallTracked())
        {
            waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategyForInstallSession);
        } else {
            waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategy);
        }

        double waitTimeSeconds = waitTimeMilliSeconds / 1000.0;
        String secondsString = Util.SecondsDisplayFormat.format(waitTimeSeconds);

        logger.verbose("Waiting for %s seconds before retrying the %d time", secondsString, retries);
        scheduler.schedule(runnable, waitTimeMilliSeconds);
    }

    // interrupt the sending loop after the current request has finished
    @Override
    public void pauseSending() {
        paused = true;
    }

    // allow sending requests again
    @Override
    public void resumeSending() {
        paused = false;
    }

    @Override
    public void updatePackages(SessionParameters sessionParameters) {
        final SessionParameters sessionParametersCopy;
        if (sessionParameters != null) {
            sessionParametersCopy = sessionParameters.deepCopy();
        } else {
            sessionParametersCopy = null;
        }
        scheduler.submit(new Runnable() {
            @Override
            public void run() {
                updatePackagesI(sessionParametersCopy);
            }
        });
    }

    @Override
    public void flush() {
        scheduler.submit(new Runnable() {
            @Override
            public void run() {
                flushI();
            }
        });
    }

    // internal methods run in dedicated queue thread
    private void initI() {
        isSending = new AtomicBoolean();

        readPackageQueueI();
    }

    private void addI(ActivityPackage newPackage) {
        packageQueue.add(newPackage);
        logger.debug("Added package %d (%s)", packageQueue.size(), newPackage);
        logger.verbose("%s", newPackage.getExtendedString());

        writePackageQueueI();
    }

    private void sendFirstI() {
        if (packageQueue.isEmpty()) {
            return;
        }

        if (paused) {
            logger.debug("Package handler is paused");
            return;
        }
        if (isSending.getAndSet(true)) {
            logger.verbose("Package handler is already sending");
            return;
        }

        Map sendingParameters = generateSendingParametersI();

        ActivityPackage firstPackage = packageQueue.get(0);
        activityPackageSender.sendActivityPackage(firstPackage,
                sendingParameters,
                this);
    }

    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;
    }

    private void sendNextI() {
        if (packageQueue.isEmpty()) {
            return;
        }

        packageQueue.remove(0);
        writePackageQueueI();
        isSending.set(false);
        logger.verbose("Package handler can send");
        sendFirstI();
    }

    public void updatePackagesI(SessionParameters sessionParameters) {
        if (sessionParameters == null) {
            return;
        }

        logger.debug("Updating package handler queue");
        logger.verbose("Session callback parameters: %s", sessionParameters.callbackParameters);
        logger.verbose("Session partner parameters: %s", sessionParameters.partnerParameters);

        for (ActivityPackage activityPackage : packageQueue) {
            Map parameters = activityPackage.getParameters();
            // callback parameters
            Map mergedCallbackParameters = Util.mergeParameters(sessionParameters.callbackParameters,
                    activityPackage.getCallbackParameters(),
                    "Callback");

            PackageBuilder.addMapJson(parameters, CALLBACK_PARAMETERS, mergedCallbackParameters);
            // partner parameters
            Map mergedPartnerParameters = Util.mergeParameters(sessionParameters.partnerParameters,
                    activityPackage.getPartnerParameters(),
                    "Partner");

            PackageBuilder.addMapJson(parameters, PARTNER_PARAMETERS, mergedPartnerParameters);
        }

        writePackageQueueI();
    }

    private void flushI() {
        packageQueue.clear();
        writePackageQueueI();
    }

    private void readPackageQueueI() {
        try {
            packageQueue = Util.readObject(context,
                    PACKAGE_QUEUE_FILENAME,
                    PACKAGE_QUEUE_NAME,
                    (Class>)(Class)List.class);
        } catch (Exception e) {
            logger.error("Failed to read %s file (%s)", PACKAGE_QUEUE_NAME, e.getMessage());
            packageQueue = null;
        }

        if (packageQueue != null) {
            logger.debug("Package handler read %d packages", packageQueue.size());
        } else {
            packageQueue = new ArrayList();
        }
    }

    private void writePackageQueueI() {
        Util.writeObject(packageQueue, context, PACKAGE_QUEUE_FILENAME, PACKAGE_QUEUE_NAME);
        logger.debug("Package handler wrote %d packages", packageQueue.size());
    }

    public static Boolean deletePackageQueue(Context context) {
        return context.deleteFile(PACKAGE_QUEUE_FILENAME);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy