Java tutorial
/** * 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 io.lqd.sdk.model.LQDataPoint; import io.lqd.sdk.model.LQDevice; import io.lqd.sdk.model.LQEvent; import io.lqd.sdk.model.LQLiquidPackage; import io.lqd.sdk.model.LQModel; import io.lqd.sdk.model.LQNetworkRequest; import io.lqd.sdk.model.LQSession; import io.lqd.sdk.model.LQUser; import io.lqd.sdk.model.LQValue; import io.lqd.sdk.model.LQVariable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.json.JSONException; import org.json.JSONObject; 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; public class Liquid { static final String TAG_LIQUID = "LIQUID"; public static final String LIQUID_VERSION = "0.8.4-beta"; private static final int LIQUID_DEFAULT_SESSION_TIMEOUT = 30; private static int mSessionTimeout; private String mApiToken; private LQUser mCurrentUser; private LQUser mPreviousUser; private LQDevice mDevice; private LQSession mCurrentSession; private Date mEnterBackgroundtime; 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; /** * 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 (mInstance == null) { 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 (mInstance == null) { mInstance = new Liquid(context, apiToken, developmentMode); } mInstance.mContext = context; return mInstance; } 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(); mLoadedLiquidPackage = LQLiquidPackage.loadFromDisk(mContext); mHttpQueuer = new LQQueuer(mContext, mApiToken, LQNetworkRequest.loadQueue(mContext, mApiToken)); mHttpQueuer.startFlushTimer(); isDevelopmentMode = developmentMode; if (isDevelopmentMode) mBundleVariablesSended = new ArrayList<String>(); // Get last user and init session mPreviousUser = LQUser.load(mContext, mApiToken); identifyUser(mPreviousUser.getIdentifier(), mPreviousUser.getAttributes(), null, 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(String senderID) { LQPushHandler.registerDevice(mContext, senderID); } 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() { LQRequestFactory.createAliasRequest(oldID, newID).sendRequest(mApiToken); } }); } /** * Identifies the current user with a generated UUID. * */ @Deprecated public void identifyUser() { resetUser(); } /** * Create a new User with a new UUID */ public void resetUser() { String automaticIdentifier = LQModel.newIdentifier(); identifyUser(automaticIdentifier, null, null, false, false); } /** * Identifies the current user with a custom UUID. * * @param identifier * Custom UUID. */ public void identifyUser(String identifier) { identifyUser(identifier, null, 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, 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, HashMap<String, Object> attributes) { identifyUser(identifier, attributes, null, 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, HashMap<String, Object> attributes, boolean alias) { identifyUser(identifier, attributes, null, true, alias); } /** * Identifies the current user with a custom UUID and additional attributes. * * @deprecated Use {@link #setCurrentLocation(android.location.Location location)} * instead.</p> * @param identifier * The custom UUID. * @param location * User Location. */ @Deprecated public void identifyUser(String identifier, Location location) { identifyUser(identifier, null, location, true, true); } public void identifyUser(String identifier, Location location, boolean alias) { identifyUser(identifier, null, location, true, alias); } /** * Identifies the current user with a custom UUID and additional attributes. * * @deprecated Use {@link #setCurrentLocation(android.location.Location location)} * instead.</p> * @param identifier * The custom UUID. * @param attributes * Additional user attributes. * @param location * User Location. */ @Deprecated public void identifyUser(String identifier, HashMap<String, Object> attributes, Location location) { identifyUser(identifier, attributes, location, true, true); } public void identifyUser(String identifier, HashMap<String, Object> attributes, Location location, boolean alias) { identifyUser(identifier, attributes, location, true, alias); } private void identifyUser(String identifier, HashMap<String, Object> attributes, Location location, boolean identified, boolean alias) { final String finalIdentifier = identifier; final HashMap<String, Object> finalAttributes = LQModel.sanitizeAttributes(attributes, isDevelopmentMode); final Location finalLocation = location; // invalid identifier, keeps the current user if (identifier == null && identifier.length() == 0) { 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 " + finalIdentifier + ". Not identifying again."); return; } destroySession(UniqueTime.newDate()); mPreviousUser = mCurrentUser; mCurrentUser = new LQUser(finalIdentifier, finalAttributes, finalLocation, identified); newSession(true); requestValues(); mCurrentUser.save(mContext, mApiToken); if (alias) { alias(); } LQLog.info("From now on we're identifying the User by the identifier '" + finalIdentifier + "'"); } /** * 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(); } /** * 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); } }); } } /** * Add or update the user location. * * @deprecated Use {@link #setCurrentLocation(android.location.Location location)} instead. * @param location * User location. */ @Deprecated public void setUserLocation(Location location) { final Location finalLocation = location; mQueue.execute(new Runnable() { @Override public void run() { mCurrentUser.setLocation(finalLocation); } }); } /** * 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); mCurrentUser.save(mContext, mApiToken); } }); } /** * Add or update the GCM registration ID * * @param id * GCM identifier */ public void setGCMregistrationID(String id) { mDevice.setPushId(id); } /** * Remove the GCM registration ID */ public void removeGCMregistrationID() { mDevice.setPushId(null); } private void newSession(boolean runInCurrentThread) { final Date now = UniqueTime.newDate(); LQLog.infoVerbose("Open Session: " + now.toString()); Runnable newSessionRunnable = new Runnable() { @Override public void run() { mCurrentSession = new LQSession(mSessionTimeout, now); track("_startSession", null, now); } }; if (runInCurrentThread) { newSessionRunnable.run(); } else { mQueue.execute(newSessionRunnable); } } /** * Closes the current session and opens a new one */ public void destroySession() { destroySession(UniqueTime.newDate()); newSession(false); } private void destroySession(Date closeDate) { if ((mCurrentUser != null) && (mCurrentSession != null) && mCurrentSession.getEndDate() == null) { LQLog.infoVerbose("Close Session: " + closeDate.toString()); mCurrentSession.setEndDate(closeDate); track("_endSession", null, closeDate); } } private void checkSessionTimeout() { if ((mCurrentSession != null) && (mEnterBackgroundtime != null)) { Date now = UniqueTime.newDate(); long interval = (now.getTime() - mEnterBackgroundtime.getTime()) / 1000; if (interval >= mSessionTimeout) { destroySession(mEnterBackgroundtime); newSession(false); } else { track("_resumeSession", null, UniqueTime.newDate()); } } } /** * 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, HashMap<String, Object> attributes) { if (LQEvent.hasvalidName(eventName, isDevelopmentMode)) { track(eventName, attributes, UniqueTime.newDate()); } } private void track(String eventName, HashMap<String, Object> attributes, Date date) { if ((eventName == null) || (eventName.length() == 0)) { eventName = "unnamedEvent"; } LQLog.infoVerbose("Tracking: " + eventName); LQEvent event = new LQEvent(eventName, LQModel.sanitizeAttributes(attributes, isDevelopmentMode), date); final String datapoint = new LQDataPoint(mCurrentUser, mDevice, mCurrentSession, 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) { } private void activityCreatedCallback(Activity activity) { } private void activityStopedCallback(Activity activity) { if (isApplicationInBackground(activity)) { track("_pauseSession", null, UniqueTime.newDate()); mEnterBackgroundtime = UniqueTime.newDate(); flush(); requestValues(); } else { mEnterBackgroundtime = null; } } private void activityStartedCallback(Activity activity) { mInstance.attachActivity(activity); if (mNeedCallbackCall) { mNeedCallbackCall = false; notifyListeners(false); } checkSessionTimeout(); } private void activityResumedCallback(Activity activity) { mInstance.attachActivity(activity); mHttpQueuer.startFlushTimer(); } private void activityPausedCallback(Activity 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 " + dataFromServer); } notifyListeners(true); if (mAutoLoadValues) { loadLiquidPackage(false); } } } }); } } 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() { mCurrentSession = null; mDevice = new LQDevice(mContext, LIQUID_VERSION); mEnterBackgroundtime = null; 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); } } }); } }