User then re-launches an App after listening music in background for more than 20 minutes, in this case
+ * since an app is not yet killed due to background music app installed event must not be raised by SDK
+ */
+ abstract void destroySession();
+
+ abstract void lazyCreateSession(Context context);
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTExecutors.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTExecutors.java
deleted file mode 100644
index 11b5d4497..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTExecutors.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.clevertap.android.sdk;
-
-import android.os.Handler;
-import android.os.Looper;
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-/**
- * Global executor pools for the whole application.
- *
- * Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind
- * webservice requests).
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class CTExecutors {
-
- private static class MainThreadExecutor implements Executor {
-
- private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
-
- @Override
- public void execute(@NonNull Runnable command) {
- mainThreadHandler.post(command);
- }
- }
-
- private static CTExecutors sInstance;
-
- private final Executor diskIO;
-
- private final Executor mainThread;
-
- public static synchronized CTExecutors getInstance() {
- if (sInstance == null) {
- sInstance = new CTExecutors(Executors.newSingleThreadExecutor(),
- new MainThreadExecutor());
- }
- return sInstance;
- }
-
- private CTExecutors(Executor diskIO, Executor mainThread) {
- this.diskIO = diskIO;
- this.mainThread = mainThread;
- }
-
- public Executor diskIO() {
- return diskIO;
- }
-
- public Executor mainThread() {
- return mainThread;
- }
-}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTExperimentsListener.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTExperimentsListener.java
deleted file mode 100644
index e65c27f1d..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTExperimentsListener.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.clevertap.android.sdk;
-
-public interface CTExperimentsListener {
-
- void CTExperimentsUpdated();
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxListener.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxListener.java
index 0702f037a..e9eb9989d 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxListener.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxListener.java
@@ -1,5 +1,7 @@
package com.clevertap.android.sdk;
+import com.clevertap.android.sdk.inbox.CTInboxMessage;
+
public interface CTInboxListener {
/**
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxStyleConfig.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxStyleConfig.java
index 063d86de7..f11a306f0 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxStyleConfig.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTInboxStyleConfig.java
@@ -2,6 +2,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.clevertap.android.sdk.inbox.CTInboxActivity;
import java.util.ArrayList;
import java.util.Arrays;
@@ -285,7 +286,7 @@ public void writeToParcel(Parcel dest, int flags) {
dest.writeString(noMessageViewTextColor);
}
- boolean isUsingTabs() {
+ public boolean isUsingTabs() {
return (tabs != null && tabs.length > 0);
}
}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTLockManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTLockManager.java
new file mode 100644
index 000000000..12b50f9f6
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTLockManager.java
@@ -0,0 +1,20 @@
+package com.clevertap.android.sdk;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+@RestrictTo(Scope.LIBRARY)
+public class CTLockManager {
+
+ private final Boolean eventLock = true;
+
+ private final Object inboxControllerLock = new Object();
+
+ public Boolean getEventLock() {
+ return eventLock;
+ }
+
+ public Object getInboxControllerLock() {
+ return inboxControllerLock;
+ }
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTWebInterface.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTWebInterface.java
index 9f25c5821..8fc984758 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTWebInterface.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTWebInterface.java
@@ -12,7 +12,7 @@
@SuppressWarnings("WeakerAccess")
public class CTWebInterface {
- private WeakReference weakReference;
+ private final WeakReference weakReference;
public CTWebInterface(CleverTapAPI instance) {
this.weakReference = new WeakReference<>(instance);
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CallbackManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CallbackManager.java
new file mode 100644
index 000000000..6391ad5af
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CallbackManager.java
@@ -0,0 +1,244 @@
+package com.clevertap.android.sdk;
+
+import static com.clevertap.android.sdk.Utils.runOnUiThread;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import com.clevertap.android.sdk.displayunits.DisplayUnitListener;
+import com.clevertap.android.sdk.displayunits.model.CleverTapDisplayUnit;
+import com.clevertap.android.sdk.product_config.CTProductConfigListener;
+import com.clevertap.android.sdk.pushnotification.CTPushNotificationListener;
+import com.clevertap.android.sdk.pushnotification.amp.CTPushAmpListener;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+@RestrictTo(Scope.LIBRARY)
+public class CallbackManager extends BaseCallbackManager {
+
+ private WeakReference displayUnitListenerWeakReference;
+
+ private GeofenceCallback geofenceCallback;
+
+ private WeakReference inAppNotificationButtonListener;
+
+ private InAppNotificationListener inAppNotificationListener;
+
+ private CTInboxListener inboxListener;
+
+ private final CleverTapInstanceConfig config;
+
+ private final DeviceInfo deviceInfo;
+
+ private FailureFlushListener failureFlushListener;
+
+ private WeakReference featureFlagListenerWeakReference;
+
+ private WeakReference productConfigListener;
+
+ private CTPushAmpListener pushAmpListener = null;
+
+ private CTPushNotificationListener pushNotificationListener = null;
+
+ private SyncListener syncListener = null;
+
+ public CallbackManager(CleverTapInstanceConfig config, DeviceInfo deviceInfo) {
+ this.config = config;
+ this.deviceInfo = deviceInfo;
+ }
+
+ @Override
+ public void _notifyInboxMessagesDidUpdate() {
+ if (this.inboxListener != null) {
+ Utils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (inboxListener != null) {
+ inboxListener.inboxMessagesDidUpdate();
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public FailureFlushListener getFailureFlushListener() {
+ return failureFlushListener;
+ }
+
+ @Override
+ public void setFailureFlushListener(final FailureFlushListener failureFlushListener) {
+ this.failureFlushListener = failureFlushListener;
+ }
+
+ @Override
+ public CTFeatureFlagsListener getFeatureFlagListener() {
+ if (featureFlagListenerWeakReference != null && featureFlagListenerWeakReference.get() != null) {
+ return featureFlagListenerWeakReference.get();
+ }
+ return null;
+ }
+
+ @Override
+ public void setFeatureFlagListener(final CTFeatureFlagsListener listener) {
+ this.featureFlagListenerWeakReference = new WeakReference<>(listener);
+ }
+
+ @Override
+ public GeofenceCallback getGeofenceCallback() {
+ return geofenceCallback;
+ }
+
+ @Override
+ public void setGeofenceCallback(final GeofenceCallback geofenceCallback) {
+ this.geofenceCallback = geofenceCallback;
+ }
+
+ @Override
+ public InAppNotificationButtonListener getInAppNotificationButtonListener() {
+ if (inAppNotificationButtonListener != null && inAppNotificationButtonListener.get() != null) {
+ return inAppNotificationButtonListener.get();
+ }
+ return null;
+ }
+
+ @Override
+ public void setInAppNotificationButtonListener(
+ InAppNotificationButtonListener inAppNotificationButtonListener) {
+ this.inAppNotificationButtonListener = new WeakReference<>(inAppNotificationButtonListener);
+ }
+
+ @Override
+ public InAppNotificationListener getInAppNotificationListener() {
+ return inAppNotificationListener;
+ }
+
+ @Override
+ public void setInAppNotificationListener(final InAppNotificationListener inAppNotificationListener) {
+ this.inAppNotificationListener = inAppNotificationListener;
+ }
+
+ @Override
+ public CTInboxListener getInboxListener() {
+ return inboxListener;
+ }
+
+ @Override
+ public void setInboxListener(final CTInboxListener inboxListener) {
+ this.inboxListener = inboxListener;
+ }
+
+ @Override
+ public CTProductConfigListener getProductConfigListener() {
+ if (productConfigListener != null && productConfigListener.get() != null) {
+ return productConfigListener.get();
+ }
+ return null;
+ }
+
+ @Override
+ public void setProductConfigListener(final CTProductConfigListener productConfigListener) {
+ if (productConfigListener != null) {
+ this.productConfigListener = new WeakReference<>(productConfigListener);
+ }
+ }
+
+ @Override
+ public CTPushAmpListener getPushAmpListener() {
+ return pushAmpListener;
+ }
+
+ @Override
+ public void setPushAmpListener(final CTPushAmpListener pushAmpListener) {
+ this.pushAmpListener = pushAmpListener;
+ }
+
+ @Override
+ public CTPushNotificationListener getPushNotificationListener() {
+ return pushNotificationListener;
+ }
+
+ @Override
+ public void setPushNotificationListener(
+ final CTPushNotificationListener pushNotificationListener) {
+ this.pushNotificationListener = pushNotificationListener;
+ }
+
+ @Override
+ public SyncListener getSyncListener() {
+ return syncListener;
+ }
+
+ @Override
+ public void setSyncListener(final SyncListener syncListener) {
+ this.syncListener = syncListener;
+ }
+
+ //Profile
+ @Override
+ public void notifyUserProfileInitialized(String deviceID) {
+ deviceID = (deviceID != null) ? deviceID : deviceInfo.getDeviceID();
+
+ if (deviceID == null) {
+ return;
+ }
+
+ final SyncListener sl;
+ try {
+ sl = getSyncListener();
+ if (sl != null) {
+ sl.profileDidInitialize(deviceID);
+ }
+ } catch (Throwable t) {
+ // Ignore
+ }
+ }
+
+ @Override
+ public void setDisplayUnitListener(DisplayUnitListener listener) {
+ if (listener != null) {
+ displayUnitListenerWeakReference = new WeakReference<>(listener);
+ } else {
+ config.getLogger().verbose(config.getAccountId(),
+ Constants.FEATURE_DISPLAY_UNIT + "Failed to set - DisplayUnitListener can't be null");
+ }
+ }
+
+ void _notifyInboxInitialized() {
+ if (this.inboxListener != null) {
+ this.inboxListener.inboxDidInitialize();
+ }
+ }
+
+ /**
+ * Notify the registered Display Unit listener about the running Display Unit campaigns
+ *
+ * @param displayUnits - Array of Display Units {@link CleverTapDisplayUnit}
+ */
+ public void notifyDisplayUnitsLoaded(final ArrayList displayUnits) {
+ if (displayUnits != null && !displayUnits.isEmpty()) {
+ if (displayUnitListenerWeakReference != null && displayUnitListenerWeakReference.get() != null) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ //double check to ensure null safety
+ if (displayUnitListenerWeakReference != null
+ && displayUnitListenerWeakReference.get() != null) {
+ displayUnitListenerWeakReference.get().onDisplayUnitsLoaded(displayUnits);
+ }
+ }
+ });
+ } else {
+ config.getLogger().verbose(config.getAccountId(),
+ Constants.FEATURE_DISPLAY_UNIT + "No registered listener, failed to notify");
+ }
+ } else {
+ config.getLogger()
+ .verbose(config.getAccountId(), Constants.FEATURE_DISPLAY_UNIT + "No Display Units found");
+ }
+ }
+
+ void notifyUserProfileInitialized() {
+ notifyUserProfileInitialized(deviceInfo.getDeviceID());
+ }
+
+}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
index c3dcf3dec..7205739d8 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
@@ -1,109 +1,53 @@
package com.clevertap.android.sdk;
-import static android.content.Context.JOB_SCHEDULER_SERVICE;
import static android.content.Context.NOTIFICATION_SERVICE;
-import static com.clevertap.android.sdk.CTJsonConverter.getErrorObject;
-import static com.clevertap.android.sdk.CTJsonConverter.getRenderedTargetList;
-import static com.clevertap.android.sdk.CTJsonConverter.getWzrkFields;
-import static com.clevertap.android.sdk.Utils.runOnUiThread;
-import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.job.JobInfo;
import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.graphics.Bitmap;
-import android.graphics.Color;
import android.location.Location;
-import android.location.LocationManager;
import android.media.AudioAttributes;
-import android.media.RingtoneManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
-import android.os.Build.VERSION_CODES;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.SystemClock;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
-import androidx.core.app.NotificationCompat;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentTransaction;
-import com.android.installreferrer.api.InstallReferrerClient;
-import com.android.installreferrer.api.InstallReferrerStateListener;
-import com.android.installreferrer.api.ReferrerDetails;
-import com.clevertap.android.sdk.ab_testing.CTABTestController;
-import com.clevertap.android.sdk.displayunits.CTDisplayUnitController;
import com.clevertap.android.sdk.displayunits.DisplayUnitListener;
import com.clevertap.android.sdk.displayunits.model.CleverTapDisplayUnit;
+import com.clevertap.android.sdk.events.EventDetail;
import com.clevertap.android.sdk.featureFlags.CTFeatureFlagsController;
-import com.clevertap.android.sdk.login.IdentityRepo;
-import com.clevertap.android.sdk.login.IdentityRepoFactory;
-import com.clevertap.android.sdk.login.LoginInfoProvider;
+import com.clevertap.android.sdk.inbox.CTInboxActivity;
+import com.clevertap.android.sdk.inbox.CTInboxMessage;
+import com.clevertap.android.sdk.inbox.CTMessageDAO;
import com.clevertap.android.sdk.product_config.CTProductConfigController;
import com.clevertap.android.sdk.product_config.CTProductConfigListener;
-import com.clevertap.android.sdk.pushnotification.CTNotificationIntentService;
import com.clevertap.android.sdk.pushnotification.CTPushNotificationListener;
-import com.clevertap.android.sdk.pushnotification.CTPushNotificationReceiver;
import com.clevertap.android.sdk.pushnotification.NotificationInfo;
import com.clevertap.android.sdk.pushnotification.PushConstants;
import com.clevertap.android.sdk.pushnotification.PushConstants.PushType;
-import com.clevertap.android.sdk.pushnotification.PushProviders;
-import com.clevertap.android.sdk.pushnotification.amp.CTBackgroundIntentService;
-import com.clevertap.android.sdk.pushnotification.amp.CTBackgroundJobService;
import com.clevertap.android.sdk.pushnotification.amp.CTPushAmpListener;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
+import com.clevertap.android.sdk.task.CTExecutorFactory;
+import com.clevertap.android.sdk.task.Task;
+import com.clevertap.android.sdk.utils.UriHelper;
+import com.clevertap.android.sdk.validation.ManifestValidator;
+import com.clevertap.android.sdk.validation.ValidationResult;
import java.lang.ref.WeakReference;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
import java.util.Collections;
-import java.util.Date;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
import java.util.Map;
-import java.util.Random;
-import java.util.TimeZone;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.Callable;
import java.util.concurrent.Future;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
import org.json.JSONArray;
-import org.json.JSONException;
import org.json.JSONObject;
@@ -111,35 +55,7 @@
* CleverTapAPI
* This is the main CleverTapAPI class that manages the SDK instances
*/
-public class CleverTapAPI implements CleverTapAPIListener {
-
- //InApp
- private final class NotificationPrepareRunnable implements Runnable {
-
- private final WeakReference cleverTapAPIWeakReference;
-
- private final JSONObject jsonObject;
-
- private final boolean videoSupport = haveVideoPlayerSupport;
-
- NotificationPrepareRunnable(CleverTapAPI cleverTapAPI, JSONObject jsonObject) {
- this.cleverTapAPIWeakReference = new WeakReference<>(cleverTapAPI);
- this.jsonObject = jsonObject;
- }
-
- @Override
- public void run() {
- final CTInAppNotification inAppNotification = new CTInAppNotification()
- .initWithJSON(jsonObject, videoSupport);
- if (inAppNotification.getError() != null) {
- getConfigLogger()
- .debug(getAccountId(), "Unable to parse inapp notification " + inAppNotification.getError());
- return;
- }
- inAppNotification.listener = cleverTapAPIWeakReference.get();
- inAppNotification.prepareForDisplay();
- }
- }
+public class CleverTapAPI implements CTInboxActivity.InboxActivityListener {
/**
* Implement to get called back when the device push token is refreshed
@@ -170,204 +86,24 @@ public int intValue() {
}
}
- private enum EventGroup {
-
- REGULAR(""),
- PUSH_NOTIFICATION_VIEWED("-spiky");
-
- private final String httpResource;
-
- EventGroup(String httpResource) {
- this.httpResource = httpResource;
- }
- }
@SuppressWarnings("unused")
public static final String NOTIFICATION_TAG = "wzrk_pn";
- static boolean haveVideoPlayerSupport;
-
private static int debugLevel = CleverTapAPI.LogLevel.INFO.intValue();
- private static CleverTapInstanceConfig defaultConfig;
+ static CleverTapInstanceConfig defaultConfig;
private static HashMap instances;
- private static boolean appForeground = false;
-
- private static int activityCount = 0;
-
- private static final List pendingNotifications = Collections
- .synchronizedList(new ArrayList());
-
- private static CTInAppNotification currentlyDisplayingInApp = null;
-
- private static WeakReference currentActivity;
-
- private static int initialAppEnteredForegroundTime = 0;
-
- private static SSLContext sslContext;
-
- private static SSLSocketFactory sslSocketFactory;
-
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private static String sdkVersion; // For Google Play Store/Android Studio analytics
- private static boolean isUIEditorEnabled = false;
-
- private long EXECUTOR_THREAD_ID = 0;
-
- private long NOTIFICATION_THREAD_ID = 0;
-
- private long appInstallTime = 0;
-
- private long appLastSeen = 0;
-
- private boolean appLaunchPushed = false;
-
- private final Object appLaunchPushedLock = new Object();
-
- private String cachedGUID = null;
-
- private Runnable commsRunnable = null;
-
- private final CleverTapInstanceConfig config;
-
private final Context context;
- private CTABTestController ctABTestController;
-
- private CTFeatureFlagsController ctFeatureFlagsController;
-
- private CTInboxController ctInboxController;
-
- private CTProductConfigController ctProductConfigController;
-
- private int currentRequestTimestamp = 0;
-
- private String currentScreenName = "";
-
- private int currentSessionId = 0;
-
- private boolean currentUserOptedOut = false;
-
- private DBAdapter dbAdapter;
-
- private final DeviceInfo deviceInfo;
-
- private final Object displayUnitControllerLock = new Object();
-
- private WeakReference displayUnitListenerWeakReference;
-
- private boolean enableNetworkInfoReporting = false;
-
- private final ExecutorService es;
-
- private final Boolean eventLock = true;
-
- private CTExperimentsListener experimentsListener = null;
-
- private WeakReference featureFlagsListener;
-
- private boolean firstRequestInSession = false;
-
- private boolean firstSession = false;
-
- private GeofenceCallback geofenceCallback;
-
- private int geofenceSDKVersion = 0;
-
- private final Handler handlerUsingMainLooper;
-
- private InAppFCManager inAppFCManager;
-
- private WeakReference inAppNotificationButtonListener;
-
- private InAppNotificationListener inAppNotificationListener;
-
- private HashSet inappActivityExclude = null;
-
- private final Object inboxControllerLock = new Object();
-
- private CTInboxListener inboxListener;
-
private WeakReference inboxMessageButtonListener;
- private boolean installReferrerDataSent = false;
-
- private final HashMap installReferrerMap = new HashMap<>(8);
-
- private boolean isBgPing = false;
-
- private boolean isLocationForGeofence = false;
-
- private boolean isProductConfigRequested;
-
- private int lastLocationPingTime = 0;
-
- private int lastLocationPingTimeForGeofence = 0;
-
- private int lastSessionLength = 0;
-
- private int lastVisitTime;
-
- private final LocalDataStore localDataStore;
-
- private Location locationFromUser = null;
-
- private CTDisplayUnitController mCTDisplayUnitController;
-
- private int mResponseFailureCount = 0;
-
- private final int maxDelayFrequency = 1000 * 60 * 10;
-
- private int minDelayFrequency = 0;
-
- private int networkRetryCount = 0;
-
- private final HashMap notificationIdTagMap = new HashMap<>();
-
- private final Object notificationMapLock = new Object();
-
- private final HashMap notificationViewedIdTagMap = new HashMap<>();
-
- private final ExecutorService ns;
-
- private boolean offline = false;
-
- private final Object optOutFlagLock = new Object();
-
- private Runnable pendingInappRunnable = null;
-
- private String processingUserLoginIdentifier = null;
-
- private final Boolean processingUserLoginLock = true;
-
- private WeakReference productConfigListener;
-
- private CTPushAmpListener pushAmpListener = null;
-
- private CTPushNotificationListener pushNotificationListener = null;
-
- private Runnable pushNotificationViewedRunnable = null;
-
- private final PushProviders pushProviders;
-
- private long referrerClickTime = 0;
-
- private String source = null, medium = null, campaign = null;
-
- private SyncListener syncListener = null;
-
- private final Object tokenLock = new Object();
-
- private DevicePushTokenRefreshListener tokenRefreshListener;
-
- private final ValidationResultStack validationResultStack;
-
- private final Validator validator;
-
- private JSONObject wzrkParams = null;
+ private CoreState coreState;
/**
* This method is used to change the credentials of CleverTap account Id and token programmatically
@@ -419,7 +155,7 @@ public static void createNotification(final Context context, final Bundle extras
if (instances == null) {
CleverTapAPI instance = createInstanceIfAvailable(context, _accountId);
if (instance != null) {
- instance._createNotification(context, extras, notificationId);
+ instance.coreState.getPushProviders()._createNotification(context, extras, notificationId);
}
return;
}
@@ -428,12 +164,14 @@ public static void createNotification(final Context context, final Bundle extras
CleverTapAPI instance = CleverTapAPI.instances.get(accountId);
boolean shouldProcess = false;
if (instance != null) {
- shouldProcess = (_accountId == null && instance.config.isDefaultInstance()) || instance.getAccountId()
+ shouldProcess = (_accountId == null && instance.coreState.getConfig().isDefaultInstance())
+ || instance
+ .getAccountId()
.equals(_accountId);
}
if (shouldProcess) {
try {
- instance._createNotification(context, extras, notificationId);
+ instance.coreState.getPushProviders()._createNotification(context, extras, notificationId);
} catch (Throwable t) {
// no-op
}
@@ -481,24 +219,26 @@ public static void createNotificationChannel(final Context context, final String
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- instance.postAsyncSafely("createNotificationChannel", new Runnable() {
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("createNotificationChannel", new Callable() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
- public void run() {
+ public Void call() {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) {
- return;
+ return null;
}
- NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName,
+ NotificationChannel notificationChannel = new NotificationChannel(channelId,
+ channelName,
importance);
notificationChannel.setDescription(channelDescription);
notificationChannel.setShowBadge(showBadge);
notificationManager.createNotificationChannel(notificationChannel);
instance.getConfigLogger().info(instance.getAccountId(),
"Notification channel " + channelName.toString() + " has been created");
-
+ return null;
}
});
}
@@ -526,6 +266,7 @@ public void run() {
* @param showBadge An boolean value as to whether this channel shows a badge
*/
@SuppressWarnings("unused")
+ @RequiresApi(api = Build.VERSION_CODES.O)
public static void createNotificationChannel(final Context context, final String channelId,
final CharSequence channelName, final String channelDescription, final int importance,
final String groupId, final boolean showBadge) {
@@ -536,17 +277,17 @@ public static void createNotificationChannel(final Context context, final String
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- instance.postAsyncSafely("creatingNotificationChannel", new Runnable() {
- @RequiresApi(api = Build.VERSION_CODES.O)
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("creatingNotificationChannel", new Callable() {
@Override
- public void run() {
-
+ public Void call() {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) {
- return;
+ return null;
}
- NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName,
+ NotificationChannel notificationChannel = new NotificationChannel(channelId,
+ channelName,
importance);
notificationChannel.setDescription(channelDescription);
notificationChannel.setGroup(groupId);
@@ -555,6 +296,7 @@ public void run() {
instance.getConfigLogger().info(instance.getAccountId(),
"Notification channel " + channelName.toString() + " has been created");
+ return null;
}
});
}
@@ -579,6 +321,7 @@ public void run() {
* @param sound A String denoting the custom sound raw file for this channel
*/
@SuppressWarnings("unused")
+ @RequiresApi(api = Build.VERSION_CODES.O)
public static void createNotificationChannel(final Context context, final String channelId,
final CharSequence channelName, final String channelDescription, final int importance,
final boolean showBadge, final String sound) {
@@ -589,15 +332,14 @@ public static void createNotificationChannel(final Context context, final String
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- instance.postAsyncSafely("createNotificationChannel", new Runnable() {
- @RequiresApi(api = Build.VERSION_CODES.O)
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("createNotificationChannel", new Callable() {
@Override
- public void run() {
-
+ public Void call() {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) {
- return;
+ return null;
}
String soundfile = "";
@@ -617,7 +359,8 @@ public void run() {
}
- NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName,
+ NotificationChannel notificationChannel = new NotificationChannel(channelId,
+ channelName,
importance);
notificationChannel.setDescription(channelDescription);
notificationChannel.setShowBadge(showBadge);
@@ -632,7 +375,7 @@ public void run() {
notificationManager.createNotificationChannel(notificationChannel);
instance.getConfigLogger().info(instance.getAccountId(),
"Notification channel " + channelName.toString() + " has been created");
-
+ return null;
}
});
}
@@ -660,6 +403,7 @@ public void run() {
* @param sound A String denoting the custom sound raw file for this channel
*/
@SuppressWarnings({"unused"})
+ @RequiresApi(api = Build.VERSION_CODES.O)
public static void createNotificationChannel(final Context context, final String channelId,
final CharSequence channelName, final String channelDescription, final int importance,
final String groupId, final boolean showBadge, final String sound) {
@@ -670,14 +414,14 @@ public static void createNotificationChannel(final Context context, final String
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- instance.postAsyncSafely("creatingNotificationChannel", new Runnable() {
- @RequiresApi(api = Build.VERSION_CODES.O)
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("creatingNotificationChannel", new Callable() {
@Override
- public void run() {
+ public Void call() {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) {
- return;
+ return null;
}
String soundfile = "";
@@ -696,7 +440,8 @@ public void run() {
}
}
- NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName,
+ NotificationChannel notificationChannel = new NotificationChannel(channelId,
+ channelName,
importance);
notificationChannel.setDescription(channelDescription);
notificationChannel.setGroup(groupId);
@@ -712,7 +457,7 @@ public void run() {
notificationManager.createNotificationChannel(notificationChannel);
instance.getConfigLogger().info(instance.getAccountId(),
"Notification channel " + channelName.toString() + " has been created");
-
+ return null;
}
});
}
@@ -733,6 +478,7 @@ public void run() {
* @param groupName A String for setting the name of the notification channel group
*/
@SuppressWarnings("unused")
+ @RequiresApi(api = Build.VERSION_CODES.O)
public static void createNotificationChannelGroup(final Context context, final String groupId,
final CharSequence groupName) {
final CleverTapAPI instance = getDefaultInstanceOrFirstOther(context);
@@ -742,21 +488,21 @@ public static void createNotificationChannelGroup(final Context context, final S
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- instance.postAsyncSafely("creatingNotificationChannelGroup", new Runnable() {
- @RequiresApi(api = Build.VERSION_CODES.O)
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("creatingNotificationChannelGroup", new Callable() {
@Override
- public void run() {
-
+ public Void call() {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) {
- return;
+ return null;
}
notificationManager
- .createNotificationChannelGroup(new NotificationChannelGroup(groupId, groupName));
+ .createNotificationChannelGroup(
+ new NotificationChannelGroup(groupId, groupName));
instance.getConfigLogger().info(instance.getAccountId(),
"Notification channel group " + groupName.toString() + " has been created");
-
+ return null;
}
});
}
@@ -776,6 +522,7 @@ public void run() {
* @param channelId A String for setting the id of the notification channel
*/
@SuppressWarnings("unused")
+ @RequiresApi(api = Build.VERSION_CODES.O)
public static void deleteNotificationChannel(final Context context, final String channelId) {
final CleverTapAPI instance = getDefaultInstanceOrFirstOther(context);
if (instance == null) {
@@ -784,20 +531,19 @@ public static void deleteNotificationChannel(final Context context, final String
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- instance.postAsyncSafely("deletingNotificationChannel", new Runnable() {
- @RequiresApi(api = Build.VERSION_CODES.O)
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("deletingNotificationChannel", new Callable() {
@Override
- public void run() {
-
+ public Void call() {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) {
- return;
+ return null;
}
notificationManager.deleteNotificationChannel(channelId);
instance.getConfigLogger().info(instance.getAccountId(),
"Notification channel " + channelId + " has been deleted");
-
+ return null;
}
});
}
@@ -816,6 +562,7 @@ public void run() {
* @param groupId A String for setting the id of the notification channel group
*/
@SuppressWarnings("unused")
+ @RequiresApi(api = Build.VERSION_CODES.O)
public static void deleteNotificationChannelGroup(final Context context, final String groupId) {
final CleverTapAPI instance = getDefaultInstanceOrFirstOther(context);
if (instance == null) {
@@ -824,20 +571,21 @@ public static void deleteNotificationChannelGroup(final Context context, final S
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- instance.postAsyncSafely("deletingNotificationChannelGroup", new Runnable() {
- @RequiresApi(api = Build.VERSION_CODES.O)
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("deletingNotificationChannelGroup", new Callable() {
@Override
- public void run() {
+ public Void call() {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) {
- return;
+ return null;
}
notificationManager.deleteNotificationChannelGroup(groupId);
instance.getConfigLogger().info(instance.getAccountId(),
"Notification channel group " + groupId + " has been deleted");
+ return null;
}
});
}
@@ -847,22 +595,21 @@ public void run() {
}
}
- //Push
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void fcmTokenRefresh(Context context, String token) {
for (CleverTapAPI instance : getAvailableInstances(context)) {
- if (instance == null || instance.getConfig().isAnalyticsOnly()) {
- Logger.d(instance.getAccountId(),
- "Instance is Analytics Only not processing device token");
+ if (instance == null || instance.getCoreState().getConfig().isAnalyticsOnly()) {
+ Logger.d("Instance is Analytics Only not processing device token");
continue;
}
//get token from Manifest
- String tokenUsingManifestMetaEntry = Utils.getFcmTokenUsingManifestMetaEntry(context, instance.config);
+ String tokenUsingManifestMetaEntry = Utils
+ .getFcmTokenUsingManifestMetaEntry(context, instance.getCoreState().getConfig());
if (!TextUtils.isEmpty(tokenUsingManifestMetaEntry)) {
token = tokenUsingManifestMetaEntry;
}
- instance.doTokenRefresh(token, PushType.FCM);
+ instance.getCoreState().getPushProviders().doTokenRefresh(token, PushType.FCM);
}
}
@@ -951,6 +698,14 @@ CleverTapAPI getGlobalInstance(Context context, String _accountId) {
return instance;
}
+ public static HashMap getInstances() {
+ return instances;
+ }
+
+ public static void setInstances(final HashMap instances) {
+ CleverTapAPI.instances = instances;
+ }
+
/**
* Checks whether this notification is from CleverTap.
*
@@ -994,7 +749,9 @@ public static void handleNotificationClicked(Context context, Bundle notificatio
CleverTapAPI instance = CleverTapAPI.instances.get(accountId);
boolean shouldProcess = false;
if (instance != null) {
- shouldProcess = (_accountId == null && instance.config.isDefaultInstance()) || instance.getAccountId()
+ shouldProcess = (_accountId == null && instance.coreState.getConfig().isDefaultInstance())
+ || instance
+ .getAccountId()
.equals(_accountId);
}
if (shouldProcess) {
@@ -1025,7 +782,7 @@ public static CleverTapAPI instanceWithConfig(Context context, CleverTapInstance
@SuppressWarnings({"unused", "WeakerAccess"})
public static CleverTapAPI instanceWithConfig(Context context, @NonNull CleverTapInstanceConfig config,
String cleverTapID) {
- //noinspection ConstantConditions
+ //noinspection Constant Conditions
if (config == null) {
Logger.v("CleverTapInstanceConfig cannot be null");
return null;
@@ -1039,22 +796,44 @@ public static CleverTapAPI instanceWithConfig(Context context, @NonNull CleverTa
instance = new CleverTapAPI(context, config, cleverTapID);
instances.put(config.getAccountId(), instance);
final CleverTapAPI finalInstance = instance;
- instance.postAsyncSafely("notifyProfileInitialized", new Runnable() {
+ Task task = CTExecutorFactory.executors(instance.coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("notifyProfileInitialized", new Callable() {
@Override
- public void run() {
+ public Void call() {
if (finalInstance.getCleverTapID() != null) {
- finalInstance.notifyUserProfileInitialized();
- finalInstance.recordDeviceIDErrors();
+ finalInstance.coreState.getCallbackManager().notifyUserProfileInitialized();
+ finalInstance.coreState.getLoginController().recordDeviceIDErrors();
}
+ return null;
}
});
} else if (instance.isErrorDeviceId() && instance.getConfig().getEnableCustomCleverTapId() && Utils
.validateCTID(cleverTapID)) {
- instance.asyncProfileSwitchUser(null, null, cleverTapID);
+ instance.coreState.getLoginController().asyncProfileSwitchUser(null, null, cleverTapID);
}
return instance;
}
+ /**
+ * Returns whether or not the app is in the foreground.
+ *
+ * @return The foreground status
+ */
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ public static boolean isAppForeground() {
+ return CoreMetaData.isAppForeground();
+ }
+
+ /**
+ * Use this method to notify CleverTap that the app is in foreground
+ *
+ * @param appForeground boolean true/false
+ */
+ @SuppressWarnings({"unused", "WeakerAccess"})
+ public static void setAppForeground(boolean appForeground) {
+ CoreMetaData.setAppForeground(appForeground);
+ }
+
@SuppressWarnings("WeakerAccess")
public static void onActivityPaused() {
if (instances == null) {
@@ -1065,7 +844,7 @@ public static void onActivityPaused() {
CleverTapAPI instance = CleverTapAPI.instances.get(accountId);
try {
if (instance != null) {
- instance.activityPaused();
+ instance.coreState.getActivityLifeCycleManager().activityPaused();
}
} catch (Throwable t) {
// Ignore
@@ -1084,28 +863,29 @@ public static void onActivityResumed(Activity activity, String cleverTapID) {
CleverTapAPI.createInstanceIfAvailable(activity.getApplicationContext(), null, cleverTapID);
}
- CleverTapAPI.setAppForeground(true);
+ CoreMetaData.setAppForeground(true);
if (instances == null) {
Logger.v("Instances is null in onActivityResumed!");
return;
}
- String currentActivityName = getCurrentActivityName();
- setCurrentActivity(activity);
+ String currentActivityName = CoreMetaData.getCurrentActivityName();
+ CoreMetaData.setCurrentActivity(activity);
if (currentActivityName == null || !currentActivityName.equals(activity.getLocalClassName())) {
- activityCount++;
+ CoreMetaData.incrementActivityCount();
}
- if (initialAppEnteredForegroundTime <= 0) {
- initialAppEnteredForegroundTime = (int) System.currentTimeMillis() / 1000;
+ if (CoreMetaData.getInitialAppEnteredForegroundTime() <= 0) {
+ int initialAppEnteredForegroundTime = Utils.getNow();
+ CoreMetaData.setInitialAppEnteredForegroundTime(initialAppEnteredForegroundTime);
}
for (String accountId : CleverTapAPI.instances.keySet()) {
CleverTapAPI instance = CleverTapAPI.instances.get(accountId);
try {
if (instance != null) {
- instance.activityResumed(activity);
+ instance.coreState.getActivityLifeCycleManager().activityResumed(activity);
}
} catch (Throwable t) {
Logger.v("Throwable - " + t.getLocalizedMessage());
@@ -1125,7 +905,7 @@ public static void processPushNotification(Context context, Bundle extras) {
if (instances == null) {
CleverTapAPI instance = createInstanceIfAvailable(context, _accountId);
if (instance != null) {
- instance.processCustomPushNotification(extras);
+ instance.coreState.getPushProviders().processCustomPushNotification(extras);
}
return;
}
@@ -1133,7 +913,7 @@ public static void processPushNotification(Context context, Bundle extras) {
for (String accountId : CleverTapAPI.instances.keySet()) {
CleverTapAPI instance = CleverTapAPI.instances.get(accountId);
if (instance != null) {
- instance.processCustomPushNotification(extras);
+ instance.coreState.getPushProviders().processCustomPushNotification(extras);
}
}
}
@@ -1144,7 +924,7 @@ public static void runBackgroundIntentService(Context context) {
CleverTapAPI instance = CleverTapAPI.getDefaultInstance(context);
if (instance != null) {
if (instance.getConfig().isBackgroundSync()) {
- instance.runInstanceJobWork(context, null);
+ instance.coreState.getPushProviders().runInstanceJobWork(context, null);
} else {
Logger.d("Instance doesn't allow Background sync, not running the Job");
}
@@ -1164,7 +944,7 @@ public static void runBackgroundIntentService(Context context) {
Logger.d(accountId, "Instance doesn't allow Background sync, not running the Job");
continue;
}
- instance.runInstanceJobWork(context, null);
+ instance.coreState.getPushProviders().runInstanceJobWork(context, null);
}
}
@@ -1174,7 +954,7 @@ public static void runJobWork(Context context, JobParameters parameters) {
CleverTapAPI instance = CleverTapAPI.getDefaultInstance(context);
if (instance != null) {
if (instance.getConfig().isBackgroundSync()) {
- instance.runInstanceJobWork(context, parameters);
+ instance.coreState.getPushProviders().runInstanceJobWork(context, parameters);
} else {
Logger.d("Instance doesn't allow Background sync, not running the Job");
}
@@ -1191,126 +971,71 @@ public static void runJobWork(Context context, JobParameters parameters) {
Logger.d(accountId, "Instance doesn't allow Background sync, not running the Job");
continue;
}
- instance.runInstanceJobWork(context, parameters);
+ instance.coreState.getPushProviders().runInstanceJobWork(context, parameters);
}
}
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Disables/Enables the ability to do UI Edits from the CleverTap Dashboard
- * Disabled by default
- */
- @Deprecated
- public static void setUIEditorConnectionEnabled(boolean enabled) {
- isUIEditorEnabled = enabled;
- }
-
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void tokenRefresh(Context context, String token, PushType pushType) {
for (CleverTapAPI instance : getAvailableInstances(context)) {
- if (instance == null || instance.getConfig().isAnalyticsOnly()) {
- Logger.d(instance.getAccountId(),
- "Instance is Analytics Only not processing device token");
- continue;
- }
- instance.doTokenRefresh(token, pushType);
+ instance.coreState.getPushProviders().doTokenRefresh(token, pushType);
}
}
// Initialize
private CleverTapAPI(final Context context, final CleverTapInstanceConfig config, String cleverTapID) {
- this.config = new CleverTapInstanceConfig(config);
this.context = context;
- this.handlerUsingMainLooper = new Handler(Looper.getMainLooper());
- this.es = Executors.newFixedThreadPool(1);
- this.ns = Executors.newFixedThreadPool(1);
- this.localDataStore = new LocalDataStore(context, config);
- validationResultStack = new ValidationResultStack();
- this.deviceInfo = new DeviceInfo(context, config, cleverTapID);
- if (this.deviceInfo.getDeviceID() != null) {
- Logger.v("Initializing InAppFC with device Id = " + this.deviceInfo.getDeviceID());
- this.inAppFCManager = new InAppFCManager(context, config, this.deviceInfo.getDeviceID());
- }
- initFeatureFlags(false);
- this.validator = new Validator();
- this.pushProviders = PushProviders.load(this);
+ CoreState coreState = CleverTapFactory
+ .getCoreState(context, config, cleverTapID);
+ setCoreState(coreState);
- postAsyncSafely("CleverTapAPI#initializeDeviceInfo", new Runnable() {
+ Task task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
+ task.execute("CleverTapAPI#initializeDeviceInfo", new Callable() {
@Override
- public void run() {
-
+ public Void call() {
if (config.isDefaultInstance()) {
manifestAsyncValidation();
}
+ return null;
}
});
- int now = (int) System.currentTimeMillis() / 1000;
- if (now - initialAppEnteredForegroundTime > 5) {
- this.config.setCreatedPostAppLaunch();
+ int now = Utils.getNow();
+ if (now - CoreMetaData.getInitialAppEnteredForegroundTime() > 5) {
+ this.coreState.getConfig().setCreatedPostAppLaunch();
}
- setLastVisitTime();
-
- // Default (flag is set in the config init) or first non-default instance gets the ABTestController
- if (!config.isDefaultInstance()) {
- if (instances == null || instances.size() <= 0) {
- config.setEnableABTesting(true);
- }
- }
- initABTesting();
+ coreState.getSessionManager().setLastVisitTime();
- postAsyncSafely("setStatesAsync", new Runnable() {
+ task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
+ task.execute("setStatesAsync", new Callable() {
@Override
- public void run() {
- setDeviceNetworkInfoReportingFromStorage();
- setCurrentUserOptOutStateFromStorage();
+ public Void call() {
+ CleverTapAPI.this.coreState.getDeviceInfo().setDeviceNetworkInfoReportingFromStorage();
+ CleverTapAPI.this.coreState.getDeviceInfo().setCurrentUserOptOutStateFromStorage();
+ return null;
}
});
- postAsyncSafely("saveConfigtoSharedPrefs", new Runnable() {
+ task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
+ task.execute("saveConfigtoSharedPrefs", new Callable() {
@Override
- public void run() {
+ public Void call() {
String configJson = config.toJSONString();
if (configJson == null) {
Logger.v("Unable to save config to SharedPrefs, config Json is null");
- return;
+ return null;
}
StorageHelper.putString(context, StorageHelper.storageKeyWithSuffix(config, "instance"), configJson);
+ return null;
}
});
- if (this.config.isBackgroundSync() && !this.config.isAnalyticsOnly()) {
- postAsyncSafely("createOrResetJobScheduler", new Runnable() {
- @Override
- public void run() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- createOrResetJobScheduler(context);
- } else {
- createAlarmScheduler(context);
- }
- }
- });
- }
Logger.i("CleverTap SDK initialized with accountId: " + config.getAccountId() + " accountToken: " + config
.getAccountToken() + " accountRegion: " + config.getAccountRegion());
}
- @Override
- public void ABExperimentsUpdated() {
- try {
- final CTExperimentsListener sl = getCTExperimentsListener();
- if (sl != null) {
- sl.CTExperimentsUpdated();
- }
- } catch (Throwable t) {
- // no-op
- }
- }
-
/**
* Add a unique value to a multi-value user profile property
* If the property does not exist it will be created
@@ -1327,7 +1052,7 @@ public void ABExperimentsUpdated() {
@SuppressWarnings({"unused", "WeakerAccess"})
public void addMultiValueForKey(String key, String value) {
if (value == null || value.isEmpty()) {
- _generateEmptyMultiValueError(key);
+ coreState.getAnalyticsManager()._generateEmptyMultiValueError(key);
return;
}
@@ -1349,26 +1074,7 @@ public void addMultiValueForKey(String key, String value) {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void addMultiValuesForKey(final String key, final ArrayList values) {
- postAsyncSafely("addMultiValuesForKey", new Runnable() {
- @Override
- public void run() {
- final String command = (getLocalDataStore().getProfileValueForKey(key) != null)
- ? Constants.COMMAND_ADD : Constants.COMMAND_SET;
- _handleMultiValues(values, key, command);
- }
- });
- }
-
- @Override
- @RestrictTo(Scope.LIBRARY)
- public CleverTapInstanceConfig config() {
- return config;
- }
-
- @Override
- @RestrictTo(Scope.LIBRARY)
- public Context context() {
- return context;
+ coreState.getAnalyticsManager().addMultiValuesForKey(key, values);
}
/**
@@ -1378,21 +1084,11 @@ public Context context() {
*/
@SuppressWarnings({"unused"})
public void deleteInboxMessage(final CTInboxMessage message) {
- postAsyncSafely("deleteInboxMessage", new Runnable() {
- @Override
- public void run() {
- synchronized (inboxControllerLock) {
- if (ctInboxController != null) {
- boolean update = ctInboxController.deleteMessageWithId(message.getMessageId());
- if (update) {
- _notifyInboxMessagesDidUpdate();
- }
- } else {
- getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
- }
- }
- }
- });
+ if (coreState.getControllerManager().getCTInboxController() != null) {
+ coreState.getControllerManager().getCTInboxController().deleteInboxMessage(message);
+ } else {
+ getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
+ }
}
/**
@@ -1406,19 +1102,13 @@ public void deleteInboxMessage(String messageId) {
deleteInboxMessage(message);
}
- @RestrictTo(Scope.LIBRARY)
- @Override
- public DeviceInfo deviceInfo() {
- return deviceInfo;
- }
-
/**
* Disables the Profile/Events Read and Synchronization API
* Personalization is enabled by default
*/
@SuppressWarnings({"unused"})
public void disablePersonalization() {
- this.config.enablePersonalization(false);
+ this.coreState.getConfig().enablePersonalization(false);
}
/**
@@ -1429,11 +1119,7 @@ public void disablePersonalization() {
*/
@SuppressWarnings({"unused"})
public void enableDeviceNetworkInfoReporting(boolean value) {
- enableNetworkInfoReporting = value;
- StorageHelper.putBoolean(context, StorageHelper.storageKeyWithSuffix(config, Constants.NETWORK_INFO),
- enableNetworkInfoReporting);
- getConfigLogger()
- .verbose(getAccountId(), "Device Network Information reporting set to " + enableNetworkInfoReporting);
+ coreState.getDeviceInfo().enableDeviceNetworkInfoReporting(value);
}
/**
@@ -1442,75 +1128,18 @@ public void enableDeviceNetworkInfoReporting(boolean value) {
*/
@SuppressWarnings({"unused"})
public void enablePersonalization() {
- this.config.enablePersonalization(true);
+ this.coreState.getConfig().enablePersonalization(true);
}
- //Push
-
/**
* @return object of {@link CTFeatureFlagsController}
* Handler to get the feature flag values
*/
public CTFeatureFlagsController featureFlag() {
- return ctFeatureFlagsController;
- }
-
- /**
- * This method is internal to the CleverTap SDK.
- * Developers should not use this method
- */
- @Override
- public void featureFlagsDidUpdate() {
- try {
- if (featureFlagsListener != null && featureFlagsListener.get() != null) {
- featureFlagsListener.get().featureFlagsUpdated();
- }
- } catch (Throwable t) {
- // no-op
- }
- }
-
- /**
- * This method is internal to the CleverTap SDK.
- * Developers should not use this method manually
- */
- @Override
- public void fetchFeatureFlags() {
- if (config.isAnalyticsOnly()) {
- return;
- }
- JSONObject event = new JSONObject();
- JSONObject notif = new JSONObject();
- try {
- notif.put("t", Constants.FETCH_TYPE_FF);
- event.put("evtName", Constants.WZRK_FETCH);
- event.put("evtData", notif);
- } catch (JSONException e) {
- e.printStackTrace();
- }
-
- queueEvent(context, event, Constants.FETCH_EVENT);
- }
-
- /**
- * This method is internal to CleverTap SDK.
- * Developers should not use this method manually.
- */
- @Override
- public void fetchProductConfig() {
- JSONObject event = new JSONObject();
- JSONObject notif = new JSONObject();
- try {
- notif.put("t", Constants.FETCH_TYPE_PC);
- event.put("evtName", Constants.WZRK_FETCH);
- event.put("evtData", notif);
- } catch (JSONException e) {
- e.printStackTrace();
+ if (getConfig().isAnalyticsOnly()) {
+ getConfig().getLogger().debug(getAccountId(),"Feature flag is not supported with analytics only configuration");
}
-
- queueEvent(context, event, Constants.FETCH_EVENT);
- isProductConfigRequested = true;
- getConfigLogger().verbose(getAccountId(), Constants.LOG_TAG_PRODUCT_CONFIG + "Fetching product config");
+ return coreState.getControllerManager().getCTFeatureFlagsController();
}
/**
@@ -1518,11 +1147,11 @@ public void fetchProductConfig() {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void flush() {
- flushQueueAsync(context, EventGroup.REGULAR);
+ coreState.getBaseEventQueueManager().flush();
}
public String getAccountId() {
- return config.getAccountId();
+ return coreState.getConfig().getAccountId();
}
/**
@@ -1532,8 +1161,9 @@ public String getAccountId() {
*/
@Nullable
public ArrayList getAllDisplayUnits() {
- if (mCTDisplayUnitController != null) {
- return mCTDisplayUnitController.getAllDisplayUnits();
+
+ if (coreState.getControllerManager().getCTDisplayUnitController() != null) {
+ return coreState.getControllerManager().getCTDisplayUnitController().getAllDisplayUnits();
} else {
getConfigLogger()
.verbose(getAccountId(), Constants.FEATURE_DISPLAY_UNIT + "Failed to get all Display Units");
@@ -1541,8 +1171,6 @@ public ArrayList getAllDisplayUnits() {
}
}
- //Push
-
/**
* Returns an ArrayList of all {@link CTInboxMessage} objects
*
@@ -1551,9 +1179,10 @@ public ArrayList getAllDisplayUnits() {
@SuppressWarnings({"unused", "WeakerAccess"})
public ArrayList getAllInboxMessages() {
ArrayList inboxMessageArrayList = new ArrayList<>();
- synchronized (inboxControllerLock) {
- if (ctInboxController != null) {
- ArrayList messageDAOArrayList = ctInboxController.getMessages();
+ synchronized (coreState.getCTLockManager().getInboxControllerLock()) {
+ if (coreState.getControllerManager().getCTInboxController() != null) {
+ ArrayList messageDAOArrayList =
+ coreState.getControllerManager().getCTInboxController().getMessages();
for (CTMessageDAO messageDAO : messageDAOArrayList) {
Logger.v("CTMessage Dao - " + messageDAO.toJSON().toString());
inboxMessageArrayList.add(new CTInboxMessage(messageDAO.toJSON()));
@@ -1566,46 +1195,7 @@ public ArrayList getAllInboxMessages() {
}
}
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link Boolean} value of the named variable set via an AB Testing Experiment or the default value
- * if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link Boolean} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public Boolean getBooleanVariable(String name, Boolean defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getBooleanVariable(name, defaultValue);
- }
-
- /**
- * Returns the CTExperimentsListener object
- *
- * @return The {@link CTExperimentsListener} object
- */
- @SuppressWarnings("WeakerAccess")
- public CTExperimentsListener getCTExperimentsListener() {
- return experimentsListener;
- }
-
- /**
- * This method is used to set the CTExperimentsListener
- *
- * @param experimentsListener The {@link CTExperimentsListener} object
- */
- @SuppressWarnings("unused")
- public void setCTExperimentsListener(CTExperimentsListener experimentsListener) {
- this.experimentsListener = experimentsListener;
- }
+ //Debug
/**
* Returns the CTInboxListener object
@@ -1614,11 +1204,9 @@ public void setCTExperimentsListener(CTExperimentsListener experimentsListener)
*/
@SuppressWarnings({"unused"})
public CTInboxListener getCTNotificationInboxListener() {
- return inboxListener;
+ return coreState.getCallbackManager().getInboxListener();
}
- //Debug
-
/**
* This method sets the CTInboxListener
*
@@ -1626,7 +1214,7 @@ public CTInboxListener getCTNotificationInboxListener() {
*/
@SuppressWarnings({"unused"})
public void setCTNotificationInboxListener(CTInboxListener notificationInboxListener) {
- inboxListener = notificationInboxListener;
+ coreState.getCallbackManager().setInboxListener(notificationInboxListener);
}
/**
@@ -1636,7 +1224,7 @@ public void setCTNotificationInboxListener(CTInboxListener notificationInboxList
*/
@SuppressWarnings("WeakerAccess")
public CTPushAmpListener getCTPushAmpListener() {
- return pushAmpListener;
+ return coreState.getCallbackManager().getPushAmpListener();
}
/**
@@ -1646,7 +1234,7 @@ public CTPushAmpListener getCTPushAmpListener() {
*/
@SuppressWarnings("unused")
public void setCTPushAmpListener(CTPushAmpListener pushAmpListener) {
- this.pushAmpListener = pushAmpListener;
+ coreState.getCallbackManager().setPushAmpListener(pushAmpListener);
}
/**
@@ -1656,7 +1244,7 @@ public void setCTPushAmpListener(CTPushAmpListener pushAmpListener) {
*/
@SuppressWarnings("WeakerAccess")
public CTPushNotificationListener getCTPushNotificationListener() {
- return pushNotificationListener;
+ return coreState.getCallbackManager().getPushNotificationListener();
}
/**
@@ -1666,9 +1254,11 @@ public CTPushNotificationListener getCTPushNotificationListener() {
*/
@SuppressWarnings("unused")
public void setCTPushNotificationListener(CTPushNotificationListener pushNotificationListener) {
- this.pushNotificationListener = pushNotificationListener;
+ coreState.getCallbackManager().setPushNotificationListener(pushNotificationListener);
}
+ //Network Info handling
+
/**
* Returns a unique CleverTap identifier suitable for use with install attribution providers.
*
@@ -1676,11 +1266,9 @@ public void setCTPushNotificationListener(CTPushNotificationListener pushNotific
*/
@SuppressWarnings("unused")
public String getCleverTapAttributionIdentifier() {
- return this.deviceInfo.getAttributionID();
+ return coreState.getDeviceInfo().getAttributionID();
}
- //Network Info handling
-
/**
* Returns a unique identifier by which CleverTap identifies this user.
*
@@ -1688,7 +1276,16 @@ public String getCleverTapAttributionIdentifier() {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public String getCleverTapID() {
- return this.deviceInfo.getDeviceID();
+ return coreState.getDeviceInfo().getDeviceID();
+ }
+
+ @RestrictTo(Scope.LIBRARY)
+ public CoreState getCoreState() {
+ return coreState;
+ }
+
+ void setCoreState(final CoreState cleverTapState) {
+ coreState = cleverTapState;
}
/**
@@ -1699,7 +1296,7 @@ public String getCleverTapID() {
*/
@SuppressWarnings({"unused"})
public int getCount(String event) {
- EventDetail eventDetail = getLocalDataStore().getEventDetail(event);
+ EventDetail eventDetail = coreState.getLocalDataStore().getEventDetail(event);
if (eventDetail != null) {
return eventDetail.getCount();
}
@@ -1717,27 +1314,7 @@ public int getCount(String event) {
*/
@SuppressWarnings({"unused"})
public EventDetail getDetails(String event) {
- return getLocalDataStore().getEventDetail(event);
- }
-
- // OptOut handling
-
- public Map getDeviceInfo() {
- final Map deviceInfo = new HashMap<>();
- deviceInfo.put("build", String.valueOf(this.deviceInfo.getBuild()));
- deviceInfo.put("versionName", this.deviceInfo.getVersionName());
- deviceInfo.put("osName", this.deviceInfo.getOsName());
- deviceInfo.put("osVersion", this.deviceInfo.getOsVersion());
- deviceInfo.put("manufacturer", this.deviceInfo.getManufacturer());
- deviceInfo.put("model", this.deviceInfo.getModel());
- deviceInfo.put("sdkVersion", String.valueOf(this.deviceInfo.getSdkVersion()));
- deviceInfo.put("dpi", String.valueOf(this.deviceInfo.getDPI()));
- deviceInfo.put("device_width", String.valueOf(this.deviceInfo.getWidthPixels()));
- deviceInfo.put("device_height", String.valueOf(this.deviceInfo.getHeightPixels()));
- if (this.deviceInfo.getLibrary() != null) {
- deviceInfo.put("library", this.deviceInfo.getLibrary());
- }
- return deviceInfo;
+ return coreState.getLocalDataStore().getEventDetail(event);
}
/**
@@ -1752,9 +1329,11 @@ public Map getDeviceInfo() {
*/
@SuppressWarnings("unused")
public String getDevicePushToken(final PushType type) {
- return pushProviders.getCachedToken(type);
+ return coreState.getPushProviders().getCachedToken(type);
}
+ //Util
+
/**
* Returns the DevicePushTokenRefreshListener
*
@@ -1762,7 +1341,7 @@ public String getDevicePushToken(final PushType type) {
*/
@SuppressWarnings("unused")
public DevicePushTokenRefreshListener getDevicePushTokenRefreshListener() {
- return tokenRefreshListener;
+ return coreState.getPushProviders().getDevicePushTokenRefreshListener();
}
/**
@@ -1772,7 +1351,8 @@ public DevicePushTokenRefreshListener getDevicePushTokenRefreshListener() {
*/
@SuppressWarnings("unused")
public void setDevicePushTokenRefreshListener(DevicePushTokenRefreshListener tokenRefreshListener) {
- this.tokenRefreshListener = tokenRefreshListener;
+ coreState.getPushProviders().setDevicePushTokenRefreshListener(tokenRefreshListener);
+
}
/**
@@ -1783,8 +1363,8 @@ public void setDevicePushTokenRefreshListener(DevicePushTokenRefreshListener tok
*/
@Nullable
public CleverTapDisplayUnit getDisplayUnitForId(String unitID) {
- if (mCTDisplayUnitController != null) {
- return mCTDisplayUnitController.getDisplayUnitForID(unitID);
+ if (coreState.getControllerManager().getCTDisplayUnitController() != null) {
+ return coreState.getControllerManager().getCTDisplayUnitController().getDisplayUnitForID(unitID);
} else {
getConfigLogger().verbose(getAccountId(),
Constants.FEATURE_DISPLAY_UNIT + "Failed to get Display Unit for id: " + unitID);
@@ -1793,35 +1373,14 @@ public CleverTapDisplayUnit getDisplayUnitForId(String unitID) {
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link Double} value of the named variable set via an AB Testing Experiment or the default value if
- * unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link Double} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public Double getDoubleVariable(String name, Double defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getDoubleVariable(name, defaultValue);
- }
-
- /**
- * Returns the timestamp of the first time the given event was raised
+ * Returns the timestamp of the first time the given event was raised
*
* @param event The event name for which you want the first time timestamp
* @return The timestamp in int
*/
@SuppressWarnings({"unused"})
public int getFirstTime(String event) {
- EventDetail eventDetail = getLocalDataStore().getEventDetail(event);
+ EventDetail eventDetail = coreState.getLocalDataStore().getEventDetail(event);
if (eventDetail != null) {
return eventDetail.getFirstTime();
}
@@ -1829,8 +1388,6 @@ public int getFirstTime(String event) {
return -1;
}
- //Util
-
/**
* Returns the GeofenceCallback object
*
@@ -1838,9 +1395,11 @@ public int getFirstTime(String event) {
*/
@SuppressWarnings("unused")
public GeofenceCallback getGeofenceCallback() {
- return this.geofenceCallback;
+ return coreState.getCallbackManager().getGeofenceCallback();
}
+ //DeepLink
+
/**
* This method is used to set the geofence callback
* Register to handle geofence responses from CleverTap
@@ -1851,7 +1410,7 @@ public GeofenceCallback getGeofenceCallback() {
@SuppressWarnings("unused")
public void setGeofenceCallback(GeofenceCallback geofenceCallback) {
- this.geofenceCallback = geofenceCallback;
+ coreState.getCallbackManager().setGeofenceCallback(geofenceCallback);
}
/**
@@ -1861,11 +1420,9 @@ public void setGeofenceCallback(GeofenceCallback geofenceCallback) {
*/
@SuppressWarnings({"unused"})
public Map getHistory() {
- return getLocalDataStore().getEventHistory(context);
+ return coreState.getLocalDataStore().getEventHistory(context);
}
- //Util
-
/**
* Returns the InAppNotificationListener object
*
@@ -1873,11 +1430,9 @@ public Map getHistory() {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public InAppNotificationListener getInAppNotificationListener() {
- return inAppNotificationListener;
+ return coreState.getCallbackManager().getInAppNotificationListener();
}
- //Util
-
/**
* This method sets the InAppNotificationListener
*
@@ -1885,11 +1440,9 @@ public InAppNotificationListener getInAppNotificationListener() {
*/
@SuppressWarnings({"unused"})
public void setInAppNotificationListener(InAppNotificationListener inAppNotificationListener) {
- this.inAppNotificationListener = inAppNotificationListener;
+ coreState.getCallbackManager().setInAppNotificationListener(inAppNotificationListener);
}
- //DeepLink
-
/**
* Returns the count of all inbox messages for the user
*
@@ -1897,9 +1450,9 @@ public void setInAppNotificationListener(InAppNotificationListener inAppNotifica
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public int getInboxMessageCount() {
- synchronized (inboxControllerLock) {
- if (this.ctInboxController != null) {
- return ctInboxController.count();
+ synchronized (coreState.getCTLockManager().getInboxControllerLock()) {
+ if (coreState.getControllerManager().getCTInboxController() != null) {
+ return coreState.getControllerManager().getCTInboxController().count();
} else {
getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
return -1;
@@ -1915,9 +1468,10 @@ public int getInboxMessageCount() {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public CTInboxMessage getInboxMessageForId(String messageId) {
- synchronized (inboxControllerLock) {
- if (this.ctInboxController != null) {
- CTMessageDAO message = ctInboxController.getMessageForId(messageId);
+ synchronized (coreState.getCTLockManager().getInboxControllerLock()) {
+ if (coreState.getControllerManager().getCTInboxController() != null) {
+ CTMessageDAO message = coreState.getControllerManager().getCTInboxController()
+ .getMessageForId(messageId);
return (message != null) ? new CTInboxMessage(message.toJSON()) : null;
} else {
getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
@@ -1933,9 +1487,9 @@ public CTInboxMessage getInboxMessageForId(String messageId) {
*/
@SuppressWarnings({"unused"})
public int getInboxMessageUnreadCount() {
- synchronized (inboxControllerLock) {
- if (this.ctInboxController != null) {
- return ctInboxController.unreadCount();
+ synchronized (coreState.getCTLockManager().getInboxControllerLock()) {
+ if (coreState.getControllerManager().getCTInboxController() != null) {
+ return coreState.getControllerManager().getCTInboxController().unreadCount();
} else {
getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
return -1;
@@ -1943,27 +1497,6 @@ public int getInboxMessageUnreadCount() {
}
}
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link Integer} value of the named variable set via an AB Testing Experiment or the default value
- * if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link Integer} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @SuppressWarnings({"unused"})
- public Integer getIntegerVariable(String name, Integer defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getIntegerVariable(name, defaultValue);
- }
-
/**
* Returns the timestamp of the last time the given event was raised
*
@@ -1972,7 +1505,7 @@ public Integer getIntegerVariable(String name, Integer defaultValue) {
*/
@SuppressWarnings({"unused"})
public int getLastTime(String event) {
- EventDetail eventDetail = getLocalDataStore().getEventDetail(event);
+ EventDetail eventDetail = coreState.getLocalDataStore().getEventDetail(event);
if (eventDetail != null) {
return eventDetail.getLastTime();
}
@@ -1980,92 +1513,6 @@ public int getLastTime(String event) {
return -1;
}
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link List} of {@link Boolean} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link List} of {@link Boolean} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public List getListOfBooleanVariable(String name, List defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getListOfBooleanVariable(name, defaultValue);
-
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link List} of {@link Double} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link List} of {@link Double} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public List getListOfDoubleVariable(String name, List defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getListOfDoubleVariable(name, defaultValue);
-
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link List} of {@link Integer} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link List} of {@link Integer} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public List getListOfIntegerVariable(String name, List defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getListOfIntegerVariable(name, defaultValue);
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link List} of {@link String} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link List} of {@link String} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public List getListOfStringVariable(String name, List defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getListOfStringVariable(name, defaultValue);
- }
-
/**
* get the current device location
* requires Location Permission in AndroidManifest e.g. "android.permission.ACCESS_COARSE_LOCATION"
@@ -2076,7 +1523,7 @@ public List getListOfStringVariable(String name, List defaultVal
*/
@SuppressWarnings({"unused"})
public Location getLocation() {
- return _getLocation();
+ return coreState.getLocationManager()._getLocation();
}
/**
@@ -2087,91 +1534,7 @@ public Location getLocation() {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void setLocation(Location location) {
- _setLocation(location);
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link Map} of {@link Boolean} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link Map} of {@link Boolean} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public Map getMapOfBooleanVariable(String name, Map defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getMapOfBooleanVariable(name, defaultValue);
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link Map} of {@link Double} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link Map} of {@link Double} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public Map getMapOfDoubleVariable(String name, Map defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getMapOfDoubleVariable(name, defaultValue);
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link Map} of {@link Integer} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link Map} of {@link Integer} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public Map getMapOfIntegerVariable(String name, Map defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getMapOfIntegerVariable(name, defaultValue);
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Returns the {@link Map} of {@link String} value of the named variable set via an AB Testing Experiment or the
- * default value if unset
- *
- * @param name - the name of the variable
- * @param defaultValue - the default value to return if the value has not been set via an AB Testing Experiment
- * @return {@link Map} of {@link String} the value set by the Experiment or the default value if unset
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public Map getMapOfStringVariable(String name, Map defaultValue) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return defaultValue;
- }
- return ctABTestController.getMapOfStringVariable(name, defaultValue);
+ coreState.getLocationManager()._setLocation(location);
}
/**
@@ -2181,7 +1544,7 @@ public Map getMapOfStringVariable(String name, Map getUnreadInboxMessages() {
ArrayList inboxMessageArrayList = new ArrayList<>();
- synchronized (inboxControllerLock) {
- if (ctInboxController != null) {
- ArrayList messageDAOArrayList = ctInboxController.getUnreadMessages();
+ synchronized (coreState.getCTLockManager().getInboxControllerLock()) {
+ if (coreState.getControllerManager().getCTInboxController() != null) {
+ ArrayList messageDAOArrayList =
+ coreState.getControllerManager().getCTInboxController().getUnreadMessages();
for (CTMessageDAO messageDAO : messageDAOArrayList) {
inboxMessageArrayList.add(new CTInboxMessage(messageDAO.toJSON()));
}
@@ -2323,81 +1666,13 @@ public ArrayList getUnreadInboxMessages() {
}
}
- @Override
- public void inAppNotificationDidClick(CTInAppNotification inAppNotification, Bundle formData,
- HashMap keyValueMap) {
- pushInAppNotificationStateEvent(true, inAppNotification, formData);
- if (keyValueMap != null && !keyValueMap.isEmpty()) {
- if (inAppNotificationButtonListener != null && inAppNotificationButtonListener.get() != null) {
- inAppNotificationButtonListener.get().onInAppButtonClick(keyValueMap);
- }
- }
- }
-
- @Override
- public void inAppNotificationDidDismiss(final Context context, final CTInAppNotification inAppNotification,
- Bundle formData) {
- inAppNotification.didDismiss();
- if (inAppFCManager != null) {
- inAppFCManager.didDismiss(inAppNotification);
- getConfigLogger().verbose(getAccountId(), "InApp Dismissed: " + inAppNotification.getCampaignId());
- }
- try {
- final InAppNotificationListener listener = getInAppNotificationListener();
- if (listener != null) {
- final HashMap notifKVS;
-
- if (inAppNotification.getCustomExtras() != null) {
- notifKVS = Utils.convertJSONObjectToHashMap(inAppNotification.getCustomExtras());
- } else {
- notifKVS = new HashMap<>();
- }
-
- Logger.v("Calling the in-app listener on behalf of " + source);
-
- if (formData != null) {
- listener.onDismissed(notifKVS, Utils.convertBundleObjectToHashMap(formData));
- } else {
- listener.onDismissed(notifKVS, null);
- }
- }
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to call the in-app notification listener", t);
- }
-
- // Fire the next one, if any
- runOnNotificationQueue(new Runnable() {
- @Override
- public void run() {
- inAppDidDismiss(context, getConfig(), inAppNotification);
- _showNotificationIfAvailable(context);
- }
- });
- }
-
- //InApp
- @Override
- public void inAppNotificationDidShow(CTInAppNotification inAppNotification, Bundle formData) {
- pushInAppNotificationStateEvent(false, inAppNotification, formData);
- }
-
/**
* Initializes the inbox controller and sends a callback to the {@link CTInboxListener}
* This method needs to be called separately for each instance of {@link CleverTapAPI}
*/
@SuppressWarnings({"unused"})
public void initializeInbox() {
- if (getConfig().isAnalyticsOnly()) {
- getConfigLogger()
- .debug(getAccountId(), "Instance is analytics only, not initializing Notification Inbox");
- return;
- }
- postAsyncSafely("initializeInbox", new Runnable() {
- @Override
- public void run() {
- _initializeInbox();
- }
- });
+ coreState.getControllerManager().initializeInbox();
}
/**
@@ -2408,23 +1683,15 @@ public void run() {
//marks the message as read
@SuppressWarnings({"unused", "WeakerAccess"})
public void markReadInboxMessage(final CTInboxMessage message) {
- postAsyncSafely("markReadInboxMessage", new Runnable() {
- @Override
- public void run() {
- synchronized (inboxControllerLock) {
- if (ctInboxController != null) {
- boolean read = ctInboxController.markReadForMessageWithId(message.getMessageId());
- if (read) {
- _notifyInboxMessagesDidUpdate();
- }
- } else {
- getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
- }
- }
- }
- });
+ if (coreState.getControllerManager().getCTInboxController() != null) {
+ coreState.getControllerManager().getCTInboxController().markReadInboxMessage(message);
+ } else {
+ getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
+ }
}
+ //Session
+
/**
* Marks the given messageId of {@link CTInboxMessage} object as read
*
@@ -2439,7 +1706,7 @@ public void markReadInboxMessage(String messageId) {
@Override
public void messageDidClick(CTInboxActivity ctInboxActivity, CTInboxMessage inboxMessage, Bundle data,
HashMap keyValue) {
- pushInboxMessageStateEvent(true, inboxMessage, data);
+ coreState.getAnalyticsManager().pushInboxMessageStateEvent(true, inboxMessage, data);
if (keyValue != null && !keyValue.isEmpty()) {
if (inboxMessageButtonListener != null && inboxMessageButtonListener.get() != null) {
inboxMessageButtonListener.get().onInboxButtonClick(keyValue);
@@ -2450,84 +1717,20 @@ public void messageDidClick(CTInboxActivity ctInboxActivity, CTInboxMessage inbo
@Override
public void messageDidShow(CTInboxActivity ctInboxActivity, final CTInboxMessage inboxMessage,
final Bundle data) {
- postAsyncSafely("handleMessageDidShow", new Runnable() {
+ Task task = CTExecutorFactory.executors(coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("handleMessageDidShow", new Callable() {
@Override
- public void run() {
+ public Void call() {
CTInboxMessage message = getInboxMessageForId(inboxMessage.getMessageId());
if (!message.isRead()) {
markReadInboxMessage(inboxMessage);
- pushInboxMessageStateEvent(false, inboxMessage, data);
+ coreState.getAnalyticsManager().pushInboxMessageStateEvent(false, inboxMessage, data);
}
+ return null;
}
});
}
- //InApp
- @Override
- public void notificationReady(final CTInAppNotification inAppNotification) {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- getHandlerUsingMainLooper().post(new Runnable() {
- @Override
- public void run() {
- notificationReady(inAppNotification);
- }
- });
- return;
- }
-
- if (inAppNotification.getError() != null) {
- getConfigLogger()
- .debug(getAccountId(), "Unable to process inapp notification " + inAppNotification.getError());
- return;
- }
- getConfigLogger().debug(getAccountId(), "Notification ready: " + inAppNotification.getJsonDescription());
- displayNotification(inAppNotification);
- }
-
- /**
- * This method is internal to CleverTap SDK.
- * Developers should not use this method manually.
- */
- @Override
- public void onActivated() {
- if (productConfigListener != null && productConfigListener.get() != null) {
- productConfigListener.get().onActivated();
- }
- }
-
- //Session
-
- /**
- * This method is internal to CleverTap SDK.
- * Developer should not use this method manually.
- */
- @Override
- public void onFetched() {
- if (productConfigListener != null && productConfigListener.get() != null) {
- productConfigListener.get().onFetched();
- }
- }
-
- /**
- * This method is internal to CleverTap SDK.
- * Developers should not use this method manually.
- */
- @Override
- public void onInit() {
- if (productConfigListener != null && productConfigListener.get() != null) {
- getConfigLogger().verbose(config.getAccountId(), "Product Config initialized");
- productConfigListener.get().onInit();
- }
- }
-
- @Override
- public void onNewToken(String freshToken, PushType pushType) {
- if (!TextUtils.isEmpty(freshToken)) {
- doTokenRefresh(freshToken, pushType);
- deviceTokenDidRefresh(freshToken, pushType);
- }
- }
-
/**
* Creates a separate and distinct user profile identified by one or more of Identity,
* Email, FBID or GPID values,
@@ -2560,18 +1763,7 @@ public void onNewToken(String freshToken, PushType pushType) {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void onUserLogin(final Map profile, final String cleverTapID) {
- if (getConfig().getEnableCustomCleverTapId()) {
- if (cleverTapID == null) {
- Logger.i(
- "CLEVERTAP_USE_CUSTOM_ID has been specified in the AndroidManifest.xml Please call onUserlogin() and pass a custom CleverTap ID");
- }
- } else {
- if (cleverTapID != null) {
- Logger.i(
- "CLEVERTAP_USE_CUSTOM_ID has not been specified in the AndroidManifest.xml Please call CleverTapAPI.defaultInstance() without a custom CleverTap ID");
- }
- }
- _onUserLogin(profile, cleverTapID);
+ coreState.getLoginController().onUserLogin(profile, cleverTapID);
}
/**
@@ -2615,10 +1807,11 @@ public void onUserLogin(final Map profile) {
*/
@SuppressWarnings("WeakerAccess")
public CTProductConfigController productConfig() {
- if (ctProductConfigController == null) {
- initProductConfig(false);
+ if (getConfig().isAnalyticsOnly()) {
+ getConfig().getLogger().debug(getAccountId(),
+ "Product config is not supported with analytics only configuration");
}
- return ctProductConfigController;
+ return coreState.getCtProductConfigController();
}
/**
@@ -2632,7 +1825,7 @@ public CTProductConfigController productConfig() {
*/
@SuppressWarnings("unused")
public void pushBaiduRegistrationId(String regId, boolean register) {
- pushProviders.handleToken(regId, PushType.BPS, register);
+ coreState.getPushProviders().handleToken(regId, PushType.BPS, register);
}
/**
@@ -2647,93 +1840,7 @@ public void pushBaiduRegistrationId(String regId, boolean register) {
@SuppressWarnings({"unused"})
public void pushChargedEvent(HashMap chargeDetails,
ArrayList> items) {
-
- if (chargeDetails == null || items == null) {
- getConfigLogger().debug(getAccountId(), "Invalid Charged event: details and or items is null");
- return;
- }
-
- if (items.size() > 50) {
- ValidationResult error = ValidationResultFactory.create(522);
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- validationResultStack.pushValidationResult(error);
- }
-
- JSONObject evtData = new JSONObject();
- JSONObject chargedEvent = new JSONObject();
- ValidationResult vr;
- try {
- for (String key : chargeDetails.keySet()) {
- Object value = chargeDetails.get(key);
- vr = validator.cleanObjectKey(key);
- key = vr.getObject().toString();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- chargedEvent.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
-
- try {
- vr = validator.cleanObjectValue(value, Validator.ValidationContext.Event);
- } catch (IllegalArgumentException e) {
- // The object was neither a String, Boolean, or any number primitives
- ValidationResult error = ValidationResultFactory.create(511,
- Constants.PROP_VALUE_NOT_PRIMITIVE, "Charged", key,
- value != null ? value.toString() : "");
- validationResultStack.pushValidationResult(error);
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- // Skip this property
- continue;
- }
- value = vr.getObject();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- chargedEvent.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
-
- evtData.put(key, value);
- }
-
- JSONArray jsonItemsArray = new JSONArray();
- for (HashMap map : items) {
- JSONObject itemDetails = new JSONObject();
- for (String key : map.keySet()) {
- Object value = map.get(key);
- vr = validator.cleanObjectKey(key);
- key = vr.getObject().toString();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- chargedEvent.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
-
- try {
- vr = validator.cleanObjectValue(value, Validator.ValidationContext.Event);
- } catch (IllegalArgumentException e) {
- // The object was neither a String, Boolean, or any number primitives
- ValidationResult error = ValidationResultFactory
- .create(511, Constants.OBJECT_VALUE_NOT_PRIMITIVE, key,
- value != null ? value.toString() : "");
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- validationResultStack.pushValidationResult(error);
- // Skip this property
- continue;
- }
- value = vr.getObject();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- chargedEvent.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
- itemDetails.put(key, value);
- }
- jsonItemsArray.put(itemDetails);
- }
- evtData.put("Items", jsonItemsArray);
-
- chargedEvent.put("evtName", Constants.CHARGED_EVENT);
- chargedEvent.put("evtData", evtData);
- queueEvent(context, chargedEvent, Constants.RAISED_EVENT);
- } catch (Throwable t) {
- // We won't get here
- }
+ coreState.getAnalyticsManager().pushChargedEvent(chargeDetails, items);
}
/**
@@ -2743,35 +1850,7 @@ public void pushChargedEvent(HashMap chargeDetails,
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void pushDeepLink(Uri uri) {
- pushDeepLink(uri, false);
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- @Override
- public void pushDeviceTokenEvent(String token, boolean register, PushType pushType) {
- if (pushType == null) {
- return;
- }
- token = !TextUtils.isEmpty(token) ? token : pushProviders.getCachedToken(pushType);
- if (TextUtils.isEmpty(token)) {
- return;
- }
- synchronized (tokenLock) {
- JSONObject event = new JSONObject();
- JSONObject data = new JSONObject();
- String action = register ? "register" : "unregister";
- try {
- data.put("action", action);
- data.put("id", token);
- data.put("type", pushType.getType());
- event.put("data", data);
- getConfigLogger().verbose(getAccountId(), pushType + action + " device token " + token);
- queueEvent(context, event, Constants.DATA_EVENT);
- } catch (Throwable t) {
- // we won't get here
- getConfigLogger().verbose(getAccountId(), pushType + action + " device token failed", t);
- }
- }
+ coreState.getAnalyticsManager().pushDeepLink(uri, false);
}
/**
@@ -2781,33 +1860,7 @@ public void pushDeviceTokenEvent(String token, boolean register, PushType pushTy
*/
@SuppressWarnings("unused")
public void pushDisplayUnitClickedEventForID(String unitID) {
- JSONObject event = new JSONObject();
-
- try {
- event.put("evtName", Constants.NOTIFICATION_CLICKED_EVENT_NAME);
-
- //wzrk fields
- if (mCTDisplayUnitController != null) {
- CleverTapDisplayUnit displayUnit = mCTDisplayUnitController.getDisplayUnitForID(unitID);
- if (displayUnit != null) {
- JSONObject eventExtraData = displayUnit.getWZRKFields();
- if (eventExtraData != null) {
- event.put("evtData", eventExtraData);
- try {
- setWzrkParams(eventExtraData);
- } catch (Throwable t) {
- // no-op
- }
- }
- }
- }
-
- queueEvent(context, event, Constants.RAISED_EVENT);
- } catch (Throwable t) {
- // We won't get here
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_DISPLAY_UNIT + "Failed to push Display Unit clicked event" + t);
- }
+ coreState.getAnalyticsManager().pushDisplayUnitClickedEventForID(unitID);
}
/**
@@ -2817,28 +1870,7 @@ public void pushDisplayUnitClickedEventForID(String unitID) {
*/
@SuppressWarnings("unused")
public void pushDisplayUnitViewedEventForID(String unitID) {
- JSONObject event = new JSONObject();
-
- try {
- event.put("evtName", Constants.NOTIFICATION_VIEWED_EVENT_NAME);
-
- //wzrk fields
- if (mCTDisplayUnitController != null) {
- CleverTapDisplayUnit displayUnit = mCTDisplayUnitController.getDisplayUnitForID(unitID);
- if (displayUnit != null) {
- JSONObject eventExtras = displayUnit.getWZRKFields();
- if (eventExtras != null) {
- event.put("evtData", eventExtras);
- }
- }
- }
-
- queueEvent(context, event, Constants.RAISED_EVENT);
- } catch (Throwable t) {
- // We won't get here
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_DISPLAY_UNIT + "Failed to push Display Unit viewed event" + t);
- }
+ coreState.getAnalyticsManager().pushDisplayUnitViewedEventForID(unitID);
}
/**
@@ -2849,23 +1881,7 @@ public void pushDisplayUnitViewedEventForID(String unitID) {
*/
@SuppressWarnings({"unused"})
public void pushError(final String errorMessage, final int errorCode) {
- final HashMap props = new HashMap<>();
- props.put("Error Message", errorMessage);
- props.put("Error Code", errorCode);
-
- try {
- final String activityName = getCurrentActivityName();
- if (activityName != null) {
- props.put("Location", activityName);
- } else {
- props.put("Location", "Unknown");
- }
- } catch (Throwable t) {
- // Ignore
- props.put("Location", "Unknown");
- }
-
- pushEvent("Error Occurred", props);
+ coreState.getAnalyticsManager().pushError(errorMessage, errorCode);
}
/**
@@ -2878,7 +1894,6 @@ public void pushEvent(String eventName) {
if (eventName == null || eventName.trim().equals("")) {
return;
}
-
pushEvent(eventName, null);
}
@@ -2892,107 +1907,22 @@ public void pushEvent(String eventName) {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void pushEvent(String eventName, Map eventActions) {
+ coreState.getAnalyticsManager().pushEvent(eventName, eventActions);
+ }
- if (eventName == null || eventName.equals("")) {
- return;
- }
-
- ValidationResult validationResult = validator.isRestrictedEventName(eventName);
- // Check for a restricted event name
- if (validationResult.getErrorCode() > 0) {
- validationResultStack.pushValidationResult(validationResult);
- return;
- }
-
- ValidationResult discardedResult = validator.isEventDiscarded(eventName);
- // Check for a discarded event name
- if (discardedResult.getErrorCode() > 0) {
- validationResultStack.pushValidationResult(discardedResult);
- return;
- }
-
- if (eventActions == null) {
- eventActions = new HashMap<>();
- }
-
- JSONObject event = new JSONObject();
- try {
- // Validate
- ValidationResult vr = validator.cleanEventName(eventName);
-
- // Check for an error
- if (vr.getErrorCode() != 0) {
- event.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
-
- eventName = vr.getObject().toString();
- JSONObject actions = new JSONObject();
- for (String key : eventActions.keySet()) {
- Object value = eventActions.get(key);
- vr = validator.cleanObjectKey(key);
- key = vr.getObject().toString();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- event.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
- try {
- vr = validator.cleanObjectValue(value, Validator.ValidationContext.Event);
- } catch (IllegalArgumentException e) {
- // The object was neither a String, Boolean, or any number primitives
- ValidationResult error = ValidationResultFactory
- .create(512, Constants.PROP_VALUE_NOT_PRIMITIVE, eventName, key,
- value != null ? value.toString() : "");
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- validationResultStack.pushValidationResult(error);
- // Skip this record
- continue;
- }
- value = vr.getObject();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- event.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
- actions.put(key, value);
- }
- event.put("evtName", eventName);
- event.put("evtData", actions);
- queueEvent(context, event, Constants.RAISED_EVENT);
- } catch (Throwable t) {
- // We won't get here
- }
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- * Pushes everything available in the JSON object returned by the Facebook GraphRequest
- *
- * @param graphUser The object returned from Facebook
- */
- @Deprecated
- @SuppressWarnings({"unused"})
- public void pushFacebookUser(final JSONObject graphUser) {
- postAsyncSafely("pushFacebookUser", new Runnable() {
- @Override
- public void run() {
- _pushFacebookUser(graphUser);
- }
- });
- }
-
- /**
- * Sends the FCM registration ID to CleverTap.
- *
- * @param fcmId The FCM registration ID
- * @param register Boolean indicating whether to register
- * or not for receiving push messages from CleverTap.
- * Set this to true to receive push messages from CleverTap,
- * and false to not receive any messages from CleverTap.
- */
- @SuppressWarnings("unused")
- public void pushFcmRegistrationId(String fcmId, boolean register) {
- pushProviders.handleToken(fcmId, PushType.FCM, register);
- }
+ /**
+ * Sends the FCM registration ID to CleverTap.
+ *
+ * @param fcmId The FCM registration ID
+ * @param register Boolean indicating whether to register
+ * or not for receiving push messages from CleverTap.
+ * Set this to true to receive push messages from CleverTap,
+ * and false to not receive any messages from CleverTap.
+ */
+ @SuppressWarnings("unused")
+ public void pushFcmRegistrationId(String fcmId, boolean register) {
+ coreState.getPushProviders().handleToken(fcmId, PushType.FCM, register);
+ }
/**
* Used to record errors of the Geofence module
@@ -3003,7 +1933,7 @@ public void pushFcmRegistrationId(String fcmId, boolean register) {
@SuppressWarnings("unused")
public void pushGeoFenceError(int errorCode, String errorMessage) {
ValidationResult validationResult = new ValidationResult(errorCode, errorMessage);
- validationResultStack.pushValidationResult(validationResult);
+ coreState.getValidationResultStack().pushValidationResult(validationResult);
}
/**
@@ -3014,7 +1944,8 @@ public void pushGeoFenceError(int errorCode, String errorMessage) {
*/
@SuppressWarnings("unused")
public Future> pushGeoFenceExitedEvent(JSONObject geoFenceProperties) {
- return raiseEventForGeofences(Constants.GEOFENCE_EXITED_EVENT_NAME, geoFenceProperties);
+ return coreState.getAnalyticsManager()
+ .raiseEventForGeofences(Constants.GEOFENCE_EXITED_EVENT_NAME, geoFenceProperties);
}
/**
@@ -3025,7 +1956,8 @@ public Future> pushGeoFenceExitedEvent(JSONObject geoFenceProperties) {
*/
@SuppressWarnings("unused")
public Future> pushGeofenceEnteredEvent(JSONObject geofenceProperties) {
- return raiseEventForGeofences(Constants.GEOFENCE_ENTERED_EVENT_NAME, geofenceProperties);
+ return coreState.getAnalyticsManager()
+ .raiseEventForGeofences(Constants.GEOFENCE_ENTERED_EVENT_NAME, geofenceProperties);
}
/**
@@ -3039,7 +1971,7 @@ public Future> pushGeofenceEnteredEvent(JSONObject geofenceProperties) {
*/
@SuppressWarnings("unused")
public void pushHuaweiRegistrationId(String regId, boolean register) {
- pushProviders.handleToken(regId, PushType.HPS, register);
+ coreState.getPushProviders().handleToken(regId, PushType.HPS, register);
}
/**
@@ -3050,7 +1982,7 @@ public void pushHuaweiRegistrationId(String regId, boolean register) {
@SuppressWarnings("unused")
public void pushInboxNotificationClickedEvent(String messageId) {
CTInboxMessage message = getInboxMessageForId(messageId);
- pushInboxMessageStateEvent(true, message, null);
+ coreState.getAnalyticsManager().pushInboxMessageStateEvent(true, message, null);
}
/**
@@ -3061,53 +1993,7 @@ public void pushInboxNotificationClickedEvent(String messageId) {
@SuppressWarnings("unused")
public void pushInboxNotificationViewedEvent(String messageId) {
CTInboxMessage message = getInboxMessageForId(messageId);
- pushInboxMessageStateEvent(false, message, null);
- }
-
- /**
- * This method is used to push install referrer via Intent
- * Deprecation warning because Google Play install referrer via intent will be deprecated in March 2020
- *
- * @param intent An Intent with the install referrer parameters
- */
- @SuppressWarnings({"unused", "WeakerAccess"})
- @Deprecated
- public void pushInstallReferrer(Intent intent) {
- try {
- final Bundle extras = intent.getExtras();
- // Preliminary checks
- if (extras == null || !extras.containsKey("referrer")) {
- return;
- }
- final String url;
- try {
- url = URLDecoder.decode(extras.getString("referrer"), "UTF-8");
-
- getConfigLogger().verbose(getAccountId(), "Referrer received: " + url);
- } catch (Throwable e) {
- // Could not decode
- return;
- }
- if (url == null) {
- return;
- }
- int now = (int) (System.currentTimeMillis() / 1000);
-
- //noinspection ConstantConditions
- if (installReferrerMap.containsKey(url) && now - installReferrerMap.get(url) < 10) {
- getConfigLogger()
- .verbose(getAccountId(), "Skipping install referrer due to duplicate within 10 seconds");
- return;
- }
-
- installReferrerMap.put(url, now);
-
- Uri uri = Uri.parse("wzrk://track?install=true&" + url);
-
- pushDeepLink(uri, true);
- } catch (Throwable t) {
- // no-op
- }
+ coreState.getAnalyticsManager().pushInboxMessageStateEvent(false, message, null);
}
/**
@@ -3117,29 +2003,7 @@ public void pushInstallReferrer(Intent intent) {
*/
@SuppressWarnings({"unused"})
public void pushInstallReferrer(String url) {
- try {
- getConfigLogger().verbose(getAccountId(), "Referrer received: " + url);
-
- if (url == null) {
- return;
- }
- int now = (int) (System.currentTimeMillis() / 1000);
-
- //noinspection ConstantConditions
- if (installReferrerMap.containsKey(url) && now - installReferrerMap.get(url) < 10) {
- getConfigLogger()
- .verbose(getAccountId(), "Skipping install referrer due to duplicate within 10 seconds");
- return;
- }
-
- installReferrerMap.put(url, now);
-
- Uri uri = Uri.parse("wzrk://track?install=true&" + url);
-
- pushDeepLink(uri, true);
- } catch (Throwable t) {
- // no-op
- }
+ coreState.getAnalyticsManager().pushInstallReferrer(url);
}
/**
@@ -3151,44 +2015,7 @@ public void pushInstallReferrer(String url) {
*/
@SuppressWarnings({"unused"})
public synchronized void pushInstallReferrer(String source, String medium, String campaign) {
- if (source == null && medium == null && campaign == null) {
- return;
- }
- try {
- // If already pushed, don't send it again
- int status = StorageHelper.getInt(context, "app_install_status", 0);
- if (status != 0) {
- Logger.d("Install referrer has already been set. Will not override it");
- return;
- }
- StorageHelper.putInt(context, "app_install_status", 1);
-
- if (source != null) {
- source = Uri.encode(source);
- }
- if (medium != null) {
- medium = Uri.encode(medium);
- }
- if (campaign != null) {
- campaign = Uri.encode(campaign);
- }
-
- String uriStr = "wzrk://track?install=true";
- if (source != null) {
- uriStr += "&utm_source=" + source;
- }
- if (medium != null) {
- uriStr += "&utm_medium=" + medium;
- }
- if (campaign != null) {
- uriStr += "&utm_campaign=" + campaign;
- }
-
- Uri uri = Uri.parse(uriStr);
- pushDeepLink(uri, true);
- } catch (Throwable t) {
- Logger.v("Failed to push install referrer", t);
- }
+ coreState.getAnalyticsManager().pushInstallReferrer(source, medium, campaign);
}
/**
@@ -3199,132 +2026,11 @@ public synchronized void pushInstallReferrer(String source, String medium, Strin
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void pushNotificationClickedEvent(final Bundle extras) {
-
- if (this.config.isAnalyticsOnly()) {
- getConfigLogger()
- .debug(getAccountId(), "is Analytics Only - will not process Notification Clicked event.");
- return;
- }
-
- if (extras == null || extras.isEmpty() || extras.get(Constants.NOTIFICATION_TAG) == null) {
- getConfigLogger().debug(getAccountId(),
- "Push notification: " + (extras == null ? "NULL" : extras.toString())
- + " not from CleverTap - will not process Notification Clicked event.");
- return;
- }
-
- String accountId = null;
- try {
- accountId = extras.getString(Constants.WZRK_ACCT_ID_KEY);
- } catch (Throwable t) {
- // no-op
- }
-
- boolean shouldProcess = (accountId == null && config.isDefaultInstance()) || getAccountId().equals(accountId);
-
- if (!shouldProcess) {
- getConfigLogger().debug(getAccountId(),
- "Push notification not targeted at this instance, not processing Notification Clicked Event");
- return;
- }
-
- if (extras.containsKey(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_KEY)) {
- pendingInappRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- Logger.v("Received in-app via push payload: " + extras
- .getString(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_KEY));
- JSONObject r = new JSONObject();
- JSONArray inappNotifs = new JSONArray();
- r.put(Constants.INAPP_JSON_RESPONSE_KEY, inappNotifs);
- inappNotifs.put(new JSONObject(extras.getString(Constants.INAPP_PREVIEW_PUSH_PAYLOAD_KEY)));
- processInAppResponse(r, context);
- } catch (Throwable t) {
- Logger.v("Failed to display inapp notification from push notification payload", t);
- }
- }
- };
- return;
- }
-
- if (extras.containsKey(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY)) {
- pendingInappRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- Logger.v("Received inbox via push payload: " + extras
- .getString(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY));
- JSONObject r = new JSONObject();
- JSONArray inappNotifs = new JSONArray();
- r.put(Constants.INBOX_JSON_RESPONSE_KEY, inappNotifs);
- JSONObject testPushObject = new JSONObject(
- extras.getString(Constants.INBOX_PREVIEW_PUSH_PAYLOAD_KEY));
- testPushObject.put("_id", String.valueOf(System.currentTimeMillis() / 1000));
- inappNotifs.put(testPushObject);
- processInboxResponse(r);
- } catch (Throwable t) {
- Logger.v("Failed to process inbox message from push notification payload", t);
- }
- }
- };
- return;
- }
-
- if (extras.containsKey(Constants.DISPLAY_UNIT_PREVIEW_PUSH_PAYLOAD_KEY)) {
- handleSendTestForDisplayUnits(extras);
- return;
- }
-
- if (!extras.containsKey(Constants.NOTIFICATION_ID_TAG) || (extras.getString(Constants.NOTIFICATION_ID_TAG)
- == null)) {
- getConfigLogger().debug(getAccountId(),
- "Push notification ID Tag is null, not processing Notification Clicked event for: " + extras
- .toString());
- return;
- }
-
- // Check for dupe notification views; if same notficationdId within specified time interval (5 secs) don't process
- boolean isDuplicate = checkDuplicateNotificationIds(extras, notificationIdTagMap,
- Constants.NOTIFICATION_ID_TAG_INTERVAL);
- if (isDuplicate) {
- getConfigLogger().debug(getAccountId(),
- "Already processed Notification Clicked event for " + extras.toString()
- + ", dropping duplicate.");
- 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);
- }
-
- event.put("evtName", Constants.NOTIFICATION_CLICKED_EVENT_NAME);
- event.put("evtData", notif);
- queueEvent(context, event, Constants.RAISED_EVENT);
-
- try {
- setWzrkParams(getWzrkFields(extras));
- } catch (Throwable t) {
- // no-op
- }
- } catch (Throwable t) {
- // We won't get here
- }
- if (getCTPushNotificationListener() != null) {
- getCTPushNotificationListener()
- .onNotificationClickedPayloadReceived(Utils.convertBundleObjectToHashMap(extras));
- } else {
- Logger.d("CTPushNotificationListener is not set");
- }
+ coreState.getAnalyticsManager().pushNotificationClickedEvent(extras);
}
+ //Session
+
/**
* Pushes the Notification Viewed event to CleverTap.
*
@@ -3333,42 +2039,7 @@ public void run() {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void pushNotificationViewedEvent(Bundle extras) {
-
- if (extras == null || extras.isEmpty() || extras.get(Constants.NOTIFICATION_TAG) == null) {
- getConfigLogger().debug(getAccountId(),
- "Push notification: " + (extras == null ? "NULL" : extras.toString())
- + " not from CleverTap - will not process Notification Viewed event.");
- return;
- }
-
- if (!extras.containsKey(Constants.NOTIFICATION_ID_TAG) || (extras.getString(Constants.NOTIFICATION_ID_TAG)
- == null)) {
- getConfigLogger().debug(getAccountId(),
- "Push notification ID Tag is null, not processing Notification Viewed event for: " + extras
- .toString());
- return;
- }
-
- // Check for dupe notification views; if same notficationdId within specified time interval (2 secs) don't process
- boolean isDuplicate = checkDuplicateNotificationIds(extras, notificationViewedIdTagMap,
- Constants.NOTIFICATION_VIEWED_ID_TAG_INTERVAL);
- if (isDuplicate) {
- getConfigLogger().debug(getAccountId(),
- "Already processed Notification Viewed event for " + extras.toString() + ", dropping duplicate.");
- return;
- }
-
- getConfigLogger().debug("Recording Notification Viewed event for notification: " + extras.toString());
-
- 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
- }
- queueEvent(context, event, Constants.NV_EVENT);
+ coreState.getAnalyticsManager().pushNotificationViewedEvent(extras);
}
/**
@@ -3380,16 +2051,7 @@ public void pushNotificationViewedEvent(Bundle extras) {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void pushProfile(final Map profile) {
- if (profile == null || profile.isEmpty()) {
- return;
- }
-
- postAsyncSafely("profilePush", new Runnable() {
- @Override
- public void run() {
- _push(profile);
- }
- });
+ coreState.getAnalyticsManager().pushProfile(profile);
}
/**
@@ -3403,7 +2065,7 @@ public void run() {
*/
@SuppressWarnings("unused")
public void pushXiaomiRegistrationId(String regId, boolean register) {
- pushProviders.handleToken(regId, PushType.XPS, register);
+ coreState.getPushProviders().handleToken(regId, PushType.XPS, register);
}
/**
@@ -3413,371 +2075,126 @@ public void pushXiaomiRegistrationId(String regId, boolean register) {
*/
@SuppressWarnings({"unused"})
public void recordScreen(String screenName) {
- if (screenName == null || (!currentScreenName.isEmpty() && currentScreenName.equals(screenName))) {
+ if (screenName == null || (!coreState.getCoreMetaData().getScreenName().isEmpty() && coreState
+ .getCoreMetaData().getScreenName().equals(screenName))) {
return;
}
getConfigLogger().debug(getAccountId(), "Screen changed to " + screenName);
- currentScreenName = screenName;
- recordPageEventWithExtras(null);
+ coreState.getCoreMetaData().setCurrentScreenName(screenName);
+ coreState.getAnalyticsManager().recordPageEventWithExtras(null);
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link Boolean} for ease of editing on the CleverTap Dashboard
+ * Remove a unique value from a multi-value user profile property
+ *
+ * If the key currently contains a scalar value, prior to performing the remove operation
+ * the key will be promoted to a multi-value property with the current value cast to a string.
+ * If the multi-value property is empty after the remove operation, the key will be removed.
*
- * @param name {@link String} the name of the variable
+ * @param key String
+ * @param value String
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerBooleanVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
+ @SuppressWarnings({"unused", "WeakerAccess"})
+ public void removeMultiValueForKey(String key, String value) {
+ if (value == null || value.isEmpty()) {
+ coreState.getAnalyticsManager()._generateEmptyMultiValueError(key);
return;
}
- ctABTestController.registerBooleanVariable(name);
+
+ removeMultiValuesForKey(key, new ArrayList<>(Collections.singletonList(value)));
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link Double} for ease of editing on the CleverTap Dashboard
+ * Remove a collection of unique values from a multi-value user profile property
+ *
+ * If the key currently contains a scalar value, prior to performing the remove operation
+ * the key will be promoted to a multi-value property with the current value cast to a string.
+ *
+ * If the multi-value property is empty after the remove operation, the key will be removed.
*
- * @param name {@link String} the name of the variable
+ * @param key String
+ * @param values {@link ArrayList} with String values
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerDoubleVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerDoubleVariable(name);
+ @SuppressWarnings({"unused", "WeakerAccess"})
+ public void removeMultiValuesForKey(final String key, final ArrayList values) {
+ coreState.getAnalyticsManager().removeMultiValuesForKey(key, values);
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link Integer} for ease of editing on the CleverTap Dashboard
+ * Remove the user profile property value specified by key from the user profile
*
- * @param name {@link String} the name of the variable
+ * @param key String
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerIntegerVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerIntegerVariable(name);
+ @SuppressWarnings({"unused", "WeakerAccess"})
+ public void removeValueForKey(final String key) {
+ coreState.getAnalyticsManager().removeValueForKey(key);
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link List} of {@link Boolean} for ease of editing on the CleverTap
- * Dashboard
+ * This method is used to set the CTFeatureFlagsListener
+ * Register to receive feature flag callbacks
*
- * @param name {@link String} the name of the variable
+ * @param featureFlagsListener The {@link CTFeatureFlagsListener} object
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerListOfBooleanVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerListOfBooleanVariable(name);
+ @SuppressWarnings("unused")
+ public void setCTFeatureFlagsListener(CTFeatureFlagsListener featureFlagsListener) {
+ coreState.getCallbackManager().setFeatureFlagListener(featureFlagsListener);
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link List} of {@link Double} for ease of editing on the CleverTap
- * Dashboard
+ * This method is used to set the product config listener
+ * Register to receive callbacks
*
- * @param name {@link String} the name of the variable
+ * @param listener The {@link CTProductConfigListener} instance
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerListOfDoubleVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerListOfDoubleVariable(name);
+ @SuppressWarnings("unused")
+ public void setCTProductConfigListener(CTProductConfigListener listener) {
+ coreState.getCallbackManager().setProductConfigListener(listener);
}
+ //Listener
+
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link List} of {@link Integer} for ease of editing on the CleverTap
- * Dashboard
+ * Sets the listener to get the list of currently running Display Campaigns via callback
*
- * @param name {@link String} the name of the variable
+ * @param listener- {@link DisplayUnitListener}
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerListOfIntegerVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerListOfIntegerVariable(name);
+ public void setDisplayUnitListener(DisplayUnitListener listener) {
+ coreState.getCallbackManager().setDisplayUnitListener(listener);
}
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link List} of {@link String} for ease of editing on the CleverTap
- * Dashboard
- *
- * @param name {@link String} the name of the variable
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerListOfStringVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerListOfStringVariable(name);
+ @SuppressWarnings("unused")
+ public void setInAppNotificationButtonListener(InAppNotificationButtonListener listener) {
+ coreState.getCallbackManager().setInAppNotificationButtonListener(listener);
}
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link Map} of {@link Boolean} for ease of editing on the CleverTap
- * Dashboard
- *
- * @param name {@link String} the name of the variable
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerMapOfBooleanVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerMapOfBooleanVariable(name);
+ @SuppressWarnings("unused")
+ public void setInboxMessageButtonListener(InboxMessageButtonListener listener) {
+ this.inboxMessageButtonListener = new WeakReference<>(listener);
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link Map} of {@link Double} for ease of editing on the CleverTap
- * Dashboard
+ * Not to be used by developers. This is used internally to help CleverTap know which library is wrapping the
+ * native SDK
*
- * @param name {@link String} the name of the variable
+ * @param library {@link String} library name
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerMapOfDoubleVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
+ public void setLibrary(String library) {
+ if (coreState.getDeviceInfo() != null) {
+ coreState.getDeviceInfo().setLibrary(library);
}
- ctABTestController.registerMapOfDoubleVariable(name);
}
/**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link Map} of {@link Integer} for ease of editing on the CleverTap
- * Dashboard
+ * Sets the location in CleverTap to get updated GeoFences
*
- * @param name {@link String} the name of the variable
+ * @param location android.location.Location
*/
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerMapOfIntegerVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerMapOfIntegerVariable(name);
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link Map} of {@link String} for ease of editing on the CleverTap
- * Dashboard
- *
- * @param name {@link String} the name of the variable
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerMapOfStringVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerMapOfStringVariable(name);
- }
-
- /**
- * Deprecation Notice - This method has been deprecated by CleverTap, this code will be removed from future
- * versions of the CleverTap Android SDK.
- *
- * Registers an ABTesting variable of type {@link String} for ease of editing on the CleverTap Dashboard
- *
- * @param name {@link String} the name of the variable
- */
- @RequiresApi(api = VERSION_CODES.KITKAT)
- @Deprecated
- public void registerStringVariable(String name) {
- if (ctABTestController == null) {
- getConfigLogger().verbose(getAccountId(), "ABTesting is not enabled for this instance");
- return;
- }
- ctABTestController.registerStringVariable(name);
- }
-
- @Override
- public ValidationResultStack remoteErrorLogger() {
- return validationResultStack;
- }
-
- //Session
-
- /**
- * Remove a unique value from a multi-value user profile property
- *
- * If the key currently contains a scalar value, prior to performing the remove operation
- * the key will be promoted to a multi-value property with the current value cast to a string.
- * If the multi-value property is empty after the remove operation, the key will be removed.
- *
- * @param key String
- * @param value String
- */
- @SuppressWarnings({"unused", "WeakerAccess"})
- public void removeMultiValueForKey(String key, String value) {
- if (value == null || value.isEmpty()) {
- _generateEmptyMultiValueError(key);
- return;
- }
-
- removeMultiValuesForKey(key, new ArrayList<>(Collections.singletonList(value)));
- }
-
- /**
- * Remove a collection of unique values from a multi-value user profile property
- *
- * If the key currently contains a scalar value, prior to performing the remove operation
- * the key will be promoted to a multi-value property with the current value cast to a string.
- *
- * If the multi-value property is empty after the remove operation, the key will be removed.
- *
- * @param key String
- * @param values {@link ArrayList} with String values
- */
- @SuppressWarnings({"unused", "WeakerAccess"})
- public void removeMultiValuesForKey(final String key, final ArrayList values) {
- postAsyncSafely("removeMultiValuesForKey", new Runnable() {
- @Override
- public void run() {
- _handleMultiValues(values, key, Constants.COMMAND_REMOVE);
- }
- });
- }
-
- /**
- * Remove the user profile property value specified by key from the user profile
- *
- * @param key String
- */
- @SuppressWarnings({"unused", "WeakerAccess"})
- public void removeValueForKey(final String key) {
- postAsyncSafely("removeValueForKey", new Runnable() {
- @Override
- public void run() {
- _removeValueForKey(key);
- }
- });
- }
-
- /**
- * This method is used to set the CTFeatureFlagsListener
- * Register to receive feature flag callbacks
- *
- * @param featureFlagsListener The {@link CTFeatureFlagsListener} object
- */
- @SuppressWarnings("unused")
- public void setCTFeatureFlagsListener(CTFeatureFlagsListener featureFlagsListener) {
- this.featureFlagsListener = new WeakReference<>(featureFlagsListener);
- }
-
- /**
- * This method is used to set the product config listener
- * Register to receive callbacks
- *
- * @param listener The {@link CTProductConfigListener} instance
- */
- @SuppressWarnings("unused")
- public void setCTProductConfigListener(CTProductConfigListener listener) {
- if (listener != null) {
- this.productConfigListener = new WeakReference<>(listener);
- }
- }
-
- /**
- * Sets the listener to get the list of currently running Display Campaigns via callback
- *
- * @param listener- {@link DisplayUnitListener}
- */
- public void setDisplayUnitListener(DisplayUnitListener listener) {
- if (listener != null) {
- displayUnitListenerWeakReference = new WeakReference<>(listener);
- } else {
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_DISPLAY_UNIT + "Failed to set - DisplayUnitListener can't be null");
- }
- }
-
- public void setInAppNotificationButtonListener(InAppNotificationButtonListener listener) {
- this.inAppNotificationButtonListener = new WeakReference<>(listener);
- }
-
- public void setInboxMessageButtonListener(InboxMessageButtonListener listener) {
- this.inboxMessageButtonListener = new WeakReference<>(listener);
- }
-
- /**
- * Not to be used by developers. This is used internally to help CleverTap know which library is wrapping the
- * native SDK
- *
- * @param library {@link String} library name
- */
- public void setLibrary(String library) {
- if (this.deviceInfo != null) {
- deviceInfo.setLibrary(library);
- }
- }
-
- //Listener
-
- /**
- * Sets the location in CleverTap to get updated GeoFences
- *
- * @param location android.location.Location
- */
- @SuppressWarnings("unused")
- public Future> setLocationForGeofences(Location location, int sdkVersion) {
- setLocationForGeofence(true);
- setGeofenceSDKVersion(sdkVersion);
- return _setLocation(location);
+ @SuppressWarnings("unused")
+ public Future> setLocationForGeofences(Location location, int sdkVersion) {
+ coreState.getCoreMetaData().setLocationForGeofence(true);
+ coreState.getCoreMetaData().setGeofenceSDKVersion(sdkVersion);
+ return coreState.getLocationManager()._setLocation(location);
}
/**
@@ -3791,4621 +2208,179 @@ public Future> setLocationForGeofences(Location location, int sdkVersion) {
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public void setMultiValuesForKey(final String key, final ArrayList values) {
- postAsyncSafely("setMultiValuesForKey", new Runnable() {
- @Override
- public void run() {
- _handleMultiValues(values, key, Constants.COMMAND_SET);
- }
- });
- }
-
- /**
- * Use this method to opt the current user out of all event/profile tracking.
- * You must call this method separately for each active user profile (e.g. when switching user profiles using
- * onUserLogin).
- * Once enabled, no events will be saved remotely or locally for the current user. To re-enable tracking call this
- * method with enabled set to false.
- *
- * @param userOptOut boolean Whether tracking opt out should be enabled/disabled.
- */
- @SuppressWarnings({"unused"})
- public void setOptOut(boolean userOptOut) {
- final boolean enable = userOptOut;
- postAsyncSafely("setOptOut", new Runnable() {
- @Override
- public void run() {
- // generate the data for a profile push to alert the server to the optOut state change
- HashMap optOutMap = new HashMap<>();
- optOutMap.put(Constants.CLEVERTAP_OPTOUT, enable);
-
- // determine order of operations depending on enabled/disabled
- if (enable) { // if opting out first push profile event then set the flag
- pushProfile(optOutMap);
- setCurrentUserOptedOut(true);
- } else { // if opting back in first reset the flag to false then push the profile event
- setCurrentUserOptedOut(false);
- pushProfile(optOutMap);
- }
- // persist the new optOut state
- String key = optOutKey();
- if (key == null) {
- getConfigLogger()
- .verbose(getAccountId(), "Unable to persist user OptOut state, storage key is null");
- return;
- }
- StorageHelper.putBoolean(context, StorageHelper.storageKeyWithSuffix(config, key), enable);
- getConfigLogger().verbose(getAccountId(), "Set current user OptOut state to: " + enable);
- }
- });
+ coreState.getAnalyticsManager().setMultiValuesForKey(key, values);
}
/**
- * Opens {@link CTInboxActivity} to display Inbox Messages
+ * If you want to stop recorded events from being sent to the server, use this method to set the SDK instance to
+ * offline.
+ * Once offline, events will be recorded and queued locally but will not be sent to the server until offline is
+ * disabled.
+ * Calling this method again with offline set to false will allow events to be sent to server and the SDK instance
+ * will immediately attempt to send events that have been queued while offline.
*
- * @param styleConfig {@link CTInboxStyleConfig} configuration of various style parameters for the {@link
- * CTInboxActivity}
- */
- @SuppressWarnings({"unused", "WeakerAccess"})
- public void showAppInbox(CTInboxStyleConfig styleConfig) {
- synchronized (inboxControllerLock) {
- if (ctInboxController == null) {
- getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
- return;
- }
- }
-
- // make styleConfig immutable
- final CTInboxStyleConfig _styleConfig = new CTInboxStyleConfig(styleConfig);
-
- Intent intent = new Intent(context, CTInboxActivity.class);
- intent.putExtra("styleConfig", _styleConfig);
- Bundle configBundle = new Bundle();
- configBundle.putParcelable("config", config);
- intent.putExtra("configBundle", configBundle);
- try {
- Activity currentActivity = getCurrentActivity();
- if (currentActivity == null) {
- throw new IllegalStateException("Current activity reference not found");
- }
- currentActivity.startActivity(intent);
- Logger.d("Displaying Notification Inbox");
-
- } catch (Throwable t) {
- Logger.v("Please verify the integration of your app." +
- " It is not setup to support Notification Inbox yet.", t);
- }
-
- }
-
- /**
- * Opens {@link CTInboxActivity} to display Inbox Messages with default {@link CTInboxStyleConfig} object
+ * @param value boolean, true sets the sdk offline, false sets the sdk back online
*/
@SuppressWarnings({"unused"})
- public void showAppInbox() {
- CTInboxStyleConfig styleConfig = new CTInboxStyleConfig();
- showAppInbox(styleConfig);
- }
-
- //To be called from DeviceInfo AdID GUID generation
- void deviceIDCreated(String deviceId) {
- Logger.v("Initializing InAppFC after Device ID Created = " + deviceId);
- this.inAppFCManager = new InAppFCManager(context, config, deviceId);
- Logger.v("Initializing ABTesting after Device ID Created = " + deviceId);
- initABTesting();
- initFeatureFlags(true);
- initProductConfig(true);
- getConfigLogger()
- .verbose("Got device id from DeviceInfo, notifying user profile initialized to SyncListener");
- notifyUserProfileInitialized(deviceId);
- }
-
- /**
- * Raises the Notification Clicked event, if {@param clicked} is true,
- * otherwise the Notification Viewed event, if {@param clicked} is false.
- *
- * @param clicked Whether or not this notification was clicked
- * @param data The data to be attached as the event data
- * @param customData Additional data such as form input to to be added to the event data
- */
- @SuppressWarnings({"unused", "WeakerAccess"})
- void pushInAppNotificationStateEvent(boolean clicked, CTInAppNotification data, Bundle customData) {
- JSONObject event = new JSONObject();
- try {
- JSONObject notif = getWzrkFields(data);
-
- if (customData != null) {
- for (String x : customData.keySet()) {
-
- Object value = customData.get(x);
- if (value != null) {
- notif.put(x, value);
- }
- }
- }
-
- if (clicked) {
- try {
- setWzrkParams(notif);
- } catch (Throwable t) {
- // no-op
- }
- event.put("evtName", Constants.NOTIFICATION_CLICKED_EVENT_NAME);
- } else {
- event.put("evtName", Constants.NOTIFICATION_VIEWED_EVENT_NAME);
- }
-
- event.put("evtData", notif);
- queueEvent(context, event, Constants.RAISED_EVENT);
- } catch (Throwable ignored) {
- // We won't get here
- }
- }
-
- /**
- * Raises the Notification Clicked event, if {@param clicked} is true,
- * otherwise the Notification Viewed event, if {@param clicked} is false.
- *
- * @param clicked Whether or not this notification was clicked
- * @param data The data to be attached as the event data
- * @param customData Additional data such as form input to to be added to the event data
- */
- @SuppressWarnings({"unused", "WeakerAccess"})
- void pushInboxMessageStateEvent(boolean clicked, CTInboxMessage data, Bundle customData) {
- JSONObject event = new JSONObject();
- try {
- JSONObject notif = getWzrkFields(data);
-
- if (customData != null) {
- for (String x : customData.keySet()) {
-
- Object value = customData.get(x);
- if (value != null) {
- notif.put(x, value);
- }
- }
- }
-
- if (clicked) {
- try {
- setWzrkParams(notif);
- } catch (Throwable t) {
- // no-op
- }
- event.put("evtName", Constants.NOTIFICATION_CLICKED_EVENT_NAME);
- } else {
- event.put("evtName", Constants.NOTIFICATION_VIEWED_EVENT_NAME);
- }
-
- event.put("evtData", notif);
- queueEvent(context, event, Constants.RAISED_EVENT);
- } catch (Throwable ignored) {
- // We won't get here
- }
- }
-
- private JSONArray _cleanMultiValues(ArrayList values, String key) {
-
- try {
- if (values == null || key == null) {
- return null;
- }
-
- JSONArray cleanedValues = new JSONArray();
- ValidationResult vr;
-
- // loop through and clean the new values
- for (String value : values) {
- value = (value == null) ? "" : value; // so we will generate a validation error later on
-
- // validate value
- vr = validator.cleanMultiValuePropertyValue(value);
-
- // Check for an error
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
-
- // reset the value
- Object _value = vr.getObject();
- value = (_value != null) ? vr.getObject().toString() : null;
-
- // if value is empty generate an error and return
- if (value == null || value.isEmpty()) {
- _generateEmptyMultiValueError(key);
- // Abort
- return null;
- }
- // add to the newValues to be merged
- cleanedValues.put(value);
- }
-
- return cleanedValues;
-
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Error cleaning multi values for key " + key, t);
- _generateEmptyMultiValueError(key);
- return null;
- }
- }
-
- private JSONArray _constructExistingMultiValue(String key, String command) {
-
- boolean remove = command.equals(Constants.COMMAND_REMOVE);
- boolean add = command.equals(Constants.COMMAND_ADD);
-
- // only relevant for add's and remove's; a set overrides the existing value, so return a new array
- if (!remove && !add) {
- return new JSONArray();
- }
-
- Object existing = _getProfilePropertyIgnorePersonalizationFlag(key);
-
- // if there is no existing value
- if (existing == null) {
- // if its a remove then return null to abort operation
- // no point in running remove against a nonexistent value
- if (remove) {
- return null;
- }
-
- // otherwise return an empty array
- return new JSONArray();
- }
-
- // value exists
-
- // the value should only ever be a JSONArray or scalar (String really)
-
- // if its already a JSONArray return that
- if (existing instanceof JSONArray) {
- return (JSONArray) existing;
- }
-
- // handle a scalar value as the existing value
- /*
- if its an add, our rule is to promote the scalar value to multi value and include the cleaned stringified
- scalar value as the first element of the resulting array
-
- NOTE: the existing scalar value is currently limited to 120 bytes; when adding it to a multi value
- it is subject to the current 40 byte limit
-
- if its a remove, our rule is to delete the key from the local copy
- if the cleaned stringified existing value is equal to any of the cleaned values passed to the remove method
-
- if its an add, return an empty array as the default,
- in the event the existing scalar value fails stringifying/cleaning
-
- returning null will signal that a remove operation should be aborted,
- as there is no valid promoted multi value to remove against
- */
-
- JSONArray _default = (add) ? new JSONArray() : null;
-
- String stringified = _stringifyAndCleanScalarProfilePropValue(existing);
-
- return (stringified != null) ? new JSONArray().put(stringified) : _default;
- }
-
- /**
- * Launches an asynchronous task to download the notification icon from CleverTap,
- * and create the Android notification.
- *
- * If your app is using CleverTap SDK's built in FCM message handling,
- * this method does not need to be called explicitly.
- *
- * Use this method when implementing your own FCM handling mechanism. Refer to the
- * SDK documentation for usage scenarios and examples.
- *
- * @param context A reference to an Android context
- * @param extras The {@link Bundle} object received by the broadcast receiver
- * @param notificationId A custom id to build a notification
- */
- private void _createNotification(final Context context, final Bundle extras, final int notificationId) {
- if (extras == null || extras.get(Constants.NOTIFICATION_TAG) == null) {
- return;
- }
-
- if (config.isAnalyticsOnly()) {
- getConfigLogger().debug(getAccountId(), "Instance is set for Analytics only, cannot create notification");
- return;
- }
-
- try {
- postAsyncSafely("CleverTapAPI#_createNotification", new Runnable() {
- @Override
- public void run() {
- try {
- getConfigLogger().debug(getAccountId(), "Handling notification: " + extras.toString());
- dbAdapter = loadDBAdapter(context);
- if (extras.getString(Constants.WZRK_PUSH_ID) != null) {
- if (dbAdapter.doesPushNotificationIdExist(extras.getString(Constants.WZRK_PUSH_ID))) {
- getConfigLogger().debug(getAccountId(),
- "Push Notification already rendered, not showing again");
- return;
- }
- }
- String notifMessage = extras.getString(Constants.NOTIF_MSG);
- notifMessage = (notifMessage != null) ? notifMessage : "";
- if (notifMessage.isEmpty()) {
- //silent notification
- getConfigLogger()
- .verbose(getAccountId(), "Push notification message is empty, not rendering");
- loadDBAdapter(context).storeUninstallTimestamp();
- String pingFreq = extras.getString("pf", "");
- if (!TextUtils.isEmpty(pingFreq)) {
- updatePingFrequencyIfNeeded(context, Integer.parseInt(pingFreq));
- }
- return;
- }
- String notifTitle = extras.getString(Constants.NOTIF_TITLE, "");
- notifTitle = notifTitle.isEmpty() ? context.getApplicationInfo().name : notifTitle;
- triggerNotification(context, extras, notifMessage, notifTitle, notificationId);
- } catch (Throwable t) {
- // Occurs if the notification image was null
- // Let's return, as we couldn't get a handle on the app's icon
- // Some devices throw a PackageManager* exception too
- getConfigLogger().debug(getAccountId(), "Couldn't render notification: ", t);
- }
- }
- });
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Failed to process push notification", t);
- }
- }
-
- private void _generateEmptyMultiValueError(String key) {
- ValidationResult error = ValidationResultFactory.create(512, Constants.INVALID_MULTI_VALUE, key);
- validationResultStack.pushValidationResult(error);
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- }
-
- private void _generateInvalidMultiValueKeyError(String key) {
- ValidationResult error = ValidationResultFactory.create(523, Constants.INVALID_MULTI_VALUE_KEY, key);
- validationResultStack.pushValidationResult(error);
- getConfigLogger().debug(getAccountId(),
- "Invalid multi-value property key " + key + " profile multi value operation aborted");
- }
-
- @SuppressLint("MissingPermission")
- private Location _getLocation() {
- try {
- LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- if (lm == null) {
- Logger.d("Location Manager is null.");
- return null;
- }
- List providers = lm.getProviders(true);
- Location bestLocation = null;
- Location l = null;
- for (String provider : providers) {
- try {
- l = lm.getLastKnownLocation(provider);
- } catch (SecurityException e) {
- //no-op
- Logger.v("Location security exception", e);
- }
-
- if (l == null) {
- continue;
- }
- if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
- bestLocation = l;
- }
- }
-
- return bestLocation;
- } catch (Throwable t) {
- Logger.v("Couldn't get user's location", t);
- return null;
- }
- }
-
- // use for internal profile getter doesn't do the personalization check
- private Object _getProfilePropertyIgnorePersonalizationFlag(String key) {
- return getLocalDataStore().getProfileValueForKey(key);
- }
-
- private void _handleMultiValues(ArrayList values, String key, String command) {
- if (key == null) {
- return;
- }
-
- if (values == null || values.isEmpty()) {
- _generateEmptyMultiValueError(key);
- return;
- }
-
- ValidationResult vr;
-
- // validate the key
- vr = validator.cleanMultiValuePropertyKey(key);
-
- // Check for an error
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
-
- // reset the key
- Object _key = vr.getObject();
- String cleanKey = (_key != null) ? vr.getObject().toString() : null;
-
- // if key is empty generate an error and return
- if (cleanKey == null || cleanKey.isEmpty()) {
- _generateInvalidMultiValueKeyError(key);
- return;
- }
-
- key = cleanKey;
-
- try {
- JSONArray currentValues = _constructExistingMultiValue(key, command);
- JSONArray newValues = _cleanMultiValues(values, key);
- _validateAndPushMultiValue(currentValues, newValues, values, key, command);
-
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Error handling multi value operation for key " + key, t);
- }
- }
-
- // always call async
- private void _initializeInbox() {
- synchronized (inboxControllerLock) {
- if (this.ctInboxController != null) {
- _notifyInboxInitialized();
- return;
- }
- if (getCleverTapID() != null) {
- this.ctInboxController = new CTInboxController(getCleverTapID(), loadDBAdapter(context),
- haveVideoPlayerSupport);
- _notifyInboxInitialized();
- } else {
- getConfigLogger().info("CRITICAL : No device ID found!");
- }
- }
- }
-
- private void _notifyInboxInitialized() {
- if (this.inboxListener != null) {
- this.inboxListener.inboxDidInitialize();
- }
- }
-
- private void _notifyInboxMessagesDidUpdate() {
- if (this.inboxListener != null) {
- Utils.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (CleverTapAPI.this.inboxListener != null) {
- CleverTapAPI.this.inboxListener.inboxMessagesDidUpdate();
- }
- }
- });
- }
- }
-
- private void _onUserLogin(final Map profile, final String cleverTapID) {
- if (profile == null) {
- return;
- }
-
- try {
- final String currentGUID = getCleverTapID();
- if (currentGUID == null) {
- return;
- }
-
- boolean haveIdentifier = false;
- LoginInfoProvider loginInfoProvider = new LoginInfoProvider(this);
- // check for valid identifier keys
- // use the first one we find
- IdentityRepo iProfileHandler = IdentityRepoFactory.getRepo(this);
- for (String key : profile.keySet()) {
- Object value = profile.get(key);
- boolean isProfileKey = iProfileHandler.hasIdentity(key);
- if (isProfileKey) {
- try {
- String identifier = null;
- if (value != null) {
- identifier = value.toString();
- }
- if (identifier != null && identifier.length() > 0) {
- haveIdentifier = true;
- cachedGUID = loginInfoProvider.getGUIDForIdentifier(key, identifier);
- if (cachedGUID != null) {
- break;
- }
- }
- } catch (Throwable t) {
- // no-op
- }
- }
- }
-
- // if no valid identifier provided or there are no identified users on the device; just push on the current profile
- if (!isErrorDeviceId()) {
- if (!haveIdentifier || loginInfoProvider.isAnonymousDevice()) {
- getConfigLogger().debug(getAccountId(),
- "onUserLogin: no identifier provided or device is anonymous, pushing on current user profile");
- pushProfile(profile);
- return;
- }
- }
-
- // if identifier maps to current guid, push on current profile
- if (cachedGUID != null && cachedGUID.equals(currentGUID)) {
- getConfigLogger().debug(getAccountId(),
- "onUserLogin: " + profile.toString() + " maps to current device id " + currentGUID
- + " pushing on current profile");
- pushProfile(profile);
- return;
- }
-
- // stringify profile to use as dupe blocker
- String profileToString = profile.toString();
-
- // as processing happens async block concurrent onUserLogin requests with the same profile, as our cache is set async
- if (isProcessUserLoginWithIdentifier(profileToString)) {
- getConfigLogger().debug(getAccountId(), "Already processing onUserLogin for " + profileToString);
- return;
- }
-
- // create new guid if necessary and reset
- // block any concurrent onUserLogin call for the same profile
- synchronized (processingUserLoginLock) {
- processingUserLoginIdentifier = profileToString;
- }
-
- getConfigLogger().verbose(getAccountId(), "onUserLogin: queuing reset profile for " + profileToString
- + " with Cached GUID " + ((cachedGUID != null) ? cachedGUID : "NULL"));
-
- asyncProfileSwitchUser(profile, cachedGUID, cleverTapID);
-
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "onUserLogin failed", t);
- }
- }
-
- // always call async
- private void _processInboxMessages(JSONArray messages) {
- synchronized (inboxControllerLock) {
- if (this.ctInboxController == null) {
- _initializeInbox();
- }
- if (this.ctInboxController != null) {
- boolean update = this.ctInboxController.updateMessages(messages);
- if (update) {
- _notifyInboxMessagesDidUpdate();
- }
- }
- }
- }
-
- private void _push(Map profile) {
- if (profile == null || profile.isEmpty()) {
- return;
- }
-
- try {
- ValidationResult vr;
- JSONObject customProfile = new JSONObject();
- JSONObject fieldsToUpdateLocally = new JSONObject();
- for (String key : profile.keySet()) {
- Object value = profile.get(key);
-
- vr = validator.cleanObjectKey(key);
- key = vr.getObject().toString();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
-
- if (key.isEmpty()) {
- ValidationResult keyError = ValidationResultFactory.create(512, Constants.PUSH_KEY_EMPTY);
- validationResultStack.pushValidationResult(keyError);
- getConfigLogger().debug(getAccountId(), keyError.getErrorDesc());
- // Skip this property
- continue;
- }
-
- try {
- vr = validator.cleanObjectValue(value, Validator.ValidationContext.Profile);
- } catch (Throwable e) {
- // The object was neither a String, Boolean, or any number primitives
- ValidationResult error = ValidationResultFactory.create(512,
- Constants.OBJECT_VALUE_NOT_PRIMITIVE_PROFILE,
- value != null ? value.toString() : "", key);
- validationResultStack.pushValidationResult(error);
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- // Skip this property
- continue;
- }
- value = vr.getObject();
- // Check for an error
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
-
- // test Phone: if no device country code, test if phone starts with +, log but always send
- if (key.equalsIgnoreCase("Phone")) {
- try {
- value = value.toString();
- String countryCode = this.deviceInfo.getCountryCode();
- if (countryCode == null || countryCode.isEmpty()) {
- String _value = (String) value;
- if (!_value.startsWith("+")) {
- ValidationResult error = ValidationResultFactory
- .create(512, Constants.INVALID_COUNTRY_CODE, _value);
- validationResultStack.pushValidationResult(error);
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- }
- }
- getConfigLogger().verbose(getAccountId(),
- "Profile phone is: " + value + " device country code is: " + ((countryCode != null)
- ? countryCode : "null"));
- } catch (Exception e) {
- validationResultStack
- .pushValidationResult(ValidationResultFactory.create(512, Constants.INVALID_PHONE));
- getConfigLogger().debug(getAccountId(), "Invalid phone number: " + e.getLocalizedMessage());
- continue;
- }
- }
-
- // add to the local profile update object
- fieldsToUpdateLocally.put(key, value);
- customProfile.put(key, value);
- }
-
- getConfigLogger().verbose(getAccountId(), "Constructed custom profile: " + customProfile.toString());
-
- // update local profile values
- if (fieldsToUpdateLocally.length() > 0) {
- getLocalDataStore().setProfileFields(fieldsToUpdateLocally);
- }
-
- pushBasicProfile(customProfile);
-
- } catch (Throwable t) {
- // Will not happen
- getConfigLogger().verbose(getAccountId(), "Failed to push profile", t);
- }
- }
-
- @SuppressWarnings("ConstantConditions")
- private void _pushFacebookUser(JSONObject graphUser) {
- try {
- if (graphUser == null) {
- return;
- }
- // Note: No validations are required here, as everything is controlled
- String name = getGraphUserPropertySafely(graphUser, "name", "");
- try {
- // Certain users have nasty looking names - unicode chars, validate for any
- // not allowed chars
- ValidationResult vr = validator.cleanObjectValue(name, Validator.ValidationContext.Profile);
- name = vr.getObject().toString();
-
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
- } catch (IllegalArgumentException e) {
- // Weird name, wasn't a string, or any number
- // This would never happen with FB
- name = "";
- }
-
- String gender = getGraphUserPropertySafely(graphUser, "gender", null);
- // Convert to WR format
- if (gender != null) {
- if (gender.toLowerCase().startsWith("m")) {
- gender = "M";
- } else if (gender.toLowerCase().startsWith("f")) {
- gender = "F";
- } else {
- gender = "";
- }
- } else {
- gender = null;
- }
- String email = getGraphUserPropertySafely(graphUser, "email", "");
-
- String birthday = getGraphUserPropertySafely(graphUser, "birthday", null);
- if (birthday != null) {
- // Some users don't have the year of birth mentioned
- // FB returns only MM/dd in those cases
- if (birthday.matches("^../..")) {
- // This means that the year is not available(~30% of the times)
- // Ignore
- birthday = "";
- } else {
- try {
- Date date = Constants.FB_DOB_DATE_FORMAT.parse(birthday);
- birthday = "$D_" + (int) (date.getTime() / 1000);
- } catch (ParseException e) {
- // Differs from the specs
- birthday = "";
- }
- }
- }
-
- String work;
- try {
- JSONArray workArray = graphUser.getJSONArray("work");
- work = (workArray.length() > 0) ? "Y" : "N";
- } catch (Throwable t) {
- work = null;
- }
-
- String education;
- try {
- JSONArray eduArray = graphUser.getJSONArray("education");
- // FB returns the education levels in a descending order - highest = last entry
- String fbEdu = eduArray.getJSONObject(eduArray.length() - 1).getString("type");
- if (fbEdu.toLowerCase().contains("high school")) {
- education = "School";
- } else if (fbEdu.toLowerCase().contains("college")) {
- education = "College";
- } else if (fbEdu.toLowerCase().contains("graduate school")) {
- education = "Graduate";
- } else {
- education = "";
- }
- } catch (Throwable t) {
- // No education info available
- education = null;
- }
-
- String id = getGraphUserPropertySafely(graphUser, "id", "");
-
- String married = getGraphUserPropertySafely(graphUser, "relationship_status", null);
- if (married != null) {
- if (married.equalsIgnoreCase("married")) {
- married = "Y";
- } else {
- married = "N";
- }
- }
-
- final JSONObject profile = new JSONObject();
- if (id != null && id.length() > 3) {
- profile.put("FBID", id);
- }
- if (name != null && name.length() > 3) {
- profile.put("Name", name);
- }
- if (email != null && email.length() > 3) {
- profile.put("Email", email);
- }
- if (gender != null && !gender.trim().equals("")) {
- profile.put("Gender", gender);
- }
- if (education != null && !education.trim().equals("")) {
- profile.put("Education", education);
- }
- if (work != null && !work.trim().equals("")) {
- profile.put("Employed", work);
- }
- if (birthday != null && birthday.length() > 3) {
- profile.put("DOB", birthday);
- }
- if (married != null && !married.trim().equals("")) {
- profile.put("Married", married);
- }
-
- pushBasicProfile(profile);
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to parse graph user object successfully", t);
- }
- }
-
- //Session
-
- private void _removeValueForKey(String key) {
- try {
- key = (key == null) ? "" : key; // so we will generate a validation error later on
-
- // validate the key
- ValidationResult vr;
-
- vr = validator.cleanObjectKey(key);
- key = vr.getObject().toString();
-
- if (key.isEmpty()) {
- ValidationResult error = ValidationResultFactory.create(512, Constants.KEY_EMPTY);
- validationResultStack.pushValidationResult(error);
- getConfigLogger().debug(getAccountId(), error.getErrorDesc());
- // Abort
- return;
- }
- // Check for an error
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
-
- // remove from the local profile
- getLocalDataStore().removeProfileField(key);
-
- // send the delete command
- JSONObject command = new JSONObject().put(Constants.COMMAND_DELETE, true);
- JSONObject update = new JSONObject().put(key, command);
- pushBasicProfile(update);
-
- getConfigLogger().verbose(getAccountId(), "removing value for key " + key + " from user profile");
-
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to remove profile value for key " + key, t);
- }
- }
-
- private Future> _setLocation(Location location) {
- if (location == null) {
- return null;
- }
-
- locationFromUser = location;
- getConfigLogger().verbose(getAccountId(),
- "Location updated (" + location.getLatitude() + ", " + location.getLongitude() + ")");
-
- // only queue the location ping if we are in the foreground
- if (!isLocationForGeofence() && !isAppForeground()) {
- return null;
- }
-
- // Queue the ping event to transmit location update to server
- // min 10 second interval between location pings
- final int now = (int) (System.currentTimeMillis() / 1000);
- Future> future = null;
-
- if (isLocationForGeofence() && now > (lastLocationPingTimeForGeofence
- + Constants.LOCATION_PING_INTERVAL_IN_SECONDS)) {
- future = queueEvent(context, new JSONObject(), Constants.PING_EVENT);
- lastLocationPingTimeForGeofence = now;
- getConfigLogger().verbose(getAccountId(),
- "Queuing location ping event for geofence location (" + location.getLatitude() + ", " + location
- .getLongitude() + ")");
- } else if (!isLocationForGeofence() && now > (lastLocationPingTime
- + Constants.LOCATION_PING_INTERVAL_IN_SECONDS)) {
- future = queueEvent(context, new JSONObject(), Constants.PING_EVENT);
- lastLocationPingTime = now;
- getConfigLogger().verbose(getAccountId(),
- "Queuing location ping event for location (" + location.getLatitude() + ", " + location
- .getLongitude() + ")");
- }
-
- return future;
- }
-
- //InApp
- private void _showNotificationIfAvailable(Context context) {
- SharedPreferences prefs = StorageHelper.getPreferences(context);
- try {
- if (!canShowInAppOnActivity()) {
- Logger.v("Not showing notification on blacklisted activity");
- return;
- }
-
- checkPendingNotifications(context, config); // see if we have any pending notifications
-
- JSONArray inapps = new JSONArray(
- StorageHelper.getStringFromPrefs(context, config, Constants.INAPP_KEY, "[]"));
- if (inapps.length() < 1) {
- return;
- }
-
- JSONObject inapp = inapps.getJSONObject(0);
- prepareNotificationForDisplay(inapp);
-
- // JSON array doesn't have the feature to remove a single element,
- // so we have to copy over the entire array, but the first element
- JSONArray inappsUpdated = new JSONArray();
- for (int i = 0; i < inapps.length(); i++) {
- if (i == 0) {
- continue;
- }
- inappsUpdated.put(inapps.get(i));
- }
- SharedPreferences.Editor editor = prefs.edit()
- .putString(StorageHelper.storageKeyWithSuffix(config, Constants.INAPP_KEY),
- inappsUpdated.toString());
- StorageHelper.persist(editor);
- } catch (Throwable t) {
- // We won't get here
- getConfigLogger().verbose(getAccountId(), "InApp: Couldn't parse JSON array string from prefs", t);
- }
- }
-
- private String _stringifyAndCleanScalarProfilePropValue(Object value) {
- String val = CTJsonConverter.toJsonString(value);
-
- if (val != null) {
- ValidationResult vr = validator.cleanMultiValuePropertyValue(val);
-
- // Check for an error
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
-
- Object _value = vr.getObject();
- val = (_value != null) ? vr.getObject().toString() : null;
- }
-
- return val;
- }
-
- private void _validateAndPushMultiValue(JSONArray currentValues, JSONArray newValues,
- ArrayList originalValues, String key, String command) {
-
- try {
-
- // if any of these are null, indicates some problem along the way so abort operation
- if (currentValues == null || newValues == null || originalValues == null || key == null
- || command == null) {
- return;
- }
-
- String mergeOperation = command.equals(Constants.COMMAND_REMOVE) ? Validator.REMOVE_VALUES_OPERATION
- : Validator.ADD_VALUES_OPERATION;
-
- // merge currentValues and newValues
- ValidationResult vr = validator
- .mergeMultiValuePropertyForKey(currentValues, newValues, mergeOperation, key);
-
- // Check for an error
- if (vr.getErrorCode() != 0) {
- validationResultStack.pushValidationResult(vr);
- }
-
- // set the merged local values array
- JSONArray localValues = (JSONArray) vr.getObject();
-
- // update local profile
- // remove an empty array
- if (localValues == null || localValues.length() <= 0) {
- getLocalDataStore().removeProfileField(key);
- } else {
- // not empty so save to local profile
- getLocalDataStore().setProfileField(key, localValues);
- }
-
- // push to server
- JSONObject commandObj = new JSONObject();
- commandObj.put(command, new JSONArray(originalValues));
-
- JSONObject fields = new JSONObject();
- fields.put(key, commandObj);
-
- pushBasicProfile(fields);
-
- getConfigLogger().verbose(getAccountId(), "Constructed multi-value profile push: " + fields.toString());
-
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Error pushing multiValue for key " + key, t);
- }
- }
-
- //Profile
-
- //Lifecycle
- private void activityPaused() {
- setAppForeground(false);
- appLastSeen = System.currentTimeMillis();
- getConfigLogger().verbose(getAccountId(), "App in background");
- final int now = (int) (System.currentTimeMillis() / 1000);
- if (inCurrentSession()) {
- try {
- StorageHelper
- .putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.LAST_SESSION_EPOCH),
- now);
- getConfigLogger().verbose(getAccountId(), "Updated session time: " + now);
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to update session time time: " + t.getMessage());
- }
- }
- }
-
- //Lifecycle
- private void activityResumed(Activity activity) {
- getConfigLogger().verbose(getAccountId(), "App in foreground");
- checkTimeoutSession();
- //Anything in this If block will run once per App Launch.
- //Will not run for Apps which disable App Launched event
- if (!isAppLaunchPushed()) {
- pushAppLaunchedEvent();
- fetchFeatureFlags();
- onTokenRefresh();
- postAsyncSafely("HandlingInstallReferrer", new Runnable() {
- @Override
- public void run() {
- if (!installReferrerDataSent && isFirstSession()) {
- handleInstallReferrerOnFirstInstall();
- }
- }
- });
-
- try {
- if (geofenceCallback != null) {
- geofenceCallback.triggerLocation();
- }
- } catch (IllegalStateException e) {
- getConfigLogger().verbose(getAccountId(), e.getLocalizedMessage());
- } catch (Exception e) {
- getConfigLogger().verbose(getAccountId(), "Failed to trigger location");
- }
- }
- if (!inCurrentSession()) {
- pushInitialEventsAsync();
- }
- checkExistingInAppNotifications(activity);
- checkPendingInAppNotifications(activity);
- }
-
- /**
- * Adds a new event to the queue, to be sent later.
- *
- * @param context The Android context
- * @param event The event to be queued
- * @param eventType The type of event to be queued
- */
-
- // only call async
- private void addToQueue(final Context context, final JSONObject event, final int eventType) {
- if (eventType == Constants.NV_EVENT) {
- getConfigLogger().verbose(getAccountId(), "Pushing Notification Viewed event onto separate queue");
- processPushNotificationViewedEvent(context, event);
- } else {
- processEvent(context, event, eventType);
- }
- }
-
- private void asyncProfileSwitchUser(final Map profile, final String cacheGuid,
- final String cleverTapID) {
- postAsyncSafely("resetProfile", new Runnable() {
- @Override
- public void run() {
- try {
- getConfigLogger().verbose(getAccountId(), "asyncProfileSwitchUser:[profile " + profile
- + " with Cached GUID " + ((cacheGuid != null) ? cachedGUID
- : "NULL" + " and cleverTapID " + cleverTapID));
- //set optOut to false on the current user to unregister the device token
- setCurrentUserOptedOut(false);
- // unregister the device token on the current user
- forcePushDeviceToken(false);
-
- // try and flush and then reset the queues
- flushQueueSync(context, EventGroup.REGULAR);
- flushQueueSync(context, EventGroup.PUSH_NOTIFICATION_VIEWED);
- clearQueues(context);
-
- // clear out the old data
- getLocalDataStore().changeUser();
- activityCount = 1;
- destroySession();
-
- // either force restore the cached GUID or generate a new one
- if (cacheGuid != null) {
- deviceInfo.forceUpdateDeviceId(cacheGuid);
- notifyUserProfileInitialized(cacheGuid);
- } else if (getConfig().getEnableCustomCleverTapId()) {
- deviceInfo.forceUpdateCustomCleverTapID(cleverTapID);
- } else {
- deviceInfo.forceNewDeviceID();
- }
- notifyUserProfileInitialized(getCleverTapID());
- setCurrentUserOptOutStateFromStorage(); // be sure to call this after the guid is updated
- forcePushAppLaunchedEvent();
- if (profile != null) {
- pushProfile(profile);
- }
- forcePushDeviceToken(true);
- synchronized (processingUserLoginLock) {
- processingUserLoginIdentifier = null;
- }
- resetInbox();
- resetABTesting();
- resetFeatureFlags();
- resetProductConfigs();
- recordDeviceIDErrors();
- resetDisplayUnits();
- inAppFCManager.changeUser(getCleverTapID());
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Reset Profile error", t);
- }
- }
- });
- }
-
- /**
- * Attaches meta info about the current state of the device to an event.
- * Typically, this meta is added only to the ping event.
- */
- private void attachMeta(final JSONObject o, final Context context) {
- // Memory consumption
- try {
- o.put("mc", Utils.getMemoryConsumption());
- } catch (Throwable t) {
- // Ignore
- }
-
- // Attach the network type
- try {
- o.put("nt", Utils.getCurrentNetworkType(context));
- } catch (Throwable t) {
- // Ignore
- }
- }
-
- //Session
- private void attachPackageNameIfRequired(final Context context, final JSONObject event) {
- try {
- final String type = event.getString("type");
- // Send it only for app launched events
- if ("event".equals(type) && Constants.APP_LAUNCHED_EVENT.equals(event.getString("evtName"))) {
- event.put("pai", context.getPackageName());
- }
- } catch (Throwable t) {
- // Ignore
- }
- }
-
- private HttpsURLConnection buildHttpsURLConnection(final String endpoint)
- throws IOException {
-
- URL url = new URL(endpoint);
- HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
- conn.setConnectTimeout(10000);
- conn.setReadTimeout(10000);
- conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
- conn.setRequestProperty("X-CleverTap-Account-ID", getAccountId());
- conn.setRequestProperty("X-CleverTap-Token", this.config.getAccountToken());
- conn.setInstanceFollowRedirects(false);
- if (this.config.isSslPinningEnabled()) {
- SSLContext _sslContext = getSSLContext();
- if (_sslContext != null) {
- conn.setSSLSocketFactory(getPinnedCertsSslSocketfactory(_sslContext));
- }
- }
- return conn;
- }
-
- private boolean canShowInAppOnActivity() {
- updateBlacklistedActivitySet();
-
- for (String blacklistedActivity : inappActivityExclude) {
- String currentActivityName = getCurrentActivityName();
- if (currentActivityName != null && currentActivityName.contains(blacklistedActivity)) {
- return false;
- }
- }
-
- return true;
- }
-
- private boolean checkDuplicateNotificationIds(Bundle extras, HashMap notificationTagMap,
- int interval) {
- synchronized (notificationMapLock) {
- // default to false; only return true if we are sure we've seen this one before
- boolean isDupe = false;
- try {
- String notificationIdTag = extras.getString(Constants.NOTIFICATION_ID_TAG);
- long now = System.currentTimeMillis();
- if (notificationTagMap.containsKey(notificationIdTag)) {
- long timestamp;
- // noinspection ConstantConditions
- timestamp = (Long) notificationTagMap.get(notificationIdTag);
- // same notificationId within time internal treat as dupe
- if (now - timestamp < interval) {
- isDupe = true;
- }
- }
- notificationTagMap.put(notificationIdTag, now);
- } catch (Throwable ignored) {
- // no-op
- }
- return isDupe;
- }
- }
-
- private void checkExistingInAppNotifications(Activity activity) {
- final boolean canShow = canShowInAppOnActivity();
- if (canShow) {
- if (currentlyDisplayingInApp != null && ((System.currentTimeMillis() / 1000) < currentlyDisplayingInApp
- .getTimeToLive())) {
- Fragment inAppFragment = ((FragmentActivity) activity).getSupportFragmentManager()
- .getFragment(new Bundle(), currentlyDisplayingInApp.getType());
- if (getCurrentActivity() != null && inAppFragment != null) {
- FragmentTransaction fragmentTransaction = ((FragmentActivity) activity)
- .getSupportFragmentManager()
- .beginTransaction();
- Bundle bundle = new Bundle();
- bundle.putParcelable("inApp", currentlyDisplayingInApp);
- bundle.putParcelable("config", config);
- inAppFragment.setArguments(bundle);
- fragmentTransaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
- fragmentTransaction.add(android.R.id.content, inAppFragment, currentlyDisplayingInApp.getType());
- Logger.v(config.getAccountId(),
- "calling InAppFragment " + currentlyDisplayingInApp.getCampaignId());
- fragmentTransaction.commit();
- }
- }
- }
- }
-
- private void checkPendingInAppNotifications(Activity activity) {
- final boolean canShow = canShowInAppOnActivity();
- if (canShow) {
- if (pendingInappRunnable != null) {
- getConfigLogger().verbose(getAccountId(), "Found a pending inapp runnable. Scheduling it");
- getHandlerUsingMainLooper().postDelayed(pendingInappRunnable, 200);
- pendingInappRunnable = null;
- } else {
- showNotificationIfAvailable(context);
- }
- } else {
- Logger.d("In-app notifications will not be shown for this activity ("
- + (activity != null ? activity.getLocalClassName() : "") + ")");
- }
- }
-
- // private multi-value handlers and helpers
-
- // SessionManager/session management
- private void checkTimeoutSession() {
- if (appLastSeen <= 0) {
- return;
- }
- long now = System.currentTimeMillis();
- if ((now - appLastSeen) > Constants.SESSION_LENGTH_MINS * 60 * 1000) {
- getConfigLogger().verbose(getAccountId(), "Session Timed Out");
- destroySession();
- setCurrentActivity(null);
- }
- }
-
- private synchronized void clearCampaign() {
- campaign = null;
- }
-
- //Session
- private void clearFirstRequestTimestampIfNeeded(Context context) {
- StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_FIRST_TS), 0);
- }
-
- //Session
- private void clearIJ(Context context) {
- final SharedPreferences prefs = StorageHelper.getPreferences(context, Constants.NAMESPACE_IJ);
- final SharedPreferences.Editor editor = prefs.edit();
- editor.clear();
- StorageHelper.persist(editor);
- }
-
- //Session
- private void clearLastRequestTimestamp(Context context) {
- StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_LAST_TS), 0);
- }
-
- private synchronized void clearMedium() {
- medium = null;
- }
-
- /**
- * Only call async
- */
- private void clearQueues(final Context context) {
- synchronized (eventLock) {
-
- DBAdapter adapter = loadDBAdapter(context);
- DBAdapter.Table tableName = DBAdapter.Table.EVENTS;
-
- adapter.removeEvents(tableName);
- tableName = DBAdapter.Table.PROFILE_EVENTS;
- adapter.removeEvents(tableName);
-
- clearUserContext(context);
- }
- }
-
- private synchronized void clearSource() {
- source = null;
- }
-
- //Session
- private void clearUserContext(final Context context) {
- clearIJ(context);
- clearFirstRequestTimestampIfNeeded(context);
- clearLastRequestTimestamp(context);
- }
-
- private synchronized void clearWzrkParams() {
- wzrkParams = null;
- }
-
- private void createAlarmScheduler(Context context) {
- int pingFrequency = getPingFrequency(context);
- if (pingFrequency > 0) {
- AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- intent.setPackage(context.getPackageName());
- PendingIntent alarmPendingIntent = PendingIntent
- .getService(context, getAccountId().hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
- if (alarmManager != null) {
- alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(),
- Constants.ONE_MIN_IN_MILLIS * pingFrequency, alarmPendingIntent);
- }
- }
- }
-
- @SuppressLint("MissingPermission")
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- private void createOrResetJobScheduler(Context context) {
-
- int existingJobId = StorageHelper.getInt(context, Constants.PF_JOB_ID, -1);
- JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
-
- //Disable push amp for devices below Api 26
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
- if (existingJobId >= 0) {//cancel already running job
- jobScheduler.cancel(existingJobId);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, -1);
- }
-
- getConfigLogger().debug(getAccountId(), "Push Amplification feature is not supported below Oreo");
- return;
- }
-
- if (jobScheduler == null) {
- return;
- }
- int pingFrequency = getPingFrequency(context);
-
- if (existingJobId < 0 && pingFrequency < 0) {
- return; //no running job and nothing to create
- }
-
- if (pingFrequency < 0) { //running job but hard cancel
- jobScheduler.cancel(existingJobId);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, -1);
- return;
- }
-
- ComponentName componentName = new ComponentName(context, CTBackgroundJobService.class);
- boolean needsCreate = (existingJobId < 0 && pingFrequency > 0);
-
- //running job, no hard cancel so check for diff in ping frequency and recreate if needed
- JobInfo existingJobInfo = getJobInfo(existingJobId, jobScheduler);
- if (existingJobInfo != null
- && existingJobInfo.getIntervalMillis() != pingFrequency * Constants.ONE_MIN_IN_MILLIS) {
- jobScheduler.cancel(existingJobId);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, -1);
- needsCreate = true;
- }
-
- if (needsCreate) {
- int jobid = getAccountId().hashCode();
- JobInfo.Builder builder = new JobInfo.Builder(jobid, componentName);
- builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
- builder.setRequiresCharging(false);
-
- builder.setPeriodic(pingFrequency * Constants.ONE_MIN_IN_MILLIS, 5 * Constants.ONE_MIN_IN_MILLIS);
- builder.setRequiresBatteryNotLow(true);
-
- if (Utils.hasPermission(context, "android.permission.RECEIVE_BOOT_COMPLETED")) {
- builder.setPersisted(true);
- }
-
- JobInfo jobInfo = builder.build();
- int resultCode = jobScheduler.schedule(jobInfo);
- if (resultCode == JobScheduler.RESULT_SUCCESS) {
- Logger.d(getAccountId(), "Job scheduled - " + jobid);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, jobid);
- } else {
- Logger.d(getAccountId(), "Job not scheduled - " + jobid);
- }
- }
- }
-
- private void createSession(final Context context) {
- currentSessionId = (int) (System.currentTimeMillis() / 1000);
-
- getConfigLogger().verbose(getAccountId(), "Session created with ID: " + currentSessionId);
-
- SharedPreferences prefs = StorageHelper.getPreferences(context);
-
- final int lastSessionID = StorageHelper.getIntFromPrefs(context, config, Constants.SESSION_ID_LAST, 0);
- final int lastSessionTime = StorageHelper.getIntFromPrefs(context, config, Constants.LAST_SESSION_EPOCH, 0);
- if (lastSessionTime > 0) {
- lastSessionLength = lastSessionTime - lastSessionID;
- }
-
- getConfigLogger().verbose(getAccountId(), "Last session length: " + lastSessionLength + " seconds");
-
- if (lastSessionID == 0) {
- firstSession = true;
- }
-
- final SharedPreferences.Editor editor = prefs.edit()
- .putInt(StorageHelper.storageKeyWithSuffix(config, Constants.SESSION_ID_LAST), currentSessionId);
- StorageHelper.persist(editor);
- }
-
- /**
- * Destroys the current session and resets firstSession flag, if first session lasts more than 20 minutes
- * For an app like Music Player user installs an app and plays music and then moves to background.
- * User then re-launches an App after listening music in background for more than 20 minutes, in this case
- * since an app is not yet killed due to background music app installed event must not be raised by SDK
- */
- private void destroySession() {
- currentSessionId = 0;
- setAppLaunchPushed(false);
- if (isFirstSession()) {
- firstSession = false;
- }
- getConfigLogger().verbose(getAccountId(), "Session destroyed; Session ID is now 0");
- clearSource();
- clearMedium();
- clearCampaign();
- clearWzrkParams();
- }
-
- //Push
- @SuppressWarnings("SameParameterValue")
- private void deviceTokenDidRefresh(String token, PushType type) {
- if (tokenRefreshListener != null) {
- getConfigLogger().debug(getAccountId(), "Notifying devicePushTokenDidRefresh: " + token);
- tokenRefreshListener.devicePushTokenDidRefresh(token, type);
- }
- }
-
- //util
-
- //Session
-
- //InApp
- private void displayNotification(final CTInAppNotification inAppNotification) {
-
- if (Looper.myLooper() != Looper.getMainLooper()) {
- getHandlerUsingMainLooper().post(new Runnable() {
- @Override
- public void run() {
- displayNotification(inAppNotification);
- }
- });
- return;
- }
-
- if (inAppFCManager != null) {
- if (!inAppFCManager.canShow(inAppNotification)) {
- getConfigLogger().verbose(getAccountId(),
- "InApp has been rejected by FC, not showing " + inAppNotification.getCampaignId());
- showInAppNotificationIfAny();
- return;
- }
-
- inAppFCManager.didShow(context, inAppNotification);
- } else {
- getConfigLogger().verbose(getAccountId(),
- "InAppFCManager is NULL, not showing " + inAppNotification.getCampaignId());
- return;
- }
-
- final InAppNotificationListener listener = getInAppNotificationListener();
-
- final boolean goFromListener;
-
- if (listener != null) {
- final HashMap kvs;
-
- if (inAppNotification.getCustomExtras() != null) {
- kvs = Utils.convertJSONObjectToHashMap(inAppNotification.getCustomExtras());
- } else {
- kvs = new HashMap<>();
- }
-
- goFromListener = listener.beforeShow(kvs);
- } else {
- goFromListener = true;
- }
-
- if (!goFromListener) {
- getConfigLogger().verbose(getAccountId(),
- "Application has decided to not show this in-app notification: " + inAppNotification
- .getCampaignId());
- showInAppNotificationIfAny();
- return;
- }
- showInApp(context, inAppNotification, config);
-
- }
-
- private void doTokenRefresh(String token, PushType pushType) {
- if (TextUtils.isEmpty(token) || pushType == null) {
- return;
- }
- switch (pushType) {
- case FCM:
- pushFcmRegistrationId(token, true);
- break;
- case XPS:
- pushXiaomiRegistrationId(token, true);
- break;
- case HPS:
- pushHuaweiRegistrationId(token, true);
- break;
- case BPS:
- pushBaiduRegistrationId(token, true);
- break;
- case ADM:
- pushAmazonRegistrationId(token, true);
- break;
- }
- }
-
- private void flushDBQueue(final Context context, final EventGroup eventGroup) {
- getConfigLogger().verbose(getAccountId(), "Somebody has invoked me to send the queue to CleverTap servers");
-
- QueueCursor cursor;
- QueueCursor previousCursor = null;
- boolean loadMore = true;
-
- while (loadMore) {
-
- cursor = getQueuedEvents(context, 50, previousCursor, eventGroup);
-
- if (cursor == null || cursor.isEmpty()) {
- getConfigLogger().verbose(getAccountId(), "No events in the queue, failing");
- break;
- }
-
- previousCursor = cursor;
- JSONArray queue = cursor.getData();
-
- if (queue == null || queue.length() <= 0) {
- getConfigLogger().verbose(getAccountId(), "No events in the queue, failing");
- break;
- }
-
- loadMore = sendQueue(context, eventGroup, queue);
- }
- }
-
- private void flushQueueAsync(final Context context, final EventGroup eventGroup) {
- postAsyncSafely("CommsManager#flushQueueAsync", new Runnable() {
- @Override
- public void run() {
- if (eventGroup == EventGroup.PUSH_NOTIFICATION_VIEWED) {
- getConfigLogger()
- .verbose(getAccountId(), "Pushing Notification Viewed event onto queue flush sync");
- } else {
- getConfigLogger().verbose(getAccountId(), "Pushing event onto queue flush sync");
- }
- flushQueueSync(context, eventGroup);
- }
- });
- }
-
- private void flushQueueSync(final Context context, final EventGroup eventGroup) {
- if (!isNetworkOnline(context)) {
- getConfigLogger().verbose(getAccountId(), "Network connectivity unavailable. Will retry later");
- return;
- }
-
- if (isOffline()) {
- getConfigLogger()
- .debug(getAccountId(), "CleverTap Instance has been set to offline, won't send events queue");
- return;
- }
-
- if (needsHandshakeForDomain(eventGroup)) {
- mResponseFailureCount = 0;
- setDomain(context, null);
- performHandshakeForDomain(context, eventGroup, new Runnable() {
- @Override
- public void run() {
- flushDBQueue(context, eventGroup);
- }
- });
- } else {
- getConfigLogger().verbose(getAccountId(), "Pushing Notification Viewed event onto queue DB flush");
- flushDBQueue(context, eventGroup);
- }
- }
-
- //Event
- private void forcePushAppLaunchedEvent() {
- setAppLaunchPushed(false);
- pushAppLaunchedEvent();
- }
-
- /**
- * push the device token outside of the normal course
- */
- private void forcePushDeviceToken(final boolean register) {
-
- for (PushType pushType : pushProviders.getAvailablePushTypes()) {
- pushDeviceTokenEvent(null, register, pushType);
- }
- }
-
- //Event
-
- /**
- * The ARP is additional request parameters, which must be sent once
- * received after any HTTP call. This is sort of a proxy for cookies.
- *
- * @return A JSON object containing the ARP key/values. Can be null.
- */
- private JSONObject getARP() {
- try {
- final String nameSpaceKey = getNewNamespaceARPKey();
- if (nameSpaceKey == null) {
- return null;
- }
-
- SharedPreferences prefs = null;
-
- //checking whether new namespace is empty or not
- //if not empty, using prefs of new namespace to send ARP
- //if empty, checking for old prefs
- if (!StorageHelper.getPreferences(context, nameSpaceKey).getAll().isEmpty()) {
- //prefs point to new namespace
- prefs = StorageHelper.getPreferences(context, nameSpaceKey);
- } else {
- //prefs point to new namespace migrated from old namespace
- prefs = migrateARPToNewNameSpace(nameSpaceKey, getNamespaceARPKey());
- }
-
- final Map all = prefs.getAll();
- final Iterator extends Map.Entry> iter = all.entrySet().iterator();
-
- while (iter.hasNext()) {
- final Map.Entry kv = iter.next();
- final Object o = kv.getValue();
- if (o instanceof Number && ((Number) o).intValue() == -1) {
- iter.remove();
- }
- }
- final JSONObject ret = new JSONObject(all);
- getConfigLogger().verbose(getAccountId(),
- "Fetched ARP for namespace key: " + nameSpaceKey + " values: " + all.toString());
- return ret;
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to construct ARP object", t);
- return null;
- }
- }
-
- //Profile
-
- private long getAppInstallTime() {
- return appInstallTime;
- }
-
- //Event
- private JSONObject getAppLaunchedFields() {
-
- try {
- boolean deviceIsMultiUser = false;
- if (deviceInfo.getGoogleAdID() != null) {
- deviceIsMultiUser = new LoginInfoProvider(this).deviceIsMultiUser();
- }
- return CTJsonConverter.from(deviceInfo, locationFromUser, enableNetworkInfoReporting,
- deviceIsMultiUser);
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to construct App Launched event", t);
- return new JSONObject();
- }
- }
-
- private synchronized String getCampaign() {
- return campaign;
- }
-
- private synchronized void setCampaign(String campaign) {
- if (this.campaign == null) {
- this.campaign = campaign;
- }
- }
-
- private CleverTapInstanceConfig getConfig() {
- return config;
- }
-
- //Push
-
- private Logger getConfigLogger() {
- return getConfig().getLogger();
- }
-
- private int getCurrentSession() {
- return currentSessionId;
- }
-
- //gives delay frequency based on region
- //randomly adds delay to 1s delay in case of non-EU regions
- private int getDelayFrequency() {
- getConfigLogger().debug(getAccountId(), "Network retry #" + networkRetryCount);
-
- //Retry with delay as 1s for first 10 retries
- if (networkRetryCount < 10) {
- getConfigLogger().debug(getAccountId(),
- "Failure count is " + networkRetryCount + ". Setting delay frequency to 1s");
- minDelayFrequency = Constants.PUSH_DELAY_MS; //reset minimum delay to 1s
- return minDelayFrequency;
- }
-
- if (config.getAccountRegion() == null) {
- //Retry with delay as 1s if region is null in case of eu1
- getConfigLogger().debug(getAccountId(), "Setting delay frequency to 1s");
- return Constants.PUSH_DELAY_MS;
- } else {
- //Retry with delay as minimum delay frequency and add random number of seconds to scatter traffic
- Random randomGen = new Random();
- int randomDelay = (randomGen.nextInt(10) + 1) * 1000;
- minDelayFrequency += randomDelay;
- if (minDelayFrequency < maxDelayFrequency) {
- getConfigLogger().debug(getAccountId(), "Setting delay frequency to " + minDelayFrequency);
- return minDelayFrequency;
- } else {
- minDelayFrequency = Constants.PUSH_DELAY_MS;
- }
- getConfigLogger().debug(getAccountId(), "Setting delay frequency to " + minDelayFrequency);
- return minDelayFrequency;
- }
- }
-
- private String getDomain(boolean defaultToHandshakeURL, final EventGroup eventGroup) {
- String domain = getDomainFromPrefsOrMetadata(eventGroup);
-
- final boolean emptyDomain = domain == null || domain.trim().length() == 0;
- if (emptyDomain && !defaultToHandshakeURL) {
- return null;
- }
-
- if (emptyDomain) {
- domain = Constants.PRIMARY_DOMAIN + "/hello";
- } else {
- domain += "/a1";
- }
-
- return domain;
- }
-
- private String getDomainFromPrefsOrMetadata(final EventGroup eventGroup) {
- try {
- final String region = this.config.getAccountRegion();
- if (region != null && region.trim().length() > 0) {
- // Always set this to 0 so that the handshake is not performed during a HTTP failure
- mResponseFailureCount = 0;
- if (eventGroup.equals(EventGroup.PUSH_NOTIFICATION_VIEWED)) {
- return region.trim().toLowerCase() + eventGroup.httpResource + "." + Constants.PRIMARY_DOMAIN;
- } else {
- return region.trim().toLowerCase() + "." + Constants.PRIMARY_DOMAIN;
- }
- }
- } catch (Throwable t) {
- // Ignore
- }
- if (eventGroup.equals(EventGroup.PUSH_NOTIFICATION_VIEWED)) {
- return StorageHelper.getStringFromPrefs(context, config, Constants.SPIKY_KEY_DOMAIN_NAME, null);
- } else {
- return StorageHelper.getStringFromPrefs(context, config, Constants.KEY_DOMAIN_NAME, null);
- }
- }
-
- private String getEndpoint(final boolean defaultToHandshakeURL, final EventGroup eventGroup) {
- String domain = getDomain(defaultToHandshakeURL, eventGroup);
- if (domain == null) {
- getConfigLogger().verbose(getAccountId(), "Unable to configure endpoint, domain is null");
- return null;
- }
-
- final String accountId = getAccountId();
- if (accountId == null) {
- getConfigLogger().verbose(getAccountId(), "Unable to configure endpoint, accountID is null");
- return null;
- }
-
- String endpoint = "https://" + domain + "?os=Android&t=" + this.deviceInfo.getSdkVersion();
- endpoint += "&z=" + accountId;
-
- final boolean needsHandshake = needsHandshakeForDomain(eventGroup);
- // Don't attach ts if its handshake
- if (needsHandshake) {
- return endpoint;
- }
-
- currentRequestTimestamp = (int) (System.currentTimeMillis() / 1000);
- endpoint += "&ts=" + currentRequestTimestamp;
-
- return endpoint;
- }
-
- private int getFirstRequestTimestamp() {
- return StorageHelper.getIntFromPrefs(context, config, Constants.KEY_FIRST_TS, 0);
- }
-
- private int getGeofenceSDKVersion() {
- return geofenceSDKVersion;
- }
-
- private void setGeofenceSDKVersion(int geofenceSDKVersion) {
- this.geofenceSDKVersion = geofenceSDKVersion;
- }
-
- private String getGraphUserPropertySafely(JSONObject graphUser, String key, String def) {
- try {
- String prop = (String) graphUser.get(key);
- if (prop != null) {
- return prop;
- } else {
- return def;
- }
- } catch (Throwable t) {
- return def;
- }
- }
-
- /**
- * Returns the generic handler object which is used to post
- * runnables. The returned value will never be null.
- *
- * @return The generic handler
- * @see Handler
- */
- private Handler getHandlerUsingMainLooper() {
- return handlerUsingMainLooper;
- }
-
- private long getI() {
- return StorageHelper.getLongFromPrefs(context, config, Constants.KEY_I, 0, Constants.NAMESPACE_IJ);
- }
-
- private long getJ() {
- return StorageHelper.getLongFromPrefs(context, config, Constants.KEY_J, 0, Constants.NAMESPACE_IJ);
- }
-
- private int getLastRequestTimestamp() {
- return StorageHelper.getIntFromPrefs(context, config, Constants.KEY_LAST_TS, 0);
- }
-
- //Session
- private int getLastSessionLength() {
- return lastSessionLength;
- }
-
- private LocalDataStore getLocalDataStore() {
- return localDataStore;
- }
-
- private synchronized String getMedium() {
- return medium;
- }
-
- // only set if not already set during the session
- private synchronized void setMedium(String medium) {
- if (this.medium == null) {
- this.medium = medium;
- }
- }
-
- //Session
- //Old namespace for ARP Shared Prefs
- private String getNamespaceARPKey() {
-
- final String accountId = getAccountId();
- if (accountId == null) {
- return null;
- }
-
- getConfigLogger().verbose(getAccountId(), "Old ARP Key = ARP:" + accountId);
- return "ARP:" + accountId;
- }
-
- //New namespace for ARP Shared Prefs
- private String getNewNamespaceARPKey() {
-
- final String accountId = getAccountId();
- if (accountId == null) {
- return null;
- }
-
- getConfigLogger().verbose(getAccountId(), "New ARP Key = ARP:" + accountId + ":" + getCleverTapID());
- return "ARP:" + accountId + ":" + getCleverTapID();
- }
-
- private int getPingFrequency(Context context) {
- return StorageHelper.getInt(context, Constants.PING_FREQUENCY,
- Constants.PING_FREQUENCY_VALUE); //intentional global key because only one Job is running
- }
-
- private QueueCursor getPushNotificationViewedQueuedEvents(final Context context, final int batchSize,
- final QueueCursor previousCursor) {
- return getQueueCursor(context, DBAdapter.Table.PUSH_NOTIFICATION_VIEWED, batchSize, previousCursor);
- }
-
- private QueueCursor getQueueCursor(final Context context, DBAdapter.Table table, final int batchSize,
- final QueueCursor previousCursor) {
- synchronized (eventLock) {
- DBAdapter adapter = loadDBAdapter(context);
- DBAdapter.Table tableName = (previousCursor != null) ? previousCursor.getTableName() : table;
-
- // if previousCursor that means the batch represented by the previous cursor was processed so remove those from the db
- if (previousCursor != null) {
- adapter.cleanupEventsFromLastId(previousCursor.getLastId(), previousCursor.getTableName());
- }
-
- // grab the new batch
- QueueCursor newCursor = new QueueCursor();
- newCursor.setTableName(tableName);
- JSONObject queuedDBEvents = adapter.fetchEvents(tableName, batchSize);
- newCursor = updateCursorForDBObject(queuedDBEvents, newCursor);
-
- return newCursor;
- }
- }
-
- private QueueCursor getQueuedDBEvents(final Context context, final int batchSize,
- final QueueCursor previousCursor) {
-
- synchronized (eventLock) {
- QueueCursor newCursor = getQueueCursor(context, DBAdapter.Table.EVENTS, batchSize, previousCursor);
-
- if (newCursor.isEmpty() && newCursor.getTableName().equals(DBAdapter.Table.EVENTS)) {
- newCursor = getQueueCursor(context, DBAdapter.Table.PROFILE_EVENTS, batchSize, null);
- }
-
- return newCursor.isEmpty() ? null : newCursor;
- }
- }
-
- @SuppressWarnings("SameParameterValue")
- private QueueCursor getQueuedEvents(final Context context, final int batchSize, final QueueCursor previousCursor,
- final EventGroup eventGroup) {
- if (eventGroup == EventGroup.PUSH_NOTIFICATION_VIEWED) {
- getConfigLogger().verbose(getAccountId(), "Returning Queued Notification Viewed events");
- return getPushNotificationViewedQueuedEvents(context, batchSize, previousCursor);
- } else {
- getConfigLogger().verbose(getAccountId(), "Returning Queued events");
- return getQueuedDBEvents(context, batchSize, previousCursor);
- }
- }
-
- private long getReferrerClickTime() {
- return referrerClickTime;
- }
-
- private String getScreenName() {
- return currentScreenName.equals("") ? null : currentScreenName;
- }
-
- private synchronized String getSource() {
- return source;
- }
-
- //UTM
- // only set if not already set during the session
- private synchronized void setSource(String source) {
- if (this.source == null) {
- this.source = source;
- }
- }
-
- private synchronized JSONObject getWzrkParams() {
- return wzrkParams;
- }
-
- private synchronized void setWzrkParams(JSONObject wzrkParams) {
- if (this.wzrkParams == null) {
- this.wzrkParams = wzrkParams;
- }
- }
-
- //Saves ARP directly to new namespace
- private void handleARPUpdate(final Context context, final JSONObject arp) {
- if (arp == null || arp.length() == 0) {
- return;
- }
-
- final String nameSpaceKey = getNewNamespaceARPKey();
- if (nameSpaceKey == null) {
- return;
- }
-
- final SharedPreferences prefs = StorageHelper.getPreferences(context, nameSpaceKey);
- final SharedPreferences.Editor editor = prefs.edit();
-
- final Iterator keys = arp.keys();
- while (keys.hasNext()) {
- final String key = keys.next();
- try {
- final Object o = arp.get(key);
- if (o instanceof Number) {
- final int update = ((Number) o).intValue();
- editor.putInt(key, update);
- } else if (o instanceof String) {
- if (((String) o).length() < 100) {
- editor.putString(key, (String) o);
- } else {
- getConfigLogger().verbose(getAccountId(),
- "ARP update for key " + key + " rejected (string value too long)");
- }
- } else if (o instanceof Boolean) {
- editor.putBoolean(key, (Boolean) o);
- } else {
- getConfigLogger()
- .verbose(getAccountId(), "ARP update for key " + key + " rejected (invalid data type)");
- }
- } catch (JSONException e) {
- // Ignore
- }
- }
- getConfigLogger().verbose(getAccountId(),
- "Stored ARP for namespace key: " + nameSpaceKey + " values: " + arp.toString());
- StorageHelper.persist(editor);
- }
-
- private void handleInstallReferrerOnFirstInstall() {
- getConfigLogger().verbose(getAccountId(), "Starting to handle install referrer");
- try {
- final InstallReferrerClient referrerClient = InstallReferrerClient.newBuilder(context).build();
- referrerClient.startConnection(new InstallReferrerStateListener() {
- @Override
- public void onInstallReferrerServiceDisconnected() {
- if (!installReferrerDataSent) {
- handleInstallReferrerOnFirstInstall();
- }
- }
-
- @Override
- public void onInstallReferrerSetupFinished(int responseCode) {
- switch (responseCode) {
- case InstallReferrerClient.InstallReferrerResponse.OK:
- // Connection established.
- ReferrerDetails response = null;
- try {
- response = referrerClient.getInstallReferrer();
- String referrerUrl = response.getInstallReferrer();
- referrerClickTime = response.getReferrerClickTimestampSeconds();
- appInstallTime = response.getInstallBeginTimestampSeconds();
- pushInstallReferrer(referrerUrl);
- installReferrerDataSent = true;
- getConfigLogger().debug(getAccountId(),
- "Install Referrer data set [Referrer URL-" + referrerUrl + "]");
- } catch (RemoteException e) {
- getConfigLogger().debug(getAccountId(),
- "Remote exception caused by Google Play Install Referrer library - " + e
- .getMessage());
- referrerClient.endConnection();
- installReferrerDataSent = false;
- }
- referrerClient.endConnection();
- break;
- case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
- // API not available on the current Play Store app.
- getConfigLogger().debug(getAccountId(),
- "Install Referrer data not set, API not supported by Play Store on device");
- break;
- case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
- // Connection couldn't be established.
- getConfigLogger().debug(getAccountId(),
- "Install Referrer data not set, connection to Play Store unavailable");
- break;
- }
- }
- });
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(),
- "Google Play Install Referrer's InstallReferrerClient Class not found - " + t
- .getLocalizedMessage()
- + " \n Please add implementation 'com.android.installreferrer:installreferrer:2.1' to your build.gradle");
- }
- }
-
- //PN
- private void handlePushNotificationsInResponse(JSONArray pushNotifications) {
- try {
- for (int i = 0; i < pushNotifications.length(); i++) {
- Bundle pushBundle = new Bundle();
- JSONObject pushObject = pushNotifications.getJSONObject(i);
- if (pushObject.has("wzrk_acct_id")) {
- pushBundle.putString("wzrk_acct_id", pushObject.getString("wzrk_acct_id"));
- }
- if (pushObject.has("wzrk_acts")) {
- pushBundle.putString("wzrk_acts", pushObject.getString("wzrk_acts"));
- }
- if (pushObject.has("nm")) {
- pushBundle.putString("nm", pushObject.getString("nm"));
- }
- if (pushObject.has("nt")) {
- pushBundle.putString("nt", pushObject.getString("nt"));
- }
- if (pushObject.has("wzrk_bp")) {
- pushBundle.putString("wzrk_bp", pushObject.getString("wzrk_bp"));
- }
- if (pushObject.has("pr")) {
- pushBundle.putString("pr", pushObject.getString("pr"));
- }
- if (pushObject.has("wzrk_pivot")) {
- pushBundle.putString("wzrk_pivot", pushObject.getString("wzrk_pivot"));
- }
- if (pushObject.has("wzrk_sound")) {
- pushBundle.putString("wzrk_sound", pushObject.getString("wzrk_sound"));
- }
- if (pushObject.has("wzrk_cid")) {
- pushBundle.putString("wzrk_cid", pushObject.getString("wzrk_cid"));
- }
- if (pushObject.has("wzrk_bc")) {
- pushBundle.putString("wzrk_bc", pushObject.getString("wzrk_bc"));
- }
- if (pushObject.has("wzrk_bi")) {
- pushBundle.putString("wzrk_bi", pushObject.getString("wzrk_bi"));
- }
- if (pushObject.has("wzrk_id")) {
- pushBundle.putString("wzrk_id", pushObject.getString("wzrk_id"));
- }
- if (pushObject.has("wzrk_pn")) {
- pushBundle.putString("wzrk_pn", pushObject.getString("wzrk_pn"));
- }
- if (pushObject.has("ico")) {
- pushBundle.putString("ico", pushObject.getString("ico"));
- }
- if (pushObject.has("wzrk_ck")) {
- pushBundle.putString("wzrk_ck", pushObject.getString("wzrk_ck"));
- }
- if (pushObject.has("wzrk_dl")) {
- pushBundle.putString("wzrk_dl", pushObject.getString("wzrk_dl"));
- }
- if (pushObject.has("wzrk_pid")) {
- pushBundle.putString("wzrk_pid", pushObject.getString("wzrk_pid"));
- }
- if (pushObject.has("wzrk_ttl")) {
- pushBundle.putLong("wzrk_ttl", pushObject.getLong("wzrk_ttl"));
- }
- if (pushObject.has("wzrk_rnv")) {
- pushBundle.putString("wzrk_rnv", pushObject.getString("wzrk_rnv"));
- }
- Iterator iterator = pushObject.keys();
- while (iterator.hasNext()) {
- String key = iterator.next().toString();
- pushBundle.putString(key, pushObject.getString(key));
- }
- if (!pushBundle.isEmpty() && !dbAdapter
- .doesPushNotificationIdExist(pushObject.getString("wzrk_pid"))) {
- getConfigLogger().verbose("Creating Push Notification locally");
- if (pushAmpListener != null) {
- pushAmpListener.onPushAmpPayloadReceived(pushBundle);
- } else {
- createNotification(context, pushBundle);
- }
- } else {
- getConfigLogger().verbose(getAccountId(),
- "Push Notification already shown, ignoring local notification :" + pushObject
- .getString("wzrk_pid"));
- }
- }
- } catch (JSONException e) {
- getConfigLogger().verbose(getAccountId(), "Error parsing push notification JSON");
- }
- }
-
- /**
- * This method handles send Test flow for Display Units
- *
- * @param extras - bundled data of notification payload
- */
- private void handleSendTestForDisplayUnits(Bundle extras) {
- try {
- String pushJsonPayload = extras.getString(Constants.DISPLAY_UNIT_PREVIEW_PUSH_PAYLOAD_KEY);
- Logger.v("Received Display Unit via push payload: " + pushJsonPayload);
- JSONObject r = new JSONObject();
- JSONArray displayUnits = new JSONArray();
- r.put(Constants.DISPLAY_UNIT_JSON_RESPONSE_KEY, displayUnits);
- JSONObject testPushObject = new JSONObject(pushJsonPayload);
- displayUnits.put(testPushObject);
- processDisplayUnitsResponse(r);
- } catch (Throwable t) {
- Logger.v("Failed to process Display Unit from push notification payload", t);
- }
- }
-
- private boolean hasDomainChanged(final String newDomain) {
- final String oldDomain = StorageHelper.getStringFromPrefs(context, config, Constants.KEY_DOMAIN_NAME, null);
- return !newDomain.equals(oldDomain);
- }
-
- private boolean inCurrentSession() {
- return currentSessionId > 0;
- }
-
- private void initABTesting() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- return;
- }
- if (!config.isAnalyticsOnly()) {
- if (!config.isABTestingEnabled()) {
- getConfigLogger().debug(config.getAccountId(), "AB Testing is not enabled for this instance");
- return;
- }
-
- if (getCleverTapID() == null) {
- getConfigLogger()
- .verbose(config.getAccountId(), "GUID not set yet, deferring ABTesting initialization");
- return;
- }
-
- config.setEnableUIEditor(isUIEditorEnabled);
- if (ctABTestController == null) {
- ctABTestController = new CTABTestController(context, config, getCleverTapID(), this);
- getConfigLogger().verbose(config.getAccountId(), "AB Testing initialized");
- }
- }
- }
-
- private void initFeatureFlags(boolean fromPlayServices) {
- Logger.v("Initializing Feature Flags with device Id = " + getCleverTapID());
-
- if (config.isAnalyticsOnly()) {
- getConfigLogger().debug(config.getAccountId(), "Feature Flag is not enabled for this instance");
- return;
- }
-
- if (ctFeatureFlagsController == null) {
- ctFeatureFlagsController = new CTFeatureFlagsController(context, getCleverTapID(), config, this);
- getConfigLogger().verbose(config.getAccountId(), "Feature Flags initialized");
- }
-
- if (fromPlayServices && !ctFeatureFlagsController.isInitialized()) {
- ctFeatureFlagsController.setGuidAndInit(getCleverTapID());
- }
- }
-
- private void initProductConfig(boolean fromPlayServices) {
- Logger.v("Initializing Product Config with device Id = " + getCleverTapID());
-
- if (config.isAnalyticsOnly()) {
- getConfigLogger().debug(config.getAccountId(), "Product Config is not enabled for this instance");
- return;
- }
-
- if (ctProductConfigController == null) {
- ctProductConfigController = new CTProductConfigController(context, getCleverTapID(), config, this);
- }
- if (fromPlayServices && !ctProductConfigController.isInitialized()) {
- ctProductConfigController.setGuidAndInit(getCleverTapID());
- }
- }
-
- //Networking
- private String insertHeader(Context context, JSONArray arr) {
- try {
- final JSONObject header = new JSONObject();
-
- String deviceId = getCleverTapID();
- if (deviceId != null && !deviceId.equals("")) {
- header.put("g", deviceId);
- } else {
- getConfigLogger().verbose(getAccountId(),
- "CRITICAL: Couldn't finalise on a device ID! Using error device ID instead!");
- }
-
- header.put("type", "meta");
-
- JSONObject appFields = getAppLaunchedFields();
- header.put("af", appFields);
-
- long i = getI();
- if (i > 0) {
- header.put("_i", i);
- }
-
- long j = getJ();
- if (j > 0) {
- header.put("_j", j);
- }
-
- String accountId = getAccountId();
- String token = this.config.getAccountToken();
-
- if (accountId == null || token == null) {
- getConfigLogger()
- .debug(getAccountId(), "Account ID/token not found, unable to configure queue request");
- return null;
- }
-
- header.put("id", accountId);
- header.put("tk", token);
- header.put("l_ts", getLastRequestTimestamp());
- header.put("f_ts", getFirstRequestTimestamp());
- header.put("ct_pi", IdentityRepoFactory.getRepo(this).getIdentitySet().toString());
- header.put("ddnd",
- !(this.deviceInfo.getNotificationsEnabledForUser() && (pushProviders.isNotificationSupported())));
- if (isBgPing) {
- header.put("bk", 1);
- isBgPing = false;
- }
- header.put("rtl", getRenderedTargetList(this.dbAdapter));
- if (!installReferrerDataSent) {
- header.put("rct", getReferrerClickTime());
- header.put("ait", getAppInstallTime());
- }
- header.put("frs", isFirstRequestInSession());
- setFirstRequestInSession(false);
-
- // Attach ARP
- try {
- final JSONObject arp = getARP();
- if (arp != null && arp.length() > 0) {
- header.put("arp", arp);
- }
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to attach ARP", t);
- }
-
- JSONObject ref = new JSONObject();
- try {
-
- String utmSource = getSource();
- if (utmSource != null) {
- ref.put("us", utmSource);
- }
-
- String utmMedium = getMedium();
- if (utmMedium != null) {
- ref.put("um", utmMedium);
- }
-
- String utmCampaign = getCampaign();
- if (utmCampaign != null) {
- ref.put("uc", utmCampaign);
- }
-
- if (ref.length() > 0) {
- header.put("ref", ref);
- }
-
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to attach ref", t);
- }
-
- JSONObject wzrkParams = getWzrkParams();
- if (wzrkParams != null && wzrkParams.length() > 0) {
- header.put("wzrk_ref", wzrkParams);
- }
-
- if (inAppFCManager != null) {
- Logger.v("Attaching InAppFC to Header");
- inAppFCManager.attachToHeader(context, header);
- }
-
- // Resort to string concat for backward compatibility
- return "[" + header.toString() + ", " + arr.toString().substring(1);
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "CommsManager: Failed to attach header", t);
- return arr.toString();
- }
- }
-
-
- private boolean isAppLaunchPushed() {
- synchronized (appLaunchPushedLock) {
- return appLaunchPushed;
- }
- }
-
- private void setAppLaunchPushed(boolean pushed) {
- synchronized (appLaunchPushedLock) {
- appLaunchPushed = pushed;
- }
- }
-
- private boolean isAppLaunchReportingDisabled() {
- return this.config.isDisableAppLaunchedEvent();
- }
-
- private boolean isCurrentUserOptedOut() {
- synchronized (optOutFlagLock) {
- return currentUserOptedOut;
- }
- }
-
- private void setCurrentUserOptedOut(boolean enable) {
- synchronized (optOutFlagLock) {
- currentUserOptedOut = enable;
- }
- }
-
- private boolean isErrorDeviceId() {
- return this.deviceInfo.isErrorDeviceId();
- }
-
- private boolean isFirstRequestInSession() {
- return firstRequestInSession;
- }
-
- private void setFirstRequestInSession(boolean firstRequestInSession) {
- this.firstRequestInSession = firstRequestInSession;
- }
-
- //Session
- private boolean isFirstSession() {
- return firstSession;
- }
-
- private boolean isLocationForGeofence() {
- return isLocationForGeofence;
- }
-
- private void setLocationForGeofence(boolean locationForGeofence) {
- isLocationForGeofence = locationForGeofence;
- }
-
- /**
- * @return true if the mute command was sent anytime between now and now - 24 hours.
- */
- private boolean isMuted() {
- final int now = (int) (System.currentTimeMillis() / 1000);
- final int muteTS = StorageHelper.getIntFromPrefs(context, config, Constants.KEY_MUTED, 0);
-
- return now - muteTS < 24 * 60 * 60;
- }
-
- //Notification Inbox public APIs
-
- //Util
- private boolean isNetworkOnline(Context context) {
- try {
- ConnectivityManager cm =
- (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (cm == null) {
- // lets be optimistic, if we are truly offline we handle the exception
- return true;
- }
- @SuppressLint("MissingPermission") NetworkInfo netInfo = cm.getActiveNetworkInfo();
- return netInfo != null && netInfo.isConnected();
- } catch (Throwable ignore) {
- // lets be optimistic, if we are truly offline we handle the exception
- return true;
- }
- }
-
- private boolean isOffline() {
- return offline;
- }
-
- /**
- * If you want to stop recorded events from being sent to the server, use this method to set the SDK instance to
- * offline.
- * Once offline, events will be recorded and queued locally but will not be sent to the server until offline is
- * disabled.
- * Calling this method again with offline set to false will allow events to be sent to server and the SDK instance
- * will immediately attempt to send events that have been queued while offline.
- *
- * @param value boolean, true sets the sdk offline, false sets the sdk back online
- */
- @SuppressWarnings({"unused"})
- public void setOffline(boolean value) {
- offline = value;
- if (offline) {
- getConfigLogger()
- .debug(getAccountId(), "CleverTap Instance has been set to offline, won't send events queue");
- } else {
- getConfigLogger()
- .debug(getAccountId(), "CleverTap Instance has been set to online, sending events queue");
- flush();
- }
- }
-
- private boolean isProcessUserLoginWithIdentifier(String identifier) {
- synchronized (processingUserLoginLock) {
- return processingUserLoginIdentifier != null && processingUserLoginIdentifier.equals(identifier);
- }
- }
-
- private boolean isTimeBetweenDNDTime(Date startTime, Date stopTime, Date currentTime) {
- //Start Time
- Calendar startTimeCalendar = Calendar.getInstance();
- startTimeCalendar.setTime(startTime);
- //Current Time
- Calendar currentTimeCalendar = Calendar.getInstance();
- currentTimeCalendar.setTime(currentTime);
- //Stop Time
- Calendar stopTimeCalendar = Calendar.getInstance();
- stopTimeCalendar.setTime(stopTime);
-
- if (stopTime.compareTo(startTime) < 0) {
- if (currentTimeCalendar.compareTo(stopTimeCalendar) < 0) {
- currentTimeCalendar.add(Calendar.DATE, 1);
- }
- stopTimeCalendar.add(Calendar.DATE, 1);
- }
- return currentTimeCalendar.compareTo(startTimeCalendar) >= 0
- && currentTimeCalendar.compareTo(stopTimeCalendar) < 0;
- }
-
- //Session
- private void lazyCreateSession(Context context) {
- if (!inCurrentSession()) {
- setFirstRequestInSession(true);
- if (validator != null) {
- validator.setDiscardedEvents(null);
- }
- createSession(context);
- pushInitialEventsAsync();
- }
- }
-
- //Util
- private DBAdapter loadDBAdapter(Context context) {
- if (dbAdapter == null) {
- dbAdapter = new DBAdapter(context, this.config);
- dbAdapter.cleanupStaleEvents(DBAdapter.Table.EVENTS);
- dbAdapter.cleanupStaleEvents(DBAdapter.Table.PROFILE_EVENTS);
- dbAdapter.cleanupStaleEvents(DBAdapter.Table.PUSH_NOTIFICATION_VIEWED);
- dbAdapter.cleanUpPushNotifications();
- }
- return dbAdapter;
- }
-
- //Run manifest validation in async
- private void manifestAsyncValidation() {
- postAsyncSafely("Manifest Validation", new Runnable() {
- @Override
- public void run() {
- ManifestValidator.validate(context, deviceInfo, pushProviders);
- }
- });
- }
-
- private SharedPreferences migrateARPToNewNameSpace(String newKey, String oldKey) {
- SharedPreferences oldPrefs = StorageHelper.getPreferences(context, oldKey);
- SharedPreferences newPrefs = StorageHelper.getPreferences(context, newKey);
- SharedPreferences.Editor editor = newPrefs.edit();
- Map all = oldPrefs.getAll();
-
- for (Map.Entry kv : all.entrySet()) {
- final Object o = kv.getValue();
- if (o instanceof Number) {
- final int update = ((Number) o).intValue();
- editor.putInt(kv.getKey(), update);
- } else if (o instanceof String) {
- if (((String) o).length() < 100) {
- editor.putString(kv.getKey(), (String) o);
- } else {
- getConfigLogger().verbose(getAccountId(),
- "ARP update for key " + kv.getKey() + " rejected (string value too long)");
- }
- } else if (o instanceof Boolean) {
- editor.putBoolean(kv.getKey(), (Boolean) o);
- } else {
- getConfigLogger().verbose(getAccountId(),
- "ARP update for key " + kv.getKey() + " rejected (invalid data type)");
- }
- }
- getConfigLogger().verbose(getAccountId(), "Completed ARP update for namespace key: " + newKey + "");
- StorageHelper.persist(editor);
- oldPrefs.edit().clear().apply();
- return newPrefs;
- }
-
- //Networking
- private boolean needsHandshakeForDomain(final EventGroup eventGroup) {
- final String domain = getDomainFromPrefsOrMetadata(eventGroup);
- return domain == null || mResponseFailureCount > 5;
- }
-
- /**
- * Notify the registered Display Unit listener about the running Display Unit campaigns
- *
- * @param displayUnits - Array of Display Units {@link CleverTapDisplayUnit}
- */
- private void notifyDisplayUnitsLoaded(final ArrayList displayUnits) {
- if (displayUnits != null && !displayUnits.isEmpty()) {
- if (displayUnitListenerWeakReference != null && displayUnitListenerWeakReference.get() != null) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- //double check to ensure null safety
- if (displayUnitListenerWeakReference != null
- && displayUnitListenerWeakReference.get() != null) {
- displayUnitListenerWeakReference.get().onDisplayUnitsLoaded(displayUnits);
- }
- }
- });
- } else {
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_DISPLAY_UNIT + "No registered listener, failed to notify");
- }
- } else {
- getConfigLogger().verbose(getAccountId(), Constants.FEATURE_DISPLAY_UNIT + "No Display Units found");
- }
- }
-
- //Profile
- private void notifyUserProfileInitialized(String deviceID) {
- deviceID = (deviceID != null) ? deviceID : getCleverTapID();
-
- if (deviceID == null) {
- return;
- }
-
- final SyncListener sl;
- try {
- sl = getSyncListener();
- if (sl != null) {
- sl.profileDidInitialize(deviceID);
- }
- } catch (Throwable t) {
- // Ignore
- }
- }
-
- private void notifyUserProfileInitialized() {
- notifyUserProfileInitialized(this.deviceInfo.getDeviceID());
- }
-
- private void onProductConfigFailed() {
- if (isProductConfigRequested) {
- if (ctProductConfigController != null) {
- ctProductConfigController.onFetchFailed();
- }
- isProductConfigRequested = false;
- }
- }
-
- //Push
- private void onTokenRefresh() {
- if (getConfig().isAnalyticsOnly()) {
- Logger.d(getAccountId(), "Instance is Analytics Only not processing device token");
- return;
- }
- pushProviders.refreshAllTokens();
- }
-
- private String optOutKey() {
- String guid = getCleverTapID();
- if (guid == null) {
- return null;
- }
- return "OptOut:" + guid;
- }
-
- /**
- * Parses the Display Units using the JSON response
- *
- * @param messages - Json array of Display Unit items
- */
- private void parseDisplayUnits(JSONArray messages) {
- if (messages == null || messages.length() == 0) {
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_DISPLAY_UNIT + "Can't parse Display Units, jsonArray is either empty or null");
- return;
- }
-
- synchronized (displayUnitControllerLock) {// lock to avoid multiple instance creation for controller
- if (mCTDisplayUnitController == null) {
- mCTDisplayUnitController = new CTDisplayUnitController();
- }
- }
- ArrayList displayUnits = mCTDisplayUnitController.updateDisplayUnits(messages);
-
- notifyDisplayUnitsLoaded(displayUnits);
- }
-
- private void parseFeatureFlags(JSONObject responseKV) throws JSONException {
- JSONArray kvArray = responseKV.getJSONArray(Constants.KEY_KV);
-
- if (kvArray != null && ctFeatureFlagsController != null) {
- ctFeatureFlagsController.updateFeatureFlags(responseKV);
- }
- }
-
- private void parseProductConfigs(JSONObject responseKV) throws JSONException {
- JSONArray kvArray = responseKV.getJSONArray(Constants.KEY_KV);
-
- if (kvArray != null && ctProductConfigController != null) {
- productConfig().onFetchSuccess(responseKV);
- } else {
- onProductConfigFailed();
- }
- }
-
- private Date parseTimeToDate(String time) {
-
- final String inputFormat = "HH:mm";
- SimpleDateFormat inputParser = new SimpleDateFormat(inputFormat, Locale.US);
- try {
- return inputParser.parse(time);
- } catch (java.text.ParseException e) {
- return new Date(0);
- }
- }
-
- private void performHandshakeForDomain(final Context context, final EventGroup eventGroup,
- final Runnable handshakeSuccessCallback) {
-
- final String endpoint = getEndpoint(true, eventGroup);
- if (endpoint == null) {
- getConfigLogger().verbose(getAccountId(), "Unable to perform handshake, endpoint is null");
- }
- getConfigLogger().verbose(getAccountId(), "Performing handshake with " + endpoint);
-
- HttpsURLConnection conn = null;
- try {
- conn = buildHttpsURLConnection(endpoint);
- final int responseCode = conn.getResponseCode();
- if (responseCode != 200) {
- getConfigLogger()
- .verbose(getAccountId(), "Invalid HTTP status code received for handshake - " + responseCode);
- return;
- }
-
- getConfigLogger().verbose(getAccountId(), "Received success from handshake :)");
-
- if (processIncomingHeaders(context, conn)) {
- getConfigLogger().verbose(getAccountId(), "We are not muted");
- // We have a new domain, run the callback
- handshakeSuccessCallback.run();
- }
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to perform handshake!", t);
- } finally {
- if (conn != null) {
- try {
- conn.getInputStream().close();
- conn.disconnect();
- } catch (Throwable t) {
- // Ignore
- }
- }
- }
- }
-
- /**
- * Use this to safely post a runnable to the async handler.
- * It adds try/catch blocks around the runnable and the handler itself.
- */
- @SuppressWarnings("UnusedParameters")
- private @Nullable
- Future> postAsyncSafely(final String name, final Runnable runnable) {
- Future> future = null;
- try {
- final boolean executeSync = Thread.currentThread().getId() == EXECUTOR_THREAD_ID;
-
- if (executeSync) {
- runnable.run();
- } else {
- future = es.submit(new Runnable() {
- @Override
- public void run() {
- EXECUTOR_THREAD_ID = Thread.currentThread().getId();
- try {
- runnable.run();
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(),
- "Executor service: Failed to complete the scheduled task", t);
- }
- }
- });
- }
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to submit task to the executor service", t);
- }
-
- return future;
- }
-
- //InApp
- private void prepareNotificationForDisplay(final JSONObject jsonObject) {
- getConfigLogger().debug(getAccountId(), "Preparing In-App for display: " + jsonObject.toString());
- runOnNotificationQueue(new NotificationPrepareRunnable(this, jsonObject));
- }
-
- /**
- * Stores silent push notification in DB for smooth working of Push Amplification
- * Background Job Service and also stores wzrk_pid to the DB to avoid duplication of Push
- * Notifications from Push Amplification.
- *
- * @param extras - Bundle
- */
- private void processCustomPushNotification(final Bundle extras) {
- postAsyncSafely("customHandlePushAmplification", new Runnable() {
- @Override
- public void run() {
- String notifMessage = extras.getString(Constants.NOTIF_MSG);
- notifMessage = (notifMessage != null) ? notifMessage : "";
- if (notifMessage.isEmpty()) {
- //silent notification
- getConfigLogger().verbose(getAccountId(), "Push notification message is empty, not rendering");
- loadDBAdapter(context).storeUninstallTimestamp();
- String pingFreq = extras.getString("pf", "");
- if (!TextUtils.isEmpty(pingFreq)) {
- updatePingFrequencyIfNeeded(context, Integer.parseInt(pingFreq));
- }
- } else {
- String wzrk_pid = extras.getString(Constants.WZRK_PUSH_ID);
- String ttl = extras.getString(Constants.WZRK_TIME_TO_LIVE,
- (System.currentTimeMillis() + Constants.DEFAULT_PUSH_TTL) / 1000 + "");
- long wzrk_ttl = Long.parseLong(ttl);
- DBAdapter dbAdapter = loadDBAdapter(context);
- getConfigLogger().verbose("Storing Push Notification..." + wzrk_pid + " - with ttl - " + ttl);
- dbAdapter.storePushNotificationId(wzrk_pid, wzrk_ttl);
- }
- }
- });
- }
-
- private void processDiscardedEventsList(JSONObject response) {
- if (!response.has(Constants.DISCARDED_EVENT_JSON_KEY)) {
- getConfigLogger().verbose(getAccountId(), "ARP doesn't contain the Discarded Events key");
- return;
- }
-
- try {
- ArrayList discardedEventsList = new ArrayList<>();
- JSONArray discardedEventsArray = response.getJSONArray(Constants.DISCARDED_EVENT_JSON_KEY);
-
- if (discardedEventsArray != null) {
- for (int i = 0; i < discardedEventsArray.length(); i++) {
- discardedEventsList.add(discardedEventsArray.getString(i));
- }
- }
- if (validator != null) {
- validator.setDiscardedEvents(discardedEventsList);
- } else {
- getConfigLogger().verbose(getAccountId(), "Validator object is NULL");
- }
- } catch (JSONException e) {
- getConfigLogger()
- .verbose(getAccountId(), "Error parsing discarded events list" + e.getLocalizedMessage());
- }
- }
-
- /**
- * Logic for the processing of Display Unit response
- *
- * @param response - Display Unit json response object
- */
- private void processDisplayUnitsResponse(JSONObject response) {
- if (response == null) {
- getConfigLogger().verbose(getAccountId(), Constants.FEATURE_DISPLAY_UNIT
- + "Can't parse Display Unit Response, JSON response object is null");
- return;
- }
-
- if (!response.has(Constants.DISPLAY_UNIT_JSON_RESPONSE_KEY)) {
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_DISPLAY_UNIT + "JSON object doesn't contain the Display Units key");
- return;
- }
- try {
- getConfigLogger()
- .verbose(getAccountId(), Constants.FEATURE_DISPLAY_UNIT + "Processing Display Unit response");
- parseDisplayUnits(response.getJSONArray(Constants.DISPLAY_UNIT_JSON_RESPONSE_KEY));
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), Constants.FEATURE_DISPLAY_UNIT + "Failed to parse response", t);
- }
- }
-
- //Event
- private void processEvent(final Context context, final JSONObject event, final int eventType) {
- synchronized (eventLock) {
- try {
- activityCount = activityCount == 0 ? 1 : activityCount;
- String type;
- if (eventType == Constants.PAGE_EVENT) {
- type = "page";
- } else if (eventType == Constants.PING_EVENT) {
- type = "ping";
- attachMeta(event, context);
- if (event.has("bk")) {
- isBgPing = true;
- event.remove("bk");
- }
-
- //Add a flag to denote, PING event is for geofences
- if (isLocationForGeofence()) {
- event.put("gf", true);
- setLocationForGeofence(false);
- event.put("gfSDKVersion", getGeofenceSDKVersion());
- setGeofenceSDKVersion(0);
- }
- } else if (eventType == Constants.PROFILE_EVENT) {
- type = "profile";
- } else if (eventType == Constants.DATA_EVENT) {
- type = "data";
- } else {
- type = "event";
- }
-
- // Complete the received event with the other params
-
- String currentActivityName = getScreenName();
- if (currentActivityName != null) {
- event.put("n", currentActivityName);
- }
-
- int session = getCurrentSession();
- event.put("s", session);
- event.put("pg", activityCount);
- event.put("type", type);
- event.put("ep", System.currentTimeMillis() / 1000);
- event.put("f", isFirstSession());
- event.put("lsl", getLastSessionLength());
- attachPackageNameIfRequired(context, event);
-
- // Report any pending validation error
- ValidationResult vr = validationResultStack.popValidationResult();
- if (vr != null) {
- event.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
- getLocalDataStore().setDataSyncFlag(event);
- queueEventToDB(context, event, eventType);
- updateLocalStore(context, event, eventType);
- scheduleQueueFlush(context);
-
- } catch (Throwable e) {
- getConfigLogger().verbose(getAccountId(), "Failed to queue event: " + event.toString(), e);
- }
- }
- }
-
- private void processFeatureFlagsResponse(JSONObject response) {
- if (response == null) {
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_FLAG_UNIT + "Can't parse Feature Flags Response, JSON response object is null");
- return;
- }
-
- if (!response.has(Constants.FEATURE_FLAG_JSON_RESPONSE_KEY)) {
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_FLAG_UNIT + "JSON object doesn't contain the Feature Flags key");
- return;
- }
- try {
- getConfigLogger()
- .verbose(getAccountId(), Constants.FEATURE_FLAG_UNIT + "Processing Feature Flags response");
- parseFeatureFlags(response.getJSONObject(Constants.FEATURE_FLAG_JSON_RESPONSE_KEY));
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), Constants.FEATURE_FLAG_UNIT + "Failed to parse response", t);
- }
- }
-
- //ABTesting
-
- private void processGeofenceResponse(JSONObject response) {
- if (response == null) {
- getConfigLogger().verbose(getAccountId(),
- Constants.LOG_TAG_GEOFENCES + "Can't parse Geofences Response, JSON response object is null");
- return;
- }
-
- if (!response.has(Constants.GEOFENCES_JSON_RESPONSE_KEY)) {
- getConfigLogger().verbose(getAccountId(),
- Constants.LOG_TAG_GEOFENCES + "JSON object doesn't contain the Geofences key");
- return;
- }
- try {
- if (this.geofenceCallback != null) {
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("geofences", response.getJSONArray(Constants.GEOFENCES_JSON_RESPONSE_KEY));
-
- getConfigLogger()
- .verbose(getAccountId(), Constants.LOG_TAG_GEOFENCES + "Processing Geofences response");
- this.geofenceCallback.handleGeoFences(jsonObject);
- } else {
- getConfigLogger().debug(getAccountId(),
- Constants.LOG_TAG_GEOFENCES + "Geofence SDK has not been initialized to handle the response");
- }
- } catch (Throwable t) {
- getConfigLogger()
- .verbose(getAccountId(), Constants.LOG_TAG_GEOFENCES + "Failed to handle Geofences response", t);
- }
-
- }
-
- //InApp
- private void processInAppResponse(final JSONObject response, final Context context) {
- try {
- getConfigLogger().verbose(getAccountId(), "InApp: Processing response");
-
- if (!response.has("inapp_notifs")) {
- getConfigLogger().verbose(getAccountId(),
- "InApp: Response JSON object doesn't contain the inapp key, failing");
- return;
- }
-
- int perSession = 10;
- int perDay = 10;
- if (response.has(Constants.INAPP_MAX_PER_SESSION) && response
- .get(Constants.INAPP_MAX_PER_SESSION) instanceof Integer) {
- perSession = response.getInt(Constants.INAPP_MAX_PER_SESSION);
- }
-
- if (response.has("imp") && response.get("imp") instanceof Integer) {
- perDay = response.getInt("imp");
- }
-
- if (inAppFCManager != null) {
- Logger.v("Updating InAppFC Limits");
- inAppFCManager.updateLimits(context, perDay, perSession);
- }
-
- JSONArray inappNotifs;
- try {
- inappNotifs = response.getJSONArray(Constants.INAPP_JSON_RESPONSE_KEY);
- } catch (JSONException e) {
- getConfigLogger().debug(getAccountId(), "InApp: In-app key didn't contain a valid JSON array");
- return;
- }
-
- // Add all the new notifications to the queue
- SharedPreferences prefs = StorageHelper.getPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- try {
- JSONArray inappsFromPrefs = new JSONArray(
- StorageHelper.getStringFromPrefs(context, config, Constants.INAPP_KEY, "[]"));
-
- // Now add the rest of them :)
- if (inappNotifs != null && inappNotifs.length() > 0) {
- for (int i = 0; i < inappNotifs.length(); i++) {
- try {
- JSONObject inappNotif = inappNotifs.getJSONObject(i);
- inappsFromPrefs.put(inappNotif);
- } catch (JSONException e) {
- Logger.v("InAppManager: Malformed inapp notification");
- }
- }
- }
-
- // Commit all the changes
- editor.putString(StorageHelper.storageKeyWithSuffix(config, Constants.INAPP_KEY),
- inappsFromPrefs.toString());
- StorageHelper.persist(editor);
- } catch (Throwable e) {
- getConfigLogger().verbose(getAccountId(), "InApp: Failed to parse the in-app notifications properly");
- getConfigLogger().verbose(getAccountId(), "InAppManager: Reason: " + e.getMessage(), e);
- }
- // Fire the first notification, if any
- runOnNotificationQueue(new Runnable() {
- @Override
- public void run() {
- _showNotificationIfAvailable(context);
- }
- });
- } catch (Throwable t) {
- Logger.v("InAppManager: Failed to parse response", t);
- }
- }
-
- //NotificationInbox
- private void processInboxResponse(final JSONObject response) {
- if (getConfig().isAnalyticsOnly()) {
- getConfigLogger().verbose(getAccountId(),
- "CleverTap instance is configured to analytics only, not processing inbox messages");
- return;
- }
-
- getConfigLogger().verbose(getAccountId(), "Inbox: Processing response");
-
- if (!response.has("inbox_notifs")) {
- getConfigLogger().verbose(getAccountId(), "Inbox: Response JSON object doesn't contain the inbox key");
- return;
- }
- try {
- _processInboxMessages(response.getJSONArray("inbox_notifs"));
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "InboxResponse: Failed to parse response", t);
- }
- }
-
- private void processIncomingExperiments(JSONObject response) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- return;
- }
- try {
- JSONArray experimentsArray = response.getJSONArray("ab_exps");
- if (this.ctABTestController != null) {
- ctABTestController.updateExperiments(experimentsArray);
- }
- } catch (JSONException e) {
- getConfigLogger()
- .debug(config.getAccountId(), "Error parsing AB Testing response " + e.getLocalizedMessage());
- }
- }
-
- /**
- * Processes the incoming response headers for a change in domain and/or mute.
- *
- * @return True to continue sending requests, false otherwise.
- */
- private boolean processIncomingHeaders(final Context context, final HttpsURLConnection conn) {
-
- final String muteCommand = conn.getHeaderField(Constants.HEADER_MUTE);
- if (muteCommand != null && muteCommand.trim().length() > 0) {
- if (muteCommand.equals("true")) {
- setMuted(context, true);
- return false;
- } else {
- setMuted(context, false);
- }
- }
-
- final String domainName = conn.getHeaderField(Constants.HEADER_DOMAIN_NAME);
- Logger.v("Getting domain from header - " + domainName);
- if (domainName == null || domainName.trim().length() == 0) {
- return true;
- }
-
- final String spikyDomainName = conn.getHeaderField(Constants.SPIKY_HEADER_DOMAIN_NAME);
- Logger.v("Getting spiky domain from header - " + spikyDomainName);
-
- setMuted(context, false);
- setDomain(context, domainName);
- Logger.v("Setting spiky domain from header as -" + spikyDomainName);
- if (spikyDomainName == null) {
- setSpikyDomain(context, domainName);
- } else {
- setSpikyDomain(context, spikyDomainName);
- }
- return true;
- }
-
- private void processProductConfigResponse(JSONObject response) {
- if (response == null) {
- getConfigLogger().verbose(getAccountId(), Constants.LOG_TAG_PRODUCT_CONFIG
- + "Can't parse Product Config Response, JSON response object is null");
- onProductConfigFailed();
- return;
- }
-
- if (!response.has(Constants.REMOTE_CONFIG_FLAG_JSON_RESPONSE_KEY)) {
- getConfigLogger().verbose(getAccountId(),
- Constants.LOG_TAG_PRODUCT_CONFIG + "JSON object doesn't contain the Product Config key");
- onProductConfigFailed();
- return;
- }
- try {
- getConfigLogger()
- .verbose(getAccountId(), Constants.LOG_TAG_PRODUCT_CONFIG + "Processing Product Config response");
- parseProductConfigs(response.getJSONObject(Constants.REMOTE_CONFIG_FLAG_JSON_RESPONSE_KEY));
- } catch (Throwable t) {
- onProductConfigFailed();
- getConfigLogger().verbose(getAccountId(),
- Constants.LOG_TAG_PRODUCT_CONFIG + "Failed to parse Product Config response", t);
- }
- }
-
- private void processPushNotificationViewedEvent(final Context context, final JSONObject event) {
- synchronized (eventLock) {
- try {
- int session = getCurrentSession();
- event.put("s", session);
- event.put("type", "event");
- event.put("ep", System.currentTimeMillis() / 1000);
- // Report any pending validation error
- ValidationResult vr = validationResultStack.popValidationResult();
- if (vr != null) {
- event.put(Constants.ERROR_KEY, getErrorObject(vr));
- }
- getConfigLogger().verbose(getAccountId(), "Pushing Notification Viewed event onto DB");
- queuePushNotificationViewedEventToDB(context, event);
- getConfigLogger().verbose(getAccountId(), "Pushing Notification Viewed event onto queue flush");
- schedulePushNotificationViewedQueueFlush(context);
- } catch (Throwable t) {
- getConfigLogger()
- .verbose(getAccountId(), "Failed to queue notification viewed event: " + event.toString(), t);
- }
- }
- }
-
- //Event
- private void processResponse(final Context context, final String responseStr) {
- if (responseStr == null) {
- getConfigLogger().verbose(getAccountId(), "Problem processing queue response, response is null");
- return;
- }
- try {
- getConfigLogger().verbose(getAccountId(), "Trying to process response: " + responseStr);
-
- JSONObject response = new JSONObject(responseStr);
- try {
- if (!this.config.isAnalyticsOnly()) {
- processInAppResponse(response, context);
- }
- } catch (Throwable t) {
- getConfigLogger()
- .verbose(getAccountId(), "Failed to process in-app notifications from the response!", t);
- }
-
- // Always look for a GUID in the response, and if present, then perform a force update
- try {
- if (response.has("g")) {
- final String deviceID = response.getString("g");
- this.deviceInfo.forceUpdateDeviceId(deviceID);
- getConfigLogger().verbose(getAccountId(), "Got a new device ID: " + deviceID);
- }
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to update device ID!", t);
- }
-
- try {
- getLocalDataStore().syncWithUpstream(context, response);
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to sync local cache with upstream", t);
- }
-
- // Handle "arp" (additional request parameters)
- try {
- if (response.has("arp")) {
- final JSONObject arp = (JSONObject) response.get("arp");
- if (arp.length() > 0) {
- if (ctProductConfigController != null) {
- ctProductConfigController.setArpValue(arp);
- }
- //Handle Discarded events in ARP
- try {
- processDiscardedEventsList(arp);
- } catch (Throwable t) {
- getConfigLogger()
- .verbose("Error handling discarded events response: " + t.getLocalizedMessage());
- }
- handleARPUpdate(context, arp);
- }
- }
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to process ARP", t);
- }
-
- // Handle i
- try {
- if (response.has("_i")) {
- final long i = response.getLong("_i");
- setI(context, i);
- }
- } catch (Throwable t) {
- // Ignore
- }
-
- // Handle j
- try {
- if (response.has("_j")) {
- final long j = response.getLong("_j");
- setJ(context, j);
- }
- } catch (Throwable t) {
- // Ignore
- }
-
- // Handle "console" - print them as info to the console
- try {
- if (response.has("console")) {
- final JSONArray console = (JSONArray) response.get("console");
- if (console.length() > 0) {
- for (int i = 0; i < console.length(); i++) {
- getConfigLogger().debug(getAccountId(), console.get(i).toString());
- }
- }
- }
- } catch (Throwable t) {
- // Ignore
- }
-
- // Handle server set debug level
- try {
- if (response.has("dbg_lvl")) {
- final int debugLevel = response.getInt("dbg_lvl");
- if (debugLevel >= 0) {
- CleverTapAPI.setDebugLevel(debugLevel);
- getConfigLogger().verbose(getAccountId(),
- "Set debug level to " + debugLevel + " for this session (set by upstream)");
- }
- }
- } catch (Throwable t) {
- // Ignore
- }
-
- // Handle stale_inapp
- try {
- if (inAppFCManager != null) {
- inAppFCManager.processResponse(context, response);
- }
- } catch (Throwable t) {
- // Ignore
- }
-
- //Handle notification inbox response
- if (!getConfig().isAnalyticsOnly()) {
- try {
- getConfigLogger().verbose(getAccountId(), "Processing inbox messages...");
- processInboxResponse(response);
- } catch (Throwable t) {
- getConfigLogger().verbose("Notification inbox exception: " + t.getLocalizedMessage());
- }
- }
-
- //Handle Push Amplification response
- if (!getConfig().isAnalyticsOnly()) {
- try {
- if (response.has("pushamp_notifs")) {
- getConfigLogger().verbose(getAccountId(), "Processing pushamp messages...");
- JSONObject pushAmpObject = response.getJSONObject("pushamp_notifs");
- final JSONArray pushNotifications = pushAmpObject.getJSONArray("list");
- if (pushNotifications.length() > 0) {
- getConfigLogger().verbose(getAccountId(), "Handling Push payload locally");
- handlePushNotificationsInResponse(pushNotifications);
- }
- if (pushAmpObject.has("pf")) {
- try {
- int frequency = pushAmpObject.getInt("pf");
- updatePingFrequencyIfNeeded(context, frequency);
- } catch (Throwable t) {
- getConfigLogger()
- .verbose("Error handling ping frequency in response : " + t.getMessage());
- }
-
- }
- if (pushAmpObject.has("ack")) {
- boolean ack = pushAmpObject.getBoolean("ack");
- getConfigLogger().verbose("Received ACK -" + ack);
- if (ack) {
- JSONArray rtlArray = getRenderedTargetList(this.dbAdapter);
- String[] rtlStringArray = new String[0];
- if (rtlArray != null) {
- rtlStringArray = new String[rtlArray.length()];
- }
- for (int i = 0; i < rtlStringArray.length; i++) {
- rtlStringArray[i] = rtlArray.getString(i);
- }
- getConfigLogger().verbose("Updating RTL values...");
- this.dbAdapter.updatePushNotificationIds(rtlStringArray);
- }
- }
- }
- } catch (Throwable t) {
- //Ignore
- }
- }
-
- //Handle ABTesting response
- if (!getConfig().isAnalyticsOnly()) {
- try {
- if (response.has("ab_exps")) {
- getConfigLogger().verbose(getAccountId(), "Processing ABTest experiments...");
- processIncomingExperiments(response);
- }
- } catch (Throwable t) {
- getConfigLogger().verbose("Error handling AB Testing response : " + t.getMessage());
- }
- }
-
- //Handle Display Unit response
- if (!getConfig().isAnalyticsOnly()) {
- try {
- getConfigLogger().verbose(getAccountId(), "Processing Display Unit items...");
- processDisplayUnitsResponse(response);
- } catch (Throwable t) {
- getConfigLogger().verbose("Error handling Display Unit response: " + t.getLocalizedMessage());
- }
- }
-
- //Handle Feature Flag response
- if (!getConfig().isAnalyticsOnly()) {
- try {
- getConfigLogger().verbose(getAccountId(), "Processing Feature Flags response...");
- processFeatureFlagsResponse(response);
- } catch (Throwable t) {
- getConfigLogger().verbose("Error handling Feature Flags response: " + t.getLocalizedMessage());
- }
- }
-
- //Handle Product Config response
- if (!getConfig().isAnalyticsOnly()) {
- try {
- getConfigLogger().verbose(getAccountId(), "Processing Product Config response...");
- processProductConfigResponse(response);
- } catch (Throwable t) {
- getConfigLogger().verbose("Error handling Product Config response: " + t.getLocalizedMessage());
- }
- }
-
- //Handle GeoFences Response
- if (!getConfig().isAnalyticsOnly()) {
- try {
- getConfigLogger().verbose(getAccountId(), "Processing GeoFences response...");
- processGeofenceResponse(response);
- } catch (Throwable t) {
- getConfigLogger().verbose("Error handling GeoFences response: " + t.getLocalizedMessage());
- }
- }
- } catch (Throwable t) {
- mResponseFailureCount++;
- getConfigLogger().verbose(getAccountId(), "Problem process send queue response", t);
- }
- }
-
- private void pushAmazonRegistrationId(String token, boolean register) {
- pushProviders.handleToken(token, PushType.ADM, register);
- }
-
- //Event
- private void pushAppLaunchedEvent() {
- if (isAppLaunchReportingDisabled()) {
- setAppLaunchPushed(true);
- getConfigLogger().debug(getAccountId(), "App Launched Events disabled in the Android Manifest file");
- return;
- }
- if (isAppLaunchPushed()) {
- getConfigLogger()
- .verbose(getAccountId(), "App Launched has already been triggered. Will not trigger it ");
- return;
- } else {
- getConfigLogger().verbose(getAccountId(), "Firing App Launched event");
- }
- setAppLaunchPushed(true);
- JSONObject event = new JSONObject();
- try {
- event.put("evtName", Constants.APP_LAUNCHED_EVENT);
- event.put("evtData", getAppLaunchedFields());
- } catch (Throwable t) {
- // We won't get here
- }
- queueEvent(context, event, Constants.RAISED_EVENT);
- }
-
- //Profile
- private void pushBasicProfile(JSONObject baseProfile) {
- try {
- String guid = getCleverTapID();
-
- JSONObject profileEvent = new JSONObject();
-
- if (baseProfile != null && baseProfile.length() > 0) {
- Iterator i = baseProfile.keys();
- IdentityRepo iProfileHandler = IdentityRepoFactory.getRepo(this);
- LoginInfoProvider loginInfoProvider = new LoginInfoProvider(this);
- while (i.hasNext()) {
- String next = i.next().toString();
-
- // need to handle command-based JSONObject props here now
- Object value = null;
- try {
- value = baseProfile.getJSONObject(next);
- } catch (Throwable t) {
- try {
- value = baseProfile.get(next);
- } catch (JSONException e) {
- //no-op
- }
- }
-
- if (value != null) {
- profileEvent.put(next, value);
-
- // cache the valid identifier: guid pairs
- boolean isProfileKey = iProfileHandler.hasIdentity(next);
- if (isProfileKey) {
- try {
- loginInfoProvider.cacheGUIDForIdentifier(guid, next, value.toString());
- } catch (Throwable t) {
- // no-op
- }
- }
- }
- }
- }
-
- try {
- String carrier = this.deviceInfo.getCarrier();
- if (carrier != null && !carrier.equals("")) {
- profileEvent.put("Carrier", carrier);
- }
-
- String cc = this.deviceInfo.getCountryCode();
- if (cc != null && !cc.equals("")) {
- profileEvent.put("cc", cc);
- }
-
- profileEvent.put("tz", TimeZone.getDefault().getID());
-
- JSONObject event = new JSONObject();
- event.put("profile", profileEvent);
- queueEvent(context, event, Constants.PROFILE_EVENT);
- } catch (JSONException e) {
- getConfigLogger().verbose(getAccountId(), "FATAL: Creating basic profile update event failed!");
- }
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Basic profile sync", t);
- }
- }
-
- private synchronized void pushDeepLink(Uri uri, boolean install) {
- if (uri == null) {
- return;
- }
-
- try {
- JSONObject referrer = UriHelper.getUrchinFromUri(uri);
- if (referrer.has("us")) {
- setSource(referrer.get("us").toString());
- }
- if (referrer.has("um")) {
- setMedium(referrer.get("um").toString());
- }
- if (referrer.has("uc")) {
- setCampaign(referrer.get("uc").toString());
- }
-
- referrer.put("referrer", uri.toString());
- if (install) {
- referrer.put("install", true);
- }
- recordPageEventWithExtras(referrer);
-
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Failed to push deep link", t);
- }
- }
-
- //Event
- private void pushInitialEventsAsync() {
- postAsyncSafely("CleverTapAPI#pushInitialEventsAsync", new Runnable() {
- @Override
- public void run() {
- try {
- getConfigLogger().verbose(getAccountId(), "Queuing daily events");
- pushBasicProfile(null);
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Daily profile sync failed", t);
- }
- }
- });
- }
-
- //Event
- private Future> queueEvent(final Context context, final JSONObject event, final int eventType) {
- return postAsyncSafely("queueEvent", new Runnable() {
- @Override
- public void run() {
- if (shouldDropEvent(event, eventType)) {
- return;
- }
- if (shouldDeferProcessingEvent(event, eventType)) {
- getConfigLogger().debug(getAccountId(),
- "App Launched not yet processed, re-queuing event " + event + "after 2s");
- getHandlerUsingMainLooper().postDelayed(new Runnable() {
- @Override
- public void run() {
- postAsyncSafely("queueEventWithDelay", new Runnable() {
- @Override
- public void run() {
- lazyCreateSession(context);
- addToQueue(context, event, eventType);
- }
- });
- }
- }, 2000);
- } else {
- if (eventType == Constants.FETCH_EVENT) {
- addToQueue(context, event, eventType);
- } else {
- lazyCreateSession(context);
- addToQueue(context, event, eventType);
- }
- }
- }
- });
- }
-
- private void queueEventInternal(final Context context, final JSONObject event, DBAdapter.Table table) {
- synchronized (eventLock) {
- DBAdapter adapter = loadDBAdapter(context);
- int returnCode = adapter.storeObject(event, table);
-
- if (returnCode > 0) {
- getConfigLogger().debug(getAccountId(), "Queued event: " + event.toString());
- getConfigLogger()
- .verbose(getAccountId(), "Queued event to DB table " + table + ": " + event.toString());
- }
- }
- }
-
- //Event
- private void queueEventToDB(final Context context, final JSONObject event, final int type) {
- DBAdapter.Table table = (type == Constants.PROFILE_EVENT) ? DBAdapter.Table.PROFILE_EVENTS
- : DBAdapter.Table.EVENTS;
- queueEventInternal(context, event, table);
- }
-
- private void queuePushNotificationViewedEventToDB(final Context context, final JSONObject event) {
- queueEventInternal(context, event, DBAdapter.Table.PUSH_NOTIFICATION_VIEWED);
- }
-
- private Future> raiseEventForGeofences(String eventName, JSONObject geofenceProperties) {
-
- Future> future = null;
-
- JSONObject event = new JSONObject();
- try {
- event.put("evtName", eventName);
- event.put("evtData", geofenceProperties);
-
- Location location = new Location("");
- location.setLatitude(geofenceProperties.getDouble("triggered_lat"));
- location.setLongitude(geofenceProperties.getDouble("triggered_lng"));
-
- geofenceProperties.remove("triggered_lat");
- geofenceProperties.remove("triggered_lng");
-
- locationFromUser = location;
-
- future = queueEvent(context, event, Constants.RAISED_EVENT);
- } catch (JSONException e) {
- getConfigLogger().debug(getAccountId(), Constants.LOG_TAG_GEOFENCES +
- "JSON Exception when raising GeoFence event "
- + eventName + " - " + e.getLocalizedMessage());
- }
-
- return future;
- }
-
- private void recordDeviceIDErrors() {
- for (ValidationResult validationResult : this.deviceInfo.getValidationResults()) {
- validationResultStack.pushValidationResult(validationResult);
- }
- }
-
- //Event
- private void recordPageEventWithExtras(JSONObject extras) {
- try {
- JSONObject jsonObject = new JSONObject();
- // Add the extras
- if (extras != null && extras.length() > 0) {
- Iterator keys = extras.keys();
- while (keys.hasNext()) {
- try {
- String key = (String) keys.next();
- jsonObject.put(key, extras.getString(key));
- } catch (ClassCastException ignore) {
- // Really won't get here
- }
- }
- }
- queueEvent(context, jsonObject, Constants.PAGE_EVENT);
- } catch (Throwable t) {
- // We won't get here
- }
- }
-
- private void resetABTesting() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- return;
- }
- if (!this.config.isAnalyticsOnly()) {
- if (!config.isABTestingEnabled()) {
- getConfigLogger().debug(config.getAccountId(), "AB Testing is not enabled for this instance");
- return;
- }
- }
- if (ctABTestController != null) {
- ctABTestController.resetWithGuid(getCleverTapID());
- }
- }
-
- private void resetAlarmScheduler(Context context) {
- if (getPingFrequency(context) <= 0) {
- stopAlarmScheduler(context);
- } else {
- stopAlarmScheduler(context);
- createAlarmScheduler(context);
- }
- }
-
- /**
- * Resets the Display Units in the cache
- */
- private void resetDisplayUnits() {
- if (mCTDisplayUnitController != null) {
- mCTDisplayUnitController.reset();
- } else {
- getConfigLogger().verbose(getAccountId(),
- Constants.FEATURE_DISPLAY_UNIT + "Can't reset Display Units, DisplayUnitcontroller is null");
- }
- }
-
- private void resetFeatureFlags() {
- if (ctFeatureFlagsController != null && ctFeatureFlagsController.isInitialized()) {
- ctFeatureFlagsController.resetWithGuid(getCleverTapID());
- ctFeatureFlagsController.fetchFeatureFlags();
- }
- }
-
- // always call async
- private void resetInbox() {
- synchronized (inboxControllerLock) {
- this.ctInboxController = null;
- }
- _initializeInbox();
- }
-
- private void resetProductConfigs() {
- if (this.config.isAnalyticsOnly()) {
- getConfigLogger().debug(config.getAccountId(), "Product Config is not enabled for this instance");
- return;
- }
- if (ctProductConfigController != null) {
- ctProductConfigController.resetSettings();
- }
- ctProductConfigController = new CTProductConfigController(context, getCleverTapID(), config, this);
- getConfigLogger().verbose(config.getAccountId(), "Product Config reset");
- }
-
- private void runInstanceJobWork(final Context context, final JobParameters parameters) {
- postAsyncSafely("runningJobService", new Runnable() {
- @Override
- public void run() {
- if (pushProviders.isNotificationSupported()) {
- Logger.v(getAccountId(), "Token is not present, not running the Job");
- return;
- }
-
- Calendar now = Calendar.getInstance();
-
- int hour = now.get(Calendar.HOUR_OF_DAY); // Get hour in 24 hour format
- int minute = now.get(Calendar.MINUTE);
-
- Date currentTime = parseTimeToDate(hour + ":" + minute);
- Date startTime = parseTimeToDate(Constants.DND_START);
- Date endTime = parseTimeToDate(Constants.DND_STOP);
-
- if (isTimeBetweenDNDTime(startTime, endTime, currentTime)) {
- Logger.v(getAccountId(), "Job Service won't run in default DND hours");
- return;
- }
-
- long lastTS = loadDBAdapter(context).getLastUninstallTimestamp();
-
- if (lastTS == 0 || lastTS > System.currentTimeMillis() - 24 * 60 * 60 * 1000) {
- try {
- JSONObject eventObject = new JSONObject();
- eventObject.put("bk", 1);
- queueEvent(context, eventObject, Constants.PING_EVENT);
-
- if (parameters == null) {
- int pingFrequency = getPingFrequency(context);
- AlarmManager alarmManager = (AlarmManager) context
- .getSystemService(Context.ALARM_SERVICE);
- Intent cancelIntent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- cancelIntent.setPackage(context.getPackageName());
- PendingIntent alarmPendingIntent = PendingIntent
- .getService(context, getAccountId().hashCode(), cancelIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- if (alarmManager != null) {
- alarmManager.cancel(alarmPendingIntent);
- }
- Intent alarmIntent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- alarmIntent.setPackage(context.getPackageName());
- PendingIntent alarmServicePendingIntent = PendingIntent
- .getService(context, getAccountId().hashCode(), alarmIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- if (alarmManager != null) {
- if (pingFrequency != -1) {
- alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + (pingFrequency
- * Constants.ONE_MIN_IN_MILLIS),
- Constants.ONE_MIN_IN_MILLIS * pingFrequency, alarmServicePendingIntent);
- }
- }
- }
- } catch (JSONException e) {
- Logger.v("Unable to raise background Ping event");
- }
-
- }
- }
- });
- }
-
- //InApp
- private void runOnNotificationQueue(final Runnable runnable) {
- try {
- final boolean executeSync = Thread.currentThread().getId() == NOTIFICATION_THREAD_ID;
-
- if (executeSync) {
- runnable.run();
- } else {
- ns.submit(new Runnable() {
- @Override
- public void run() {
- NOTIFICATION_THREAD_ID = Thread.currentThread().getId();
- try {
- runnable.run();
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(),
- "Notification executor service: Failed to complete the scheduled task", t);
- }
- }
- });
- }
- } catch (Throwable t) {
+ public void setOffline(boolean value) {
+ coreState.getCoreMetaData().setOffline(value);
+ if (value) {
getConfigLogger()
- .verbose(getAccountId(), "Failed to submit task to the notification executor service", t);
- }
- }
-
- private void schedulePushNotificationViewedQueueFlush(final Context context) {
- if (pushNotificationViewedRunnable == null) {
- pushNotificationViewedRunnable = new Runnable() {
- @Override
- public void run() {
- getConfigLogger()
- .verbose(getAccountId(), "Pushing Notification Viewed event onto queue flush async");
- flushQueueAsync(context, EventGroup.PUSH_NOTIFICATION_VIEWED);
- }
- };
- }
- getHandlerUsingMainLooper().removeCallbacks(pushNotificationViewedRunnable);
- getHandlerUsingMainLooper().post(pushNotificationViewedRunnable);
- }
-
- //Event
- private void scheduleQueueFlush(final Context context) {
- if (commsRunnable == null) {
- commsRunnable = new Runnable() {
- @Override
- public void run() {
- flushQueueAsync(context, EventGroup.REGULAR);
- flushQueueAsync(context, EventGroup.PUSH_NOTIFICATION_VIEWED);
- }
- };
- }
- // Cancel any outstanding send runnables, and issue a new delayed one
- getHandlerUsingMainLooper().removeCallbacks(commsRunnable);
- getHandlerUsingMainLooper().postDelayed(commsRunnable, getDelayFrequency());
-
- getConfigLogger().verbose(getAccountId(), "Scheduling delayed queue flush on main event loop");
- }
-
- /**
- * @return true if the network request succeeded. Anything non 200 results in a false.
- */
- private boolean sendQueue(final Context context, final EventGroup eventGroup, final JSONArray queue) {
-
- if (queue == null || queue.length() <= 0) {
- return false;
- }
-
- if (getCleverTapID() == null) {
- getConfigLogger().debug(getAccountId(), "CleverTap Id not finalized, unable to send queue");
- return false;
- }
-
- HttpsURLConnection conn = null;
- try {
- final String endpoint = getEndpoint(false, eventGroup);
-
- // This is just a safety check, which would only arise
- // if upstream didn't adhere to the protocol (sent nothing during the initial handshake)
- if (endpoint == null) {
- getConfigLogger().debug(getAccountId(), "Problem configuring queue endpoint, unable to send queue");
- return false;
- }
-
- conn = buildHttpsURLConnection(endpoint);
-
- final String body;
- final String req = insertHeader(context, queue);
- if (req == null) {
- getConfigLogger().debug(getAccountId(), "Problem configuring queue request, unable to send queue");
- return false;
- }
-
- getConfigLogger().debug(getAccountId(), "Send queue contains " + queue.length() + " items: " + req);
- getConfigLogger().debug(getAccountId(), "Sending queue to: " + endpoint);
- conn.setDoOutput(true);
- // noinspection all
- conn.getOutputStream().write(req.getBytes("UTF-8"));
-
- final int responseCode = conn.getResponseCode();
-
- // Always check for a 200 OK
- if (responseCode != 200) {
- throw new IOException("Response code is not 200. It is " + responseCode);
- }
-
- // Check for a change in domain
- final String newDomain = conn.getHeaderField(Constants.HEADER_DOMAIN_NAME);
- if (newDomain != null && newDomain.trim().length() > 0) {
- if (hasDomainChanged(newDomain)) {
- // The domain has changed. Return a status of -1 so that the caller retries
- setDomain(context, newDomain);
- getConfigLogger().debug(getAccountId(),
- "The domain has changed to " + newDomain + ". The request will be retried shortly.");
- return false;
- }
- }
-
- if (processIncomingHeaders(context, conn)) {
- // noinspection all
- BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
-
- StringBuilder sb = new StringBuilder();
- String line;
- while ((line = br.readLine()) != null) {
- sb.append(line);
- }
- body = sb.toString();
- processResponse(context, body);
- }
-
- setLastRequestTimestamp(context, currentRequestTimestamp);
- setFirstRequestTimestampIfNeeded(context, currentRequestTimestamp);
-
- getConfigLogger().debug(getAccountId(), "Queue sent successfully");
-
- mResponseFailureCount = 0;
- networkRetryCount = 0; //reset retry count when queue is sent successfully
- return true;
- } catch (Throwable e) {
- getConfigLogger().debug(getAccountId(),
- "An exception occurred while sending the queue, will retry: " + e.getLocalizedMessage());
- mResponseFailureCount++;
- networkRetryCount++;
- scheduleQueueFlush(context);
- return false;
- } finally {
- if (conn != null) {
- try {
- conn.getInputStream().close();
- conn.disconnect();
- } catch (Throwable t) {
- // Ignore
- }
- }
- }
- }
-
- // -----------------------------------------------------------------------//
- // ******** Display Unit LOGIC *****//
- // -----------------------------------------------------------------------//
-
- private void setCurrentUserOptOutStateFromStorage() {
- String key = optOutKey();
- if (key == null) {
- getConfigLogger().verbose(getAccountId(),
- "Unable to set current user OptOut state from storage: storage key is null");
- return;
- }
- boolean storedOptOut = StorageHelper.getBooleanFromPrefs(context, config, key);
- setCurrentUserOptedOut(storedOptOut);
- getConfigLogger().verbose(getAccountId(),
- "Set current user OptOut state from storage to: " + storedOptOut + " for key: " + key);
- }
-
- private void setDeviceNetworkInfoReportingFromStorage() {
- boolean enabled = StorageHelper.getBooleanFromPrefs(context, config, Constants.NETWORK_INFO);
- getConfigLogger()
- .verbose(getAccountId(), "Setting device network info reporting state from storage to " + enabled);
- enableNetworkInfoReporting = enabled;
- }
-
- private void setDomain(final Context context, String domainName) {
- getConfigLogger().verbose(getAccountId(), "Setting domain to " + domainName);
- StorageHelper.putString(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_DOMAIN_NAME),
- domainName);
- }
-
- private void setFirstRequestTimestampIfNeeded(Context context, int ts) {
- if (getFirstRequestTimestamp() > 0) {
- return;
- }
- StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_FIRST_TS), ts);
- }
-
- @SuppressLint("CommitPrefEdits")
- private void setI(Context context, long i) {
- final SharedPreferences prefs = StorageHelper.getPreferences(context, Constants.NAMESPACE_IJ);
- final SharedPreferences.Editor editor = prefs.edit();
- editor.putLong(StorageHelper.storageKeyWithSuffix(config, Constants.KEY_I), i);
- StorageHelper.persist(editor);
- }
-
- @SuppressLint("CommitPrefEdits")
- private void setJ(Context context, long j) {
- final SharedPreferences prefs = StorageHelper.getPreferences(context, Constants.NAMESPACE_IJ);
- final SharedPreferences.Editor editor = prefs.edit();
- editor.putLong(StorageHelper.storageKeyWithSuffix(config, Constants.KEY_J), j);
- StorageHelper.persist(editor);
- }
-
- //Session
- private void setLastRequestTimestamp(Context context, int ts) {
- StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_LAST_TS), ts);
- }
-
- //Session
- private void setLastVisitTime() {
- EventDetail ed = getLocalDataStore().getEventDetail(Constants.APP_LAUNCHED_EVENT);
- if (ed == null) {
- lastVisitTime = -1;
- } else {
- lastVisitTime = ed.getLastTime();
- }
- }
-
- //Util
- private void setMuted(final Context context, boolean mute) {
- if (mute) {
- final int now = (int) (System.currentTimeMillis() / 1000);
- StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_MUTED), now);
- setDomain(context, null);
-
- // Clear all the queues
- postAsyncSafely("CommsManager#setMuted", new Runnable() {
- @Override
- public void run() {
- clearQueues(context);
- }
- });
- } else {
- StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_MUTED), 0);
- }
- }
-
- private void setPingFrequency(Context context, int pingFrequency) {
- StorageHelper.putInt(context, Constants.PING_FREQUENCY, pingFrequency);
- }
-
- // -----------------------------------------------------------------------//
- // ******** Feature Flags Logic *****//
- // -----------------------------------------------------------------------//
-
- // ******** Feature Flags Public API *****//
-
- private void setSpikyDomain(final Context context, String spikyDomainName) {
- getConfigLogger().verbose(getAccountId(), "Setting spiky domain to " + spikyDomainName);
- StorageHelper.putString(context, StorageHelper.storageKeyWithSuffix(config, Constants.SPIKY_KEY_DOMAIN_NAME),
- spikyDomainName);
- }
-
- private boolean shouldDeferProcessingEvent(JSONObject event, int eventType) {
- //noinspection SimplifiableIfStatement
- if (getConfig().isCreatedPostAppLaunch()) {
- return false;
- }
- if (event.has("evtName")) {
- try {
- if (Arrays.asList(Constants.SYSTEM_EVENTS).contains(event.getString("evtName"))) {
- return false;
- }
- } catch (JSONException e) {
- //no-op
- }
- }
- return (eventType == Constants.RAISED_EVENT && !isAppLaunchPushed());
- }
-
- // ******** Feature Flags Internal methods *****//
-
- private boolean shouldDropEvent(JSONObject event, int eventType) {
- if (eventType == Constants.FETCH_EVENT) {
- return false;
- }
-
- if (isCurrentUserOptedOut()) {
- String eventString = event == null ? "null" : event.toString();
- getConfigLogger().debug(getAccountId(), "Current user is opted out dropping event: " + eventString);
- return true;
- }
-
- if (isMuted()) {
- getConfigLogger().verbose(getAccountId(), "CleverTap is muted, dropping event - " + event.toString());
- return true;
- }
-
- return false;
- }
-
- private void showInAppNotificationIfAny() {
- if (!this.config.isAnalyticsOnly()) {
- runOnNotificationQueue(new Runnable() {
- @Override
- public void run() {
- _showNotificationIfAvailable(context);
- }
- });
- }
- }
-
- //InApp
- @SuppressWarnings({"unused"})
- private void showNotificationIfAvailable(final Context context) {
- if (!this.config.isAnalyticsOnly()) {
- runOnNotificationQueue(new Runnable() {
- @Override
- public void run() {
- _showNotificationIfAvailable(context);
- }
- });
- }
- }
-
- private void stopAlarmScheduler(Context context) {
- AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- Intent cancelIntent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- cancelIntent.setPackage(context.getPackageName());
- PendingIntent alarmPendingIntent = PendingIntent
- .getService(context, getAccountId().hashCode(), cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- if (alarmManager != null && alarmPendingIntent != null) {
- alarmManager.cancel(alarmPendingIntent);
- }
- }
-
- private void triggerNotification(Context context, Bundle extras, String notifMessage, String notifTitle,
- int notificationId) {
- NotificationManager notificationManager =
- (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
-
- if (notificationManager == null) {
- String notificationManagerError = "Unable to render notification, Notification Manager is null.";
- getConfigLogger().debug(getAccountId(), notificationManagerError);
- return;
- }
-
- String channelId = extras.getString(Constants.WZRK_CHANNEL_ID, "");
- boolean requiresChannelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- int messageCode = -1;
- String value = "";
-
- if (channelId.isEmpty()) {
- messageCode = Constants.CHANNEL_ID_MISSING_IN_PAYLOAD;
- value = extras.toString();
- } else if (notificationManager.getNotificationChannel(channelId) == null) {
- messageCode = Constants.CHANNEL_ID_NOT_REGISTERED;
- value = channelId;
- }
- if (messageCode != -1) {
- ValidationResult channelIdError = ValidationResultFactory.create(512, messageCode, value);
- getConfigLogger().debug(getAccountId(), channelIdError.getErrorDesc());
- validationResultStack.pushValidationResult(channelIdError);
- return;
- }
- }
-
- String icoPath = extras.getString(Constants.NOTIF_ICON);
- Intent launchIntent = new Intent(context, CTPushNotificationReceiver.class);
-
- PendingIntent pIntent;
-
- // Take all the properties from the notif and add it to the intent
- launchIntent.putExtras(extras);
- launchIntent.removeExtra(Constants.WZRK_ACTIONS);
- pIntent = PendingIntent.getBroadcast(context, (int) System.currentTimeMillis(),
- launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- NotificationCompat.Style style;
- String bigPictureUrl = extras.getString(Constants.WZRK_BIG_PICTURE);
- if (bigPictureUrl != null && bigPictureUrl.startsWith("http")) {
- try {
- Bitmap bpMap = Utils.getNotificationBitmap(bigPictureUrl, false, context);
-
- if (bpMap == null) {
- throw new Exception("Failed to fetch big picture!");
- }
-
- if (extras.containsKey(Constants.WZRK_MSG_SUMMARY)) {
- String summaryText = extras.getString(Constants.WZRK_MSG_SUMMARY);
- style = new NotificationCompat.BigPictureStyle()
- .setSummaryText(summaryText)
- .bigPicture(bpMap);
- } else {
- style = new NotificationCompat.BigPictureStyle()
- .setSummaryText(notifMessage)
- .bigPicture(bpMap);
- }
- } catch (Throwable t) {
- style = new NotificationCompat.BigTextStyle()
- .bigText(notifMessage);
- getConfigLogger()
- .verbose(getAccountId(), "Falling back to big text notification, couldn't fetch big picture",
- t);
- }
- } else {
- style = new NotificationCompat.BigTextStyle()
- .bigText(notifMessage);
- }
-
- int smallIcon;
- try {
- String x = ManifestInfo.getInstance(context).getNotificationIcon();
- if (x == null) {
- throw new IllegalArgumentException();
- }
- smallIcon = context.getResources().getIdentifier(x, "drawable", context.getPackageName());
- if (smallIcon == 0) {
- throw new IllegalArgumentException();
- }
- } catch (Throwable t) {
- smallIcon = DeviceInfo.getAppIconAsIntId(context);
- }
-
- int priorityInt = NotificationCompat.PRIORITY_DEFAULT;
- String priority = extras.getString(Constants.NOTIF_PRIORITY);
- if (priority != null) {
- if (priority.equals(Constants.PRIORITY_HIGH)) {
- priorityInt = NotificationCompat.PRIORITY_HIGH;
- }
- if (priority.equals(Constants.PRIORITY_MAX)) {
- priorityInt = NotificationCompat.PRIORITY_MAX;
- }
- }
-
- // if we have no user set notificationID then try collapse key
- if (notificationId == Constants.EMPTY_NOTIFICATION_ID) {
- try {
- Object collapse_key = extras.get(Constants.WZRK_COLLAPSE);
- if (collapse_key != null) {
- if (collapse_key instanceof Number) {
- notificationId = ((Number) collapse_key).intValue();
- } else if (collapse_key instanceof String) {
- try {
- notificationId = Integer.parseInt(collapse_key.toString());
- getConfigLogger().debug(getAccountId(),
- "Converting collapse_key: " + collapse_key + " to notificationId int: "
- + notificationId);
- } catch (NumberFormatException e) {
- notificationId = (collapse_key.toString().hashCode());
- getConfigLogger().debug(getAccountId(),
- "Converting collapse_key: " + collapse_key + " to notificationId int: "
- + notificationId);
- }
- }
- }
- } catch (NumberFormatException e) {
- // no-op
- }
- } else {
- getConfigLogger().debug(getAccountId(), "Have user provided notificationId: " + notificationId
- + " won't use collapse_key (if any) as basis for notificationId");
- }
-
- // if after trying collapse_key notification is still empty set to random int
- if (notificationId == Constants.EMPTY_NOTIFICATION_ID) {
- notificationId = (int) (Math.random() * 100);
- getConfigLogger().debug(getAccountId(), "Setting random notificationId: " + notificationId);
- }
-
- NotificationCompat.Builder nb;
- if (requiresChannelId) {
- nb = new NotificationCompat.Builder(context, channelId);
-
- // choices here are Notification.BADGE_ICON_NONE = 0, Notification.BADGE_ICON_SMALL = 1, Notification.BADGE_ICON_LARGE = 2. Default is Notification.BADGE_ICON_LARGE
- String badgeIconParam = extras.getString(Constants.WZRK_BADGE_ICON, null);
- if (badgeIconParam != null) {
- try {
- int badgeIconType = Integer.parseInt(badgeIconParam);
- if (badgeIconType >= 0) {
- nb.setBadgeIconType(badgeIconType);
- }
- } catch (Throwable t) {
- // no-op
- }
- }
-
- String badgeCountParam = extras.getString(Constants.WZRK_BADGE_COUNT, null);
- if (badgeCountParam != null) {
- try {
- int badgeCount = Integer.parseInt(badgeCountParam);
- if (badgeCount >= 0) {
- nb.setNumber(badgeCount);
- }
- } catch (Throwable t) {
- // no-op
- }
- }
- if (extras.containsKey(Constants.WZRK_SUBTITLE)) {
- nb.setSubText(extras.getString(Constants.WZRK_SUBTITLE));
- }
- } else {
- // noinspection all
- nb = new NotificationCompat.Builder(context);
- }
-
- if (extras.containsKey(Constants.WZRK_COLOR)) {
- int color = Color.parseColor(extras.getString(Constants.WZRK_COLOR));
- nb.setColor(color);
- nb.setColorized(true);
- }
-
- nb.setContentTitle(notifTitle)
- .setContentText(notifMessage)
- .setContentIntent(pIntent)
- .setAutoCancel(true)
- .setStyle(style)
- .setPriority(priorityInt)
- .setSmallIcon(smallIcon);
-
- nb.setLargeIcon(Utils.getNotificationBitmap(icoPath, true, context));
-
- try {
- if (extras.containsKey(Constants.WZRK_SOUND)) {
- Uri soundUri = null;
-
- Object o = extras.get(Constants.WZRK_SOUND);
-
- if ((o instanceof Boolean && (Boolean) o)) {
- soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
- } else if (o instanceof String) {
- String s = (String) o;
- if (s.equals("true")) {
- soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
- } else if (!s.isEmpty()) {
- if (s.contains(".mp3") || s.contains(".ogg") || s.contains(".wav")) {
- s = s.substring(0, (s.length() - 4));
- }
- soundUri = Uri
- .parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName()
- + "/raw/" + s);
-
- }
- }
-
- if (soundUri != null) {
- nb.setSound(soundUri);
- }
- }
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Could not process sound parameter", t);
- }
-
- // add actions if any
- JSONArray actions = null;
- String actionsString = extras.getString(Constants.WZRK_ACTIONS);
- if (actionsString != null) {
- try {
- actions = new JSONArray(actionsString);
- } catch (Throwable t) {
- getConfigLogger()
- .debug(getAccountId(), "error parsing notification actions: " + t.getLocalizedMessage());
- }
- }
-
- String intentServiceName = ManifestInfo.getInstance(context).getIntentServiceName();
- Class clazz = null;
- if (intentServiceName != null) {
- try {
- clazz = Class.forName(intentServiceName);
- } catch (ClassNotFoundException e) {
- try {
- clazz = Class.forName("com.clevertap.android.sdk.pushnotification.CTNotificationIntentService");
- } catch (ClassNotFoundException ex) {
- Logger.d("No Intent Service found");
- }
- }
+ .debug(getAccountId(), "CleverTap Instance has been set to offline, won't send events queue");
} else {
- try {
- clazz = Class.forName("com.clevertap.android.sdk.pushnotification.CTNotificationIntentService");
- } catch (ClassNotFoundException ex) {
- Logger.d("No Intent Service found");
- }
+ getConfigLogger()
+ .debug(getAccountId(), "CleverTap Instance has been set to online, sending events queue");
+ flush();
}
+ }
- boolean isCTIntentServiceAvailable = isServiceAvailable(context, clazz);
-
- if (actions != null && actions.length() > 0) {
- for (int i = 0; i < actions.length(); i++) {
- try {
- JSONObject action = actions.getJSONObject(i);
- String label = action.optString("l");
- String dl = action.optString("dl");
- String ico = action.optString(Constants.NOTIF_ICON);
- String id = action.optString("id");
- boolean autoCancel = action.optBoolean("ac", true);
- if (label.isEmpty() || id.isEmpty()) {
- getConfigLogger().debug(getAccountId(),
- "not adding push notification action: action label or id missing");
- continue;
- }
- int icon = 0;
- if (!ico.isEmpty()) {
- try {
- icon = context.getResources().getIdentifier(ico, "drawable", context.getPackageName());
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(),
- "unable to add notification action icon: " + t.getLocalizedMessage());
- }
- }
-
- boolean sendToCTIntentService = (autoCancel && isCTIntentServiceAvailable);
-
- Intent actionLaunchIntent;
- if (sendToCTIntentService) {
- actionLaunchIntent = new Intent(CTNotificationIntentService.MAIN_ACTION);
- actionLaunchIntent.setPackage(context.getPackageName());
- actionLaunchIntent.putExtra("ct_type", CTNotificationIntentService.TYPE_BUTTON_CLICK);
- if (!dl.isEmpty()) {
- actionLaunchIntent.putExtra("dl", dl);
- }
- } else {
- if (!dl.isEmpty()) {
- actionLaunchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(dl));
- } else {
- actionLaunchIntent = context.getPackageManager()
- .getLaunchIntentForPackage(context.getPackageName());
- }
- }
-
- if (actionLaunchIntent != null) {
- actionLaunchIntent.putExtras(extras);
- actionLaunchIntent.removeExtra(Constants.WZRK_ACTIONS);
- actionLaunchIntent.putExtra("actionId", id);
- actionLaunchIntent.putExtra("autoCancel", autoCancel);
- actionLaunchIntent.putExtra("wzrk_c2a", id);
- actionLaunchIntent.putExtra("notificationId", notificationId);
-
- actionLaunchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- }
-
- PendingIntent actionIntent;
- int requestCode = ((int) System.currentTimeMillis()) + i;
- if (sendToCTIntentService) {
- actionIntent = PendingIntent.getService(context, requestCode,
- actionLaunchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- } else {
- actionIntent = PendingIntent.getActivity(context, requestCode,
- actionLaunchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
- nb.addAction(icon, label, actionIntent);
+ /**
+ * Use this method to opt the current user out of all event/profile tracking.
+ * You must call this method separately for each active user profile (e.g. when switching user profiles using
+ * onUserLogin).
+ * Once enabled, no events will be saved remotely or locally for the current user. To re-enable tracking call this
+ * method with enabled set to false.
+ *
+ * @param userOptOut boolean Whether tracking opt out should be enabled/disabled.
+ */
+ @SuppressWarnings({"unused"})
+ public void setOptOut(boolean userOptOut) {
+ final boolean enable = userOptOut;
+ Task task = CTExecutorFactory.executors(coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("setOptOut", new Callable() {
+ @Override
+ public Void call() {
+ // generate the data for a profile push to alert the server to the optOut state change
+ HashMap optOutMap = new HashMap<>();
+ optOutMap.put(Constants.CLEVERTAP_OPTOUT, enable);
- } catch (Throwable t) {
+ // determine order of operations depending on enabled/disabled
+ if (enable) { // if opting out first push profile event then set the flag
+ pushProfile(optOutMap);
+ coreState.getCoreMetaData().setCurrentUserOptedOut(true);
+ } else { // if opting back in first reset the flag to false then push the profile event
+ coreState.getCoreMetaData().setCurrentUserOptedOut(false);
+ pushProfile(optOutMap);
+ }
+ // persist the new optOut state
+ String key = coreState.getDeviceInfo().optOutKey();
+ if (key == null) {
getConfigLogger()
- .debug(getAccountId(), "error adding notification action : " + t.getLocalizedMessage());
+ .verbose(getAccountId(), "Unable to persist user OptOut state, storage key is null");
+ return null;
}
+ StorageHelper.putBoolean(context, StorageHelper.storageKeyWithSuffix(getConfig(), key), enable);
+ getConfigLogger().verbose(getAccountId(), "Set current user OptOut state to: " + enable);
+ return null;
}
- }
-
- Notification n = nb.build();
- notificationManager.notify(notificationId, n);
- getConfigLogger().debug(getAccountId(), "Rendered notification: " + n.toString());
-
- String ttl = extras.getString(Constants.WZRK_TIME_TO_LIVE,
- (System.currentTimeMillis() + Constants.DEFAULT_PUSH_TTL) / 1000 + "");
- long wzrk_ttl = Long.parseLong(ttl);
- String wzrk_pid = extras.getString(Constants.WZRK_PUSH_ID);
- DBAdapter dbAdapter = loadDBAdapter(context);
- getConfigLogger().verbose("Storing Push Notification..." + wzrk_pid + " - with ttl - " + ttl);
- dbAdapter.storePushNotificationId(wzrk_pid, wzrk_ttl);
-
- boolean notificationViewedEnabled = "true".equals(extras.getString(Constants.WZRK_RNV, ""));
- if (!notificationViewedEnabled) {
- ValidationResult notificationViewedError = ValidationResultFactory
- .create(512, Constants.NOTIFICATION_VIEWED_DISABLED, extras.toString());
- getConfigLogger().debug(notificationViewedError.getErrorDesc());
- validationResultStack.pushValidationResult(notificationViewedError);
- return;
- }
- pushNotificationViewedEvent(extras);
+ });
}
- private void updateBlacklistedActivitySet() {
- if (inappActivityExclude == null) {
- inappActivityExclude = new HashSet<>();
- try {
- String activities = ManifestInfo.getInstance(context).getExcludedActivities();
- if (activities != null) {
- String[] split = activities.split(",");
- for (String a : split) {
- inappActivityExclude.add(a.trim());
- }
- }
- } catch (Throwable t) {
- // Ignore
+ /**
+ * Opens {@link CTInboxActivity} to display Inbox Messages
+ *
+ * @param styleConfig {@link CTInboxStyleConfig} configuration of various style parameters for the {@link
+ * CTInboxActivity}
+ */
+ @SuppressWarnings({"unused", "WeakerAccess"})
+ public void showAppInbox(CTInboxStyleConfig styleConfig) {
+ synchronized (coreState.getCTLockManager().getInboxControllerLock()) {
+ if (coreState.getControllerManager().getCTInboxController() == null) {
+ getConfigLogger().debug(getAccountId(), "Notification Inbox not initialized");
+ return;
}
- getConfigLogger().debug(getAccountId(),
- "In-app notifications will not be shown on " + Arrays.toString(inappActivityExclude.toArray()));
}
- }
- // -----------------------------------------------------------------------//
- // ******** PRODUCT CONFIG Logic *****//
- // -----------------------------------------------------------------------//
-
- // ******** PRODUCT CONFIG Public API *****//
-
- // helper extracts the cursor data from the db object
- private QueueCursor updateCursorForDBObject(JSONObject dbObject, QueueCursor cursor) {
- if (dbObject == null) {
- return cursor;
- }
+ // make styleConfig immutable
+ final CTInboxStyleConfig _styleConfig = new CTInboxStyleConfig(styleConfig);
- Iterator keys = dbObject.keys();
- if (keys.hasNext()) {
- String key = keys.next();
- cursor.setLastId(key);
- try {
- cursor.setData(dbObject.getJSONArray(key));
- } catch (JSONException e) {
- cursor.setLastId(null);
- cursor.setData(null);
+ Intent intent = new Intent(context, CTInboxActivity.class);
+ intent.putExtra("styleConfig", _styleConfig);
+ Bundle configBundle = new Bundle();
+ configBundle.putParcelable("config", getConfig());
+ intent.putExtra("configBundle", configBundle);
+ try {
+ Activity currentActivity = CoreMetaData.getCurrentActivity();
+ if (currentActivity == null) {
+ throw new IllegalStateException("Current activity reference not found");
}
+ currentActivity.startActivity(intent);
+ Logger.d("Displaying Notification Inbox");
+
+ } catch (Throwable t) {
+ Logger.v("Please verify the integration of your app." +
+ " It is not setup to support Notification Inbox yet.", t);
}
- return cursor;
}
- //Util
- // only call async
- private void updateLocalStore(final Context context, final JSONObject event, final int type) {
- if (type == Constants.RAISED_EVENT) {
- getLocalDataStore().persistEvent(context, event, type);
- }
+ /**
+ * Opens {@link CTInboxActivity} to display Inbox Messages with default {@link CTInboxStyleConfig} object
+ */
+ @SuppressWarnings({"unused"})
+ public void showAppInbox() {
+ CTInboxStyleConfig styleConfig = new CTInboxStyleConfig();
+ showAppInbox(styleConfig);
}
- // ******** PRODUCT CONFIG Internal API *****//
+ //To be called from DeviceInfo AdID GUID generation
+ void deviceIDCreated(String deviceId) {
+ Logger.v("Initializing InAppFC after Device ID Created = " + deviceId);
+ coreState.getControllerManager()
+ .setInAppFCManager(new InAppFCManager(context, coreState.getConfig(), deviceId));
+ Logger.v("Initializing ABTesting after Device ID Created = " + deviceId);
- /**
- * updates the ping frequency if there is a change & reschedules existing ping tasks.
- */
- private void updatePingFrequencyIfNeeded(final Context context, int frequency) {
- getConfigLogger().verbose("Ping frequency received - " + frequency);
- getConfigLogger().verbose("Stored Ping Frequency - " + getPingFrequency(context));
- if (frequency != getPingFrequency(context)) {
- setPingFrequency(context, frequency);
- if (this.config.isBackgroundSync() && !this.config.isAnalyticsOnly()) {
- postAsyncSafely("createOrResetJobScheduler", new Runnable() {
- @Override
- public void run() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- getConfigLogger().verbose("Creating job");
- createOrResetJobScheduler(context);
- } else {
- getConfigLogger().verbose("Resetting alarm");
- resetAlarmScheduler(context);
- }
- }
- });
- }
+ /*
+ Reinitialising product config & Feature Flag controllers with google ad id.
+ */
+ if (coreState.getControllerManager().getCTFeatureFlagsController() != null) {
+ coreState.getControllerManager().getCTFeatureFlagsController().setGuidAndInit(deviceId);
+ }
+ if (coreState.getControllerManager().getCTProductConfigController() != null) {
+ coreState.getControllerManager().getCTProductConfigController().setGuidAndInit(deviceId);
}
+ getConfigLogger()
+ .verbose("Got device id from DeviceInfo, notifying user profile initialized to SyncListener");
+ coreState.getCallbackManager().notifyUserProfileInitialized(deviceId);
}
- //Deprecation warning because Google Play install referrer via intent will be deprecated in March 2020
- @Deprecated
- static void handleInstallReferrer(Context context, Intent intent) {
- if (instances == null) {
- Logger.v("No CleverTap Instance found");
- CleverTapAPI instance = CleverTapAPI.getDefaultInstance(context);
- if (instance != null) {
- instance.pushInstallReferrer(intent);
- }
- return;
- }
+ private CleverTapInstanceConfig getConfig() {
+ return coreState.getConfig();
+ }
- for (String accountId : CleverTapAPI.instances.keySet()) {
- CleverTapAPI instance = CleverTapAPI.instances.get(accountId);
- if (instance != null) {
- instance.pushInstallReferrer(intent);
- }
- }
+ private Logger getConfigLogger() {
+ return getConfig().getLogger();
+ }
+ private boolean isErrorDeviceId() {
+ return coreState.getDeviceInfo().isErrorDeviceId();
}
- /**
- * Returns whether or not the app is in the foreground.
- *
- * @return The foreground status
- */
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- static boolean isAppForeground() {
- return appForeground;
+ //Run manifest validation in async
+ private void manifestAsyncValidation() {
+ Task task = CTExecutorFactory.executors(coreState.getConfig()).postAsyncSafelyTask();
+ task.execute("Manifest Validation", new Callable() {
+ @Override
+ public Void call() {
+ ManifestValidator
+ .validate(context, coreState.getDeviceInfo(), coreState.getPushProviders());
+ return null;
+ }
+ });
}
/**
- * Use this method to notify CleverTap that the app is in foreground
+ * Sends the ADM registration ID to CleverTap.
*
- * @param appForeground boolean true/false
+ * @param token The ADM registration ID
+ * @param register Boolean indicating whether to register
+ * or not for receiving push messages from CleverTap.
+ * Set this to true to receive push messages from CleverTap,
+ * and false to not receive any messages from CleverTap.
*/
- @SuppressWarnings({"unused", "WeakerAccess"})
- public static void setAppForeground(boolean appForeground) {
- CleverTapAPI.appForeground = appForeground;
+ @SuppressWarnings("unused")
+ private void pushAmazonRegistrationId(String token, boolean register) {
+ coreState.getPushProviders().handleToken(token, PushType.ADM, register);
}
static void onActivityCreated(Activity activity) {
@@ -8467,80 +2442,9 @@ static void onActivityCreated(Activity activity, String cleverTapID) {
return;
}
- try {
- for (String accountId : CleverTapAPI.instances.keySet()) {
- CleverTapAPI instance = CleverTapAPI.instances.get(accountId);
-
- boolean shouldProcess = false;
- if (instance != null) {
- shouldProcess = (_accountId == null && instance.config.isDefaultInstance()) || instance
- .getAccountId().equals(_accountId);
- }
-
- if (shouldProcess) {
- if (notification != null && !notification.isEmpty() && notification
- .containsKey(Constants.NOTIFICATION_TAG)) {
- instance.pushNotificationClickedEvent(notification);
- }
-
- if (deepLink != null) {
- try {
- instance.pushDeepLink(deepLink);
- } catch (Throwable t) {
- // no-op
- }
- }
- break;
- }
- }
- } catch (Throwable t) {
- Logger.v("Throwable - " + t.getLocalizedMessage());
- }
- }
-
- /**
- * Method to check whether app has ExoPlayer dependencies
- *
- * @return boolean - true/false depending on app's availability of ExoPlayer dependencies
- */
- private static boolean checkForExoPlayer() {
- boolean exoPlayerPresent = false;
- Class className = null;
- try {
- className = Class.forName("com.google.android.exoplayer2.SimpleExoPlayer");
- className = Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource");
- className = Class.forName("com.google.android.exoplayer2.ui.PlayerView");
- Logger.d("ExoPlayer is present");
- exoPlayerPresent = true;
- } catch (Throwable t) {
- Logger.d("ExoPlayer library files are missing!!!");
- Logger.d(
- "Please add ExoPlayer dependencies to render InApp or Inbox messages playing video. For more information checkout CleverTap documentation.");
- if (className != null) {
- Logger.d("ExoPlayer classes not found " + className.getName());
- } else {
- Logger.d("ExoPlayer classes not found");
- }
- }
- return exoPlayerPresent;
- }
-
- private static void checkPendingNotifications(final Context context, final CleverTapInstanceConfig config) {
- Logger.v(config.getAccountId(), "checking Pending Notifications");
- if (pendingNotifications != null && !pendingNotifications.isEmpty()) {
- try {
- final CTInAppNotification notification = pendingNotifications.get(0);
- pendingNotifications.remove(0);
- Handler mainHandler = new Handler(context.getMainLooper());
- mainHandler.post(new Runnable() {
- @Override
- public void run() {
- showInApp(context, notification, config);
- }
- });
- } catch (Throwable t) {
- // no-op
- }
+ CleverTapAPI instance = CleverTapAPI.instances.get(_accountId);
+ if (instance != null) {
+ instance.coreState.getActivityLifeCycleManager().onActivityCreated(notification, deepLink);
}
}
@@ -8567,7 +2471,8 @@ CleverTapAPI createInstanceIfAvailable(Context context, String _accountId, Strin
} else {
try {
CleverTapAPI instance = CleverTapAPI.getDefaultInstance(context);
- return (instance != null && instance.config.getAccountId().equals(_accountId)) ? instance : null;
+ return (instance != null && instance.coreState.getConfig().getAccountId().equals(_accountId))
+ ? instance : null;
} catch (Throwable t) {
Logger.v("Error creating shared Instance: ", t.getCause());
return null;
@@ -8578,8 +2483,6 @@ CleverTapAPI createInstanceIfAvailable(Context context, String _accountId, Strin
}
}
- //GEOFENCE APIs
-
private static ArrayList getAvailableInstances(Context context) {
ArrayList apiArrayList = new ArrayList<>();
if (instances == null || instances.isEmpty()) {
@@ -8593,25 +2496,6 @@ private static ArrayList getAvailableInstances(Context context) {
return apiArrayList;
}
- private static Activity getCurrentActivity() {
- return (currentActivity == null) ? null : currentActivity.get();
- }
-
- private static void setCurrentActivity(@Nullable Activity activity) {
- if (activity == null) {
- currentActivity = null;
- return;
- }
- if (!activity.getLocalClassName().contains("InAppNotificationActivity")) {
- currentActivity = new WeakReference<>(activity);
- }
- }
-
- private static String getCurrentActivityName() {
- Activity current = getCurrentActivity();
- return (current != null) ? current.getLocalClassName() : null;
- }
-
/**
* Creates default {@link CleverTapInstanceConfig} object and returns it
*
@@ -8649,180 +2533,4 @@ CleverTapAPI getDefaultInstanceOrFirstOther(Context context) {
}
return instance;
}
-
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- private static JobInfo getJobInfo(int jobId, JobScheduler jobScheduler) {
- for (JobInfo jobInfo : jobScheduler.getAllPendingJobs()) {
- if (jobInfo.getId() == jobId) {
- return jobInfo;
- }
- }
- return null;
- }
-
- private static SSLSocketFactory getPinnedCertsSslSocketfactory(SSLContext sslContext) {
- if (sslContext == null) {
- return null;
- }
-
- if (sslSocketFactory == null) {
- try {
- sslSocketFactory = sslContext.getSocketFactory();
- Logger.d("Pinning SSL session to DigiCertGlobalRoot CA certificate");
- } catch (Throwable e) {
- Logger.d("Issue in pinning SSL,", e);
- }
- }
- return sslSocketFactory;
- }
-
- private static synchronized SSLContext getSSLContext() {
- if (sslContext == null) {
- sslContext = new SSLContextBuilder().build();
- }
- return sslContext;
- }
-
- //InApp
- private static void inAppDidDismiss(Context context, CleverTapInstanceConfig config,
- CTInAppNotification inAppNotification) {
- Logger.v(config.getAccountId(), "Running inAppDidDismiss");
- if (currentlyDisplayingInApp != null && (currentlyDisplayingInApp.getCampaignId()
- .equals(inAppNotification.getCampaignId()))) {
- currentlyDisplayingInApp = null;
- checkPendingNotifications(context, config);
- }
- }
-
- @SuppressWarnings("SameParameterValue")
- private static boolean isServiceAvailable(Context context, Class clazz) {
- if (clazz == null) {
- return false;
- }
-
- PackageManager pm = context.getPackageManager();
- String packageName = context.getPackageName();
-
- PackageInfo packageInfo;
- try {
- packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES);
- ServiceInfo[] services = packageInfo.services;
- for (ServiceInfo serviceInfo : services) {
- if (serviceInfo.name.equals(clazz.getName())) {
- Logger.v("Service " + serviceInfo.name + " found");
- return true;
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- Logger.d("Intent Service name not found exception - " + e.getLocalizedMessage());
- }
- return false;
- }
-
- //InApp
- private static void showInApp(Context context, final CTInAppNotification inAppNotification,
- CleverTapInstanceConfig config) {
-
- Logger.v(config.getAccountId(), "Attempting to show next In-App");
-
- if (!appForeground) {
- pendingNotifications.add(inAppNotification);
- Logger.v(config.getAccountId(), "Not in foreground, queueing this In App");
- return;
- }
-
- if (currentlyDisplayingInApp != null) {
- pendingNotifications.add(inAppNotification);
- Logger.v(config.getAccountId(), "In App already displaying, queueing this In App");
- return;
- }
-
- if ((System.currentTimeMillis() / 1000) > inAppNotification.getTimeToLive()) {
- Logger.d("InApp has elapsed its time to live, not showing the InApp");
- return;
- }
-
- currentlyDisplayingInApp = inAppNotification;
-
- CTInAppBaseFragment inAppFragment = null;
- CTInAppType type = inAppNotification.getInAppType();
- switch (type) {
- case CTInAppTypeCoverHTML:
- case CTInAppTypeInterstitialHTML:
- case CTInAppTypeHalfInterstitialHTML:
- case CTInAppTypeCover:
- case CTInAppTypeHalfInterstitial:
- case CTInAppTypeInterstitial:
- case CTInAppTypeAlert:
- case CTInAppTypeInterstitialImageOnly:
- case CTInAppTypeHalfInterstitialImageOnly:
- case CTInAppTypeCoverImageOnly:
-
- Intent intent = new Intent(context, InAppNotificationActivity.class);
- intent.putExtra("inApp", inAppNotification);
- Bundle configBundle = new Bundle();
- configBundle.putParcelable("config", config);
- intent.putExtra("configBundle", configBundle);
- try {
- Activity currentActivity = getCurrentActivity();
- if (currentActivity == null) {
- throw new IllegalStateException("Current activity reference not found");
- }
- config.getLogger().verbose(config.getAccountId(),
- "calling InAppActivity for notification: " + inAppNotification.getJsonDescription());
- currentActivity.startActivity(intent);
- Logger.d("Displaying In-App: " + inAppNotification.getJsonDescription());
-
- } catch (Throwable t) {
- Logger.v("Please verify the integration of your app." +
- " It is not setup to support in-app notifications yet.", t);
- }
- break;
- case CTInAppTypeFooterHTML:
- inAppFragment = new CTInAppHtmlFooterFragment();
- break;
- case CTInAppTypeHeaderHTML:
- inAppFragment = new CTInAppHtmlHeaderFragment();
- break;
- case CTInAppTypeFooter:
- inAppFragment = new CTInAppNativeFooterFragment();
- break;
- case CTInAppTypeHeader:
- inAppFragment = new CTInAppNativeHeaderFragment();
- break;
- default:
- Logger.d(config.getAccountId(), "Unknown InApp Type found: " + type);
- currentlyDisplayingInApp = null;
- return;
- }
-
- if (inAppFragment != null) {
- Logger.d("Displaying In-App: " + inAppNotification.getJsonDescription());
- try {
- //noinspection ConstantConditions
- FragmentTransaction fragmentTransaction = ((FragmentActivity) getCurrentActivity())
- .getSupportFragmentManager()
- .beginTransaction();
- Bundle bundle = new Bundle();
- bundle.putParcelable("inApp", inAppNotification);
- bundle.putParcelable("config", config);
- inAppFragment.setArguments(bundle);
- fragmentTransaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
- fragmentTransaction.add(android.R.id.content, inAppFragment, inAppNotification.getType());
- Logger.v(config.getAccountId(), "calling InAppFragment " + inAppNotification.getCampaignId());
- fragmentTransaction.commit();
-
- } catch (ClassCastException e) {
- Logger.v(config.getAccountId(),
- "Fragment not able to render, please ensure your Activity is an instance of AppCompatActivity"
- + e.getMessage());
- } catch (Throwable t) {
- Logger.v(config.getAccountId(), "Fragment not able to render", t);
- }
- }
- }
-
- static {
- haveVideoPlayerSupport = checkForExoPlayer();
- }
}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPIListener.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPIListener.java
deleted file mode 100644
index e7cd8d15e..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPIListener.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.clevertap.android.sdk;
-
-import com.clevertap.android.sdk.ab_testing.CTABTestListener;
-import com.clevertap.android.sdk.featureFlags.FeatureFlagListener;
-import com.clevertap.android.sdk.product_config.CTProductConfigControllerListener;
-import com.clevertap.android.sdk.product_config.CTProductConfigListener;
-import com.clevertap.android.sdk.pushnotification.CTApiPushListener;
-
-public interface CleverTapAPIListener extends
- CTInAppNotification.CTInAppNotificationListener,
- InAppNotificationActivity.InAppActivityListener,
- CTInAppBaseFragment.InAppListener,
- CTInboxActivity.InboxActivityListener,
- CTABTestListener,
- FeatureFlagListener,
- CTProductConfigControllerListener,
- CTProductConfigListener,
- CTApiPushListener {
-
-}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.java
new file mode 100644
index 000000000..7960a497e
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.java
@@ -0,0 +1,125 @@
+package com.clevertap.android.sdk;
+
+import android.content.Context;
+import com.clevertap.android.sdk.db.DBManager;
+import com.clevertap.android.sdk.events.EventMediator;
+import com.clevertap.android.sdk.events.EventQueueManager;
+import com.clevertap.android.sdk.featureFlags.CTFeatureFlagsFactory;
+import com.clevertap.android.sdk.inapp.InAppController;
+import com.clevertap.android.sdk.login.LoginController;
+import com.clevertap.android.sdk.network.NetworkManager;
+import com.clevertap.android.sdk.pushnotification.PushProviders;
+import com.clevertap.android.sdk.task.MainLooperHandler;
+import com.clevertap.android.sdk.validation.ValidationResultStack;
+import com.clevertap.android.sdk.validation.Validator;
+
+class CleverTapFactory {
+
+ static CoreState getCoreState(Context context, CleverTapInstanceConfig cleverTapInstanceConfig,
+ String cleverTapID) {
+ CoreState coreState = new CoreState(context);
+
+ CoreMetaData coreMetaData = new CoreMetaData();
+ coreState.setCoreMetaData(coreMetaData);
+
+ Validator validator = new Validator();
+
+ ValidationResultStack validationResultStack = new ValidationResultStack();
+ coreState.setValidationResultStack(validationResultStack);
+
+ CTLockManager ctLockManager = new CTLockManager();
+ coreState.setCTLockManager(ctLockManager);
+
+ MainLooperHandler mainLooperHandler = new MainLooperHandler();
+ coreState.setMainLooperHandler(mainLooperHandler);
+
+ CleverTapInstanceConfig config = new CleverTapInstanceConfig(cleverTapInstanceConfig);
+ coreState.setConfig(config);
+
+ EventMediator eventMediator = new EventMediator(context, config, coreMetaData);
+ coreState.setEventMediator(eventMediator);
+
+ LocalDataStore localDataStore = new LocalDataStore(context, config);
+ coreState.setLocalDataStore(localDataStore);
+
+ DeviceInfo deviceInfo = new DeviceInfo(context, config, cleverTapID, coreMetaData);
+ coreState.setDeviceInfo(deviceInfo);
+
+ BaseCallbackManager callbackManager = new CallbackManager(config, deviceInfo);
+ coreState.setCallbackManager(callbackManager);
+
+ SessionManager sessionManager = new SessionManager(config, coreMetaData, validator, localDataStore);
+ coreState.setSessionManager(sessionManager);
+
+ DBManager baseDatabaseManager = new DBManager(config, ctLockManager);
+ coreState.setDatabaseManager(baseDatabaseManager);
+
+ ControllerManager controllerManager = new ControllerManager(context, config,
+ ctLockManager, callbackManager, deviceInfo, baseDatabaseManager);
+ coreState.setControllerManager(controllerManager);
+
+ if (coreState.getDeviceInfo() != null && coreState.getDeviceInfo().getDeviceID() != null) {
+ coreState.getConfig().getLogger()
+ .verbose("Initializing InAppFC with device Id = " + coreState.getDeviceInfo().getDeviceID());
+ controllerManager
+ .setInAppFCManager(new InAppFCManager(context, config, coreState.getDeviceInfo().getDeviceID()));
+ }
+
+ NetworkManager networkManager = new NetworkManager(context, config, deviceInfo, coreMetaData,
+ validationResultStack, controllerManager,baseDatabaseManager,
+ callbackManager, ctLockManager, validator, localDataStore);
+ coreState.setNetworkManager(networkManager);
+
+ EventQueueManager baseEventQueueManager = new EventQueueManager(baseDatabaseManager, context, config,
+ eventMediator,
+ sessionManager, callbackManager,
+ mainLooperHandler, deviceInfo, validationResultStack,
+ networkManager, coreMetaData, ctLockManager, localDataStore);
+ coreState.setBaseEventQueueManager(baseEventQueueManager);
+
+ AnalyticsManager analyticsManager = new AnalyticsManager(context, config, baseEventQueueManager, validator,
+ validationResultStack, coreMetaData, localDataStore, deviceInfo,
+ mainLooperHandler, callbackManager, controllerManager, ctLockManager);
+ coreState.setAnalyticsManager(analyticsManager);
+
+ InAppController inAppController = new InAppController(context, config, mainLooperHandler,
+ controllerManager, callbackManager, analyticsManager, coreMetaData);
+ coreState.setInAppController(inAppController);
+ coreState.getControllerManager().setInAppController(inAppController);
+
+ initFeatureFlags(context, controllerManager, config, deviceInfo, callbackManager, analyticsManager);
+
+ LocationManager locationManager = new LocationManager(context, config, coreMetaData, baseEventQueueManager);
+ coreState.setLocationManager(locationManager);
+
+ PushProviders pushProviders = PushProviders
+ .load(context, config, baseDatabaseManager, validationResultStack,
+ analyticsManager, controllerManager);
+ coreState.setPushProviders(pushProviders);
+
+ ActivityLifeCycleManager activityLifeCycleManager = new ActivityLifeCycleManager(context, config,
+ analyticsManager, coreMetaData, sessionManager, pushProviders, callbackManager, inAppController,
+ baseEventQueueManager);
+ coreState.setActivityLifeCycleManager(activityLifeCycleManager);
+
+ LoginController loginController = new LoginController(context, config, deviceInfo,
+ validationResultStack, baseEventQueueManager, analyticsManager,
+ coreMetaData, controllerManager, sessionManager,
+ localDataStore, callbackManager, baseDatabaseManager, ctLockManager);
+ coreState.setLoginController(loginController);
+ return coreState;
+ }
+
+ static void initFeatureFlags(Context context, ControllerManager controllerManager, CleverTapInstanceConfig config,
+ DeviceInfo deviceInfo, BaseCallbackManager callbackManager, AnalyticsManager analyticsManager) {
+ Logger.v("Initializing Feature Flags with device Id = " + deviceInfo.getDeviceID());
+ if (config.isAnalyticsOnly()) {
+ config.getLogger().debug(config.getAccountId(), "Feature Flag is not enabled for this instance");
+ } else {
+ controllerManager.setCTFeatureFlagsController(CTFeatureFlagsFactory.getInstance(context,
+ deviceInfo.getDeviceID(),
+ config, callbackManager, analyticsManager));
+ config.getLogger().verbose(config.getAccountId(), "Feature Flags initialized");
+ }
+ }
+}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapInstanceConfig.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapInstanceConfig.java
index 5bcffe200..6be7b2dde 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapInstanceConfig.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapInstanceConfig.java
@@ -1,9 +1,9 @@
package com.clevertap.android.sdk;
-import static com.clevertap.android.sdk.JsonUtil.toArray;
-import static com.clevertap.android.sdk.JsonUtil.toJsonArray;
-import static com.clevertap.android.sdk.JsonUtil.toList;
import static com.clevertap.android.sdk.pushnotification.PushNotificationUtil.getAll;
+import static com.clevertap.android.sdk.utils.CTJsonConverter.toArray;
+import static com.clevertap.android.sdk.utils.CTJsonConverter.toJsonArray;
+import static com.clevertap.android.sdk.utils.CTJsonConverter.toList;
import android.content.Context;
import android.os.Parcel;
@@ -11,7 +11,9 @@
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
import com.clevertap.android.sdk.Constants.IdentityType;
+import com.clevertap.android.sdk.login.LoginConstants;
import java.util.ArrayList;
import java.util.Arrays;
import org.json.JSONObject;
@@ -53,12 +55,8 @@ public CleverTapInstanceConfig[] newArray(int size) {
private boolean disableAppLaunchedEvent;
- private boolean enableABTesting;
-
private boolean enableCustomCleverTapId;
- private boolean enableUIEditor;
-
private String fcmSenderId;
private boolean isDefaultInstance;
@@ -69,7 +67,7 @@ public CleverTapInstanceConfig[] newArray(int size) {
private boolean personalization;
- private String[] identityKeys = NullConstants.NULL_STRING_ARRAY;
+ private String[] identityKeys = Constants.NULL_STRING_ARRAY;
private boolean sslPinning;
@@ -114,8 +112,6 @@ public static CleverTapInstanceConfig createInstance(Context context, @NonNull S
this.backgroundSync = config.backgroundSync;
this.enableCustomCleverTapId = config.enableCustomCleverTapId;
this.fcmSenderId = config.fcmSenderId;
- this.enableABTesting = config.enableABTesting;
- this.enableUIEditor = config.enableUIEditor;
this.packageName = config.packageName;
this.beta = config.beta;
this.allowedPushTypes = config.allowedPushTypes;
@@ -133,8 +129,6 @@ private CleverTapInstanceConfig(Context context, String accountId, String accoun
this.debugLevel = CleverTapAPI.LogLevel.INFO.intValue();
this.logger = new Logger(this.debugLevel);
this.createdPostAppLaunch = false;
- this.enableABTesting = this.isDefaultInstance;
- this.enableUIEditor = this.enableABTesting;
ManifestInfo manifest = ManifestInfo.getInstance(context);
this.useGoogleAdId = manifest.useGoogleAdId();
@@ -150,7 +144,7 @@ private CleverTapInstanceConfig(Context context, String accountId, String accoun
*/
if (isDefaultInstance) {
identityKeys = manifest.getProfileKeys();
- log(LogConstants.LOG_TAG_ON_USER_LOGIN, "Setting Profile Keys from Manifest: " + Arrays
+ log(LoginConstants.LOG_TAG_ON_USER_LOGIN, "Setting Profile Keys from Manifest: " + Arrays
.toString(identityKeys));
}
}
@@ -186,12 +180,7 @@ private CleverTapInstanceConfig(String jsonString) throws Throwable {
this.debugLevel = configJsonObject.getInt(Constants.KEY_DEBUG_LEVEL);
}
this.logger = new Logger(this.debugLevel);
- if (configJsonObject.has(Constants.KEY_ENABLE_ABTEST)) {
- this.enableABTesting = configJsonObject.getBoolean(Constants.KEY_ENABLE_ABTEST);
- }
- if (configJsonObject.has(Constants.KEY_ENABLE_UIEDITOR)) {
- this.enableUIEditor = configJsonObject.getBoolean(Constants.KEY_ENABLE_UIEDITOR);
- }
+
if (configJsonObject.has(Constants.KEY_PACKAGE_NAME)) {
this.packageName = configJsonObject.getString(Constants.KEY_PACKAGE_NAME);
}
@@ -241,8 +230,6 @@ private CleverTapInstanceConfig(Parcel in) {
backgroundSync = in.readByte() != 0x00;
enableCustomCleverTapId = in.readByte() != 0x00;
fcmSenderId = in.readString();
- enableABTesting = in.readByte() != 0x00;
- enableUIEditor = in.readByte() != 0x00;
packageName = in.readString();
logger = new Logger(debugLevel);
beta = in.readByte() != 0x00;
@@ -315,11 +302,6 @@ public String[] getIdentityKeys() {
return identityKeys;
}
- @SuppressWarnings({"BooleanMethodIsAlwaysInverted", "WeakerAccess"})
- public boolean isABTestingEnabled() {
- return enableABTesting;
- }
-
@SuppressWarnings({"unused", "WeakerAccess"})
public boolean isAnalyticsOnly() {
return analyticsOnly;
@@ -338,11 +320,6 @@ public boolean isDefaultInstance() {
return isDefaultInstance;
}
- @SuppressWarnings({"unused"})
- public boolean isUIEditorEnabled() {
- return enableUIEditor;
- }
-
@RestrictTo(RestrictTo.Scope.LIBRARY)
public void log(@NonNull String tag, @NonNull String message) {
logger.verbose(getDefaultSuffix(tag), message);
@@ -353,22 +330,10 @@ public void log(@NonNull String tag, @NonNull String message, Throwable throwabl
logger.verbose(getDefaultSuffix(tag), message, throwable);
}
- @SuppressWarnings("SameParameterValue")
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public void setEnableABTesting(boolean enableABTesting) {
- this.enableABTesting = enableABTesting;
- }
-
- @SuppressWarnings({"unused"})
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public void setEnableUIEditor(boolean enableUIEditor) {
- this.enableUIEditor = enableUIEditor;
- }
-
public void setIdentityKeys(@IdentityType String... identityKeys) {
if (!isDefaultInstance) {
this.identityKeys = identityKeys;
- log(LogConstants.LOG_TAG_ON_USER_LOGIN, "Setting Profile Keys via setter: " + Arrays
+ log(LoginConstants.LOG_TAG_ON_USER_LOGIN, "Setting Profile Keys via setter: " + Arrays
.toString(this.identityKeys));
}
}
@@ -394,15 +359,13 @@ public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (backgroundSync ? 0x01 : 0x00));
dest.writeByte((byte) (enableCustomCleverTapId ? 0x01 : 0x00));
dest.writeString(fcmSenderId);
- dest.writeByte((byte) (enableABTesting ? 0x01 : 0x00));
- dest.writeByte((byte) (enableUIEditor ? 0x01 : 0x00));
dest.writeString(packageName);
dest.writeByte((byte) (beta ? 0x01 : 0x00));
dest.writeList(allowedPushTypes);
dest.writeStringArray(identityKeys);
}
- boolean getEnableCustomCleverTapId() {
+ public boolean getEnableCustomCleverTapId() {
return enableCustomCleverTapId;
}
@@ -411,7 +374,8 @@ public void setEnableCustomCleverTapId(boolean enableCustomCleverTapId) {
this.enableCustomCleverTapId = enableCustomCleverTapId;
}
- boolean isBackgroundSync() {
+ @RestrictTo(Scope.LIBRARY)
+ public boolean isBackgroundSync() {
return backgroundSync;
}
@@ -420,7 +384,7 @@ public void setBackgroundSync(boolean backgroundSync) {
this.backgroundSync = backgroundSync;
}
- boolean isCreatedPostAppLaunch() {
+ public boolean isCreatedPostAppLaunch() {
return createdPostAppLaunch;
}
@@ -437,7 +401,7 @@ boolean isPersonalizationEnabled() {
return personalization;
}
- boolean isSslPinningEnabled() {
+ public boolean isSslPinningEnabled() {
return sslPinning;
}
@@ -468,8 +432,6 @@ String toJSONString() {
configJsonObject.put(Constants.KEY_ENABLE_CUSTOM_CT_ID, getEnableCustomCleverTapId());
configJsonObject.put(Constants.KEY_PACKAGE_NAME, getPackageName());
configJsonObject.put(Constants.KEY_BETA, isBeta());
- configJsonObject.put(Constants.KEY_ENABLE_UIEDITOR, isUIEditorEnabled());
- configJsonObject.put(Constants.KEY_ENABLE_ABTEST, isABTestingEnabled());
configJsonObject.put(Constants.KEY_ALLOWED_PUSH_TYPES, toJsonArray(allowedPushTypes));
return configJsonObject.toString();
} catch (Throwable e) {
@@ -479,7 +441,7 @@ String toJSONString() {
}
private String getDefaultSuffix(@NonNull String tag) {
- return "[" + ((!TextUtils.isEmpty(tag) ? ": " + tag : "") + ":" + accountId + "]");
+ return "[" + ((!TextUtils.isEmpty(tag) ? ":" + tag : "") + ":" + accountId + "]");
}
// convenience to construct the internal only default config
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapMetaData.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapMetaData.java
new file mode 100644
index 000000000..317f3deec
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapMetaData.java
@@ -0,0 +1,8 @@
+package com.clevertap.android.sdk;
+
+/*
+Marker interface for future abstraction purposes.
+Example - If any other module uses MetaData, then they can extend this class and common
+properties can be abstracted.
+ */
+abstract class CleverTapMetaData {}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapState.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapState.java
new file mode 100644
index 000000000..007b8d937
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapState.java
@@ -0,0 +1,31 @@
+package com.clevertap.android.sdk;
+
+import android.content.Context;
+import com.clevertap.android.sdk.db.BaseDatabaseManager;
+import com.clevertap.android.sdk.network.BaseNetworkManager;
+
+abstract class CleverTapState {
+
+ protected final Context context;
+
+ CleverTapState(final Context context) {
+ this.context = context;
+ }
+
+ public Context getContext() {
+ return context;
+ }
+
+ abstract BaseDatabaseManager getDatabaseManager();
+
+ abstract void setDatabaseManager(final BaseDatabaseManager databaseManager);
+
+ abstract BaseNetworkManager getNetworkManager();
+
+ abstract void setNetworkManager(final BaseNetworkManager networkManager);
+
+ abstract BaseLocationManager getLocationManager();
+
+ abstract void setLocationManager(BaseLocationManager baseLocationManager);
+
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/Constants.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/Constants.java
index 8b0f3ef35..3460d2f2c 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/Constants.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/Constants.java
@@ -18,7 +18,7 @@ public interface Constants {
@interface IdentityType {
}
-
+ String TAG_FEATURE_IN_APPS = "TAG_FEATURE_IN_APPS";
@NonNull
String TYPE_IDENTITY = "Identity";
@NonNull
@@ -136,7 +136,7 @@ public interface Constants {
String[] SYSTEM_EVENTS = {NOTIFICATION_CLICKED_EVENT_NAME,
NOTIFICATION_VIEWED_EVENT_NAME, GEOFENCE_ENTERED_EVENT_NAME,
GEOFENCE_EXITED_EVENT_NAME};
- long DEFAULT_PUSH_TTL = 1000 * 60 * 60 * 24 * 4;
+ long DEFAULT_PUSH_TTL = 1000L * 60 * 60 * 24 * 4;// 4 days
String PF_JOB_ID = "pfjobid";
int PING_FREQUENCY_VALUE = 240;
String PING_FREQUENCY = "pf";
@@ -187,8 +187,6 @@ public interface Constants {
String KEY_ENABLE_CUSTOM_CT_ID = "getEnableCustomCleverTapId";
String KEY_BETA = "beta";
String KEY_PACKAGE_NAME = "packageName";
- String KEY_ENABLE_UIEDITOR = "enableUIEditor";
- String KEY_ENABLE_ABTEST = "enableABTesting";
String KEY_ALLOWED_PUSH_TYPES = "allowedPushTypes";
String KEY_IDENTITY_TYPES = "identityTypes";
String WZRK_PUSH_ID = "wzrk_pid";
@@ -282,4 +280,8 @@ public interface Constants {
// valid profile identifier keys
HashSet LEGACY_IDENTITY_KEYS = new HashSet<>(Arrays.asList(TYPE_IDENTITY, TYPE_EMAIL));
HashSet ALL_IDENTITY_KEYS = new HashSet<>(Arrays.asList(TYPE_IDENTITY, TYPE_EMAIL, TYPE_PHONE));
+
+ int MAX_DELAY_FREQUENCY = 1000 * 60 * 10;
+
+ String[] NULL_STRING_ARRAY = new String[0];
}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ControllerManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ControllerManager.java
new file mode 100644
index 000000000..48e1d1bc5
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/ControllerManager.java
@@ -0,0 +1,156 @@
+package com.clevertap.android.sdk;
+
+import android.content.Context;
+import com.clevertap.android.sdk.db.BaseDatabaseManager;
+import com.clevertap.android.sdk.displayunits.CTDisplayUnitController;
+import com.clevertap.android.sdk.featureFlags.CTFeatureFlagsController;
+import com.clevertap.android.sdk.inapp.InAppController;
+import com.clevertap.android.sdk.inbox.CTInboxController;
+import com.clevertap.android.sdk.product_config.CTProductConfigController;
+import com.clevertap.android.sdk.pushnotification.PushProviders;
+import com.clevertap.android.sdk.task.CTExecutorFactory;
+import com.clevertap.android.sdk.task.Task;
+import java.util.concurrent.Callable;
+
+public class ControllerManager {
+
+ private InAppFCManager inAppFCManager;
+
+ private final BaseDatabaseManager baseDatabaseManager;
+
+ private CTDisplayUnitController ctDisplayUnitController;
+
+ private CTFeatureFlagsController ctFeatureFlagsController;
+
+ private CTInboxController ctInboxController;
+
+ private final CTLockManager ctLockManager;
+
+ private CTProductConfigController ctProductConfigController;
+
+ private final BaseCallbackManager callbackManager;
+
+ private final CleverTapInstanceConfig config;
+
+ private final Context context;
+
+ private final DeviceInfo deviceInfo;
+
+ private InAppController inAppController;
+
+ private PushProviders pushProviders;
+
+ public ControllerManager(Context context,
+ CleverTapInstanceConfig config,
+ CTLockManager ctLockManager,
+ BaseCallbackManager callbackManager,
+ DeviceInfo deviceInfo,
+ BaseDatabaseManager databaseManager) {
+ this.config = config;
+ this.ctLockManager = ctLockManager;
+ this.callbackManager = callbackManager;
+ this.deviceInfo = deviceInfo;
+ this.context = context;
+ baseDatabaseManager = databaseManager;
+ }
+
+ public CTDisplayUnitController getCTDisplayUnitController() {
+ return ctDisplayUnitController;
+ }
+
+ public void setCTDisplayUnitController(
+ final CTDisplayUnitController CTDisplayUnitController) {
+ ctDisplayUnitController = CTDisplayUnitController;
+ }
+
+ public CTFeatureFlagsController getCTFeatureFlagsController() {
+
+ return ctFeatureFlagsController;
+ }
+
+ public void setCTFeatureFlagsController(
+ final CTFeatureFlagsController CTFeatureFlagsController) {
+ ctFeatureFlagsController = CTFeatureFlagsController;
+ }
+
+ public CTInboxController getCTInboxController() {
+ return ctInboxController;
+ }
+
+ public void setCTInboxController(final CTInboxController CTInboxController) {
+ ctInboxController = CTInboxController;
+ }
+
+ public CTProductConfigController getCTProductConfigController() {
+ return ctProductConfigController;
+ }
+
+ public void setCTProductConfigController(
+ final CTProductConfigController CTProductConfigController) {
+ ctProductConfigController = CTProductConfigController;
+ }
+
+ public CleverTapInstanceConfig getConfig() {
+ return config;
+ }
+
+ public InAppController getInAppController() {
+ return inAppController;
+ }
+
+ public void setInAppController(final InAppController inAppController) {
+ this.inAppController = inAppController;
+ }
+
+ public InAppFCManager getInAppFCManager() {
+ return inAppFCManager;
+ }
+
+ public void setInAppFCManager(final InAppFCManager inAppFCManager) {
+ this.inAppFCManager = inAppFCManager;
+ }
+
+ public PushProviders getPushProviders() {
+ return pushProviders;
+ }
+
+ public void setPushProviders(final PushProviders pushProviders) {
+ this.pushProviders = pushProviders;
+ }
+
+ public void initializeInbox() {
+ if (config.isAnalyticsOnly()) {
+ config.getLogger()
+ .debug(config.getAccountId(), "Instance is analytics only, not initializing Notification Inbox");
+ return;
+ }
+ Task task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
+ task.execute("initializeInbox", new Callable() {
+ @Override
+ public Void call() {
+ _initializeInbox();
+ return null;
+ }
+ });
+ }
+
+ // always call async
+ private void _initializeInbox() {
+ synchronized (ctLockManager.getInboxControllerLock()) {
+ if (getCTInboxController() != null) {
+ callbackManager._notifyInboxInitialized();
+ return;
+ }
+ if (deviceInfo.getDeviceID() != null) {
+ setCTInboxController(new CTInboxController(config, deviceInfo.getDeviceID(),
+ baseDatabaseManager.loadDBAdapter(context),
+ ctLockManager,
+ callbackManager,
+ Utils.haveVideoPlayerSupport));
+ callbackManager._notifyInboxInitialized();
+ } else {
+ config.getLogger().info("CRITICAL : No device ID found!");
+ }
+ }
+ }
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CoreMetaData.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CoreMetaData.java
new file mode 100644
index 000000000..431e80e28
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CoreMetaData.java
@@ -0,0 +1,312 @@
+package com.clevertap.android.sdk;
+
+import android.app.Activity;
+import android.location.Location;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import java.lang.ref.WeakReference;
+import org.json.JSONObject;
+
+/**
+ * This class stores run time state of CleverTap's instance
+ */
+@RestrictTo(Scope.LIBRARY)
+public class CoreMetaData extends CleverTapMetaData {
+
+ private static boolean appForeground = false;
+
+ private static WeakReference currentActivity;
+
+ private static int activityCount = 0;
+
+ private long appInstallTime = 0;
+
+ private boolean appLaunchPushed = false;
+
+ private final Object appLaunchPushedLock = new Object();
+
+ private String currentScreenName = "";
+
+ private int currentSessionId = 0;
+
+ private boolean currentUserOptedOut = false;
+
+ private boolean firstRequestInSession = false;
+
+ private boolean firstSession = false;
+
+ private int geofenceSDKVersion = 0;
+
+ private boolean installReferrerDataSent = false;
+
+ private boolean isBgPing = false;
+
+ private boolean isLocationForGeofence = false;
+
+ private boolean isProductConfigRequested;
+
+ private int lastSessionLength = 0;
+
+ private Location locationFromUser = null;
+
+ private boolean offline;
+
+ private final Object optOutFlagLock = new Object();
+
+ private long referrerClickTime = 0;
+
+ private String source = null, medium = null, campaign = null;
+
+ private JSONObject wzrkParams = null;
+
+ private static int initialAppEnteredForegroundTime = 0;
+
+ public static Activity getCurrentActivity() {
+ return (currentActivity == null) ? null : currentActivity.get();
+ }
+
+ static int getInitialAppEnteredForegroundTime() {
+ return initialAppEnteredForegroundTime;
+ }
+
+ public static void setCurrentActivity(@Nullable Activity activity) {
+ if (activity == null) {
+ currentActivity = null;
+ return;
+ }
+ if (!activity.getLocalClassName().contains("InAppNotificationActivity")) {
+ currentActivity = new WeakReference<>(activity);
+ }
+ }
+
+ public static String getCurrentActivityName() {
+ Activity current = getCurrentActivity();
+ return (current != null) ? current.getLocalClassName() : null;
+ }
+
+ public static boolean isAppForeground() {
+ return appForeground;
+ }
+
+ public static void setAppForeground(boolean isForeground) {
+ appForeground = isForeground;
+ }
+
+ static void setInitialAppEnteredForegroundTime(final int initialAppEnteredForegroundTime) {
+ CoreMetaData.initialAppEnteredForegroundTime = initialAppEnteredForegroundTime;
+ }
+
+ public long getAppInstallTime() {
+ return appInstallTime;
+ }
+
+ public void setAppInstallTime(final long appInstallTime) {
+ this.appInstallTime = appInstallTime;
+ }
+
+ public Location getLocationFromUser() {
+ return locationFromUser;
+ }
+
+ public void setLocationFromUser(final Location locationFromUser) {
+ this.locationFromUser = locationFromUser;
+ }
+
+ public boolean isProductConfigRequested() {
+ return isProductConfigRequested;
+ }
+
+ public void setProductConfigRequested(final boolean productConfigRequested) {
+ isProductConfigRequested = productConfigRequested;
+ }
+
+ public void setCurrentScreenName(final String currentScreenName) {
+ this.currentScreenName = currentScreenName;
+ }
+
+ synchronized void clearCampaign() {
+ campaign = null;
+ }
+
+ synchronized void clearMedium() {
+ medium = null;
+ }
+
+ synchronized void clearSource() {
+ source = null;
+ }
+
+ synchronized void clearWzrkParams() {
+ wzrkParams = null;
+ }
+
+ public static int getActivityCount() {
+ return activityCount;
+ }
+
+ synchronized void setCampaign(String campaign) {
+ if (this.campaign == null) {
+ this.campaign = campaign;
+ }
+ }
+
+ public synchronized String getCampaign() {
+ return campaign;
+ }
+
+ public int getCurrentSessionId() {
+ return currentSessionId;
+ }
+
+ public int getGeofenceSDKVersion() {
+ return geofenceSDKVersion;
+ }
+
+ public void setGeofenceSDKVersion(int geofenceSDKVersion) {
+ this.geofenceSDKVersion = geofenceSDKVersion;
+ }
+
+ void setLastSessionLength(final int lastSessionLength) {
+ this.lastSessionLength = lastSessionLength;
+ }
+
+ //Session
+ public int getLastSessionLength() {
+ return lastSessionLength;
+ }
+
+ // only set if not already set during the session
+ synchronized void setMedium(String medium) {
+ if (this.medium == null) {
+ this.medium = medium;
+ }
+ }
+
+ public synchronized String getMedium() {
+ return medium;
+ }
+
+ void setReferrerClickTime(final long referrerClickTime) {
+ this.referrerClickTime = referrerClickTime;
+ }
+
+ public long getReferrerClickTime() {
+ return referrerClickTime;
+ }
+
+ public synchronized String getSource() {
+ return source;
+ }
+
+ //UTM
+ // only set if not already set during the session
+ synchronized void setSource(String source) {
+ if (this.source == null) {
+ this.source = source;
+ }
+ }
+
+ public String getScreenName() {
+ return currentScreenName.equals("") ? null : currentScreenName;
+ }
+
+ public synchronized void setWzrkParams(JSONObject wzrkParams) {
+ if (this.wzrkParams == null) {
+ this.wzrkParams = wzrkParams;
+ }
+ }
+
+ public synchronized JSONObject getWzrkParams() {
+ return wzrkParams;
+ }
+
+ public boolean inCurrentSession() {
+ return currentSessionId > 0;
+ }
+
+ void setAppLaunchPushed(boolean pushed) {
+ synchronized (appLaunchPushedLock) {
+ appLaunchPushed = pushed;
+ }
+ }
+
+ public boolean isAppLaunchPushed() {
+ synchronized (appLaunchPushedLock) {
+ return appLaunchPushed;
+ }
+ }
+
+ public boolean isBgPing() {
+ return isBgPing;
+ }
+
+ public void setBgPing(final boolean bgPing) {
+ isBgPing = bgPing;
+ }
+
+ public void setCurrentUserOptedOut(boolean enable) {
+ synchronized (optOutFlagLock) {
+ currentUserOptedOut = enable;
+ }
+ }
+
+ public boolean isCurrentUserOptedOut() {
+ synchronized (optOutFlagLock) {
+ return currentUserOptedOut;
+ }
+ }
+
+ public boolean isFirstRequestInSession() {
+ return firstRequestInSession;
+ }
+
+ public void setFirstRequestInSession(boolean firstRequestInSession) {
+ this.firstRequestInSession = firstRequestInSession;
+ }
+
+ void setFirstSession(final boolean firstSession) {
+ this.firstSession = firstSession;
+ }
+
+ //Session
+ public boolean isFirstSession() {
+ return firstSession;
+ }
+
+ void setInstallReferrerDataSent(final boolean installReferrerDataSent) {
+ this.installReferrerDataSent = installReferrerDataSent;
+ }
+
+ public boolean isInstallReferrerDataSent() {
+ return installReferrerDataSent;
+ }
+
+ public boolean isLocationForGeofence() {
+ return isLocationForGeofence;
+ }
+
+ public void setLocationForGeofence(boolean locationForGeofence) {
+ isLocationForGeofence = locationForGeofence;
+ }
+
+ void setOffline(boolean value) {
+ offline = value;
+ }
+
+ void setCurrentSessionId(int sessionId) {
+ this.currentSessionId = sessionId;
+ }
+
+ public boolean isOffline() {
+ return offline;
+ }
+
+ public static void setActivityCount(final int count) {
+ activityCount = count;
+ }
+
+ static void incrementActivityCount() {
+ activityCount++;
+ }
+}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CoreState.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CoreState.java
new file mode 100644
index 000000000..2fa72dea1
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CoreState.java
@@ -0,0 +1,247 @@
+package com.clevertap.android.sdk;
+
+import android.content.Context;
+import com.clevertap.android.sdk.db.BaseDatabaseManager;
+import com.clevertap.android.sdk.events.BaseEventQueueManager;
+import com.clevertap.android.sdk.events.EventMediator;
+import com.clevertap.android.sdk.inapp.InAppController;
+import com.clevertap.android.sdk.login.LoginController;
+import com.clevertap.android.sdk.network.BaseNetworkManager;
+import com.clevertap.android.sdk.product_config.CTProductConfigController;
+import com.clevertap.android.sdk.product_config.CTProductConfigFactory;
+import com.clevertap.android.sdk.pushnotification.PushProviders;
+import com.clevertap.android.sdk.task.MainLooperHandler;
+import com.clevertap.android.sdk.validation.ValidationResultStack;
+
+public class CoreState extends CleverTapState {
+
+ private BaseLocationManager baseLocationManager;
+
+ private CleverTapInstanceConfig config;
+
+ private CoreMetaData coreMetaData;
+
+ private BaseDatabaseManager databaseManager;
+
+ private DeviceInfo deviceInfo;
+
+ private EventMediator eventMediator;
+
+ private LocalDataStore localDataStore;
+
+ private ActivityLifeCycleManager activityLifeCycleManager;
+
+ private AnalyticsManager analyticsManager;
+
+ private BaseEventQueueManager baseEventQueueManager;
+
+ private CTLockManager ctLockManager;
+
+ private BaseCallbackManager callbackManager;
+
+ private ControllerManager controllerManager;
+
+ private InAppController inAppController;
+
+ private LoginController loginController;
+
+ private SessionManager sessionManager;
+
+ private ValidationResultStack validationResultStack;
+
+ private MainLooperHandler mainLooperHandler;
+
+ private BaseNetworkManager networkManager;
+
+ private PushProviders pushProviders;
+
+ CoreState(final Context context) {
+ super(context);
+ }
+
+ public ActivityLifeCycleManager getActivityLifeCycleManager() {
+ return activityLifeCycleManager;
+ }
+
+ public void setActivityLifeCycleManager(final ActivityLifeCycleManager activityLifeCycleManager) {
+ this.activityLifeCycleManager = activityLifeCycleManager;
+ }
+
+ public AnalyticsManager getAnalyticsManager() {
+ return analyticsManager;
+ }
+
+ public void setAnalyticsManager(final AnalyticsManager analyticsManager) {
+ this.analyticsManager = analyticsManager;
+ }
+
+ public BaseEventQueueManager getBaseEventQueueManager() {
+ return baseEventQueueManager;
+ }
+
+ void setBaseEventQueueManager(final BaseEventQueueManager baseEventQueueManager) {
+ this.baseEventQueueManager = baseEventQueueManager;
+ }
+
+ public CTLockManager getCTLockManager() {
+ return ctLockManager;
+ }
+
+ public void setCTLockManager(final CTLockManager CTLockManager) {
+ ctLockManager = CTLockManager;
+ }
+
+ public BaseCallbackManager getCallbackManager() {
+ return callbackManager;
+ }
+
+ void setCallbackManager(final BaseCallbackManager callbackManager) {
+ this.callbackManager = callbackManager;
+ }
+
+ public CleverTapInstanceConfig getConfig() {
+ return config;
+ }
+
+ public void setConfig(final CleverTapInstanceConfig config) {
+ this.config = config;
+ }
+
+ public ControllerManager getControllerManager() {
+ return controllerManager;
+ }
+
+ public void setControllerManager(final ControllerManager controllerManager) {
+ this.controllerManager = controllerManager;
+ }
+
+ public CoreMetaData getCoreMetaData() {
+ return coreMetaData;
+ }
+
+ void setCoreMetaData(final CoreMetaData coreMetaData) {
+ this.coreMetaData = coreMetaData;
+ }
+
+ public CTProductConfigController getCtProductConfigController() {
+ initProductConfig();
+ return getControllerManager().getCTProductConfigController();
+ }
+
+ @Override
+ public BaseDatabaseManager getDatabaseManager() {
+ return databaseManager;
+ }
+
+ @Override
+ void setDatabaseManager(final BaseDatabaseManager databaseManager) {
+ this.databaseManager = databaseManager;
+ }
+
+ public DeviceInfo getDeviceInfo() {
+ return deviceInfo;
+ }
+
+ public void setDeviceInfo(final DeviceInfo deviceInfo) {
+ this.deviceInfo = deviceInfo;
+ }
+
+ public InAppController getInAppController() {
+ return inAppController;
+ }
+
+ public void setInAppController(final InAppController inAppController) {
+ this.inAppController = inAppController;
+ }
+
+ public LocalDataStore getLocalDataStore() {
+ return localDataStore;
+ }
+
+ public void setLocalDataStore(final LocalDataStore localDataStore) {
+ this.localDataStore = localDataStore;
+ }
+
+ public LoginController getLoginController() {
+ return loginController;
+ }
+
+ public void setLoginController(final LoginController loginController) {
+ this.loginController = loginController;
+ }
+
+ @Override
+ public BaseNetworkManager getNetworkManager() {
+ return networkManager;
+ }
+
+ @Override
+ void setNetworkManager(final BaseNetworkManager networkManager) {
+ this.networkManager = networkManager;
+ }
+
+ public PushProviders getPushProviders() {
+ return pushProviders;
+ }
+
+ public void setPushProviders(final PushProviders pushProviders) {
+ this.pushProviders = pushProviders;
+ }
+
+ public SessionManager getSessionManager() {
+ return sessionManager;
+ }
+
+ public void setSessionManager(final SessionManager sessionManager) {
+ this.sessionManager = sessionManager;
+ }
+
+ public ValidationResultStack getValidationResultStack() {
+ return validationResultStack;
+ }
+
+ public void setValidationResultStack(final ValidationResultStack validationResultStack) {
+ this.validationResultStack = validationResultStack;
+ }
+
+ @Override
+ BaseLocationManager getLocationManager() {
+ return baseLocationManager;
+ }
+
+ @Override
+ void setLocationManager(final BaseLocationManager baseLocationManager) {
+ this.baseLocationManager = baseLocationManager;
+ }
+
+ public EventMediator getEventMediator() {
+ return eventMediator;
+ }
+
+ public void setEventMediator(final EventMediator eventMediator) {
+ this.eventMediator = eventMediator;
+ }
+
+ public MainLooperHandler getMainLooperHandler() {
+ return mainLooperHandler;
+ }
+
+ public void setMainLooperHandler(final MainLooperHandler mainLooperHandler) {
+ this.mainLooperHandler = mainLooperHandler;
+ }
+
+ private void initProductConfig() {
+ Logger.v("Initializing Product Config with device Id = " + getDeviceInfo().getDeviceID());
+ if (getConfig().isAnalyticsOnly()) {
+ getConfig().getLogger()
+ .debug(getConfig().getAccountId(), "Product Config is not enabled for this instance");
+ return;
+ }
+ if (getControllerManager().getCTProductConfigController() == null) {
+ CTProductConfigController ctProductConfigController = CTProductConfigFactory
+ .getInstance(context, getDeviceInfo(),
+ getConfig(), analyticsManager, coreMetaData, callbackManager);
+ getControllerManager().setCTProductConfigController(ctProductConfigController);
+ }
+ }
+}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/DeviceInfo.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/DeviceInfo.java
index e123be719..30568da8e 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/DeviceInfo.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/DeviceInfo.java
@@ -19,11 +19,16 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.core.app.NotificationManagerCompat;
+import com.clevertap.android.sdk.login.LoginInfoProvider;
+import com.clevertap.android.sdk.utils.CTJsonConverter;
+import com.clevertap.android.sdk.validation.ValidationResult;
+import com.clevertap.android.sdk.validation.ValidationResultFactory;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.UUID;
+import org.json.JSONObject;
@RestrictTo(Scope.LIBRARY)
public class DeviceInfo {
@@ -304,11 +309,19 @@ private double toTwoPlaces(double n) {
private final ArrayList validationResults = new ArrayList<>();
+ private boolean enableNetworkInfoReporting = false;
- DeviceInfo(Context context, CleverTapInstanceConfig config, String cleverTapID) {
+ private final CoreMetaData mCoreMetaData;
+
+ DeviceInfo(Context context, CleverTapInstanceConfig config, String cleverTapID, CoreMetaData coreMetaData) {
this.context = context;
this.config = config;
this.library = null;
+ mCoreMetaData = coreMetaData;
+ onInitDeviceInfo(cleverTapID);
+ }
+
+ void onInitDeviceInfo(final String cleverTapID) {
Thread deviceInfoCacheThread = new Thread(new Runnable() {
@Override
public void run() {
@@ -323,12 +336,12 @@ public boolean isErrorDeviceId() {
return getDeviceID() != null && getDeviceID().startsWith(Constants.ERROR_PROFILE_PREFIX);
}
- void forceNewDeviceID() {
+ public void forceNewDeviceID() {
String deviceID = generateGUID();
forceUpdateDeviceId(deviceID);
}
- void forceUpdateCustomCleverTapID(String cleverTapID) {
+ public void forceUpdateCustomCleverTapID(String cleverTapID) {
if (Utils.validateCTID(cleverTapID)) {
getConfigLogger()
.info(config.getAccountId(), "Setting CleverTap ID to custom CleverTap ID : " + cleverTapID);
@@ -350,7 +363,7 @@ void forceUpdateCustomCleverTapID(String cleverTapID) {
* @param id The new device ID
*/
@SuppressLint("CommitPrefEdits")
- void forceUpdateDeviceId(String id) {
+ public void forceUpdateDeviceId(String id) {
getConfigLogger().verbose(this.config.getAccountId(), "Force updating the device ID to " + id);
synchronized (deviceIDLock) {
StorageHelper.putString(context, getDeviceIdStorageKey(), id);
@@ -361,49 +374,97 @@ String getAttributionID() {
return getDeviceID();
}
- String getBluetoothVersion() {
+ /**
+ * Determines if a device is tablet, smart phone or TV
+ *
+ * @param context context
+ * @return one of the possible value of {@link DeviceType}
+ */
+ @DeviceType
+ public static int getDeviceType(final Context context) {
+
+ if (sDeviceType == NULL) {
+
+ try {
+ UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
+ if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+ sDeviceType = TV;
+ return sDeviceType;
+ }
+ } catch (Exception e) {
+ //uiModeManager or context is null
+ Logger.d("Failed to decide whether device is a TV!");
+ e.printStackTrace();
+ }
+
+ try {
+ sDeviceType = context.getResources().getBoolean(R.bool.ctIsTablet) ? TABLET : SMART_PHONE;
+ } catch (Exception e) {
+ // resource not found or context is null
+ Logger.d("Failed to decide whether device is a smart phone or tablet!");
+ e.printStackTrace();
+ sDeviceType = UNKNOWN;
+ }
+
+ }
+ return sDeviceType;
+ }
+
+ //Event
+ public JSONObject getAppLaunchedFields() {
+
+ try {
+ boolean deviceIsMultiUser = false;
+ if (getGoogleAdID() != null) {
+ deviceIsMultiUser = new LoginInfoProvider(context, config, this).deviceIsMultiUser();
+ }
+ return CTJsonConverter.from(this, mCoreMetaData.getLocationFromUser(), enableNetworkInfoReporting,
+ deviceIsMultiUser);
+ } catch (Throwable t) {
+ config.getLogger().verbose(config.getAccountId(), "Failed to construct App Launched event", t);
+ return new JSONObject();
+ }
+ }
+
+ public String getBluetoothVersion() {
return getDeviceCachedInfo().bluetoothVersion;
}
- int getBuild() {
+ public int getBuild() {
return getDeviceCachedInfo().build;
}
- String getCarrier() {
+ public String getCarrier() {
return getDeviceCachedInfo().carrier;
}
- Context getContext() {
+ public Context getContext() {
return context;
}
- String getCountryCode() {
- return getDeviceCachedInfo().countryCode;
+ public String getDeviceID() {
+ return _getDeviceID() != null ? _getDeviceID() : getFallBackDeviceID();
}
- int getDPI() {
- return getDeviceCachedInfo().dpi;
+ public String getCountryCode() {
+ return getDeviceCachedInfo().countryCode;
}
- String getDeviceID() {
- return _getDeviceID() != null ? _getDeviceID() : getFallBackDeviceID();
+ public int getDPI() {
+ return getDeviceCachedInfo().dpi;
}
- String getGoogleAdID() {
+ public String getGoogleAdID() {
synchronized (adIDLock) {
return googleAdID;
}
}
- double getHeight() {
+ public double getHeight() {
return getDeviceCachedInfo().height;
}
- int getHeightPixels() {
- return getDeviceCachedInfo().heightPixels;
- }
-
- String getLibrary() {
+ public String getLibrary() {
return library;
}
@@ -411,56 +472,52 @@ void setLibrary(String library) {
this.library = library;
}
- String getManufacturer() {
+ public String getManufacturer() {
return getDeviceCachedInfo().manufacturer;
}
- String getModel() {
+ public String getModel() {
return getDeviceCachedInfo().model;
}
- String getNetworkType() {
+ public String getNetworkType() {
return getDeviceCachedInfo().networkType;
}
- boolean getNotificationsEnabledForUser() {
+ public boolean getNotificationsEnabledForUser() {
return getDeviceCachedInfo().notificationsEnabled;
}
- String getOsName() {
+ public String getOsName() {
return getDeviceCachedInfo().osName;
}
- String getOsVersion() {
+ public String getOsVersion() {
return getDeviceCachedInfo().osVersion;
}
- int getSdkVersion() {
- return getDeviceCachedInfo().sdkVersion;
- }
-
- ArrayList getValidationResults() {
+ public ArrayList getValidationResults() {
// noinspection unchecked
ArrayList tempValidationResults = (ArrayList) validationResults.clone();
validationResults.clear();
return tempValidationResults;
}
- String getVersionName() {
- return getDeviceCachedInfo().versionName;
+ public int getSdkVersion() {
+ return getDeviceCachedInfo().sdkVersion;
}
- double getWidth() {
- return getDeviceCachedInfo().width;
+ public String getVersionName() {
+ return getDeviceCachedInfo().versionName;
}
- int getWidthPixels() {
- return getDeviceCachedInfo().widthPixels;
+ public double getWidth() {
+ return getDeviceCachedInfo().width;
}
@SuppressLint("MissingPermission")
@SuppressWarnings("MissingPermission")
- Boolean isBluetoothEnabled() {
+ public Boolean isBluetoothEnabled() {
Boolean isBluetoothEnabled = null;
try {
PackageManager pm = context.getPackageManager();
@@ -477,13 +534,13 @@ Boolean isBluetoothEnabled() {
return isBluetoothEnabled;
}
- boolean isLimitAdTrackingEnabled() {
+ public boolean isLimitAdTrackingEnabled() {
synchronized (adIDLock) {
return limitAdTracking;
}
}
- Boolean isWifiConnected() {
+ public Boolean isWifiConnected() {
Boolean ret = null;
if (PackageManager.PERMISSION_GRANTED == context
@@ -671,44 +728,54 @@ private void updateFallbackID(String fallbackId) {
* @param context The Android context
* @return The integer identifier for the image resource
*/
- static int getAppIconAsIntId(final Context context) {
+ public static int getAppIconAsIntId(final Context context) {
ApplicationInfo ai = context.getApplicationInfo();
return ai.icon;
}
- /**
- * Determines if a device is tablet, smart phone or TV
- *
- * @param context context
- * @return one of the possible value of {@link DeviceType}
- */
- @DeviceType
- static int getDeviceType(final Context context) {
+ int getHeightPixels() {
+ return getDeviceCachedInfo().heightPixels;
+ }
- if (sDeviceType == NULL) {
+ void enableDeviceNetworkInfoReporting(boolean value) {
+ enableNetworkInfoReporting = value;
+ StorageHelper.putBoolean(context, StorageHelper.storageKeyWithSuffix(config, Constants.NETWORK_INFO),
+ enableNetworkInfoReporting);
+ config.getLogger()
+ .verbose(config.getAccountId(),
+ "Device Network Information reporting set to " + enableNetworkInfoReporting);
+ }
- try {
- UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
- if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
- sDeviceType = TV;
- return sDeviceType;
- }
- } catch (Exception e) {
- //uiModeManager or context is null
- Logger.d("Failed to decide whether device is a TV!");
- e.printStackTrace();
- }
+ void setDeviceNetworkInfoReportingFromStorage() {
+ boolean enabled = StorageHelper.getBooleanFromPrefs(context, config, Constants.NETWORK_INFO);
+ config.getLogger()
+ .verbose(config.getAccountId(),
+ "Setting device network info reporting state from storage to " + enabled);
+ enableNetworkInfoReporting = enabled;
+ }
- try {
- sDeviceType = context.getResources().getBoolean(R.bool.ctIsTablet) ? TABLET : SMART_PHONE;
- } catch (Exception e) {
- // resource not found or context is null
- Logger.d("Failed to decide whether device is a smart phone or tablet!");
- e.printStackTrace();
- sDeviceType = UNKNOWN;
- }
+ int getWidthPixels() {
+ return getDeviceCachedInfo().widthPixels;
+ }
+ public void setCurrentUserOptOutStateFromStorage() {
+ String key = optOutKey();
+ if (key == null) {
+ config.getLogger().verbose(config.getAccountId(),
+ "Unable to set current user OptOut state from storage: storage key is null");
+ return;
}
- return sDeviceType;
+ boolean storedOptOut = StorageHelper.getBooleanFromPrefs(context, config, key);
+ mCoreMetaData.setCurrentUserOptedOut(storedOptOut);
+ config.getLogger().verbose(config.getAccountId(),
+ "Set current user OptOut state from storage to: " + storedOptOut + " for key: " + key);
+ }
+
+ String optOutKey() {
+ String guid = getDeviceID();
+ if (guid == null) {
+ return null;
+ }
+ return "OptOut:" + guid;
}
}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/FailureFlushListener.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/FailureFlushListener.java
new file mode 100644
index 000000000..5fd436324
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/FailureFlushListener.java
@@ -0,0 +1,8 @@
+package com.clevertap.android.sdk;
+
+import android.content.Context;
+
+public interface FailureFlushListener {
+
+ void failureFlush(Context context);
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/FileUtils.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/FileUtils.java
deleted file mode 100644
index b68c90abd..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/FileUtils.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.clevertap.android.sdk;
-
-import android.content.Context;
-import android.text.TextUtils;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileWriter;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import org.json.JSONObject;
-
-public class FileUtils {
-
- public static void deleteDirectory(Context context, CleverTapInstanceConfig config, String dirName) {
- if (TextUtils.isEmpty(dirName) || context == null) {
- return;
- }
- try {
- File file = new File(context.getFilesDir(), dirName);
- if (file.exists() && file.isDirectory()) {
- String[] children = file.list();
- for (String child : children) {
- new File(file, child).delete();
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- if (config != null) {
- config.getLogger().verbose(config.getAccountId(),
- "writeFileOnInternalStorage: failed" + dirName + " Error:" + e.getLocalizedMessage());
- }
- }
- }
-
- public static void deleteFile(Context context, CleverTapInstanceConfig config, String fileName) throws Exception {
- if (TextUtils.isEmpty(fileName) || context == null) {
- return;
- }
- try {
- File file = new File(context.getFilesDir(), fileName);
- if (file.exists()) {
- if (file.delete()) {
- if (config != null) {
- config.getLogger().verbose(config.getAccountId(), "File Deleted:" + fileName);
- }
- } else {
- if (config != null) {
- config.getLogger().verbose(config.getAccountId(), "Failed to delete file" + fileName);
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- if (config != null) {
- config.getLogger().verbose(config.getAccountId(),
- "writeFileOnInternalStorage: failed" + fileName + " Error:" + e.getLocalizedMessage());
- }
- }
- }
-
- public static String readFromFile(Context context, CleverTapInstanceConfig config, String fileNameWithPath)
- throws Exception {
-
- String content = "";
- //Make sure to use a try-catch statement to catch any errors
- try {
- //Make your FilePath and File
- String yourFilePath = context.getFilesDir() + "/" + fileNameWithPath;
- File yourFile = new File(yourFilePath);
- //Make an InputStream with your File in the constructor
- InputStream inputStream = new FileInputStream(yourFile);
- StringBuilder stringBuilder = new StringBuilder();
- //Check to see if your inputStream is null
- //If it isn't use the inputStream to make a InputStreamReader
- //Use that to make a BufferedReader
- //Also create an empty String
- InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
- BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
- String receiveString = "";
- //Use a while loop to append the lines from the Buffered reader
- while ((receiveString = bufferedReader.readLine()) != null) {
- stringBuilder.append(receiveString);
- }
- //Close your InputStream and save stringBuilder as a String
- inputStream.close();
- content = stringBuilder.toString();
- } catch (Exception e) {
- if (config != null) {
- config.getLogger()
- .verbose(config.getAccountId(), "[Exception While Reading: " + e.getLocalizedMessage());
- }
- //Log your error with Log.e
- }
- return content;
- }
-
- public static void writeJsonToFile(Context context, CleverTapInstanceConfig config, String dirName,
- String fileName, JSONObject jsonObject) {
- try {
- if (jsonObject == null || TextUtils.isEmpty(dirName) || TextUtils.isEmpty(fileName)) {
- return;
- }
- File file = new File(context.getFilesDir(), dirName);
- if (!file.exists()) {
- if (!file.mkdir()) {
- return;// if directory is not created don't proceed and return
- }
- }
-
- File file1 = new File(file, fileName);
- FileWriter writer = new FileWriter(file1, false);
- writer.append(jsonObject.toString());
- writer.flush();
- writer.close();
- } catch (Exception e) {
- e.printStackTrace();
- if (config != null) {
- config.getLogger().verbose(config.getAccountId(),
- "writeFileOnInternalStorage: failed" + e.getLocalizedMessage());
- }
- }
- }
-}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java
index 3370c1bfa..33b3e9fa7 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java
@@ -4,6 +4,9 @@
import android.content.Context;
import android.content.SharedPreferences;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import com.clevertap.android.sdk.inapp.CTInAppNotification;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -13,13 +16,14 @@
import org.json.JSONArray;
import org.json.JSONObject;
-class InAppFCManager {
+@RestrictTo(Scope.LIBRARY)
+public class InAppFCManager {
- private static final SimpleDateFormat ddMMyyyy = new SimpleDateFormat("ddMMyyyy", Locale.US);
+ private final SimpleDateFormat ddMMyyyy = new SimpleDateFormat("ddMMyyyy", Locale.US);
- private CleverTapInstanceConfig config;
+ private final CleverTapInstanceConfig config;
- private Context context;
+ private final Context context;
private String deviceId;
@@ -37,38 +41,7 @@ class InAppFCManager {
init(deviceId);
}
- void attachToHeader(final Context context, JSONObject header) {
- try {
- // Trigger reset for dates
-
- header.put("imp", getIntFromPrefs(getKeyWithDeviceId(Constants.KEY_COUNTS_SHOWN_TODAY, deviceId), 0));
-
- // tlc: [[targetID, todayCount, lifetime]]
- JSONArray arr = new JSONArray();
- final SharedPreferences prefs = StorageHelper
- .getPreferences(context, getKeyWithDeviceId(Constants.KEY_COUNTS_PER_INAPP, deviceId));
- final Map all = prefs.getAll();
- for (String inapp : all.keySet()) {
- final Object o = all.get(inapp);
- if (o instanceof String) {
- final String[] parts = ((String) o).split(",");
- if (parts.length == 2) {
- JSONArray a = new JSONArray();
- a.put(0, inapp);
- a.put(1, Integer.parseInt(parts[0]));
- a.put(2, Integer.parseInt(parts[1]));
- arr.put(a);
- }
- }
- }
-
- header.put("tlc", arr);
- } catch (Throwable t) {
- Logger.v("Failed to attach FC to header", t);
- }
- }
-
- boolean canShow(CTInAppNotification inapp) {
+ public boolean canShow(CTInAppNotification inapp) {
try {
if (inapp == null) {
return false;
@@ -95,7 +68,7 @@ boolean canShow(CTInAppNotification inapp) {
return false;
}
- void changeUser(String deviceId) {
+ public void changeUser(String deviceId) {
// reset counters
mShownThisSession.clear();
mShownThisSessionCount = 0;
@@ -104,14 +77,14 @@ void changeUser(String deviceId) {
init(deviceId);
}
- void didDismiss(CTInAppNotification inapp) {
+ public void didDismiss(CTInAppNotification inapp) {
final Object id = inapp.getId();
if (id != null) {
mDismissedThisSession.add(id.toString());
}
}
- void didShow(final Context context, CTInAppNotification inapp) {
+ public void didShow(final Context context, CTInAppNotification inapp) {
final String id = getInAppID(inapp);
if (id == null) {
return;
@@ -134,7 +107,38 @@ void didShow(final Context context, CTInAppNotification inapp) {
++shownToday);
}
- void processResponse(final Context context, final JSONObject response) {
+ public void attachToHeader(final Context context, JSONObject header) {
+ try {
+ // Trigger reset for dates
+
+ header.put("imp", getIntFromPrefs(getKeyWithDeviceId(Constants.KEY_COUNTS_SHOWN_TODAY, deviceId), 0));
+
+ // tlc: [[targetID, todayCount, lifetime]]
+ JSONArray arr = new JSONArray();
+ final SharedPreferences prefs = StorageHelper
+ .getPreferences(context, getKeyWithDeviceId(Constants.KEY_COUNTS_PER_INAPP, deviceId));
+ final Map all = prefs.getAll();
+ for (String inapp : all.keySet()) {
+ final Object o = all.get(inapp);
+ if (o instanceof String) {
+ final String[] parts = ((String) o).split(",");
+ if (parts.length == 2) {
+ JSONArray a = new JSONArray();
+ a.put(0, inapp);
+ a.put(1, Integer.parseInt(parts[0]));
+ a.put(2, Integer.parseInt(parts[1]));
+ arr.put(a);
+ }
+ }
+ }
+
+ header.put("tlc", arr);
+ } catch (Throwable t) {
+ Logger.v("Failed to attach FC to header", t);
+ }
+ }
+
+ public void processResponse(final Context context, final JSONObject response) {
try {
if (!response.has("inapp_stale")) {
return;
@@ -163,7 +167,7 @@ void processResponse(final Context context, final JSONObject response) {
}
}
- synchronized void updateLimits(final Context context, int perDay, int perSession) {
+ public synchronized void updateLimits(final Context context, int perDay, int perSession) {
StorageHelper.putInt(context, storageKeyWithSuffix(getKeyWithDeviceId(Constants.KEY_MAX_PER_DAY, deviceId)),
perDay);
StorageHelper
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java
index 62b2a9a4a..e8faae6d7 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java
@@ -9,20 +9,23 @@
import android.os.Bundle;
import android.view.WindowManager;
import androidx.fragment.app.FragmentActivity;
+import com.clevertap.android.sdk.inapp.CTInAppBaseFullFragment;
+import com.clevertap.android.sdk.inapp.CTInAppHtmlCoverFragment;
+import com.clevertap.android.sdk.inapp.CTInAppHtmlHalfInterstitialFragment;
+import com.clevertap.android.sdk.inapp.CTInAppHtmlInterstitialFragment;
+import com.clevertap.android.sdk.inapp.CTInAppNativeCoverFragment;
+import com.clevertap.android.sdk.inapp.CTInAppNativeCoverImageFragment;
+import com.clevertap.android.sdk.inapp.CTInAppNativeHalfInterstitialFragment;
+import com.clevertap.android.sdk.inapp.CTInAppNativeHalfInterstitialImageFragment;
+import com.clevertap.android.sdk.inapp.CTInAppNativeInterstitialFragment;
+import com.clevertap.android.sdk.inapp.CTInAppNativeInterstitialImageFragment;
+import com.clevertap.android.sdk.inapp.CTInAppNotification;
+import com.clevertap.android.sdk.inapp.CTInAppType;
+import com.clevertap.android.sdk.inapp.InAppListener;
import java.lang.ref.WeakReference;
import java.util.HashMap;
-public final class InAppNotificationActivity extends FragmentActivity implements CTInAppBaseFragment.InAppListener {
-
- interface InAppActivityListener {
-
- void inAppNotificationDidClick(CTInAppNotification inAppNotification, Bundle formData,
- HashMap keyValuePayload);
-
- void inAppNotificationDidDismiss(Context context, CTInAppNotification inAppNotification, Bundle formData);
-
- void inAppNotificationDidShow(CTInAppNotification inAppNotification, Bundle formData);
- }
+public final class InAppNotificationActivity extends FragmentActivity implements InAppListener {
private static boolean isAlertVisible = false;
@@ -30,7 +33,7 @@ void inAppNotificationDidClick(CTInAppNotification inAppNotification, Bundle for
private CTInAppNotification inAppNotification;
- private WeakReference listenerWeakReference;
+ private WeakReference listenerWeakReference;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -48,7 +51,7 @@ public void onCreate(Bundle savedInstanceState) {
if (configBundle != null) {
config = configBundle.getParcelable("config");
}
- setListener(CleverTapAPI.instanceWithConfig(getApplicationContext(), config));
+ setListener(CleverTapAPI.instanceWithConfig(this, config).getCoreState().getInAppController());
} catch (Throwable t) {
Logger.v("Cannot find a valid notification bundle to show!", t);
finish();
@@ -137,7 +140,7 @@ public void setTheme(int resid) {
}
void didClick(Bundle data, HashMap keyValueMap) {
- InAppActivityListener listener = getListener();
+ InAppListener listener = getListener();
if (listener != null) {
listener.inAppNotificationDidClick(inAppNotification, data, keyValueMap);
}
@@ -148,14 +151,14 @@ void didDismiss(Bundle data) {
isAlertVisible = false;
}
finish();
- InAppActivityListener listener = getListener();
+ InAppListener listener = getListener();
if (listener != null && getBaseContext() != null) {
listener.inAppNotificationDidDismiss(getBaseContext(), inAppNotification, data);
}
}
void didShow(Bundle data) {
- InAppActivityListener listener = getListener();
+ InAppListener listener = getListener();
if (listener != null) {
listener.inAppNotificationDidShow(inAppNotification, data);
}
@@ -171,8 +174,8 @@ void fireUrlThroughIntent(String url, Bundle formData) {
didDismiss(formData);
}
- InAppActivityListener getListener() {
- InAppActivityListener listener = null;
+ InAppListener getListener() {
+ InAppListener listener = null;
try {
listener = listenerWeakReference.get();
} catch (Throwable t) {
@@ -185,7 +188,7 @@ InAppActivityListener getListener() {
return listener;
}
- void setListener(InAppActivityListener listener) {
+ void setListener(InAppListener listener) {
listenerWeakReference = new WeakReference<>(listener);
}
@@ -347,10 +350,13 @@ public void onClick(DialogInterface dialogInterface, int i) {
});
}
}
- //noinspection ConstantConditions
- alertDialog.show();
- isAlertVisible = true;
- didShow(null);
+ if(alertDialog != null){
+ alertDialog.show();
+ isAlertVisible = true;
+ didShow(null);
+ }else{
+ config.getLogger().debug("InAppNotificationActivity: Alert Dialog is null, not showing Alert InApp");
+ }
break;
}
default: {
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/JsonUtil.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/JsonUtil.java
deleted file mode 100644
index 3dc0e2906..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/JsonUtil.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.clevertap.android.sdk;
-
-import androidx.annotation.NonNull;
-import java.util.ArrayList;
-import java.util.List;
-import org.json.JSONArray;
-import org.json.JSONException;
-
-public class JsonUtil {
-
- public static Object[] toArray(@NonNull JSONArray jsonArray) {
- Object[] array = new Object[jsonArray.length()];
- try {
- for (int i = 0; i < jsonArray.length(); i++) {
- array[i] = jsonArray.get(i);
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return array;
- }
-
- public static JSONArray toJsonArray(@NonNull List> list) {
- JSONArray array = new JSONArray();
- for (Object item : list) {
- if (item != null) {
- array.put(item);
- }
- }
- return array;
- }
-
- public static ArrayList> toList(@NonNull JSONArray array) {
- ArrayList list = new ArrayList<>();
- for (int i = 0; i < array.length(); i++) {
- try {
- list.add(array.get(i));
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- return list;
- }
-}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/LocalDataStore.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/LocalDataStore.java
index a9219c7c9..decd53cd3 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/LocalDataStore.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/LocalDataStore.java
@@ -3,6 +3,10 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import com.clevertap.android.sdk.db.DBAdapter;
+import com.clevertap.android.sdk.events.EventDetail;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -14,7 +18,8 @@
import org.json.JSONObject;
@SuppressWarnings("unused")
-class LocalDataStore {
+@RestrictTo(Scope.LIBRARY)
+public class LocalDataStore {
private static long EXECUTOR_THREAD_ID = 0;
@@ -28,13 +33,13 @@ class LocalDataStore {
private final HashMap PROFILE_FIELDS_IN_THIS_SESSION = new HashMap<>();
- private CleverTapInstanceConfig config;
+ private final CleverTapInstanceConfig config;
- private Context context;
+ private final Context context;
private DBAdapter dbAdapter;
- private ExecutorService es;
+ private final ExecutorService es;
private final String eventNamespace = "local_events";
@@ -46,7 +51,7 @@ class LocalDataStore {
inflateLocalProfileAsync(context);
}
- void changeUser() {
+ public void changeUser() {
resetLocalProfileSync();
}
@@ -98,7 +103,7 @@ Object getProfileValueForKey(String key) {
return _getProfileProperty(key);
}
- void persistEvent(Context context, JSONObject event, int type) {
+ public void persistEvent(Context context, JSONObject event, int type) {
if (event == null) {
return;
@@ -124,7 +129,7 @@ void removeProfileFields(ArrayList fields) {
removeProfileFields(fields, false);
}
- void setDataSyncFlag(JSONObject event) {
+ public void setDataSyncFlag(JSONObject event) {
try {
// Check the personalisation flag
boolean enablePersonalisation = this.config.isPersonalizationEnabled();
@@ -180,7 +185,7 @@ void setProfileFields(JSONObject fields) {
}
@SuppressWarnings("rawtypes")
- void syncWithUpstream(Context context, JSONObject response) {
+ public void syncWithUpstream(Context context, JSONObject response) {
try {
JSONObject eventUpdates = null;
JSONObject profileUpdates = null;
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/LocationManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/LocationManager.java
new file mode 100644
index 000000000..b86d273cf
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/LocationManager.java
@@ -0,0 +1,138 @@
+package com.clevertap.android.sdk;
+
+import static com.clevertap.android.sdk.CleverTapAPI.isAppForeground;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.location.Location;
+import com.clevertap.android.sdk.events.BaseEventQueueManager;
+import java.util.List;
+import java.util.concurrent.Future;
+import org.json.JSONObject;
+
+class LocationManager extends BaseLocationManager {
+
+ private int lastLocationPingTime = 0;
+
+ private int lastLocationPingTimeForGeofence = 0;
+
+ private final BaseEventQueueManager mBaseEventQueueManager;
+
+ private final CleverTapInstanceConfig mConfig;
+
+ private final Context mContext;
+
+ private final CoreMetaData mCoreMetaData;
+
+ private final Logger mLogger;
+
+ LocationManager(Context context,
+ CleverTapInstanceConfig config,
+ CoreMetaData coreMetaData,
+ BaseEventQueueManager baseEventQueueManager) {
+ mContext = context;
+ mConfig = config;
+ mLogger = mConfig.getLogger();
+ mCoreMetaData = coreMetaData;
+ mBaseEventQueueManager = baseEventQueueManager;
+ }
+
+ @SuppressLint("MissingPermission")
+ @Override
+ public Location _getLocation() {
+ try {
+ android.location.LocationManager lm = (android.location.LocationManager) mContext
+ .getSystemService(Context.LOCATION_SERVICE);
+ if (lm == null) {
+ Logger.d("Location Manager is null.");
+ return null;
+ }
+ List providers = lm.getProviders(true);
+ Location bestLocation = null;
+ Location l = null;
+ for (String provider : providers) {
+ try {
+ l = lm.getLastKnownLocation(provider);
+ } catch (SecurityException e) {
+ //no-op
+ Logger.v("Location security exception", e);
+ }
+
+ if (l == null) {
+ continue;
+ }
+ if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
+ bestLocation = l;
+ }
+ }
+
+ return bestLocation;
+ } catch (Throwable t) {
+ Logger.v("Couldn't get user's location", t);
+ return null;
+ }
+ }
+
+ @Override
+ Future> _setLocation(Location location) {
+ if (location == null) {
+ return null;
+ }
+
+ mCoreMetaData.setLocationFromUser(location);
+ mLogger.verbose(mConfig.getAccountId(),
+ "Location updated (" + location.getLatitude() + ", " + location.getLongitude() + ")");
+
+ // only queue the location ping if we are in the foreground
+ if (!mCoreMetaData.isLocationForGeofence() && !isAppForeground()) {
+ return null;
+ }
+
+ // Queue the ping event to transmit location update to server
+ // min 10 second interval between location pings
+ final int now = getNow();
+ Future> future = null;
+
+ if (mCoreMetaData.isLocationForGeofence() && now > (lastLocationPingTimeForGeofence
+ + Constants.LOCATION_PING_INTERVAL_IN_SECONDS)) {
+
+ future = mBaseEventQueueManager.queueEvent(mContext, new JSONObject(), Constants.PING_EVENT);
+ setLastLocationPingTimeForGeofence(now);
+ mLogger.verbose(mConfig.getAccountId(),
+ "Queuing location ping event for geofence location (" + location.getLatitude() + ", " + location
+ .getLongitude() + ")");
+
+ } else if (!mCoreMetaData.isLocationForGeofence() && now > (lastLocationPingTime
+ + Constants.LOCATION_PING_INTERVAL_IN_SECONDS)) {
+
+ future = mBaseEventQueueManager.queueEvent(mContext, new JSONObject(), Constants.PING_EVENT);
+ setLastLocationPingTime(now);
+ mLogger.verbose(mConfig.getAccountId(),
+ "Queuing location ping event for location (" + location.getLatitude() + ", " + location
+ .getLongitude() + ")");
+ }
+
+ return future;
+ }
+
+ int getLastLocationPingTime() {
+ return lastLocationPingTime;
+ }
+
+ void setLastLocationPingTime(final int lastLocationPingTime) {
+ this.lastLocationPingTime = lastLocationPingTime;
+ }
+
+ int getLastLocationPingTimeForGeofence() {
+ return lastLocationPingTimeForGeofence;
+ }
+
+ void setLastLocationPingTimeForGeofence(final int lastLocationPingTimeForGeofence) {
+ this.lastLocationPingTimeForGeofence = lastLocationPingTimeForGeofence;
+ }
+
+ int getNow() {
+ return (int) (System.currentTimeMillis() / 1000);
+ }
+
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/LogConstants.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/LogConstants.java
deleted file mode 100644
index 4c805bacb..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/LogConstants.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.clevertap.android.sdk;
-
-public interface LogConstants {
-
- String LOG_TAG_ON_USER_LOGIN = "ON_USER_LOGIN";
-
-}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ManifestInfo.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ManifestInfo.java
index cd1bd696d..a5bd20a38 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ManifestInfo.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/ManifestInfo.java
@@ -129,15 +129,15 @@ String getAcountToken() {
return accountToken;
}
- String getExcludedActivities() {
+ public String getExcludedActivities() {
return excludedActivities;
}
- String getIntentServiceName() {
+ public String getIntentServiceName() {
return intentServiceName;
}
- String getNotificationIcon() {
+ public String getNotificationIcon() {
return notificationIcon;
}
@@ -165,10 +165,11 @@ boolean useGoogleAdId() {
return useADID;
}
+ @SuppressWarnings("ConstantConditions")
private String[] parseProfileKeys(final Bundle metaData) {
String profileKeyString = _getManifestStringValueForKey(metaData, Constants.CLEVERTAP_IDENTIFIER);
return !TextUtils.isEmpty(profileKeyString) ? profileKeyString.split(Constants.SEPARATOR_COMMA)
- : NullConstants.NULL_STRING_ARRAY;
+ : Constants.NULL_STRING_ARRAY;
}
static void changeCredentials(String id, String token, String region) {
@@ -177,6 +178,13 @@ static void changeCredentials(String id, String token, String region) {
accountRegion = region;
}
+ /**
+ * This returns string representation of int,boolean,string,float value of given key
+ *
+ * @param manifest bundle to retrieve values from
+ * @param name key of bundle
+ * @return string representation of int,boolean,string,float
+ */
private static String _getManifestStringValueForKey(Bundle manifest, String name) {
try {
Object o = manifest.get(name);
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/NullConstants.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/NullConstants.java
deleted file mode 100644
index cb232d9e0..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/NullConstants.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.clevertap.android.sdk;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface NullConstants {
-
- String[] NULL_STRING_ARRAY = new String[0];
-}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/SessionManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/SessionManager.java
new file mode 100644
index 000000000..104645edd
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/SessionManager.java
@@ -0,0 +1,118 @@
+package com.clevertap.android.sdk;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import com.clevertap.android.sdk.events.EventDetail;
+import com.clevertap.android.sdk.validation.Validator;
+
+public class SessionManager extends BaseSessionManager {
+
+ private long appLastSeen = 0;
+
+ private int lastVisitTime;
+
+ private final CoreMetaData cleverTapMetaData;
+
+ private final CleverTapInstanceConfig config;
+
+ private final LocalDataStore localDataStore;
+
+ private final Validator validator;
+
+ public SessionManager(CleverTapInstanceConfig config, CoreMetaData coreMetaData, Validator validator,
+ LocalDataStore localDataStore) {
+ this.config = config;
+ cleverTapMetaData = coreMetaData;
+ this.validator = validator;
+ this.localDataStore = localDataStore;
+ }
+
+ // SessionManager/session management
+ public void checkTimeoutSession() {
+ if (appLastSeen <= 0) {
+ return;
+ }
+ long now = System.currentTimeMillis();
+ if ((now - appLastSeen) > Constants.SESSION_LENGTH_MINS * 60 * 1000) {
+ config.getLogger().verbose(config.getAccountId(), "Session Timed Out");
+ destroySession();
+ CoreMetaData.setCurrentActivity(null);
+ }
+ }
+
+ @Override
+ public void destroySession() {
+ cleverTapMetaData.setCurrentSessionId(0);
+ cleverTapMetaData.setAppLaunchPushed(false);
+ if (cleverTapMetaData.isFirstSession()) {
+ cleverTapMetaData.setFirstSession(false);
+ }
+ config.getLogger().verbose(config.getAccountId(), "Session destroyed; Session ID is now 0");
+ cleverTapMetaData.clearSource();
+ cleverTapMetaData.clearMedium();
+ cleverTapMetaData.clearCampaign();
+ cleverTapMetaData.clearWzrkParams();
+ }
+
+ public long getAppLastSeen() {
+ return appLastSeen;
+ }
+
+ public void setAppLastSeen(final long appLastSeen) {
+ this.appLastSeen = appLastSeen;
+ }
+
+ public int getLastVisitTime() {
+ return lastVisitTime;
+ }
+
+ @Override
+ public void lazyCreateSession(Context context) {
+ if (!cleverTapMetaData.inCurrentSession()) {
+ cleverTapMetaData.setFirstRequestInSession(true);
+ if (validator != null) {
+ validator.setDiscardedEvents(null);
+ }
+ createSession(context);
+ }
+ }
+
+ //Session
+ void setLastVisitTime() {
+ EventDetail ed = localDataStore.getEventDetail(Constants.APP_LAUNCHED_EVENT);
+ if (ed == null) {
+ lastVisitTime = -1;
+ } else {
+ lastVisitTime = ed.getLastTime();
+ }
+ }
+
+ private void createSession(final Context context) {
+ int sessionId = (int) (System.currentTimeMillis() / 1000);
+ cleverTapMetaData.setCurrentSessionId(sessionId);
+
+ config.getLogger().verbose(config.getAccountId(),
+ "Session created with ID: " + cleverTapMetaData.getCurrentSessionId());
+
+ SharedPreferences prefs = StorageHelper.getPreferences(context);
+
+ final int lastSessionID = StorageHelper.getIntFromPrefs(context, config, Constants.SESSION_ID_LAST, 0);
+ final int lastSessionTime = StorageHelper.getIntFromPrefs(context, config, Constants.LAST_SESSION_EPOCH, 0);
+ if (lastSessionTime > 0) {
+ cleverTapMetaData.setLastSessionLength(lastSessionTime - lastSessionID);
+ }
+
+ config.getLogger().verbose(config.getAccountId(),
+ "Last session length: " + cleverTapMetaData.getLastSessionLength() + " seconds");
+
+ if (lastSessionID == 0) {
+ cleverTapMetaData.setFirstSession(true);
+ }
+
+ final SharedPreferences.Editor editor = prefs.edit()
+ .putInt(StorageHelper.storageKeyWithSuffix(config, Constants.SESSION_ID_LAST),
+ cleverTapMetaData.getCurrentSessionId());
+ StorageHelper.persist(editor);
+ }
+
+}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/StorageHelper.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/StorageHelper.java
index 2d96a46b1..820b074e4 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/StorageHelper.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/StorageHelper.java
@@ -108,12 +108,13 @@ static boolean getBooleanFromPrefs(Context context, CleverTapInstanceConfig conf
}
}
- static int getInt(Context context, String key, int defaultValue) {
+ public static int getInt(Context context, String key, int defaultValue) {
return getPreferences(context).getInt(key, defaultValue);
}
@SuppressWarnings("SameParameterValue")
- static int getIntFromPrefs(Context context, CleverTapInstanceConfig config, String rawKey, int defaultValue) {
+ public static int getIntFromPrefs(Context context, CleverTapInstanceConfig config, String rawKey,
+ int defaultValue) {
if (config.isDefaultInstance()) {
int dummy = -1000;
int _new = getInt(context, storageKeyWithSuffix(config, rawKey), dummy);
@@ -132,7 +133,8 @@ static long getLong(Context context, String nameSpace, String key, long defaultV
}
@SuppressWarnings("SameParameterValue")
- static long getLongFromPrefs(Context context, CleverTapInstanceConfig config, String rawKey, int defaultValue,
+ public static long getLongFromPrefs(Context context, CleverTapInstanceConfig config, String rawKey,
+ int defaultValue,
String nameSpace) {
if (config.isDefaultInstance()) {
long dummy = -1000;
@@ -153,7 +155,7 @@ static void putBoolean(Context context, String key, boolean value) {
persist(editor);
}
- static void putInt(Context context, String key, int value) {
+ public static void putInt(Context context, String key, int value) {
SharedPreferences prefs = getPreferences(context);
SharedPreferences.Editor editor = prefs.edit().putInt(key, value);
persist(editor);
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/TaskManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/TaskManager.java
deleted file mode 100644
index b5c8098c3..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/TaskManager.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.clevertap.android.sdk;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Singleton class to do heavy loaded task in the background and get the result on the main thread.
- * Suitable for Android general purpose use cases.
- */
-public class TaskManager {
-
- /**
- * Interface for the callbacks
- */
- public interface TaskListener {
-
- /**
- * does task in the background thread
- */
- Result doInBackground(Params params);
-
- /**
- * Gives callback on the main thread
- */
- void onPostExecute(Result result);
- }
-
- private static TaskManager sInstance;
-
- private final ExecutorService service;
-
- public static synchronized TaskManager getInstance() {
- if (sInstance == null) {
- sInstance = new TaskManager();
- }
- return sInstance;
- }
-
- private TaskManager() {
- this.service = Executors.newFixedThreadPool(10);
- }
-
- /**
- * Execute task in the background with a callback
- *
- * @param listener - to get the callback
- * @param - no parameter
- * @param - result returned by the background task
- */
- public void execute(final TaskListener listener) {
- execute(null, listener);
- }
-
- /**
- * Execute the task with parameters with a callback
- *
- * @param params params to be passed on the background execution
- * @param listener - to get the callback
- * @param - params to be passed on the background execution
- * @param - result returned by the background task
- */
- public void execute(final Params params, final TaskListener listener) {
-
- service.execute(new Runnable() {
- @Override
- public void run() {
- if (listener != null) {
- final Result result = listener.doInBackground(params);
-
- // post the result callback on the main thread
- Utils.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- listener.onPostExecute(result);
- }
- });
- }
- }
- });
- }
-}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/Utils.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/Utils.java
index c00d43182..91e83e4f0 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/Utils.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/Utils.java
@@ -40,9 +40,10 @@
import org.json.JSONException;
import org.json.JSONObject;
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final class Utils {
+ public static boolean haveVideoPlayerSupport;
+
public static boolean containsIgnoreCase(Collection collection, String key) {
if (collection == null || key == null) {
return false;
@@ -55,6 +56,41 @@ public static boolean containsIgnoreCase(Collection collection, String k
return false;
}
+ public static HashMap convertBundleObjectToHashMap(Bundle b) {
+ final HashMap map = new HashMap<>();
+ for (String s : b.keySet()) {
+ final Object o = b.get(s);
+
+ if (o instanceof Bundle) {
+ map.putAll(convertBundleObjectToHashMap((Bundle) o));
+ } else {
+ map.put(s, b.get(s));
+ }
+ }
+ return map;
+ }
+
+ public static HashMap convertJSONObjectToHashMap(JSONObject b) {
+ final HashMap map = new HashMap<>();
+ final Iterator keys = b.keys();
+
+ while (keys.hasNext()) {
+ try {
+ final String s = keys.next();
+ final Object o = b.get(s);
+ if (o instanceof JSONObject) {
+ map.putAll(convertJSONObjectToHashMap((JSONObject) o));
+ } else {
+ map.put(s, b.get(s));
+ }
+ } catch (Throwable ignored) {
+ // Ignore
+ }
+ }
+
+ return map;
+ }
+
public static String convertToTitleCase(String text) {
if (text == null || text.isEmpty()) {
return text;
@@ -78,6 +114,88 @@ public static String convertToTitleCase(String text) {
return converted.toString();
}
+ public static Bitmap getBitmapFromURL(String srcUrl) {
+ // Safe bet, won't have more than three /s
+ srcUrl = srcUrl.replace("///", "/");
+ srcUrl = srcUrl.replace("//", "/");
+ srcUrl = srcUrl.replace("http:/", "http://");
+ srcUrl = srcUrl.replace("https:/", "https://");
+ HttpURLConnection connection = null;
+ try {
+ URL url = new URL(srcUrl);
+ connection = (HttpURLConnection) url.openConnection();
+ connection.setDoInput(true);
+ connection.connect();
+ InputStream input = connection.getInputStream();
+ return BitmapFactory.decodeStream(input);
+ } catch (IOException e) {
+
+ Logger.v("Couldn't download the notification icon. URL was: " + srcUrl);
+ return null;
+ } finally {
+ try {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ } catch (Throwable t) {
+ Logger.v("Couldn't close connection!", t);
+ }
+ }
+ }
+
+ public static byte[] getByteArrayFromImageURL(String srcUrl) {
+ srcUrl = srcUrl.replace("///", "/");
+ srcUrl = srcUrl.replace("//", "/");
+ srcUrl = srcUrl.replace("http:/", "http://");
+ srcUrl = srcUrl.replace("https:/", "https://");
+ HttpsURLConnection connection = null;
+ try {
+ URL url = new URL(srcUrl);
+ connection = (HttpsURLConnection) url.openConnection();
+ InputStream is = connection.getInputStream();
+ byte[] buffer = new byte[8192];
+ int bytesRead;
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ while ((bytesRead = is.read(buffer)) != -1) {
+ baos.write(buffer, 0, bytesRead);
+ }
+ return baos.toByteArray();
+ } catch (IOException e) {
+ Logger.v("Error processing image bytes from url: " + srcUrl);
+ return null;
+ } finally {
+ try {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ } catch (Throwable t) {
+ Logger.v("Couldn't close connection!", t);
+ }
+ }
+ }
+
+ public static Bitmap getNotificationBitmap(String icoPath, boolean fallbackToAppIcon, final Context context)
+ throws NullPointerException {
+ // If the icon path is not specified
+ if (icoPath == null || icoPath.equals("")) {
+ return fallbackToAppIcon ? getAppIcon(context) : null;
+ }
+ // Simply stream the bitmap
+ if (!icoPath.startsWith("http")) {
+ icoPath = Constants.ICON_BASE_URL + "/" + icoPath;
+ }
+ Bitmap ic = getBitmapFromURL(icoPath);
+ return (ic != null) ? ic : ((fallbackToAppIcon) ? getAppIcon(context) : null);
+ }
+
+ public static int getThumbnailImage(Context context, String image) {
+ if (context != null) {
+ return context.getResources().getIdentifier(image, "drawable", context.getPackageName());
+ } else {
+ return -1;
+ }
+ }
+
public static boolean isActivityDead(Activity activity) {
if (activity == null) {
return true;
@@ -152,20 +270,7 @@ public static Bundle stringToBundle(String content) throws JSONException {
return bundle;
}
- static HashMap convertBundleObjectToHashMap(Bundle b) {
- final HashMap map = new HashMap<>();
- for (String s : b.keySet()) {
- final Object o = b.get(s);
- if (o instanceof Bundle) {
- map.putAll(convertBundleObjectToHashMap((Bundle) o));
- } else {
- map.put(s, b.get(s));
- }
- }
- return map;
- }
-
- static ArrayList convertJSONArrayToArrayList(JSONArray array) {
+ public static ArrayList convertJSONArrayToArrayList(JSONArray array) {
ArrayList listdata = new ArrayList<>();
if (array != null) {
for (int i = 0; i < array.length(); i++) {
@@ -179,27 +284,6 @@ static ArrayList convertJSONArrayToArrayList(JSONArray array) {
return listdata;
}
- static HashMap convertJSONObjectToHashMap(JSONObject b) {
- final HashMap map = new HashMap<>();
- final Iterator keys = b.keys();
-
- while (keys.hasNext()) {
- try {
- final String s = keys.next();
- final Object o = b.get(s);
- if (o instanceof JSONObject) {
- map.putAll(convertJSONObjectToHashMap((JSONObject) o));
- } else {
- map.put(s, b.get(s));
- }
- } catch (Throwable ignored) {
- // Ignore
- }
- }
-
- return map;
- }
-
static Bitmap drawableToBitmap(Drawable drawable)
throws NullPointerException {
if (drawable instanceof BitmapDrawable) {
@@ -215,68 +299,8 @@ static Bitmap drawableToBitmap(Drawable drawable)
return bitmap;
}
- static Bitmap getBitmapFromURL(String srcUrl) {
- // Safe bet, won't have more than three /s
- srcUrl = srcUrl.replace("///", "/");
- srcUrl = srcUrl.replace("//", "/");
- srcUrl = srcUrl.replace("http:/", "http://");
- srcUrl = srcUrl.replace("https:/", "https://");
- HttpURLConnection connection = null;
- try {
- URL url = new URL(srcUrl);
- connection = (HttpURLConnection) url.openConnection();
- connection.setDoInput(true);
- connection.connect();
- InputStream input = connection.getInputStream();
- return BitmapFactory.decodeStream(input);
- } catch (IOException e) {
-
- Logger.v("Couldn't download the notification icon. URL was: " + srcUrl);
- return null;
- } finally {
- try {
- if (connection != null) {
- connection.disconnect();
- }
- } catch (Throwable t) {
- Logger.v("Couldn't close connection!", t);
- }
- }
- }
-
- static byte[] getByteArrayFromImageURL(String srcUrl) {
- srcUrl = srcUrl.replace("///", "/");
- srcUrl = srcUrl.replace("//", "/");
- srcUrl = srcUrl.replace("http:/", "http://");
- srcUrl = srcUrl.replace("https:/", "https://");
- HttpsURLConnection connection = null;
- try {
- URL url = new URL(srcUrl);
- connection = (HttpsURLConnection) url.openConnection();
- InputStream is = connection.getInputStream();
- byte[] buffer = new byte[8192];
- int bytesRead;
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- while ((bytesRead = is.read(buffer)) != -1) {
- baos.write(buffer, 0, bytesRead);
- }
- return baos.toByteArray();
- } catch (IOException e) {
- Logger.v("Error processing image bytes from url: " + srcUrl);
- return null;
- } finally {
- try {
- if (connection != null) {
- connection.disconnect();
- }
- } catch (Throwable t) {
- Logger.v("Couldn't close connection!", t);
- }
- }
- }
-
@SuppressLint("MissingPermission")
- static String getCurrentNetworkType(final Context context) {
+ public static String getCurrentNetworkType(final Context context) {
try {
// First attempt to check for WiFi connectivity
ConnectivityManager connManager = (ConnectivityManager) context
@@ -299,7 +323,7 @@ static String getCurrentNetworkType(final Context context) {
}
@SuppressLint("MissingPermission")
- static String getDeviceNetworkType(final Context context) {
+ public static String getDeviceNetworkType(final Context context) {
// Fall back to network type
TelephonyManager teleMan = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (teleMan == null) {
@@ -307,7 +331,7 @@ static String getDeviceNetworkType(final Context context) {
}
int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (hasPermission(context, Manifest.permission.READ_PHONE_STATE)) {
try {
networkType = teleMan.getDataNetworkType();
@@ -318,11 +342,7 @@ static String getDeviceNetworkType(final Context context) {
Logger.d("READ_PHONE_STATE permission not asked by the app or not granted by the user");
}
} else {
- try {
- networkType = teleMan.getNetworkType();
- } catch (SecurityException se) {
- Logger.d("Security Exception caught while fetch network type" + se.getMessage());
- }
+ networkType = teleMan.getNetworkType();
}
switch (networkType) {
@@ -351,41 +371,19 @@ static String getDeviceNetworkType(final Context context) {
}
}
- static long getMemoryConsumption() {
+ public static long getMemoryConsumption() {
long free = Runtime.getRuntime().freeMemory();
long total = Runtime.getRuntime().totalMemory();
return total - free;
}
- static Bitmap getNotificationBitmap(String icoPath, boolean fallbackToAppIcon, final Context context)
- throws NullPointerException {
- // If the icon path is not specified
- if (icoPath == null || icoPath.equals("")) {
- return fallbackToAppIcon ? getAppIcon(context) : null;
- }
- // Simply stream the bitmap
- if (!icoPath.startsWith("http")) {
- icoPath = Constants.ICON_BASE_URL + "/" + icoPath;
- }
- Bitmap ic = getBitmapFromURL(icoPath);
- return (ic != null) ? ic : ((fallbackToAppIcon) ? getAppIcon(context) : null);
- }
-
- static int getThumbnailImage(Context context, String image) {
- if (context != null) {
- return context.getResources().getIdentifier(image, "drawable", context.getPackageName());
- } else {
- return -1;
- }
- }
-
/**
* Checks whether a particular permission is available or not.
*
* @param context The Android {@link Context}
* @param permission The fully qualified Android permission name
*/
- static boolean hasPermission(final Context context, String permission) {
+ public static boolean hasPermission(final Context context, String permission) {
try {
return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, permission);
} catch (Throwable t) {
@@ -393,7 +391,7 @@ static boolean hasPermission(final Context context, String permission) {
}
}
- static boolean validateCTID(String cleverTapID) {
+ public static boolean validateCTID(String cleverTapID) {
if (cleverTapID == null) {
Logger.i(
"CLEVERTAP_USE_CUSTOM_ID has been set as 1 in AndroidManifest.xml but custom CleverTap ID passed is NULL.");
@@ -415,6 +413,37 @@ static boolean validateCTID(String cleverTapID) {
return true;
}
+ public static int getNow() {
+ return (int) (System.currentTimeMillis() / 1000);
+ }
+
+ /**
+ * Method to check whether app has ExoPlayer dependencies
+ *
+ * @return boolean - true/false depending on app's availability of ExoPlayer dependencies
+ */
+ private static boolean checkForExoPlayer() {
+ boolean exoPlayerPresent = false;
+ Class className = null;
+ try {
+ className = Class.forName("com.google.android.exoplayer2.SimpleExoPlayer");
+ className = Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource");
+ className = Class.forName("com.google.android.exoplayer2.ui.PlayerView");
+ Logger.d("ExoPlayer is present");
+ exoPlayerPresent = true;
+ } catch (Throwable t) {
+ Logger.d("ExoPlayer library files are missing!!!");
+ Logger.d(
+ "Please add ExoPlayer dependencies to render InApp or Inbox messages playing video. For more information checkout CleverTap documentation.");
+ if (className != null) {
+ Logger.d("ExoPlayer classes not found " + className.getName());
+ } else {
+ Logger.d("ExoPlayer classes not found");
+ }
+ }
+ return exoPlayerPresent;
+ }
+
private static Bitmap getAppIcon(final Context context) throws NullPointerException {
// Try to get the app logo first
try {
@@ -430,6 +459,10 @@ private static Bitmap getAppIcon(final Context context) throws NullPointerExcept
}
}
+ static {
+ haveVideoPlayerSupport = checkForExoPlayer();
+ }
+
@RestrictTo(Scope.LIBRARY)
public static String getFcmTokenUsingManifestMetaEntry(Context context, CleverTapInstanceConfig config) {
String token = null;
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTABTestController.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTABTestController.java
deleted file mode 100644
index e9db22a43..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTABTestController.java
+++ /dev/null
@@ -1,1253 +0,0 @@
-package com.clevertap.android.sdk.ab_testing;
-
-import android.app.Activity;
-import android.app.Application;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-import com.clevertap.android.sdk.CleverTapAPI;
-import com.clevertap.android.sdk.CleverTapInstanceConfig;
-import com.clevertap.android.sdk.Logger;
-import com.clevertap.android.sdk.ab_testing.gesture.ConnectionGesture;
-import com.clevertap.android.sdk.ab_testing.models.CTABVariant;
-import com.clevertap.android.sdk.ab_testing.uieditor.UIEditor;
-import com.clevertap.android.sdk.java_websocket.client.WebSocketClient;
-import com.clevertap.android.sdk.java_websocket.drafts.Draft_6455;
-import com.clevertap.android.sdk.java_websocket.enums.Opcode;
-import com.clevertap.android.sdk.java_websocket.exceptions.NotSendableException;
-import com.clevertap.android.sdk.java_websocket.exceptions.WebsocketNotConnectedException;
-import com.clevertap.android.sdk.java_websocket.handshake.ServerHandshake;
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.lang.ref.WeakReference;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-@RequiresApi(api = VERSION_CODES.KITKAT)
-public class CTABTestController {
-
- @SuppressWarnings("unused")
- public static class LayoutErrorMessage {
-
- private final String errorName;
-
- private final String errorType;
-
- public LayoutErrorMessage(String type, String name) {
- errorType = type;
- errorName = name;
- }
-
- public String getName() {
- return errorName;
- }
-
- public String getType() {
- return errorType;
- }
- }
-
- private class ExecutionThreadHandler extends Handler {
-
- private class DashboardClient extends WebSocketClient {
-
- private URI dashboardURI;
-
- private DashboardClient(URI uri, int connectTimeout) {
- super(uri, new Draft_6455(), null, connectTimeout);
- this.dashboardURI = uri;
- setSocketFactory(SSLSocketFactory);
- }
-
- @Override
- public void onClose(int code, String reason, boolean remote) {
- getConfigLogger().verbose(getAccountId(),
- "WebSocket closed. Code: " + code + ", reason: " + reason + "\nURI: " + dashboardURI);
- handleOnClose();
- }
-
- @Override
- public void onError(Exception ex) {
- if (ex != null && ex.getMessage() != null) {
- getConfigLogger().verbose(getAccountId(), "Websocket Error: " + ex.getMessage());
- } else {
- getConfigLogger().verbose(getAccountId(), "Unknown websocket error");
- }
- }
-
- @Override
- public void onMessage(String message) {
- try {
- final JSONObject messageJson = new JSONObject(message);
- if (messageJson.has("data")) {
- if (messageJson.getJSONObject("data").keys().hasNext()) {
- getConfigLogger().verbose(getAccountId(), "Received message from dashboard:\n" + message);
- }
- }
- if (!connectionIsValid()) {
- getConfigLogger().verbose(getAccountId(),
- "Dashboard connection is stale, dropping message: " + message);
- return;
- }
- handleDashboardMessage(messageJson);
- } catch (final JSONException e) {
- getConfigLogger().verbose(getAccountId(), "Bad JSON message received:" + message, e);
- }
- }
-
- @Override
- public void onOpen(ServerHandshake handshakedata) {
- getConfigLogger().verbose(getAccountId(), "Websocket connected");
- handleOnOpen();
- }
- }
-
- private class WebSocketOutputStream extends OutputStream {
-
- @Override
- public void close() {
- try {
- wsClient.sendFragmentedFrame(Opcode.TEXT, EMPTY_BYTE_BUFFER, true);
- } catch (final WebsocketNotConnectedException e) {
- getConfigLogger().debug(getAccountId(), "Web socket not connected", e);
- } catch (final NotSendableException e) {
- getConfigLogger().debug(getAccountId(), "Unable to send data to web socket", e);
- }
- }
-
- @Override
- public void write(@NonNull byte[] b) {
- write(b, 0, b.length);
- }
-
- @Override
- public void write(@NonNull byte[] b, int off, int len) {
- final ByteBuffer message = ByteBuffer.wrap(b, off, len);
- try {
- wsClient.sendFragmentedFrame(Opcode.TEXT, message, false);
- } catch (final WebsocketNotConnectedException e) {
- getConfigLogger().debug(getAccountId(), "Web socket not connected", e);
- } catch (final NotSendableException e) {
- getConfigLogger().debug(getAccountId(), "Unable to send data to web socket", e);
- }
- }
-
- @Override
- public void write(int b) {
- final byte[] oneByte = new byte[1];
- oneByte[0] = (byte) b;
- write(oneByte, 0, 1);
- }
- }
-
- static final int MESSAGE_UNKNOWN = -1;
-
- static final int MESSAGE_INITIALIZE_EXPERIMENTS = 0;
-
- static final int MESSAGE_CONNECT_TO_EDITOR = 1;
-
- static final int MESSAGE_SEND_SNAPSHOT = 2;
-
- static final int MESSAGE_HANDLE_EDITOR_CHANGES_RECEIVED = 3;
-
- static final int MESSAGE_SEND_DEVICE_INFO = 4;
-
- static final int MESSAGE_HANDLE_DISCONNECT = 5;
-
- static final int MESSAGE_EXPERIMENTS_RECEIVED = 6;
-
- static final int MESSAGE_HANDLE_EDITOR_CHANGES_CLEARED = 7;
-
- static final int MESSAGE_HANDLE_EDITOR_VARS_RECEIVED = 8;
-
- static final int MESSAGE_SEND_LAYOUT_ERROR = 9;
-
- static final int MESSAGE_PERSIST_EXPERIMENTS = 10;
-
- static final int MESSAGE_SEND_VARS = 11;
-
- static final int MESSAGE_TEST_VARS = 12;
-
- static final int MESSAGE_MATCHED = 13;
-
- private static final String EXPERIMENTS_KEY = "experiments";
-
- private static final int CONNECT_TIMEOUT = 5000;
-
- private CleverTapInstanceConfig config;
-
- private Context context;
-
- private CTABVariant editorSessionVariant;
-
- private HashSet editorSessionVariantSet;
-
- private final Lock lock = new ReentrantLock();
-
- private Set variants;
-
- private DashboardClient wsClient;
-
- @SuppressWarnings("unused")
- ExecutionThreadHandler(Context context, CleverTapInstanceConfig config, Looper looper) {
- super(looper);
- this.config = config;
- this.context = context;
- this.variants = new HashSet<>();
- lock.lock();
- }
-
- @Override
- public void handleMessage(Message msg) {
- lock.lock();
- try {
- final int what = msg.what;
- Object data = msg.obj;
- switch (what) {
- case MESSAGE_INITIALIZE_EXPERIMENTS:
- loadStoredExperiments();
- break;
- case MESSAGE_CONNECT_TO_EDITOR:
- createConnection();
- break;
- case MESSAGE_MATCHED:
- handleMatched();
- break;
- case MESSAGE_HANDLE_DISCONNECT:
- handleDashboardDisconnect();
- break;
- case MESSAGE_SEND_DEVICE_INFO:
- sendDeviceInfo();
- break;
- case MESSAGE_SEND_SNAPSHOT:
- sendSnapshot((JSONObject) data);
- break;
- case MESSAGE_SEND_LAYOUT_ERROR:
- sendLayoutError((LayoutErrorMessage) msg.obj);
- break;
- case MESSAGE_EXPERIMENTS_RECEIVED:
- applyExperiments((JSONArray) data, true);
- break;
- case MESSAGE_HANDLE_EDITOR_CHANGES_RECEIVED:
- handleEditorChangesReceived((JSONObject) data);
- break;
- case MESSAGE_HANDLE_EDITOR_CHANGES_CLEARED:
- handleEditorChangesCleared((JSONObject) data);
- break;
- case MESSAGE_HANDLE_EDITOR_VARS_RECEIVED:
- case MESSAGE_TEST_VARS:
- handleEditorVarsReceived((JSONObject) data);
- break;
- case MESSAGE_PERSIST_EXPERIMENTS:
- persistExperiments((JSONArray) data);
- break;
- case MESSAGE_SEND_VARS:
- sendVars();
- break;
- }
- } finally {
- lock.unlock();
- }
- }
-
- public void start() {
- lock.unlock();
- }
-
- void handleMatched() {
- varCache.reset();
- stopVariants();
- }
-
- boolean isConnected() {
- return wsClient != null && wsClient.isOpen();
- }
-
- private void applyExperiments(JSONArray experiments, boolean areNew) {
- loadVariants(experiments);
- applyVariants();
- if (areNew) {
- persistExperiments(experiments);
- }
- notifyExperimentsUpdated();
- }
-
- private void applyVariants() {
- for (CTABVariant variant : variants) {
- applyVars(variant.getVars());
- }
- uiEditor.applyVariants(variants, false);
- }
-
- private void applyVars(JSONArray vars) {
- try {
- for (int i = 0; i < vars.length(); i++) {
- JSONObject var = vars.getJSONObject(i);
- _registerVar(var.getString("name"), CTVar.CTVarType.fromString(var.getString("type")),
- var.get("value"));
- }
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to apply Vars - " + t);
- }
- }
-
- private void closeConnection() {
- if (connectionIsValid()) {
- try {
- getConfigLogger().verbose(getAccountId(), "disconnecting from dashboard");
- wsClient.closeBlocking();
- } catch (final Exception e) {
- getConfigLogger().verbose(getAccountId(), "Unable to close dashboard connection", e);
- }
- }
- }
-
- private boolean connectionIsValid() {
- return wsClient != null && !wsClient.isClosed() && !wsClient.isClosing() && !wsClient.isFlushAndClose();
- }
-
- private void createConnection() {
- getConfigLogger().verbose(getAccountId(), "connecting to dashboard");
- if (isConnected() && connectionIsValid()) {
- getConfigLogger().verbose(getAccountId(), "There is already a valid dashboard connection.");
- return;
- }
-
- if (SSLSocketFactory == null) {
- getConfigLogger().verbose(getAccountId(),
- "SSL is not available on this device, dashboard connection is not available.");
- return;
- }
-
- final String protocol = "wss";
- String region = config.getAccountRegion() != null ? config.getAccountRegion() : DEFAULT_REGION;
- region = config.isBeta() ? region + "-dashboard-beta" : region;
- final String domain = region + "." + DASHBOARD_URL;
- final String url = protocol + "://" + domain + "/" + getAccountId() + "/" + "websocket/screenab/sdk?tk="
- + config.getAccountToken();
- getConfigLogger().verbose(getAccountId(), "Websocket URL - " + url);
- try {
- wsClient = new DashboardClient(new URI(url), CONNECT_TIMEOUT);
- wsClient.connectBlocking();
- } catch (final Exception e) {
- getConfigLogger().verbose(getAccountId(), "Unable to connect to dashboard", e);
- }
- }
-
- private String getAccountId() {
- return config.getAccountId();
- }
-
- private BufferedOutputStream getBufferedOutputStream() {
- return new BufferedOutputStream(new WebSocketOutputStream());
- }
-
- private Logger getConfigLogger() {
- return config.getLogger();
- }
-
- private JSONObject getDeviceInfo() {
- if (cachedDeviceInfo == null) {
- JSONObject data = new JSONObject();
- try {
- Map deviceInfo = CleverTapAPI.instanceWithConfig(context, config).getDeviceInfo();
- for (final Map.Entry entry : deviceInfo.entrySet()) {
- data.put(entry.getKey(), entry.getValue());
- }
- } catch (Throwable t) {
- // no-op
- }
- cachedDeviceInfo = data;
- }
- return cachedDeviceInfo;
- }
-
- private CTABVariant getEditorSessionVariant() {
- if (editorSessionVariant == null) {
- try {
- JSONObject variant = new JSONObject();
- variant.put("id", "0");
- variant.put("experiment_id", "0");
- editorSessionVariant = CTABVariant.initWithJSON(variant);
- editorSessionVariantSet = new HashSet<>();
- editorSessionVariantSet.add(editorSessionVariant);
- } catch (Throwable t) {
- getConfigLogger().verbose(getAccountId(), "Error creating editor session variant", t);
- }
- }
- return editorSessionVariant;
- }
-
- private SharedPreferences getSharedPreferences() {
- return context.getSharedPreferences(getSharedPrefsName(), Context.MODE_PRIVATE);
- }
-
- private String getSharedPrefsName() {
- return "clevertap.abtesting." + getAccountId() + "." + guid;
- }
-
- private void handleDashboardDisconnect() {
- stopVariants();
- closeConnection();
- }
-
- private void handleEditorChangesCleared(JSONObject request) {
- try {
- JSONArray changes = request.optJSONArray("actions");
- if (changes == null || changes.length() <= 0) {
- getEditorSessionVariant().clearActions();
- } else {
- getEditorSessionVariant().removeActionsByName(changes);
- }
- uiEditor.applyVariants(editorSessionVariantSet, true);
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to clear dashboard changes - " + t);
- }
- }
-
- private void handleEditorChangesReceived(JSONObject request) {
- try {
- JSONArray changes = request.optJSONArray("actions");
- if (changes == null || changes.length() <= 0) {
- getConfigLogger().debug(getAccountId(), "No changes received from dashboard");
- return;
- } else {
- getEditorSessionVariant().addActions(changes);
- }
- uiEditor.applyVariants(editorSessionVariantSet, true);
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to handle dashboard changes received - " + t);
- }
- }
-
- private void handleEditorVarsReceived(JSONObject request) {
- try {
- JSONArray vars = request.optJSONArray("vars");
- if (vars == null || vars.length() <= 0) {
- getConfigLogger().debug(getAccountId(), "No Vars received from dashboard");
- return;
- }
- applyVars(vars);
- notifyExperimentsUpdated();
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to handle dashboard Vars received - " + t);
- }
- }
-
- private void handleOnClose() {
- getConfigLogger().verbose(getAccountId(), "handle websocket on close");
- stopVariants();
- getEditorSessionVariant().clearActions();
- varCache.reset();
- applyVariants();
- }
-
- private void handleOnOpen() {
- sendHandshake();
- }
-
- private void loadStoredExperiments() {
- final SharedPreferences preferences = getSharedPreferences();
- final String storedExperiments = preferences.getString(EXPERIMENTS_KEY, null);
- if (storedExperiments != null) {
- try {
- getConfigLogger().debug(getAccountId(),
- "Loading Stored Experiments: " + storedExperiments + " for key: " + getSharedPrefsName());
- final JSONArray _experiments = new JSONArray(storedExperiments);
- applyExperiments(_experiments, false);
- } catch (JSONException e) {
- final SharedPreferences.Editor editor = preferences.edit();
- editor.remove(EXPERIMENTS_KEY);
- editor.apply();
- }
- } else {
- getConfigLogger().debug(getAccountId(), "No Stored Experiments for key: " + getSharedPrefsName());
- }
- }
-
- private void loadVariants(JSONArray experiments) {
- if (experiments == null) {
- return;
- }
- // note: experiments here will be all the currently running experiments for the user
- try {
- Set toRemove = new HashSet<>(this.variants);
- Set allVariants = new HashSet<>(this.variants);
-
- final int experimentsLength = experiments.length();
- for (int i = 0; i < experimentsLength; i++) {
- final JSONObject nextVariant = experiments.getJSONObject(i);
- final CTABVariant variant = CTABVariant.initWithJSON(nextVariant);
- if (variant != null) {
- boolean added = allVariants.add(variant);
- if (added) {
- toRemove.remove(variant);
- }
- }
- }
- if (!allVariants.containsAll(toRemove)) {
- if (toRemove.size() > 0) {
- for (CTABVariant v : toRemove) {
- v.cleanup();
- allVariants.remove(v);
- }
- }
- }
-
- //This will revert changes at SDK level when all experiments are stopped/revert without needing
- //another App Launched event
- if (experiments.length() == 0) {
- allVariants.clear();
- }
-
- this.variants = allVariants;
- } catch (JSONException e) {
- getConfigLogger().verbose(getAccountId(), "Error loading variants, clearing all running variants", e);
- this.variants.clear();
- }
- }
-
- private void notifyExperimentsUpdated() {
- CTABTestListener listener = getListener();
- if (listener != null) {
- listener.ABExperimentsUpdated();
- }
- }
-
- private void persistExperiments(JSONArray experiments) {
- final SharedPreferences preferences = getSharedPreferences();
- final SharedPreferences.Editor editor = preferences.edit();
- editor.putString(EXPERIMENTS_KEY, experiments.toString());
- editor.apply();
- }
-
- private void sendDeviceInfo() {
- try {
- JSONObject payload = new JSONObject();
- payload.put(TYPE_KEY, MESSAGE_TYPE_DEVICE_INFO_RESPONSE);
- payload.put(DATA_KEY, getDeviceInfo());
- sendMessage(payload.toString());
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to create deviceInfo message", t);
- }
- }
-
- @SuppressWarnings("SameParameterValue")
- private void sendError(String errorMessage) {
- try {
- JSONObject data = new JSONObject();
- data.put("error", errorMessage);
- JSONObject payload = new JSONObject();
- payload.put(TYPE_KEY, MESSAGE_TYPE_GENERIC_ERROR);
- payload.put(DATA_KEY, data);
- sendMessage(payload.toString());
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to create error message", t);
- }
- }
-
- private void sendHandshake() {
- try {
- JSONObject deviceInfo = getDeviceInfo();
- JSONObject data = new JSONObject();
- data.put("id", guid);
- data.put("os", deviceInfo.getString("osName"));
- data.put("name", deviceInfo.getString("manufacturer") + " " + deviceInfo.getString("model"));
- if (deviceInfo.has("library")) {
- data.put("library", deviceInfo.getString("library"));
- }
- JSONObject payload = new JSONObject();
- payload.put(TYPE_KEY, MESSAGE_TYPE_HANDSHAKE);
- payload.put(DATA_KEY, data);
- sendMessage(payload.toString());
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to create handshake message", t);
- }
- }
-
- private void sendLayoutError(LayoutErrorMessage errorMessage) {
- try {
- JSONObject data = new JSONObject();
- data.put("type", errorMessage.getType());
- data.put("name", errorMessage.getName());
- JSONObject payload = new JSONObject();
- payload.put(TYPE_KEY, MESSAGE_TYPE_LAYOUT_ERROR);
- payload.put(DATA_KEY, data);
- sendMessage(payload.toString());
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to create error message", t);
- }
- }
-
- private void sendMessage(String message) {
- if (!connectionIsValid()) {
- getConfigLogger().debug(getAccountId(),
- "Unable to send websocket message: " + message + " connection is invalid");
- return;
- }
- final OutputStreamWriter writer = new OutputStreamWriter(getBufferedOutputStream());
- getConfigLogger().verbose("Sending message to dashboard - " + message);
- try {
- writer.write(message);
- } catch (final IOException e) {
- getConfigLogger().verbose(getAccountId(), "Can't message to editor", e);
- } finally {
- try {
- writer.close();
- } catch (final IOException e) {
- getConfigLogger().verbose(getAccountId(), "Could not close output writer to editor", e);
- }
- }
- }
-
- private void sendSnapshot(JSONObject data) {
- final long startSnapshot = System.currentTimeMillis();
- boolean isValidSnapshot = uiEditor.loadSnapshotConfig(data);
- if (!isValidSnapshot) {
- String err = "Missing or invalid snapshot configuration.";
- sendError(err);
- getConfigLogger().debug(getAccountId(), err);
- return;
- }
-
- final OutputStream out = getBufferedOutputStream();
- final OutputStreamWriter writer = new OutputStreamWriter(out);
-
- try {
- writer.write("{");
- writer.write("\"" + TYPE_KEY + "\": \"" + MESSAGE_TYPE_SNAPSHOT_RESPONSE + "\",");
- writer.write("\"" + DATA_KEY + "\": {");
- {
- writer.write("\"activities\":");
- writer.flush();
- uiEditor.writeSnapshot(out);
- }
-
- final long snapshotTime = System.currentTimeMillis() - startSnapshot;
- writer.write(",\"snapshot_time_millis\": ");
- writer.write(Long.toString(snapshotTime));
-
- writer.write("}"); // } payload
- writer.write("}"); // } whole message
- } catch (final IOException e) {
- getConfigLogger().verbose(getAccountId(), "Failure sending snapshot", e);
- } finally {
- try {
- writer.close();
- } catch (final IOException e) {
- getConfigLogger().verbose(getAccountId(), "Failure closing json writer", e);
- }
- }
- }
-
- private void sendVars() {
- try {
- JSONObject data = new JSONObject();
- data.put("vars", varCache.serializeVars());
- JSONObject payload = new JSONObject();
- payload.put(TYPE_KEY, MESSAGE_TYPE_VARS_RESPONSE);
- payload.put(DATA_KEY, data);
- sendMessage(payload.toString());
- } catch (Throwable t) {
- getConfigLogger().debug(getAccountId(), "Unable to create vars message", t);
- }
- }
-
- private void stopVariants() {
- uiEditor.stopVariants();
- }
- }
-
- private class EmulatorConnectRunnable implements Runnable {
-
- private volatile boolean stopped;
-
- EmulatorConnectRunnable() {
- stopped = true;
- }
-
- @Override
- public void run() {
- if (!stopped) {
- final Message message = executionThreadHandler
- .obtainMessage(ExecutionThreadHandler.MESSAGE_CONNECT_TO_EDITOR);
- executionThreadHandler.sendMessage(message);
- }
- executionThreadHandler.postDelayed(this, EMULATOR_CONNECT_ATTEMPT_INTERVAL_MILLIS);
- }
-
- void start() {
- stopped = false;
- executionThreadHandler.post(this);
- }
-
- void stop() {
- stopped = true;
- executionThreadHandler.removeCallbacks(this);
- }
- }
-
- private class LifecycleCallbacks
- implements Application.ActivityLifecycleCallbacks, ConnectionGesture.OnGestureListener {
-
- private EmulatorConnectRunnable emulatorConnectRunnable;
-
- private ConnectionGesture gesture;
-
- private LifecycleCallbacks() {
- gesture = new ConnectionGesture(this);
- emulatorConnectRunnable = new EmulatorConnectRunnable();
- }
-
- @Override
- public void onActivityCreated(Activity activity, Bundle bundle) {
-
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- uiEditor.removeActivity(activity);
- deregisterConnectionTrigger(activity);
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
- registerConnectionTrigger(activity);
- uiEditor.addActivity(activity);
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
- }
-
- @Override
- public void onActivityStarted(Activity activity) {
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- }
-
- @Override
- public void onGesture() {
- final Message message = executionThreadHandler
- .obtainMessage(ExecutionThreadHandler.MESSAGE_CONNECT_TO_EDITOR);
- executionThreadHandler.sendMessage(message);
- }
-
- private void deregisterConnectionTrigger(final Activity activity) {
- if (!enableEditor) {
- config.getLogger().debug(config.getAccountId(), "UIEditor is disabled");
- return;
- }
- if (inEmulator()) {
- emulatorConnectRunnable.stop();
- } else {
- final SensorManager sensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
- if (sensorManager != null) {
- sensorManager.unregisterListener(gesture);
- }
- }
- }
-
- private boolean inEmulator() {
- if (!Build.HARDWARE.toLowerCase().equals("goldfish") && !Build.HARDWARE.toLowerCase().equals("ranchu")) {
- return false;
- }
-
- if (!Build.BRAND.toLowerCase().startsWith("generic") && !Build.BRAND.toLowerCase().equals("android")
- && !Build.BRAND.toLowerCase().equals("google")) {
- return false;
- }
-
- if (!Build.DEVICE.toLowerCase().startsWith("generic")) {
- return false;
- }
-
- if (!Build.PRODUCT.toLowerCase().contains("sdk")) {
- return false;
- }
-
- return Build.MODEL.toLowerCase(Locale.US).contains("sdk");
- }
-
- private void registerConnectionTrigger(final Activity activity) {
- if (!enableEditor) {
- config.getLogger().debug(config.getAccountId(), "UIEditor is disabled");
- return;
- }
- if (inEmulator()) {
- emulatorConnectRunnable.start();
- } else {
- try {
- final SensorManager sensorManager = (SensorManager) activity
- .getSystemService(Context.SENSOR_SERVICE);
- // noinspection ConstantConditions
- final Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- sensorManager.registerListener(gesture, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
- } catch (Throwable t) {
- // no-op
- config.getLogger().debug(config.getAccountId(), "Unable to register UIEditor connection gesture");
- }
- }
- }
- }
-
- private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0);
-
- private static final int EMULATOR_CONNECT_ATTEMPT_INTERVAL_MILLIS = 1000 * 30;
-
- private static final String DASHBOARD_URL = "dashboard.clevertap.com";
-
- private static final String DEFAULT_REGION = "eu1";
-
- private static final String MESSAGE_TYPE_HANDSHAKE = "handshake";
-
- private static final String MESSAGE_TYPE_CLEAR_REQUEST = "clear_request";
-
- private static final String MESSAGE_TYPE_CHANGE_REQUEST = "change_request";
-
- private static final String MESSAGE_TYPE_DEVICE_INFO_REQUEST = "device_info_request";
-
- private static final String MESSAGE_TYPE_DEVICE_INFO_RESPONSE = "device_info_response";
-
- private static final String MESSAGE_TYPE_SNAPSHOT_REQUEST = "snapshot_request";
-
- private static final String MESSAGE_TYPE_SNAPSHOT_RESPONSE = "snapshot_response";
-
- private static final String MESSAGE_TYPE_VARS_REQUEST = "vars_request";
-
- private static final String MESSAGE_TYPE_VARS_RESPONSE = "vars_response";
-
- private static final String MESSAGE_TYPE_LAYOUT_ERROR = "layout_error";
-
- private static final String MESSAGE_TYPE_GENERIC_ERROR = "error";
-
- private static final String MESSAGE_TYPE_VARS_TEST = "test_vars";
-
- private static final String MESSAGE_TYPE_MATCHED = "matched";
-
- private static final String MESSAGE_TYPE_DISCONNECT = "disconnect";
-
- private static final String DATA_KEY = "data";
-
- private static final String TYPE_KEY = "type";
-
- private static javax.net.ssl.SSLSocketFactory SSLSocketFactory;
-
- private JSONObject cachedDeviceInfo;
-
- private CleverTapInstanceConfig config;
-
- private boolean enableEditor;
-
- private ExecutionThreadHandler executionThreadHandler;
-
- private String guid;
-
- private WeakReference listenerWeakReference;
-
- private UIEditor uiEditor;
-
- private CTVarCache varCache;
-
- public CTABTestController(Context context, CleverTapInstanceConfig config, String guid,
- CTABTestListener listener) {
- try {
- this.varCache = new CTVarCache();
- this.enableEditor = config.isUIEditorEnabled();
- this.config = config;
- this.guid = guid;
- this.setListener(listener);
- this.uiEditor = new UIEditor(context, config);
-
- final HandlerThread thread = new HandlerThread(CTABTestController.class.getCanonicalName());
- thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- executionThreadHandler = new ExecutionThreadHandler(context, config, thread.getLooper());
- executionThreadHandler.start();
-
- if (enableEditor) {
- final Application app = (Application) context.getApplicationContext();
- app.registerActivityLifecycleCallbacks(new LifecycleCallbacks());
- } else {
- config.getLogger().debug(config.getAccountId(), "UIEditor connection is disabled");
- }
- applyStoredExperiments();
- } catch (Throwable t) {
- config.setEnableABTesting(false);
- config.setEnableUIEditor(false);
- config.getLogger().debug(config.getAccountId(), t);
- }
- }
-
- @SuppressWarnings({"unused"})
- public Boolean getBooleanVariable(String name, Boolean defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.booleanValue() != null) {
- return var.booleanValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public Double getDoubleVariable(String name, Double defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.doubleValue() != null) {
- return var.doubleValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public Integer getIntegerVariable(String name, Integer defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.integerValue() != null) {
- return var.integerValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public List getListOfBooleanVariable(String name, List defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.listValue() != null) {
- // noinspection unchecked
- return (List) var.listValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public List getListOfDoubleVariable(String name, List defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.listValue() != null) {
- // noinspection unchecked
- return (List) var.listValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public List getListOfIntegerVariable(String name, List defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.listValue() != null) {
- // noinspection unchecked
- return (List) var.listValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public List getListOfStringVariable(String name, List defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.listValue() != null) {
- // noinspection unchecked
- return (List) var.listValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public Map getMapOfBooleanVariable(String name, Map defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.mapValue() != null) {
- // noinspection unchecked
- return (Map) var.mapValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public Map getMapOfDoubleVariable(String name, Map defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.mapValue() != null) {
- // noinspection unchecked
- return (Map) var.mapValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public Map getMapOfIntegerVariable(String name, Map defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.mapValue() != null) {
- // noinspection unchecked
- return (Map) var.mapValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public Map getMapOfStringVariable(String name, Map defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.mapValue() != null) {
- // noinspection unchecked
- return (Map) var.mapValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public String getStringVariable(String name, String defaultValue) {
- CTVar var = this.varCache.getVar(name);
- try {
- if (var != null && var.stringValue() != null) {
- return var.stringValue();
- }
- } catch (Throwable t) {
- config.getLogger().debug(config.getAccountId(), "Error getting variable with name: " + name, t);
- return defaultValue;
- }
- return defaultValue;
- }
-
- @SuppressWarnings({"unused"})
- public void registerBooleanVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeBool, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerDoubleVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeDouble, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerIntegerVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeInteger, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerListOfBooleanVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeListOfBool, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerListOfDoubleVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeListOfDouble, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerListOfIntegerVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeListOfInteger, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerListOfStringVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeListOfString, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerMapOfBooleanVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeMapOfBool, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerMapOfDoubleVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeMapOfDouble, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerMapOfIntegerVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeMapOfInteger, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerMapOfStringVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeMapOfString, null);
- }
-
- @SuppressWarnings({"unused"})
- public void registerStringVariable(String name) {
- _registerVar(name, CTVar.CTVarType.CTVarTypeString, null);
- }
-
- public void resetWithGuid(String guid) {
- this.guid = guid;
- this.varCache.reset();
- uiEditor.stopVariants();
- applyStoredExperiments();
- }
-
- public void updateExperiments(JSONArray experiments) {
- if (experiments != null) {
- final Message message = executionThreadHandler
- .obtainMessage(ExecutionThreadHandler.MESSAGE_EXPERIMENTS_RECEIVED);
- message.obj = experiments;
- executionThreadHandler.sendMessage(message);
- }
- }
-
- @SuppressWarnings("SameParameterValue")
- private void _registerVar(String name, CTVar.CTVarType type, Object value) {
- this.varCache.registerVar(name, type, value);
- config.getLogger().verbose(config.getAccountId(),
- "Registered Var with name: " + name + " type: " + type.toString() + " and value: " + ((value != null)
- ? value.toString() : "null"));
- }
-
- private void applyStoredExperiments() {
- executionThreadHandler.sendMessage(
- executionThreadHandler.obtainMessage(ExecutionThreadHandler.MESSAGE_INITIALIZE_EXPERIMENTS));
- }
-
- private CTABTestListener getListener() {
- CTABTestListener listener = null;
- try {
- listener = listenerWeakReference.get();
- } catch (Throwable t) {
- // no-op
- }
- if (listener == null) {
- config.getLogger().verbose(config.getAccountId(), "CTABTestListener is null in CTABTestController");
- }
- return listener;
- }
-
- private void setListener(CTABTestListener listener) {
- listenerWeakReference = new WeakReference<>(listener);
- }
-
- private void handleDashboardMessage(JSONObject msg) {
-
- String type = msg.optString(TYPE_KEY, "unknown");
- int messageCode = ExecutionThreadHandler.MESSAGE_UNKNOWN;
- switch (type) {
- case MESSAGE_TYPE_CHANGE_REQUEST:
- messageCode = ExecutionThreadHandler.MESSAGE_HANDLE_EDITOR_CHANGES_RECEIVED;
- break;
- case MESSAGE_TYPE_CLEAR_REQUEST:
- messageCode = ExecutionThreadHandler.MESSAGE_HANDLE_EDITOR_CHANGES_CLEARED;
- break;
- case MESSAGE_TYPE_DEVICE_INFO_REQUEST:
- messageCode = ExecutionThreadHandler.MESSAGE_SEND_DEVICE_INFO;
- break;
- case MESSAGE_TYPE_SNAPSHOT_REQUEST:
- messageCode = ExecutionThreadHandler.MESSAGE_SEND_SNAPSHOT;
- break;
- case MESSAGE_TYPE_VARS_REQUEST:
- messageCode = ExecutionThreadHandler.MESSAGE_SEND_VARS;
- break;
- case MESSAGE_TYPE_VARS_TEST:
- messageCode = ExecutionThreadHandler.MESSAGE_TEST_VARS;
- break;
- case MESSAGE_TYPE_MATCHED:
- messageCode = ExecutionThreadHandler.MESSAGE_MATCHED;
- break;
- case MESSAGE_TYPE_DISCONNECT:
- messageCode = ExecutionThreadHandler.MESSAGE_HANDLE_DISCONNECT;
- default:
- break;
- }
- final Message m = executionThreadHandler.obtainMessage(messageCode);
-
- JSONObject messageObject;
- try {
- messageObject = msg.getJSONObject(DATA_KEY);
- } catch (Throwable t) {
- // no-op
- messageObject = new JSONObject();
- }
- m.obj = messageObject;
-
- executionThreadHandler.sendMessage(m);
- }
-
- static {
- SSLSocketFactory found;
- try {
- final SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, null, null);
- found = sslContext.getSocketFactory();
- } catch (final GeneralSecurityException e) {
- Logger.d("No SSL support. ABTest editor not available", e.getLocalizedMessage());
- found = null;
- }
- SSLSocketFactory = found;
- }
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTABTestListener.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTABTestListener.java
deleted file mode 100644
index 9a0016f8e..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTABTestListener.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.clevertap.android.sdk.ab_testing;
-
-public interface CTABTestListener {
-
- void ABExperimentsUpdated();
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTVar.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTVar.java
deleted file mode 100644
index 605069bb7..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTVar.java
+++ /dev/null
@@ -1,291 +0,0 @@
-package com.clevertap.android.sdk.ab_testing;
-
-import androidx.annotation.NonNull;
-import com.clevertap.android.sdk.Logger;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.json.JSONObject;
-
-final class CTVar {
-
- enum CTVarType {
- CTVarTypeBool("bool"),
- CTVarTypeDouble("double"),
- CTVarTypeInteger("integer"),
- CTVarTypeString("string"),
- CTVarTypeListOfBool("arrayofbool"),
- CTVarTypeListOfDouble("arrayofdouble"),
- CTVarTypeListOfInteger("arrayofinteger"),
- CTVarTypeListOfString("arrayofstring"),
- CTVarTypeMapOfBool("dictionaryofbool"),
- CTVarTypeMapOfDouble("dictionaryofdouble"),
- CTVarTypeMapOfInteger("dictionaryofinteger"),
- CTVarTypeMapOfString("dictionaryofstring"),
- CTVarTypeUnknown("unknown");
-
- private final String varType;
-
- CTVarType(String type) {
- this.varType = type;
- }
-
- @NonNull
- @Override
- public String toString() {
- return varType;
- }
-
- @SuppressWarnings({"unused"})
- static CTVarType fromString(String type) {
- switch (type) {
- case "bool": {
- return CTVarTypeBool;
- }
- case "double": {
- return CTVarTypeDouble;
- }
- case "integer": {
- return CTVarTypeInteger;
- }
- case "string": {
- return CTVarTypeString;
- }
- case "arrayofbool": {
- return CTVarTypeListOfBool;
- }
- case "arrayofdouble": {
- return CTVarTypeListOfDouble;
- }
- case "arrayofinteger": {
- return CTVarTypeListOfInteger;
- }
- case "arrayofstring": {
- return CTVarTypeListOfString;
- }
- case "dictionaryofbool": {
- return CTVarTypeMapOfBool;
- }
- case "dictionaryofdouble": {
- return CTVarTypeMapOfDouble;
- }
- case "dictionaryofinteger": {
- return CTVarTypeMapOfInteger;
- }
- case "dictionaryofstring": {
- return CTVarTypeMapOfString;
- }
- default:
- return CTVarTypeUnknown;
- }
- }
-
- }
-
- private List> _listValue;
-
- private Map, ?> _mapValue;
-
- private Double _numberValue;
-
- private String _stringValue;
-
- private Object _value;
-
- private String name;
-
- private CTVarType type;
-
- CTVar(String name, CTVarType type, Object value) {
- this.name = name;
- this.type = type;
- this._value = value;
- _computeValue();
- }
-
- Boolean booleanValue() {
- if (_stringValue == null) {
- return null;
- }
- try {
- return Boolean.valueOf(_stringValue);
- } catch (Throwable t) {
- return null;
- }
- }
-
- void clearValue() {
- this._value = null;
- _computeValue();
- }
-
- Double doubleValue() {
- return _numberValue;
- }
-
- String getName() {
- return name;
- }
-
- CTVarType getType() {
- return type;
- }
-
- Integer integerValue() {
- if (_numberValue == null) {
- return null;
- }
- try {
- return _numberValue.intValue();
- } catch (Throwable t) {
- return null;
- }
- }
-
- List> listValue() {
- return _listValue;
- }
-
- Map, ?> mapValue() {
- return _mapValue;
- }
-
- String stringValue() {
- return _stringValue;
- }
-
- JSONObject toJSON() {
- JSONObject json = new JSONObject();
- try {
- json.put("name", name);
- json.put("type", type.toString());
- } catch (Throwable t) {
- // no-op
- }
- return json;
- }
-
- @SuppressWarnings("unused")
- void update(CTVarType type, Object value) {
- this.type = type;
- this._value = value;
- _computeValue();
- }
-
- private void _computeValue() {
- _stringValue = null;
- _numberValue = null;
- _listValue = null;
- _mapValue = null;
- if (_value == null) {
- return;
- }
-
- if (_value instanceof String) {
- _stringValue = (String) _value;
- try {
- _numberValue = Double.valueOf(_stringValue);
- } catch (Throwable t) {
- // no-op
- }
- } else if (_value instanceof Number) {
- _stringValue = "" + _value;
- _numberValue = ((Number) _value).doubleValue();
- } else {
- try {
- _stringValue = _value.toString();
- } catch (Throwable t) {
- Logger.d("Error parsing var", t);
- return;
- }
- }
-
- switch (type) {
- case CTVarTypeListOfBool:
- case CTVarTypeListOfDouble:
- case CTVarTypeListOfInteger:
- case CTVarTypeListOfString:
- _listValue = listFromString(_stringValue, type);
- break;
- case CTVarTypeMapOfBool:
- case CTVarTypeMapOfDouble:
- case CTVarTypeMapOfInteger:
- case CTVarTypeMapOfString:
- _mapValue = mapFromString(_stringValue, type);
- break;
- }
- }
-
- private List> listFromString(String stringValue, CTVarType type) {
- try {
- String[] stringArray = stringValue.replace("[", "")
- .replace("]", "").replace("\"", "")
- .split(","); // ["value", "value"...]
-
- if (type == CTVarType.CTVarTypeListOfString) {
- return Arrays.asList(stringArray);
- }
-
- ArrayList parsed = new ArrayList<>();
-
- for (String s : stringArray) {
- switch (type) {
- case CTVarTypeListOfBool:
- parsed.add(Boolean.valueOf(s));
- break;
- case CTVarTypeListOfDouble:
- parsed.add(Double.valueOf(s));
- break;
- case CTVarTypeListOfInteger:
- parsed.add(Integer.valueOf(s));
- break;
- }
- }
- return parsed;
- } catch (Throwable t) {
- Logger.d("Unable to parse list of type: " + type.toString() + " from : " + stringValue);
- return null;
- }
- }
-
- private Map mapFromString(String stringValue, CTVarType type) {
- try {
- String[] stringArray = stringValue.replace("\"", "")
- .replace("{", "")
- .replace("}", "")
- .split(","); // ["key:value", "key:value"...]
-
- Map objectMap = new HashMap<>();
-
- for (String s : stringArray) {
- String[] stringValuesArray = s.split(":");
- String key = stringValuesArray[0];
- String _stringValue = stringValuesArray[1];
- Object value = null;
- switch (type) {
- case CTVarTypeMapOfBool:
- value = Boolean.valueOf(_stringValue);
- break;
- case CTVarTypeMapOfDouble:
- value = Double.valueOf(_stringValue);
- break;
- case CTVarTypeMapOfInteger:
- value = Integer.valueOf(_stringValue);
- break;
- case CTVarTypeMapOfString:
- value = _stringValue;
- break;
- }
- if (value != null) {
- objectMap.put(key, value);
- }
- }
- return objectMap;
- } catch (Throwable t) {
- Logger.d("Unable to parse map of type: " + type.toString() + " from : " + stringValue);
- return null;
- }
- }
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTVarCache.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTVarCache.java
deleted file mode 100644
index 0a17749f7..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/CTVarCache.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.clevertap.android.sdk.ab_testing;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import org.json.JSONArray;
-
-final class CTVarCache {
-
- private final Map vars = new ConcurrentHashMap<>();
-
- @SuppressWarnings({"WeakerAccess"})
- void clearVar(String name) {
- CTVar var = getVar(name);
- if (var != null) {
- var.clearValue();
- }
- }
-
- CTVar getVar(String name) {
- return vars.get(name);
- }
-
- void registerVar(String name, CTVar.CTVarType type, Object value) {
- CTVar var = getVar(name);
- if (var == null) {
- vars.put(name, new CTVar(name, type, value));
- } else if (value
- != null) { // only overwrite if we have a new value, to explicitly clear the value use clearVar
- var.update(type, value);
- }
- }
-
- @SuppressWarnings("unused")
- void reset() {
- for (String name : new HashMap<>(vars).keySet()) {
- clearVar(name);
- }
- }
-
- JSONArray serializeVars() {
- JSONArray serialized = new JSONArray();
- for (String name : new HashMap<>(vars).keySet()) {
- CTVar var = vars.get(name);
- if (var != null) {
- serialized.put(var.toJSON());
- }
- }
- return serialized;
- }
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/gesture/ConnectionGesture.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/gesture/ConnectionGesture.java
deleted file mode 100644
index c4ff4cd0a..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/gesture/ConnectionGesture.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.clevertap.android.sdk.ab_testing.gesture;
-
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import com.clevertap.android.sdk.Logger;
-
-public class ConnectionGesture implements SensorEventListener {
-
- public interface OnGestureListener {
-
- void onGesture();
- }
-
- private static final float MINIMUM_GRAVITY = 9.8f - 2.0f;
-
- private static final float MAXIMUM_GRAVITY = 9.8f + 2.0f;
-
- private static final long MINIMUM_UP_DOWN_DURATION = 250000000; // 1/4 second
-
- private static final long MINIMUM_CANCEL_DURATION = 1000000000; // one second
-
- private static final int STATE_UP = -1;
-
- private static final int STATE_NONE = 0;
-
- private static final int STATE_DOWN = 1;
-
- private static final int TRIGGER_NONE = 0;
-
- private static final int TRIGGER_BEGIN = 1;
-
- private static final float SMOOTHING_FACTOR = 0.7f;
-
- private int gestureState = STATE_NONE;
-
- private long lastTime = -1;
-
- private final OnGestureListener listener;
-
- private final float[] smoothed = new float[3];
-
- private int triggerState = -1;
-
- public ConnectionGesture(OnGestureListener listener) {
- this.listener = listener;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // no-op
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- final float[] smoothedValues = smoothSamples(event.values);
- final int oldState = gestureState;
- gestureState = STATE_NONE;
-
- final float totalGravitySquared = smoothedValues[0] * smoothedValues[0]
- + smoothedValues[1] * smoothedValues[1] + smoothedValues[2] * smoothedValues[2];
-
- final float minimumGravitySquared = MINIMUM_GRAVITY * MINIMUM_GRAVITY;
- final float maximumGravitySquared = MAXIMUM_GRAVITY * MAXIMUM_GRAVITY;
-
- if (smoothed[2] > MINIMUM_GRAVITY && smoothed[2] < MAXIMUM_GRAVITY) {
- gestureState = STATE_UP;
- }
-
- if (smoothed[2] < -MINIMUM_GRAVITY && smoothed[2] > -MAXIMUM_GRAVITY) {
- gestureState = STATE_DOWN;
- }
-
- if (totalGravitySquared < minimumGravitySquared || totalGravitySquared > maximumGravitySquared) {
- gestureState = STATE_NONE;
- }
-
- if (oldState != gestureState) {
- lastTime = event.timestamp;
- }
-
- final long durationNanos = event.timestamp - lastTime;
-
- switch (gestureState) {
- case STATE_DOWN:
- if (durationNanos > MINIMUM_UP_DOWN_DURATION && triggerState == TRIGGER_NONE) {
- Logger.v("Connection gesture started");
- triggerState = TRIGGER_BEGIN;
- }
- break;
- case STATE_UP:
- if (durationNanos > MINIMUM_UP_DOWN_DURATION && triggerState == TRIGGER_BEGIN) {
- Logger.v("Connection gesture completed");
- triggerState = TRIGGER_NONE;
- listener.onGesture();
- }
- break;
- case STATE_NONE:
- if (durationNanos > MINIMUM_CANCEL_DURATION && triggerState != TRIGGER_NONE) {
- Logger.v("Connection gesture canceled");
- triggerState = TRIGGER_NONE;
- }
- break;
- }
- }
-
- private float[] smoothSamples(final float[] samples) {
- for (int i = 0; i < 3; i++) {
- final float old = smoothed[i];
- smoothed[i] = old + (SMOOTHING_FACTOR * (samples[i] - old));
- }
- return smoothed;
- }
-}
-
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/models/CTABVariant.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/models/CTABVariant.java
deleted file mode 100644
index 503f39228..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/models/CTABVariant.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package com.clevertap.android.sdk.ab_testing.models;
-
-import androidx.annotation.NonNull;
-import com.clevertap.android.sdk.ImageCache;
-import com.clevertap.android.sdk.Logger;
-import com.clevertap.android.sdk.Utils;
-import java.util.ArrayList;
-import java.util.List;
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-public class CTABVariant {
-
- final public class CTVariantAction {
-
- private String activityName;
-
- private JSONObject change;
-
- private String name;
-
- CTVariantAction(String name, String activityName, JSONObject change) {
- this.name = name;
- this.activityName = activityName;
- this.change = change;
- }
-
- public String getActivityName() {
- return activityName;
- }
-
- public JSONObject getChange() {
- return change;
- }
-
- public String getName() {
- return name;
- }
- }
-
- private ArrayList actions = new ArrayList<>();
-
- private final Object actionsLock = new Object();
-
- private String experimentId;
-
- private String id;
-
- private ArrayList imageUrls;
-
- private String variantId;
-
- private JSONArray vars;
-
- private int version;
-
- public static CTABVariant initWithJSON(JSONObject json) {
- try {
- String experimentId = json.optString("exp_id", "0");
- String variantId = json.optString("var_id", "0");
- int version = json.optInt("version", 0);
- final JSONArray actions = json.optJSONArray("actions");
- final JSONArray vars = json.optJSONArray("vars");
- CTABVariant variant = new CTABVariant(experimentId, variantId, version, actions, vars);
- Logger.v("Created CTABVariant: " + variant.toString());
- return variant;
- } catch (Throwable t) {
- Logger.v("Error creating variant", t);
- return null;
- }
- }
-
- private CTABVariant(String experimentId, String variantId, int version, JSONArray actions, JSONArray vars) {
- this.experimentId = experimentId;
- this.variantId = variantId;
- this.id = variantId + ":" + experimentId;
- this.version = version;
- imageUrls = new ArrayList<>();
- addActions(actions);
- this.vars = vars == null ? new JSONArray() : vars;
- }
-
- public void addActions(JSONArray actions) {
- synchronized (actionsLock) {
- if (actions == null || actions.length() <= 0) {
- return;
- }
- final int actionsLength = actions.length();
- try {
- for (int j = 0; j < actionsLength; j++) {
- final JSONObject change = actions.getJSONObject(j);
- if (change == null) {
- continue;
- }
- final String targetActivity = Utils.optionalStringKey(change, "target_activity");
- final String name = change.getString("name");
- boolean exists = false;
- CTVariantAction existingAction = null;
- for (CTVariantAction action : this.actions) {
- if (action.getName().equals(name)) {
- exists = true;
- existingAction = action;
- break;
- }
- }
- if (exists) {
- this.actions.remove(existingAction);
- }
- final CTVariantAction action = new CTVariantAction(name, targetActivity, change);
- this.actions.add(action);
- }
- } catch (Throwable t) {
- Logger.v("Error adding variant actions", t);
- }
- }
- }
-
- public void addImageUrls(List urls) {
- if (urls == null) {
- return;
- }
- this.imageUrls.addAll(urls);
- }
-
- public void cleanup() {
- for (String url : imageUrls) {
- ImageCache.removeBitmap(url, true);
- }
- }
-
- public void clearActions() {
- synchronized (actionsLock) {
- actions.clear();
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof CTABVariant) {
- CTABVariant other = (CTABVariant) o;
- return this.getId().equals(other.getId()) && this.getVersion() == other.getVersion();
- }
- return false;
- }
-
- public ArrayList getActions() {
- synchronized (actionsLock) {
- return this.actions;
- }
- }
-
- public String getId() {
- return id;
- }
-
- public JSONArray getVars() {
- return vars;
- }
-
- public int getVersion() {
- return version;
- }
-
- @Override
- public int hashCode() {
- return this.getId().hashCode();
- }
-
- public void removeActionsByName(JSONArray names) {
- if (names == null || names.length() <= 0) {
- return;
- }
- synchronized (actionsLock) {
- ArrayList _names = new ArrayList<>();
- for (int i = 0; i < names.length(); i++) {
- try {
- _names.add(names.getString(i));
- } catch (Throwable t) {
- // no-op
- }
- }
- ArrayList newActions = new ArrayList<>();
- for (CTVariantAction action : actions) {
- if (!_names.contains(action.getName())) {
- newActions.add(action);
- }
- }
- this.actions = newActions;
- }
- }
-
- @NonNull
- @Override
- public String toString() {
- return "< id: " + getId() + ", version: " + getVersion() + ", actions count: " + actions.size()
- + ", vars count: " + getVars().length() + " >";
- }
-
- @SuppressWarnings("unused")
- String getExperimentId() {
- return experimentId;
- }
-
- @SuppressWarnings("unused")
- String getVariantId() {
- return variantId;
- }
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/uieditor/ResourceIds.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/uieditor/ResourceIds.java
deleted file mode 100644
index aa0715d97..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ab_testing/uieditor/ResourceIds.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.clevertap.android.sdk.ab_testing.uieditor;
-
-import android.util.SparseArray;
-import com.clevertap.android.sdk.Logger;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.HashMap;
-import java.util.Map;
-
-final class ResourceIds {
-
- private final Map idNameToId;
-
- private final SparseArray idToIdName;
-
- private final String resourcePackageName;
-
- ResourceIds(String resourcePackageName) {
- idNameToId = new HashMap<>();
- idToIdName = new SparseArray<>();
- this.resourcePackageName = resourcePackageName;
- read();
- }
-
- int idFromName(String name) {
- // noinspection ConstantConditions
- return idNameToId.get(name);
- }
-
- boolean knownIdName(String name) {
- return idNameToId.containsKey(name);
- }
-
- String nameForId(int id) {
- return idToIdName.get(id);
- }
-
- private String getLocalClassName() {
- return resourcePackageName + ".R$id";
- }
-
- private Class> getSystemClass() {
- return android.R.id.class;
- }
-
- private void read() {
- idNameToId.clear();
- idToIdName.clear();
-
- final Class> sysIdClass = getSystemClass();
- readClassIds(sysIdClass, "android", idNameToId);
-
- final String localClassName = getLocalClassName();
- try {
- final Class> rIdClass = Class.forName(localClassName);
- readClassIds(rIdClass, null, idNameToId);
- } catch (ClassNotFoundException e) {
- Logger.d("Can't load names for Android view ids from '" + localClassName
- + "', ids by name will not be available in the events editor.");
- Logger.d("You may be missing a Resources class for your package due to your proguard configuration, " +
- "or you may be using an applicationId in your build that isn't the same as the package declared in your AndroidManifest.xml file.\n"
- +
- "If you're using proguard, you can fix this issue by adding the following to your proguard configuration:\n\n"
- +
- "-keep class **.R$* {\n" +
- " ;\n" +
- "}\n\n" +
- "If you're not using proguard, or if your proguard configuration already contains the directive above, "
- +
- "you can add the following to your AndroidManifest.xml file to explicitly point CleverTap SDK to "
- +
- "the appropriate library for your resources class:\n\n" +
- " \n\n" +
- "where YOUR_PACKAGE_NAME is the same string you use for the \"package\" attribute in your tag."
- );
- }
-
- for (Map.Entry idMapping : idNameToId.entrySet()) {
- idToIdName.put(idMapping.getValue(), idMapping.getKey());
- }
- }
-
- private static void readClassIds(Class> platformIdClass, String namespace, Map