com.bluekai.sdk.BlueKai.java Source code

Java tutorial

Introduction

Here is the source code for com.bluekai.sdk.BlueKai.java

Source

/*
 * Copyright 2013-present BlueKai, Inc.
 *
 * 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 com.bluekai.sdk;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Handler;
import android.provider.Settings.Secure;
import android.support.v4.app.FragmentManager;
import android.text.TextUtils;
import android.webkit.WebSettings;
import android.widget.RelativeLayout;

import com.bluekai.sdk.listeners.BKViewListener;
import com.bluekai.sdk.listeners.DataPostedListener;
import com.bluekai.sdk.listeners.SettingsChangedListener;
import com.bluekai.sdk.model.Params;
import com.bluekai.sdk.model.ParamsList;
import com.bluekai.sdk.model.Settings;
import com.bluekai.sdk.utils.Logger;

public class BlueKai implements SettingsChangedListener, BKViewListener {

    private final static String TAG = "BlueKai";
    private static BlueKai instance = null;

    private boolean devMode = false;
    private Activity activity = null;
    private Context context = null;
    private boolean httpsEnabled = false;
    private final String HTTP = "http://";
    private final String HTTPS = "https://";
    private final String BASE_URL = "mobileproxy.bluekai.com/m.html";
    private final String SANDBOX_URL = "mobileproxy.bluekai.com/m-sandbox.html";
    private String siteId = "2";
    private String appVersion = "1.0";
    private String imei = "";
    private BlueKaiWebView blueKaiView;
    private BlueKaiDataSource database = null;
    private Settings settings;
    private FragmentManager fManager = null;
    private DataPostedListener listener;
    private Handler handler;

    private BlueKai() {
        database = BlueKaiDataSource.getInstance(context);
        database.setSettingsChangedListener(this);
        settings = database.getSettings();
    }

    private BlueKai(Activity activity, Context context, boolean devMode, String siteId, String appVersion,
            DataPostedListener listener, Handler handler) {
        this(activity, context, devMode, false, siteId, appVersion, listener, handler);
    }

    private BlueKai(Activity activity, Context context, boolean devMode, boolean useHttps, String siteId,
            String appVersion, DataPostedListener listener, Handler handler) {
        this.activity = activity;
        this.context = context;
        this.devMode = devMode;
        Logger.setDebug(devMode);
        this.appVersion = appVersion;
        this.imei = getImei(context);
        if (!TextUtils.isEmpty(siteId) && !this.siteId.equals(siteId)) {
            this.siteId = siteId;
        }
        this.listener = listener;
        this.handler = handler;
        this.httpsEnabled = useHttps;
        Logger.debug(TAG, " onCreate Dev Mode ? " + devMode);
        Logger.debug(TAG, " onCreate BK URL --> " + (useHttps ? HTTPS : HTTP) + (devMode ? SANDBOX_URL : BASE_URL));
        database = BlueKaiDataSource.getInstance(context);
        database.setSettingsChangedListener(this);
        settings = database.getSettings();
    }

    /**
     * Set the fragment manager. Used when devMode is enabled to show webview in
     * a popup dialog
     * 
     * @param fm FragmentManager
     */
    public void setFragmentManager(FragmentManager fm) {
        this.fManager = fm;
    }

    /**
     * Method to resume BlueKai process after calling application resumes. To
     * use in onResume() of the calling activity
     */
    public void resume() {
        Logger.debug(TAG, " resume Dev Mode ? " + devMode);
        if (!devMode) {
            addBlueKaiWebView(context);
        }
        if (settings.isAllowDataPosting()) {
            checkForExistingData();
        }
    }

    /**
     * Method to get BlueKai instance
     * 
     * @param activity
     *            Calling application activity reference
     * @param context
     *            Calling application context
     * @param devMode
     *            Developer mode. Set to enable webview to popup in a dialog.
     *            Strictly for developer purposes only
     * @param siteId
     *            BlueKai site id
     * @param appVersion
     *            Version of the calling application
     * @param listener
     *            DataPostedListener. Calling activity should implement this
     *            interface
     * @param handler
     *          Handler. Android os handler.
     *
     * @return BlueKai instance
     */
    public static BlueKai getInstance(Activity activity, Context context, boolean devMode, String siteId,
            String appVersion, DataPostedListener listener, Handler handler) {
        Logger.debug(TAG, "Called get instance...");
        if (instance == null) {
            instance = new BlueKai(activity, context, devMode, siteId, appVersion, listener, handler);
        } else {
            instance.setActivity(activity);
            instance.setAppContext(context);
            instance.setDevMode(devMode);
            Logger.setDebug(devMode);
            instance.setSiteId(siteId);
            instance.setAppVersion(appVersion);
            instance.setDataPostedListener(listener);
            instance.setHandler(handler);
        }
        return instance;
    }

    /**
     * Method to get BlueKai instance
     *
     * @param activity
     *          Calling application activity reference
     * @param context
     *          Calling application context
     * @param devMode
     *          Developer mode. Set to enable webview to popup in a dialog.
     *          Strictly for developer purposes only
     * @param httpsEnabled
     *          Secure mode. Set to enable data transfer to BlueKai over https.
     * @param siteId
     *          BlueKai site id
     * @param appVersion
     *          Version of the calling application
     * @param listener
     *          DataPostedListener. Calling activity should implement this
     *          interface
     * @param handler
     *          Handler. Android os handler.
     *
     * @return BlueKai instance
     */
    public static BlueKai getInstance(Activity activity, Context context, boolean devMode, boolean httpsEnabled,
            String siteId, String appVersion, DataPostedListener listener, Handler handler) {
        Logger.debug(TAG, "Called get instance...");
        if (instance == null) {
            instance = new BlueKai(activity, context, devMode, httpsEnabled, siteId, appVersion, listener, handler);
        } else {
            instance.setActivity(activity);
            instance.setAppContext(context);
            instance.setDevMode(devMode);
            Logger.setDebug(devMode);
            instance.setHttpsEnabled(httpsEnabled);
            instance.setSiteId(siteId);
            instance.setAppVersion(appVersion);
            instance.setDataPostedListener(listener);
            instance.setHandler(handler);
        }
        return instance;
    }

    /**
     * Convenience method to initialize and get instance of BlueKai without arguments.
     * This method returns the previously created instance, if any, or returns an instance 
     * with bare minimum settings initialized. In ideal cases, first create a BlueKai
     * instance using the other two getInstance() methods that take arguments and then subsequently 
     * use this method to get previously created instance.
     * 
     * @return BlueKai instance
     */
    public static BlueKai getInstance() {
        Logger.debug(TAG, "Called get instance...");
        if (instance == null) {
            Logger.debug(TAG, "Creating new instance...");
            instance = new BlueKai();
        }
        return instance;
    }

    /**
     * Set the calling activity reference
     * 
     * @param activity Calling activity reference
     */
    public void setActivity(Activity activity) {
        this.activity = activity;
    }

    /**
     * Get calling activity reference.
     *
     * @return activity Activity
     */
    public Activity getActivity() {
        return activity;
    }

    /**
     * Set the calling application context
     * 
     * @param context Context
     */
    public void setAppContext(Context context) {
        this.context = context;
        database = BlueKaiDataSource.getInstance(context);
        database.setSettingsChangedListener(this);
        settings = database.getSettings();
    }

    /**
     * Get calling application context.
     *
     * @return context Context
     */
    public Context getContext() {
        return context;
    }

    /**
     * Set developer mode (True or False)
     * 
     * @param devMode
     *            Developer mode
     */
    public void setDevMode(boolean devMode) {
        this.devMode = devMode;
    }

    /**
     * Get Developer mode setting.
     *
     * @return devMode boolean
     */
    public boolean isDevMode() {
        return devMode;
    }

    /**
     * Set BlueKai site id
     * 
     * @param siteId Site ID
     */
    public void setSiteId(String siteId) {
        this.siteId = siteId;
    }

    /**
     * Get BlueKai Site ID
     *
     * @return siteId String
     */
    public String getSiteId() {
        return this.siteId;
    }

    /**
     * Set the calling application's version
     * 
     * @param appVersion
     *            Application version
     */
    public void setAppVersion(String appVersion) {
        this.appVersion = appVersion;
    }

    /**
     * Get calling application's version
     *
     * @return appVersion String
     */
    public String getAppVersion() {
        return appVersion;
    }

    /**
     * Set the DataPostedListener to get notifications about status of a data
     * posting. Calling activity should implement this interface
     * 
     * @param listener
     *            Listener implementation
     */
    public void setDataPostedListener(DataPostedListener listener) {
        this.listener = listener;
    }

    /**
     * Get the listener that is configured to get notifications about status of
     * data posting.
     *
     * @return listener DataPostedListener
     */
    public DataPostedListener getDataPostedListener() {
        return listener;
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

    public Handler getHandler() {
        return handler;
    }

    /**
    * Method to send data to BlueKai. Accepts a single key-value pair
    *
        
    * @param key
    *            Key
    * @param value
    *            Value
    */
    public void put(String key, String value) {
        sendData(key, value);
    }

    /**
     * Convenience method to send a bunch of key-value pairs to BlueKai
     * 
     * @param map
     *            Map with keys and values
     * @deprecated as of release v1.0.3. Replaced by {@link #putAll(java.util.Map)}
     */
    @Deprecated
    public void put(Map<String, String> map) {
        sendData(map);
    }

    /**
     * Convenience method to send a bunch of key-value pairs to BlueKai
     *
     * @param map
     *            Map with keys and values
     */
    public void putAll(Map<String, String> map) {
        sendData(map);
    }

    /**
     * Method to show BlueKai in-build opt-in screen
     * 
     * @param listener
     *            Listener to get callback on settings change
     */
    public void showSettingsScreen(SettingsChangedListener listener) {
        if (activity == null || context == null) {
            Logger.error(TAG, "Activity or context reference is null. Cannot show settings page");
        } else {
            Logger.debug(TAG, "Settings activity called...");
            Intent intent = new Intent(context, SettingsActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            activity.startActivity(intent);
        }
    }

    /**
     * Method to set user opt-in or opt-out preference
     * 
     * @param optIn
     *            Opt-in (true or false)
     * @Deprecated as of release v1.0.3. Replaced by {@link #setOptInPreference(boolean)}
     */
    @Deprecated
    public void setOptIn(boolean optIn) {
        setOptInPreference(optIn);
    }

    /**
     * Method to set user opt-in or opt-out preference
     *
     * @param optIn
     *            Opt-in (true or false)
     */
    public void setOptInPreference(boolean optIn) {
        this.settings.setAllowDataPosting(optIn);
        if (database != null) {
            database.createSettings(this.settings);
        }
    }

    /**
     * Method to get user opt-in or opt-out preference
     *
     * @return user's opt-in or opt-out preference
     */
    public boolean getOptInPreference() {
        return this.settings.isAllowDataPosting();
    }

    /**
     * Method to check if httpsEnabled is true. If httpsEnabled is set then data is sent to BlueKai over https
     *
     * @return httpsEnabled flag that enables/disables data being sent to BlueKai over https.
     */
    public boolean isHttpsEnabled() {
        return httpsEnabled;
    }

    /**
     * Method to change httpsEnabled settings. If httpsEnabled is set then data is sent to BlueKai over https
     * @param httpsEnabled
     */
    public void setHttpsEnabled(boolean httpsEnabled) {
        this.httpsEnabled = httpsEnabled;
    }

    private void addBlueKaiWebView(Context context) {
        try {
            blueKaiView = new BlueKaiWebView(context, this);
            blueKaiView.setWebClient();
            WebSettings webSettings = blueKaiView.getSettings();
            webSettings.setJavaScriptEnabled(true);
            int height = 1, width = 1;
            if (devMode) {
                height = width = RelativeLayout.LayoutParams.MATCH_PARENT;
            }
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
            params.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
            params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
            params.setMargins(10, 10, 10, 10);
            blueKaiView.setBackgroundColor(Color.LTGRAY);
            if (activity != null) {
                activity.addContentView(blueKaiView, params);
            } else {
                Logger.warn(TAG, "Activity is null. Cannot add webview");
            }
        } catch (Exception ex) {
            Logger.error(TAG, "Error while adding BlueKai webview", ex);
        }
    }

    private void checkForExistingData() {
        if (database != null) {
            ParamsList paramsList = database.getParams();
            if (paramsList != null && !paramsList.isEmpty()) {
                sendExistingData(paramsList);
            }
        }
    }

    private void sendExistingData(ParamsList paramsList) {
        Logger.debug(TAG, "IsAllowDataPosting --> " + settings.isAllowDataPosting());
        if (settings.isAllowDataPosting()) {
            SendData sendData = new SendData(paramsList, handler, true);
            Thread thread = new Thread(sendData);
            thread.start();
        }
    }

    private void sendData(String key, String value) {
        Map<String, String> paramsMap = new HashMap<String, String>();
        paramsMap.put(key, value);
        sendData(paramsMap);
    }

    private void sendData(Map<String, String> paramsMap) {
        Logger.debug(TAG, "IsAllowDataPosting --> " + settings.isAllowDataPosting());
        ParamsList paramsList = new ParamsList();
        Iterator<String> it = paramsMap.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            String value = paramsMap.get(key);
            Params params = new Params();
            params.setKey(key);
            params.setValue(value);
            paramsList.add(params);
        }
        if (settings.isAllowDataPosting()) {
            if (blueKaiView == null) {
                addBlueKaiWebView(context);
            }
            SendData sendData = new SendData(paramsList, handler, false);
            Thread thread = new Thread(sendData);
            thread.start();
        }
    }

    private class SendData implements Runnable, BKViewListener {

        private ParamsList paramsList = null, backupList = null;
        private Handler handler = null;
        private boolean existingData = false;
        private ParamsList currentList = null;

        public SendData(ParamsList paramsList, Handler handler, boolean existingData) {
            this.paramsList = paramsList;
            this.backupList = new ParamsList(paramsList);
            this.handler = handler;
            this.existingData = existingData;
            if (blueKaiView != null) {
                blueKaiView.setBKViewListerner(this);
            }
        }

        @Override
        public void run() {
            try {
                final String url = getURL();
                Logger.debug(TAG, "URL to call ---> " + url);
                if (url != null && !url.trim().equals("")) {
                    try {
                        currentList = new ParamsList(backupList.subList(0, backupList.size() - paramsList.size()));
                        backupList.removeAll(currentList);
                        Logger.debug(TAG, "Lists size :: " + paramsList.size() + " :: " + backupList.size());
                        Logger.debug(TAG, "Sending list to be loaded in loadUrl() " + currentList.size());
                        if (handler != null) {
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    showBlueKaiDialog(url, existingData, currentList, SendData.this);
                                }
                            });
                        }
                    } catch (Exception ex) {
                        Logger.error(TAG, "Error while posting data", ex);
                        currentList = new ParamsList(backupList.subList(0, backupList.size() - paramsList.size()));
                        backupList.removeAll(currentList);
                        onDataPosted(false, "Error posting data -- " + ex.getMessage(), existingData, currentList);
                    }
                } else {
                    onDataPosted(false, "Nothing to post", existingData, null);
                }

            } catch (Exception ex) {
                final String message = ex.getMessage();
                Logger.error(TAG, "Error while sending data", ex);
                currentList = new ParamsList(backupList.subList(0, backupList.size() - paramsList.size()));
                backupList.removeAll(currentList);
                onDataPosted(false, message, existingData, currentList);
            }
        }

        private String getURL() throws UnsupportedEncodingException {
            StringBuffer buffer = null;
            String url = (httpsEnabled ? HTTPS : HTTP) + (devMode ? SANDBOX_URL : BASE_URL) + "?site=" + getSiteId()
                    + "&";
            String queryPart = "";
            Iterator<Params> it = paramsList.iterator();
            buffer = new StringBuffer();
            String tailString = "&appVersion=" + appVersion;
            int tailLength = tailString.length();
            while (it.hasNext()) {
                Params params = it.next();
                String key = URLEncoder.encode(params.getKey(), "UTF-8");
                String value = URLEncoder.encode(params.getValue(), "UTF-8");
                if (buffer.length() + tailLength + key.length() + value.length() + 2 > 255) {
                    break;
                } else {
                    buffer.append(key + "=" + value);
                    if (it.hasNext()) {
                        buffer.append("&");
                    }
                    it.remove();
                }
            }
            buffer.append(tailString);
            queryPart = buffer.toString();
            return url + queryPart;
        }

        private void onDataPosted(boolean success, String message, boolean existingData, ParamsList paramsList) {
            Logger.debug(TAG, "OnDataPosted called ... status -> " + success + " this.ParamsList size --> "
                    + this.paramsList.size());
            if (paramsList != null) {
                Logger.debug(TAG, "ParamsList --> " + paramsList.size());
            }
            if (!success) {
                if (existingData) {
                    // updateData
                    updateData(paramsList);
                } else {
                    // persist
                    persistData(paramsList);
                }
            } else {
                if (existingData) {
                    // Clear data
                    clearData(paramsList);
                }
            }
            if (BlueKai.this.listener != null) {
                BlueKai.this.listener.onDataPosted(success, message);
            }
            if (!this.paramsList.isEmpty()) {
                run();
            }
        }

        @Override
        public void onViewLoaded(boolean success, boolean existingData, ParamsList paramsList) {
            Logger.debug(TAG, "OnViewLoaded() called ... Status --> " + success);
            if (success) {
                onDataPosted(success, "Data posted successfully", existingData, paramsList);
            } else {
                onDataPosted(success, "Problem posting data", existingData, paramsList);
            }
        }
    }

    private void onDataPosted(boolean success, String message, boolean existingData, ParamsList paramsList) {
        Logger.debug(TAG, "OnDataPosted called ... status -> " + success);
        if (paramsList != null) {
            Logger.debug(TAG, "ParamsList --> " + paramsList.size());
        }
        if (!success) {
            if (existingData) {
                // updateData
                updateData(paramsList);
            } else {
                // persist
                persistData(paramsList);
            }
        } else {
            if (existingData) {
                // Clear data
                clearData(paramsList);
            }
        }
        if (this.listener != null) {
            this.listener.onDataPosted(success, message);
        }
    }

    private void clearData(ParamsList paramsList) {
        if (database != null && paramsList != null && !paramsList.isEmpty()) {
            database.clearParams(paramsList);
        }

    }

    private void persistData(ParamsList paramsList) {
        if (database != null && paramsList != null && !paramsList.isEmpty()) {
            database.persistData(paramsList);
        }
    }

    private void updateData(ParamsList paramsList) {
        if (database != null && paramsList != null && !paramsList.isEmpty()) {
            database.updateData(paramsList);
        }
    }

    @Override
    public void onSettingsChanged(Settings settings) {
        Logger.debug(TAG, "On Settings changed");
        this.settings = settings;
        if (settings.isAllowDataPosting()) {
            checkForExistingData();
        }
    }

    private synchronized void showBlueKaiDialog(String url, boolean existingData, ParamsList paramsList,
            BKViewListener listener) {
        if (devMode) {
            BlueKaiViewDialog dialog = new BlueKaiViewDialog();
            dialog.setLoadURL(url, existingData, paramsList);
            dialog.setBKViewListener(listener);
            dialog.show(fManager, "bkdialog");
        } else {
            blueKaiView.loadUrl(url, existingData, paramsList);
        }
    }

    @Override
    public void onViewLoaded(boolean success, boolean existingData, ParamsList paramsList) {
        Logger.debug(TAG, "OnViewLoaded() called ... Status --> " + success);
        if (success) {
            onDataPosted(success, "Data posted successfully", existingData, null);
        } else {
            onDataPosted(success, "Problem posting data", existingData, paramsList);
        }
    }

    private String getImei(Context ctx) {
        String androidId = "";
        try {
            androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
            Logger.debug(TAG, "Android ID --> " + androidId);
        } catch (Exception e1) {
            Logger.error(TAG, "IMEIManager :: Exception in processData" + e1.getMessage());
        }

        if ((androidId == null) || (androidId.equals(""))
                || (androidId.equals("0") || (androidId.startsWith("00000")))) {
            androidId = getPsudeoIMEI();
        }
        return androidId;
    }

    private String getPsudeoIMEI() {
        String imei = "";
        String format = "yyyyMMddHHmmssSSSS";
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        imei = sdf.format(new Date());
        return imei;
    }
}