/**
* Copyright 2014-present Liquid Data Intelligence S.A.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.lqd.sdk;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.lqd.sdk.gcm.LQClientManager;
import io.lqd.sdk.model.LQDataPoint;
import io.lqd.sdk.model.LQDevice;
import io.lqd.sdk.model.LQEvent;
import io.lqd.sdk.model.LQInAppMessage;
import io.lqd.sdk.model.LQLiquidPackage;
import io.lqd.sdk.model.LQModel;
import io.lqd.sdk.model.LQNetworkRequest;
import io.lqd.sdk.model.LQUser;
import io.lqd.sdk.model.LQValue;
import io.lqd.sdk.model.LQVariable;
import io.lqd.sdk.visual.InappMessage;
import io.lqd.sdk.visual.Modal;
import io.lqd.sdk.visual.SlideUp;
public class Liquid {
protected static final String TAG_LIQUID = "LIQUID";
public static final String LIQUID_VERSION = "2.1.1";
private static final int LIQUID_DEFAULT_SESSION_TIMEOUT = 30;
private int mSessionTimeout;
private String mApiToken;
private LQUser mCurrentUser;
private LQUser mPreviousUser;
private LQDevice mDevice;
protected ExecutorService mQueue;
private boolean mAutoLoadValues;
private Context mContext;
private static Liquid mInstance;
private LQLiquidPackage mLoadedLiquidPackage;
private HashMap<String, LQValue> mAppliedValues = new HashMap<String, LQValue>();
private HashMap<String, Activity> mAttachedActivities = new HashMap<String, Activity>();
private HashMap<String, LiquidOnEventListener> mListeners = new HashMap<String, LiquidOnEventListener>();
private ArrayList<String> mBundleVariablesSended;
private boolean mNeedCallbackCall = false;
private LQQueuer mHttpQueuer;
private boolean isDevelopmentMode;
private Activity mCurrentActivity;
private LinkedList<InappMessage> mInAppMessagesQueue;
private boolean isStarted;
/**
* Retrieves the Liquid shared instance.
* <p>
* You can use this method across all your activities.
* </p>
*
* @throws IllegalStateException
* if you didn't call initialize() previously.
*
* @return A Liquid instance.
*/
public static Liquid getInstance() {
if (mInstance == null) {
throw new IllegalStateException("Can't call getInstance() before initialize(context,apiToken)");
}
return mInstance;
}
/**
* Call this method to initialize Liquid.
*
* @param context
* The Android context of your application.
* @param apiToken
* The Liquid ApiToken of your app.
*
* @return A Liquid instance.
*/
public static Liquid initialize(Context context, String apiToken) {
if (!isInitialized()) {
mInstance = new Liquid(context, apiToken, false);
}
mInstance.mContext = context;
return mInstance;
}
/**
* Call this method to initialize Liquid.
*
* @param context
* The Android context of your application.
* @param apiToken
* The Liquid ApiToken of your app.
* @param developmentMode
* The flag to send to Liquid server the variables used in
* methods with <b>fallbackVariable</b> param.
*
* @return The Liquid instance.
*/
public static Liquid initialize(Context context, String apiToken, boolean developmentMode) {
if (!isInitialized()) {
mInstance = new Liquid(context, apiToken, developmentMode);
}
mInstance.mContext = context;
return mInstance;
}
public static boolean isInitialized() {
return mInstance != null;
}
private Liquid(Context context, String apiToken, boolean developmentMode) {
LiquidTools.checkForPermission(permission.INTERNET, context);
if (apiToken == null || apiToken.length() == 0) {
throw new IllegalArgumentException("Your API Token is invalid: \'" + apiToken + "\'.");
}
mContext = context;
if (Build.VERSION.SDK_INT >= 14) {
attachActivityCallbacks();
}
mSessionTimeout = LIQUID_DEFAULT_SESSION_TIMEOUT;
mApiToken = apiToken;
mDevice = new LQDevice(context, LIQUID_VERSION);
mQueue = Executors.newSingleThreadExecutor();
loadLiquidPackage(true);
mInAppMessagesQueue = new LinkedList<>();
mHttpQueuer = LQQueuer.load(mContext, mApiToken);
mHttpQueuer.setLiquidInstance(this);
mHttpQueuer.startFlushTimer();
isDevelopmentMode = developmentMode;
isStarted = false;
if(isDevelopmentMode)
mBundleVariablesSended = new ArrayList<String>();
// Get last user and init session
mPreviousUser = LQUser.load(mContext, mApiToken + ".user");
identifyUser(mPreviousUser.getIdentifier(), mPreviousUser.getAttributes(), mPreviousUser.isIdentified(), false);
LQLog.info("Initialized Liquid with API Token " + apiToken);
}
/*
* *******************
* Setters and Getters
* *******************
*/
/**
* Attach a listener to be notified of Liquid Events
* {@link LiquidOnEventListener}
*
* @see LiquidOnEventListener
*
* @param l Listener to be attached.
*/
public void attachLiquidEventListener(LiquidOnEventListener l) {
mListeners.put(l.getClass().getName(), l);
}
/**
* Detach a listener to stop being notified by Liquid Events
* {@link LiquidOnEventListener}
*
* @see LiquidOnEventListener
*
* @param l Listener to be detached.
*/
public void detachLiquidEventListener(LiquidOnEventListener l) {
mListeners.remove(l.getClass().getName());
}
private void attachActivity(Activity activity) {
mAttachedActivities.put(activity.getClass().getName(), activity);
if(LiquidOnEventListener.class.isInstance(activity)) {
attachLiquidEventListener((LiquidOnEventListener) activity);
}
}
private void detachActivity(Activity activity) {
mAttachedActivities.remove(activity.getClass().getName());
if(LiquidOnEventListener.class.isInstance(activity)) {
detachLiquidEventListener((LiquidOnEventListener) activity);
}
}
/**
* Returns whether or not the {@link LiquidOnEventListener#onValuesLoaded()}
* will be called after {@link LiquidOnEventListener#onValuesReceived()}.
*
* @see LiquidOnEventListener
* @return true if is auto loading variables, otherwise false.
*/
public boolean willAutoloadVariables() {
return mAutoLoadValues;
}
/**
* By default Liquid will not auto load variables.
*
* Set Liquid behavior to auto load variables.
*
* @param autoloadVariables
* whether or not Liquid will auto load the variables.
*/
public void setAutoLoadVariables(boolean autoloadVariables) {
mAutoLoadValues = autoloadVariables;
}
/**
* Get the timeout value that Liquid is using to close automatically a
* session.
*
* @return In seconds the value of the timeout.
*/
public int getSessionTimeout() {
return mSessionTimeout;
}
/**
* Set the timeout value that Liquid will use to close automatically a
* session.
*
* @param sessionTimeout
* value in seconds of the timeout
*/
public void setSessionTimeout(int sessionTimeout) {
mSessionTimeout = sessionTimeout;
}
/**
* Set the flush interval
*
* @param flushInterval
* value in seconds.
*/
public void setFlushInterval(int flushInterval) {
mHttpQueuer.setFlushTimer(flushInterval);
}
/**
* Get the flush interval
*
* @return value in seconds of the flush interval.
*/
public int getFlushInterval() {
return mHttpQueuer.getFlushTimer();
}
/*
* *******************
* User Interaction
* *******************
*/
public void setupPushNotifications(Activity activity, final String senderID) {
LQLog.infoVerbose("Requesting device push token");
LQClientManager pushClientManager = new LQClientManager(activity, senderID);
pushClientManager.registerIfNeeded(new LQClientManager.RegistrationCompletedHandler() {
@Override
public void onSuccess(String registrationId, boolean isNewRegistration) {
Liquid.getInstance().setGCMregistrationID(registrationId);
}
@Override
public void onFailure(String ex) {
super.onFailure(ex);
}
});
}
public void alias() {
final String oldID = mPreviousUser.getIdentifier();
final String newID = mCurrentUser.getIdentifier();
if (mPreviousUser.isIdentified()) {
LQLog.warning("Can't alias (" + oldID + "): Isn't an anonymous user.");
return;
}
LQLog.infoVerbose("Making alias between (" + oldID + ") and (" + newID + ").");
mQueue.execute(new Runnable() {
@Override
public void run() {
mHttpQueuer.addToHttpQueue(LQRequestFactory.createAliasRequest(oldID, newID));
}
});
}
/**
* Create a new User with a new UUID if the user isn't an identified one
*/
public void resetUser() {
if(mCurrentUser.isIdentified()) {
identifyUser(LQModel.newIdentifier(), null, false, false);
} else {
mCurrentUser.clearCustomAttributes();
mCurrentUser.save(mContext, mApiToken);
}
}
/**
* Identifies the current user with a custom UUID.
*
* @param identifier
* Custom UUID.
*/
public void identifyUser(String identifier) {
identifyUser(identifier, null, true, true);
}
/**
* Identifies the current user with a custom UUID.
*
* @param identifier
* @param alias
* if true, will make an alias with previous user if previous
* user is anonymous.
*/
public void identifyUser(String identifier, boolean alias) {
identifyUser(identifier, null, true, alias);
}
/**
* Identifies the current user with a custom UUID and additional attributes.
*
* @param identifier
* The custom UUID.
* @param attributes
* Additional user attributes.
*/
public void identifyUser(String identifier, Map<String, Object> attributes) {
identifyUser(identifier, attributes, true, true);
}
/**
* Identifies the current user with a custom UUID and additional attributes.
*
* @param identifier
* The custom UUID.
* @param attributes
* Additional user attributes.
* @param alias
* if true, will make an alias with previous user if previous
* user is anonymous.
*/
public void identifyUser(String identifier, Map<String, Object> attributes, boolean alias) {
identifyUser(identifier, attributes, true, alias);
}
private void identifyUser(final String identifier, Map<String, Object> attributes, boolean identified, boolean alias) {
final HashMap<String, Object> finalAttributes = LQModel.sanitizeAttributes(attributes, isDevelopmentMode);
// invalid identifier, keeps the current user
if (identifier == null || identifier.isEmpty()) {
return;
}
// same id -> just update attributes
if (mCurrentUser != null && mCurrentUser.getIdentifier().equals(identifier)) {
mCurrentUser.setAttributes(finalAttributes);
mCurrentUser.save(mContext, mApiToken);
LQLog.infoVerbose("Already identified with user " + identifier + ". Not identifying again.");
return;
}
mPreviousUser = mCurrentUser;
mCurrentUser = new LQUser(identifier, finalAttributes, identified);
requestValues();
mCurrentUser.save(mContext, mApiToken);
if (alias) {
alias();
}
LQLog.info("From now on we're identifying the User by the identifier '" + identifier + "'");
}
/**
* Get the user UUID
*
* @return the user UUID, null if the user isn't identified.
*/
public String getUserIdentifier() {
if (mCurrentUser == null) {
return null;
}
return mCurrentUser.getIdentifier();
}
/**
* Get the device UUID
*
* @return the device UUID in String format
*/
public String getDeviceIdentifier() {
return LQDevice.getDeviceID(mContext);
}
/**
* Add or update an additional attribute to the user.
*
* @param key
* Attribute key
* @param attribute
* Attribute value
*/
public void setUserAttribute(String key, Object attribute) {
if (LQModel.validKey(key, isDevelopmentMode)) {
final String finalKey = key;
final Object finalAttribute = attribute;
mQueue.execute(new Runnable() {
@Override
public void run() {
mCurrentUser.setAttribute(finalKey, finalAttribute);
mCurrentUser.save(mContext, mApiToken);
}
});
}
}
public void setUserAttributes(final Map<String, Object> attributes) {
mQueue.execute(new Runnable() {
@Override
public void run() {
for (String key : attributes.keySet()) {
if (LQModel.validKey(key, isDevelopmentMode)) {
mCurrentUser.setAttribute(key, attributes.get(key));
}
}
mCurrentUser.save(mContext, mApiToken);
}
});
}
/**
* Add or update the current location.
*
* @param location
* Current location.
*/
public void setCurrentLocation(final Location location) {
mQueue.execute(new Runnable() {
@Override
public void run() {
mDevice.setLocation(location);
}
});
}
/**
* Add or update the GCM registration ID
*
* @param id
* GCM identifier
*/
public void setGCMregistrationID(final String id) {
mQueue.execute(new Runnable() {
@Override
public void run() {
mDevice.setPushId(id);
}
});
}
/**
* Remove the GCM registration ID
*/
public void removeGCMregistrationID() {
mQueue.execute(new Runnable() {
@Override
public void run() {
mDevice.setPushId(null);
}
});
}
/**
* Track an event.
*
* <p>
* If the <b>eventName</b> is a empty string or null, the event will be
* tracked with name <b>unnamedEvent</b>
* </p>
*
* @param eventName
* Name of the event.
*/
public void track(String eventName) {
if (LQEvent.hasValidName(eventName, isDevelopmentMode)) {
track(eventName, null, UniqueTime.newDate());
} else {
LQLog.warning("Event can't begin with \' _ \' character ");
}
}
/**
* Track an event.
*
* <p>
* If the <b>eventName</b> is a empty string or null, the event will be
* tracked with name <b>unnamedEvent</b>
* </p>
*
* @param eventName
* Name of the event.
* @param attributes
* Additional attributes of the event.
*/
public void track(String eventName, Map<String, Object> attributes) {
if (LQEvent.hasValidName(eventName, isDevelopmentMode)) {
track(eventName, attributes, UniqueTime.newDate());
}
}
/**
* Track the dismissed action of the In-APP message
*
* @param inAppMessage
* The message itself.
*/
public void trackDismiss(final LQInAppMessage inAppMessage) {
mQueue.execute(new Runnable() {
@Override
public void run() {
mHttpQueuer.addToHttpQueue(LQRequestFactory.inappMessagesReportRequest(mCurrentUser.getIdentifier(), inAppMessage.getFormulaId()));
}
});
track(inAppMessage.getDismissEventName(), inAppMessage.getDismissAttributes(), UniqueTime.newDate());
}
/**
* Track the button click action.
*
* @param inAppMessageCta
* The button itself.
*/
public void trackCta(final LQInAppMessage.Cta inAppMessageCta){
mQueue.execute(new Runnable() {
@Override
public void run() {
try {
JSONObject payload = new JSONObject();
payload.put("cta_id", inAppMessageCta.getCtasAttributes().get("cta_id"));
mHttpQueuer.addToHttpQueue(LQRequestFactory.inappMessagesReportRequest(mCurrentUser.getIdentifier(), (String) inAppMessageCta.getCtasAttributes().get("formula_id"), payload));
} catch (JSONException e) {
}
}
});
track(inAppMessageCta.getCtasEventName(), inAppMessageCta.getCtasAttributes(), UniqueTime.newDate());
}
private void track(String eventName, Map<String, Object> attributes, Date date) {
LQEvent event = new LQEvent(eventName, LQModel.sanitizeAttributes(attributes, isDevelopmentMode), date);
LQLog.infoVerbose("Tracking: " + event.getName());
final String datapoint = new LQDataPoint(mCurrentUser, mDevice, event, mLoadedLiquidPackage.getValues(), date).toJSON().toString();
LQLog.data(datapoint);
mQueue.execute(new Runnable() {
@Override
public void run() {
mHttpQueuer.addToHttpQueue(LQRequestFactory.createDataPointRequest(datapoint));
}
});
}
/*
* *******************
* Activity Lifecycle
* *******************
*/
/**
* Override this method to the Activity onResume() You only need to do this
* if your android minSDK is < 14
*
* @param activity
* the resumed activity
*/
public void activityResumed(Activity activity) {
if (Build.VERSION.SDK_INT < 14) {
activityResumedCallback(activity);
}
}
/**
* Override this method to the Activity onPaused() You only need to do this
* if your android minSDK is < 14
*
* @param activity
* the paused activity
*/
public void activityPaused(Activity activity) {
if (Build.VERSION.SDK_INT < 14) {
activityPausedCallback(activity);
}
}
/**
* Override this method to the Activity onStopped() You only need to do this
* if your android minSDK is < 14
*
* @param activity
* the stopped activity
*/
public void activityStopped(Activity activity) {
if (Build.VERSION.SDK_INT < 14) {
activityStopedCallback(activity);
}
}
/**
* Override this method to the Activity onStart() You only need to do this
* if your android minSDK is < 14
*
* @param activity
* the started activity
*/
public void activityStarted(Activity activity) {
if (Build.VERSION.SDK_INT < 14) {
activityStartedCallback(activity);
}
}
@SuppressLint("NewApi")
private boolean isApplicationInBackground(Activity activity) {
boolean configurationChanged;
if(Build.VERSION.SDK_INT < 11) {
int changingConfigs = activity.getChangingConfigurations();
configurationChanged = (changingConfigs == Configuration.SCREENLAYOUT_LAYOUTDIR_RTL || changingConfigs == Configuration.SCREENLAYOUT_LAYOUTDIR_LTR);
} else {
configurationChanged = activity.isChangingConfigurations();
}
return mAttachedActivities.size() == 0 && !configurationChanged;
}
/**
* Override this method to the Activity onDestroy() You only need to do this
* if your android minSDK is < 14
*
* @param activity
* the destroyed activity
*/
public void activityDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT < 14) {
activityDestroyedCallback(activity);
}
}
public void activityCreated(Activity activity) {
if (Build.VERSION.SDK_INT < 14) {
activityCreatedCallback(activity);
}
}
private void activityDestroyedCallback(Activity activity) {
//mCurrentActivity = activity;
}
private void activityCreatedCallback(Activity activity) {
mCurrentActivity = activity;
}
private void activityStopedCallback(Activity activity) {
// mCurrentActivity = activity;
if (isApplicationInBackground(activity)) {
track("app background", null, UniqueTime.newDate());
flush();
requestValues();
isStarted = false;
}
}
private void activityStartedCallback(Activity activity) {
mCurrentActivity = activity;
mInstance.attachActivity(activity);
if (mNeedCallbackCall) {
mNeedCallbackCall = false;
notifyListeners(false);
}
}
private void activityResumedCallback(Activity activity) {
mCurrentActivity = activity;
mInstance.attachActivity(activity);
if(!isApplicationInBackground(activity) && !isStarted) {
track("app foreground", null, UniqueTime.newDate());
isStarted = true;
showInAppMessages();
}
mHttpQueuer.startFlushTimer();
}
private void activityPausedCallback(Activity activity) {
mCurrentActivity = activity;
mInstance.detachActivity(activity);
mHttpQueuer.stopFlushTimer();
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void attachActivityCallbacks() {
final Application app = (Application) mContext.getApplicationContext();
app.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStopped(Activity activity) {
activityStopedCallback(activity);
}
@Override
public void onActivityStarted(Activity activity) {
activityStartedCallback(activity);
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityResumed(Activity activity) {
activityResumedCallback(activity);
}
@Override
public void onActivityPaused(Activity activity) {
activityPausedCallback(activity);
}
@Override
public void onActivityDestroyed(Activity activity) {
activityDestroyedCallback(activity);
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
activityCreatedCallback(activity);
}
});
}
/**
* Request values from the server.
*/
public void requestValues() {
if ((mCurrentUser != null) && (mDevice != null)) {
mQueue.execute(new Runnable() {
@Override
public void run() {
LQNetworkRequest req = LQRequestFactory.requestLiquidPackageRequest(mCurrentUser.getIdentifier(), mDevice.getUID());
String dataFromServer = req.sendRequest(mApiToken).getRequestResponse();
if (dataFromServer != null) {
try {
JSONObject jsonObject = new JSONObject(dataFromServer);
LQLiquidPackage liquidPackage = new LQLiquidPackage(jsonObject);
LQLog.http(jsonObject.toString());
liquidPackage.saveToDisk(mContext);
} catch (JSONException e) {
LQLog.error("Could not parse JSON (Liquid Variables):" + dataFromServer);
}
notifyListeners(true);
if (mAutoLoadValues) {
loadLiquidPackage(false);
}
}
}
});
}
}
public void addInapp(LQInAppMessage inapp) {
if(mCurrentActivity == null)
return;
if (inapp.getLayout().equals("modal")) {
mInAppMessagesQueue.add(new Modal(mContext, mCurrentActivity.findViewById(android.R.id.content).getRootView(), inapp));
} else if (inapp.getLayout().equals("slide_up")) {
mInAppMessagesQueue.add(new SlideUp(mContext, mCurrentActivity.findViewById(android.R.id.content).getRootView(), inapp));
}
}
public void showInAppMessages(){
mQueue.execute(new Runnable() {
@Override
public void run() {
InappMessage in_app = mInAppMessagesQueue.poll();
if (in_app == null) {
LQLog.infoVerbose("Not anymore inapp messages in the queue");
} else {
in_app.show();
}
}
});
}
private void notifyListeners(final boolean received) {
Handler mainHandler = new Handler(mContext.getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
if (mListeners.size() == 0) {
mNeedCallbackCall = true;
return;
} else {
mNeedCallbackCall = false;
}
for (LiquidOnEventListener listener : mListeners.values()) {
if (received) {
listener.onValuesReceived();
} else {
listener.onValuesLoaded();
}
}
}
});
}
/**
* Load a values retrieved previously from the server.
*/
public void loadValues() {
loadLiquidPackage(false);
}
private void loadLiquidPackage(boolean runInCurrentThread) {
Runnable runnable = new Runnable() {
@Override
public void run() {
mLoadedLiquidPackage = LQLiquidPackage.loadFromDisk(mContext);
mAppliedValues = LQValue.convertToHashMap(mLoadedLiquidPackage.getValues());
notifyListeners(false);
}
};
if (runInCurrentThread) {
runnable.run();
} else {
mQueue.execute(runnable);
}
}
/*
* *******************
* Getters for liquid Package
* *******************
*/
/**
* Get a variable value.
*
* @param variableKey
* Variable Key of the Value.
* @param fallbackValue
* is the value returned if the value for variableKey doesn't
* exist in Liquid instance.
* @return The value in Liquid instance or fallbackValue if the variable
* don't exist.
*/
public Date getDateVariable(String variableKey, Date fallbackValue) {
if (isDevelopmentMode) {
sendBundleVariable(LQVariable.buildJsonObject(variableKey, fallbackValue, LQVariable.DATE_TYPE));
}
if (!mAppliedValues.containsKey(variableKey)) {
return fallbackValue;
}
if (mAppliedValues.get(variableKey).getDataType().equals(LQVariable.DATE_TYPE)) {
try {
Object value = mAppliedValues.get(variableKey).getValue();
return value == null ? null : LiquidTools.stringToDate((String) value);
} catch (IllegalArgumentException e) {
LQLog.error("Error parsing Date with key: \"" + variableKey + "\"");
}
}
invalidateVariables(variableKey);
return fallbackValue;
}
/**
* Get a variable value.
*
* @param variableKey
* Variable Key of the Value.
* @param fallbackValue
* is the value returned if the value for variableKey doesn't
* exist in Liquid instance.
* @return The value in Liquid instance or fallbackValue if the variable
* don't exist.
*/
public int getColorVariable(String variableKey, int fallbackValue) {
if (isDevelopmentMode) {
sendBundleVariable(LQVariable.buildJsonObject(variableKey, LiquidTools.colorToHex(fallbackValue), LQVariable.COLOR_TYPE));
}
if (!mAppliedValues.containsKey(variableKey)) {
return fallbackValue;
}
if (mAppliedValues.get(variableKey).getDataType()
.equals(LQVariable.COLOR_TYPE)) {
try {
return Color.parseColor(mAppliedValues.get(variableKey).getValue().toString());
} catch (IllegalArgumentException e) {
LQLog.error("Error parsing Color with key: \"" + variableKey + "\"");
}
}
invalidateVariables(variableKey);
return fallbackValue;
}
/**
* Get a variable value.
*
* @param variableKey
* Variable Key of the Value.
* @param fallbackValue
* is the value returned if the value for variableKey doesn't
* exist in Liquid instance.
* @return The value in Liquid instance or fallbackValue if the variable
* don't exist.
*/
public String getStringVariable(String variableKey, String fallbackValue) {
if (isDevelopmentMode) {
sendBundleVariable(LQVariable.buildJsonObject(variableKey, fallbackValue, LQVariable.STRING_TYPE));
}
if (!mAppliedValues.containsKey(variableKey)) {
return fallbackValue;
}
if (mAppliedValues.get(variableKey).getDataType().equals(LQVariable.STRING_TYPE)) {
Object value = mAppliedValues.get(variableKey).getValue();
return value == null ? null : value.toString();
}
invalidateVariables(variableKey);
return fallbackValue;
}
/**
* Get a variable value.
*
* @param variableKey
* Variable Key of the Value.
* @param fallbackValue
* is the value returned if the value for variableKey doesn't
* exist in Liquid instance.
* @return The value in Liquid instance or fallbackValue if the variable
* don't exist.
*/
public int getIntVariable(String variableKey, int fallbackValue) {
if (isDevelopmentMode) {
sendBundleVariable(LQVariable.buildJsonObject(variableKey,fallbackValue, LQVariable.INT_TYPE));
}
if (!mAppliedValues.containsKey(variableKey)) {
return fallbackValue;
}
if (mAppliedValues.get(variableKey).getDataType().equals(LQVariable.INT_TYPE)) {
try {
return Integer.parseInt(mAppliedValues.get(variableKey).getValue().toString());
} catch (NumberFormatException e) {
LQLog.error("Error parsing Integer with key: \"" + variableKey + "\"");
}
}
invalidateVariables(variableKey);
return fallbackValue;
}
/**
* Get a variable value.
*
* @param variableKey
* Variable Key of the Value.
* @param fallbackValue
* is the value returned if the value for variableKey doesn't
* exist in Liquid instance.
* @return The value in Liquid instance or fallbackValue if the variable
* don't exist.
*/
public float getFloatVariable(String variableKey, float fallbackValue) {
if (isDevelopmentMode) {
sendBundleVariable(LQVariable.buildJsonObject(variableKey, fallbackValue, LQVariable.FLOAT_TYPE));
}
if (!mAppliedValues.containsKey(variableKey)) {
return fallbackValue;
}
if (mAppliedValues.get(variableKey).getDataType().equals(LQVariable.FLOAT_TYPE)) {
try {
return Float.parseFloat(mAppliedValues.get(variableKey).getValue().toString());
} catch (NumberFormatException e) {
LQLog.error("Error parsing Float with key: \"" + variableKey + "\"");
}
}
invalidateVariables(variableKey);
return fallbackValue;
}
public boolean getBooleanVariable(String variableKey, boolean fallbackValue) {
if (isDevelopmentMode) {
sendBundleVariable(LQVariable.buildJsonObject(variableKey, fallbackValue, LQVariable.BOOLEAN_TYPE));
}
if (!mAppliedValues.containsKey(variableKey)) {
return fallbackValue;
}
if (mAppliedValues.get(variableKey).getDataType().equals(LQVariable.BOOLEAN_TYPE)) {
return Boolean.parseBoolean(mAppliedValues.get(variableKey).getValue().toString());
}
invalidateVariables(variableKey);
return fallbackValue;
}
/**
* Force Liquid to send locally saved data.
*/
public void flush() {
LQLog.infoVerbose("Flushing");
mQueue.execute(new Runnable() {
@Override
public void run() {
mHttpQueuer.flush();
}
});
}
private void sendBundleVariable(final JSONObject variable) {
if(!mBundleVariablesSended.contains(variable.optString("name"))) {
mQueue.execute(new Runnable() {
@Override
public void run() {
LQLog.infoVerbose("Sending bundle variable " + variable);
LQRequestFactory.createVariableRequest(variable).sendRequest(mApiToken);
}
});
mBundleVariablesSended.add(variable.optString("name"));
}
}
/**
* Reset all collected data that is stored locally.
*
* <p>
* This includes, user, device, session, values, events
* </p>
*/
public void reset() {
reset(false);
}
/**
* Same as reset but preserves the tracked events that aren't in Liquid Server.
*/
public void softReset() {
reset(true);
}
private void reset(final boolean soft) {
mQueue.execute(new Runnable() {
@Override
public void run() {
mDevice = new LQDevice(mContext, LIQUID_VERSION);
mLoadedLiquidPackage = new LQLiquidPackage();
mAppliedValues = new HashMap<String, LQValue>();
if(!soft) {
mHttpQueuer = new LQQueuer(mContext, mApiToken);
}
resetUser();
}
});
}
private void invalidateVariables(final String variableKey) {
mQueue.execute(new Runnable() {
@Override
public void run() {
LQLog.infoVerbose("invalidating: " + variableKey);
boolean removed = mLoadedLiquidPackage.invalidateTargetFromVariableKey(variableKey);
if (removed) {
LQLog.infoVerbose("invalidated: " + variableKey);
mAppliedValues = LQValue.convertToHashMap(mLoadedLiquidPackage.getValues());
mLoadedLiquidPackage.saveToDisk(mContext);
notifyListeners(false);
}
}
});
}
}