Java tutorial
// // CleverTapPlugin.java // Copyright (C) 2015 CleverTap // // This code is provided under a commercial License. // A copy of this license has been distributed in a file called LICENSE // with this source code. // // package com.clevertap.cordova; import android.util.Log; import android.location.Location; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaWebView; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CallbackContext; import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Locale; import java.util.Date; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import java.util.ArrayList; import com.clevertap.android.sdk.CleverTapAPI; import com.clevertap.android.sdk.SyncListener; import com.clevertap.android.sdk.EventDetail; import com.clevertap.android.sdk.UTMDetail; import com.clevertap.android.sdk.exceptions.CleverTapMetaDataNotFoundException; import com.clevertap.android.sdk.exceptions.CleverTapPermissionsNotSatisfied; import com.clevertap.android.sdk.exceptions.InvalidEventNameException; public class CleverTapPlugin extends CordovaPlugin implements SyncListener { private static final String LOG_TAG = "CLEVERTAP_PLUGIN"; private static String CLEVERTAP_API_ERROR; private static CleverTapAPI cleverTap; @Override public void initialize(CordovaInterface cordova, CordovaWebView webView) { super.initialize(cordova, webView); try { cleverTap = CleverTapAPI.getInstance(cordova.getActivity().getApplicationContext()); cleverTap.setSyncListener(this); } catch (CleverTapMetaDataNotFoundException e) { CLEVERTAP_API_ERROR = e.getLocalizedMessage(); //Log.d(LOG_TAG, e.getLocalizedMessage()); } catch (CleverTapPermissionsNotSatisfied e) { CLEVERTAP_API_ERROR = e.getLocalizedMessage(); //Log.d(LOG_TAG, e.getLocalizedMessage()); } } @Override public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { Log.d(LOG_TAG, "handling action " + action); boolean haveError = false; String errorMsg = "unhandled CleverTapPlugin action"; PluginResult result = null; if (!checkCleverTapInitialized()) { result = new PluginResult(PluginResult.Status.ERROR, "CleverTap API not initialized"); result.setKeepCallback(true); callbackContext.sendPluginResult(result); return true; } // not required for Android here but handle as its in the JS interface else if (action.equals("registerPush")) { result = new PluginResult(PluginResult.Status.NO_RESULT); result.setKeepCallback(true); callbackContext.sendPluginResult(result); return true; } // not required for Android here but handle as its in the JS interface else if (action.equals("setPushTokenAsString")) { result = new PluginResult(PluginResult.Status.NO_RESULT); result.setKeepCallback(true); callbackContext.sendPluginResult(result); return true; } else if (action.equals("setDebugLevel")) { int level = (args.length() == 1 ? args.getInt(0) : -1); if (level >= 0) { CleverTapAPI.setDebugLevel(level); result = new PluginResult(PluginResult.Status.NO_RESULT); result.setKeepCallback(true); callbackContext.sendPluginResult(result); return true; } } else if (action.equals("enablePersonalization")) { cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.enablePersonalization(); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else if (action.equals("recordEventWithName")) { final String eventName = (args.length() == 1 ? args.getString(0) : null); if (eventName != null) { cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.event.push(eventName); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else { errorMsg = "eventName cannot be null"; } } else if (action.equals("recordEventWithNameAndProps")) { String eventName = null; JSONObject jsonProps; HashMap<String, Object> _props = null; if (args.length() == 2) { if (!args.isNull(0)) { eventName = args.getString(0); } else { haveError = true; errorMsg = "eventName cannot be null"; } if (!args.isNull(1)) { jsonProps = args.getJSONObject(1); try { _props = toMap(jsonProps); } catch (JSONException e) { haveError = true; errorMsg = "Error parsing event properties"; } } else { haveError = true; errorMsg = "Arg cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final String _eventName = eventName; final HashMap<String, Object> props = _props; cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.event.push(_eventName, props); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } } else if (action.equals("recordChargedEventWithDetailsAndItems")) { JSONObject jsonDetails; JSONArray jsonItems; HashMap<String, Object> _details = null; ArrayList<HashMap<String, Object>> _items = null; if (args.length() == 2) { if (!args.isNull(0)) { jsonDetails = args.getJSONObject(0); try { _details = toMap(jsonDetails); } catch (JSONException e) { haveError = true; errorMsg = "Error parsing arg " + e.getLocalizedMessage(); } } else { haveError = true; errorMsg = "Arg cannot be null"; } if (!args.isNull(1)) { jsonItems = args.getJSONArray(1); try { _items = toArrayListOfStringObjectMaps(jsonItems); } catch (JSONException e) { haveError = true; errorMsg = "Error parsing arg " + e.getLocalizedMessage(); } } else { haveError = true; errorMsg = "Arg cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final HashMap<String, Object> details = _details; final ArrayList<HashMap<String, Object>> items = _items; cordova.getThreadPool().execute(new Runnable() { public void run() { try { cleverTap.event.push(CleverTapAPI.CHARGED_EVENT, details, items); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } catch (InvalidEventNameException e) { PluginResult _result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } } }); return true; } } else if (action.equals("eventGetFirstTime")) { final String eventName = (args.length() == 1 ? args.getString(0) : null); if (eventName != null) { cordova.getThreadPool().execute(new Runnable() { public void run() { double first = cleverTap.event.getFirstTime(eventName); PluginResult _result = new PluginResult(PluginResult.Status.OK, (float) first); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else { errorMsg = "eventName cannot be null"; } } else if (action.equals("eventGetLastTime")) { final String eventName = (args.length() == 1 ? args.getString(0) : null); if (eventName != null) { cordova.getThreadPool().execute(new Runnable() { public void run() { double lastTime = cleverTap.event.getLastTime(eventName); PluginResult _result = new PluginResult(PluginResult.Status.OK, (float) lastTime); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else { errorMsg = "eventName cannot be null"; } } else if (action.equals("eventGetOccurrences")) { final String eventName = (args.length() == 1 ? args.getString(0) : null); if (eventName != null) { cordova.getThreadPool().execute(new Runnable() { public void run() { int num = cleverTap.event.getCount(eventName); PluginResult _result = new PluginResult(PluginResult.Status.OK, num); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else { errorMsg = "eventName cannot be null"; } } else if (action.equals("eventGetDetails")) { final String eventName = (args.length() == 1 ? args.getString(0) : null); if (eventName != null) { cordova.getThreadPool().execute(new Runnable() { public void run() { EventDetail details = cleverTap.event.getDetails(eventName); try { JSONObject jsonDetails = CleverTapPlugin.eventDetailsToJSON(details); PluginResult _result = new PluginResult(PluginResult.Status.OK, jsonDetails); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } catch (JSONException e) { PluginResult _result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } } }); return true; } else { errorMsg = "eventName cannot be null"; } } else if (action.equals("getEventHistory")) { cordova.getThreadPool().execute(new Runnable() { public void run() { Map<String, EventDetail> history = cleverTap.event.getHistory(); try { JSONObject jsonDetails = CleverTapPlugin.eventHistoryToJSON(history); PluginResult _result = new PluginResult(PluginResult.Status.OK, jsonDetails); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } catch (JSONException e) { PluginResult _result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } } }); return true; } else if (action.equals("setLocation")) { Double lat = null; Double lon = null; if (args.length() == 2) { if (!args.isNull(0)) { lat = args.getDouble(0); } else { haveError = true; errorMsg = "lat cannot be null"; } if (!args.isNull(1)) { lon = args.getDouble(1); } else { haveError = true; errorMsg = "lon cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final Location location = new Location("CleverTapPlugin"); location.setLatitude(lat); location.setLongitude(lon); cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.updateLocation(location); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } } else if (action.equals("profileSet")) { JSONObject jsonProfile = null; if (args.length() == 1) { if (!args.isNull(0)) { jsonProfile = args.getJSONObject(0); } else { haveError = true; errorMsg = "profile cannot be null"; } } else { haveError = true; errorMsg = "Expected 1 argument"; } if (!haveError) { final JSONObject _jsonProfile = jsonProfile; cordova.getThreadPool().execute(new Runnable() { public void run() { try { HashMap<String, Object> profile = toMap(_jsonProfile); String dob = (String) profile.get("DOB"); if (dob != null) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); try { Date date = format.parse(dob); profile.put("DOB", date); } catch (ParseException e) { profile.remove("DOB"); Log.d(LOG_TAG, "invalid DOB format in profileSet"); } } cleverTap.profile.push(profile); } catch (Exception e) { Log.d(LOG_TAG, "Error setting profile " + e.getLocalizedMessage()); } PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } } else if (action.equals("profileSetGraphUser")) { JSONObject jsonGraphUser = null; if (args.length() == 1) { if (!args.isNull(0)) { jsonGraphUser = args.getJSONObject(0); } else { haveError = true; errorMsg = "profile cannot be null"; } } else { haveError = true; errorMsg = "Expected 1 argument"; } if (!haveError) { final JSONObject _jsonGraphUser = jsonGraphUser; cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.pushFacebookUser(_jsonGraphUser); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } } else if (action.equals("profileSetGooglePlusUser")) { JSONObject jsonGooglePlusUser; HashMap<String, Object> _profile = null; if (args.length() == 1) { if (!args.isNull(0)) { jsonGooglePlusUser = args.getJSONObject(0); try { _profile = toMapFromGooglePlusUser(jsonGooglePlusUser); } catch (JSONException e) { haveError = true; errorMsg = "Error parsing arg " + e.getLocalizedMessage(); } } else { haveError = true; errorMsg = "profile cannot be null"; } } else { haveError = true; errorMsg = "Expected 1 argument"; } if (!haveError) { final HashMap<String, Object> profile = _profile; cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.push(profile); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } } else if (action.equals("profileGetProperty")) { final String propertyName = (args.length() == 1 ? args.getString(0) : null); if (propertyName != null) { cordova.getThreadPool().execute(new Runnable() { public void run() { PluginResult _result; Object prop = cleverTap.profile.getProperty(propertyName); if (prop instanceof JSONArray) { JSONArray _prop = (JSONArray) prop; _result = new PluginResult(PluginResult.Status.OK, _prop); } else { String _prop; if (prop != null) { _prop = prop.toString(); } else { _prop = null; } _result = new PluginResult(PluginResult.Status.OK, _prop); } _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else { errorMsg = "propertyName cannot be null"; } } else if (action.equals("profileGetCleverTapID")) { cordova.getThreadPool().execute(new Runnable() { public void run() { String CleverTapID = cleverTap.getCleverTapID(); PluginResult _result = new PluginResult(PluginResult.Status.OK, CleverTapID); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else if (action.equals("profileRemoveValueForKey")) { final String key = (args.length() == 1 ? args.getString(0) : null); if (key != null) { cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.removeValueForKey(key); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else { errorMsg = "property key cannot be null"; } } else if (action.equals("profileSetMultiValues")) { String key = null; JSONArray values = null; if (args.length() == 2) { if (!args.isNull(0)) { key = args.getString(0); } else { haveError = true; errorMsg = "key cannot be null"; } if (!args.isNull(1)) { values = args.getJSONArray(1); if (values == null) { haveError = true; errorMsg = "values cannot be null"; } } else { haveError = true; errorMsg = "values cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final String _key = key; final ArrayList<String> _values = new ArrayList<String>(); try { for (int i = 0; i < values.length(); i++) { _values.add(values.get(i).toString()); } cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.setMultiValuesForKey(_key, _values); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } catch (Exception e) { // no-op } } } else if (action.equals("profileAddMultiValues")) { String key = null; JSONArray values = null; if (args.length() == 2) { if (!args.isNull(0)) { key = args.getString(0); } else { haveError = true; errorMsg = "key cannot be null"; } if (!args.isNull(1)) { values = args.getJSONArray(1); if (values == null) { haveError = true; errorMsg = "values cannot be null"; } } else { haveError = true; errorMsg = "values cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final String _key = key; final ArrayList<String> _values = new ArrayList<String>(); try { for (int i = 0; i < values.length(); i++) { _values.add(values.get(i).toString()); } cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.addMultiValuesForKey(_key, _values); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } catch (Exception e) { // no-op } } } else if (action.equals("profileRemoveMultiValues")) { String key = null; JSONArray values = null; if (args.length() == 2) { if (!args.isNull(0)) { key = args.getString(0); } else { haveError = true; errorMsg = "key cannot be null"; } if (!args.isNull(1)) { values = args.getJSONArray(1); if (values == null) { haveError = true; errorMsg = "values cannot be null"; } } else { haveError = true; errorMsg = "values cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final String _key = key; final ArrayList<String> _values = new ArrayList<String>(); try { for (int i = 0; i < values.length(); i++) { _values.add(values.get(i).toString()); } cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.removeMultiValuesForKey(_key, _values); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } catch (Exception e) { // no-op } } } else if (action.equals("profileAddMultiValue")) { String key = null; String value = null; if (args.length() == 2) { if (!args.isNull(0)) { key = args.getString(0); } else { haveError = true; errorMsg = "key cannot be null"; } if (!args.isNull(1)) { value = args.getString(1); } else { haveError = true; errorMsg = "value cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final String _key = key; final String _value = value; cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.addMultiValueForKey(_key, _value); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } } else if (action.equals("profileRemoveMultiValue")) { String key = null; String value = null; if (args.length() == 2) { if (!args.isNull(0)) { key = args.getString(0); } else { haveError = true; errorMsg = "key cannot be null"; } if (!args.isNull(1)) { value = args.getString(1); } else { haveError = true; errorMsg = "value cannot be null"; } } else { haveError = true; errorMsg = "Expected 2 arguments"; } if (!haveError) { final String _key = key; final String _value = value; cordova.getThreadPool().execute(new Runnable() { public void run() { cleverTap.profile.removeMultiValueForKey(_key, _value); PluginResult _result = new PluginResult(PluginResult.Status.NO_RESULT); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } } else if (action.equals("sessionGetTimeElapsed")) { cordova.getThreadPool().execute(new Runnable() { public void run() { int time = cleverTap.session.getTimeElapsed(); PluginResult _result = new PluginResult(PluginResult.Status.OK, time); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else if (action.equals("sessionGetTotalVisits")) { cordova.getThreadPool().execute(new Runnable() { public void run() { int count = cleverTap.session.getTotalVisits(); PluginResult _result = new PluginResult(PluginResult.Status.OK, count); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else if (action.equals("sessionGetScreenCount")) { cordova.getThreadPool().execute(new Runnable() { public void run() { int count = cleverTap.session.getScreenCount(); PluginResult _result = new PluginResult(PluginResult.Status.OK, count); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else if (action.equals("sessionGetPreviousVisitTime")) { cordova.getThreadPool().execute(new Runnable() { public void run() { int time = cleverTap.session.getPreviousVisitTime(); PluginResult _result = new PluginResult(PluginResult.Status.OK, time); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } }); return true; } else if (action.equals("sessionGetUTMDetails")) { cordova.getThreadPool().execute(new Runnable() { public void run() { UTMDetail details = cleverTap.session.getUTMDetails(); try { JSONObject jsonDetails = CleverTapPlugin.utmDetailsToJSON(details); PluginResult _result = new PluginResult(PluginResult.Status.OK, jsonDetails); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } catch (JSONException e) { PluginResult _result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); _result.setKeepCallback(true); callbackContext.sendPluginResult(_result); } } }); return true; } result = new PluginResult(PluginResult.Status.ERROR, errorMsg); result.setKeepCallback(true); callbackContext.sendPluginResult(result); return true; } /******************* * Private Methods ******************/ // SyncListener public void profileDataUpdated(JSONObject updates) { if (updates == null) { return; } final String json = "{'updates':" + updates.toString() + "}"; webView.getView().post(new Runnable() { public void run() { webView.loadUrl("javascript:cordova.fireDocumentEvent('onCleverTapProfileSync'," + json + ");"); } }); } public void profileDidInitialize(String CleverTapID) { if (CleverTapID == null) { return; } final String json = "{'CleverTapID':" + CleverTapID + "}"; webView.getView().post(new Runnable() { public void run() { webView.loadUrl( "javascript:cordova.fireDocumentEvent('onCleverTapProfileDidInitialize'," + json + ");"); } }); } private static boolean checkCleverTapInitialized() { boolean initialized = (cleverTap != null); if (!initialized) { Log.d(LOG_TAG, "CleverTap API not initialized: " + CLEVERTAP_API_ERROR); } return initialized; } private static Object fromJson(Object json) throws JSONException { if (json == JSONObject.NULL) { return null; } else if (json instanceof JSONObject) { return toMap((JSONObject) json); } else { return json; } } private static HashMap<String, Object> toMap(JSONObject object) throws JSONException { HashMap<String, Object> map = new HashMap<String, Object>(); Iterator keys = object.keys(); while (keys.hasNext()) { String key = (String) keys.next(); map.put(key, fromJson(object.get(key))); } return map; } private static HashMap<String, Object> toMapFromGooglePlusUser(JSONObject object) throws JSONException { HashMap<String, Object> map = new HashMap<String, Object>(); JSONObject nameObj = (JSONObject) object.get("name"); if (nameObj != null) { String name = (String) nameObj.get("formatted"); if (name != null) { map.put("gpName", name); } } String id1 = (String) object.get("id"); if (id1 != null) { map.put("gpID", id1); } String gender1 = (String) object.get("gender"); if (gender1 != null) { if (gender1.toLowerCase().startsWith("m")) { gender1 = "M"; } else if (gender1.toLowerCase().startsWith("f")) { gender1 = "F"; } else { gender1 = ""; } map.put("gpGender", gender1); } JSONArray organizations = (JSONArray) object.get("organizations"); if (organizations != null) { String work1 = "N"; for (int i = 0; i < organizations.length(); i++) { JSONObject org = organizations.getJSONObject(i); String type = (String) org.get("type"); if (type.equals("work")) { work1 = "Y"; break; } } map.put("gpEmployed", work1); } String birthday = (String) object.get("birthday"); if (birthday != null) { Date DOB = null; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); try { DOB = format.parse(birthday); } catch (ParseException e) { Log.d(LOG_TAG, "invalid DOB format"); } if (DOB != null) { map.put("gpDOB", DOB); } } String relationshipStatus = (String) object.get("relationshipStatus"); if (relationshipStatus != null) { String married1 = relationshipStatus.equals("married") ? "Y" : "N"; map.put("gpRS", married1); } return map; } private static ArrayList<HashMap<String, Object>> toArrayListOfStringObjectMaps(JSONArray array) throws JSONException { ArrayList<HashMap<String, Object>> aList = new ArrayList<HashMap<String, Object>>(); for (int i = 0; i < array.length(); i++) { aList.add(toMap((JSONObject) array.get(i))); } return aList; } private static JSONObject eventDetailsToJSON(EventDetail details) throws JSONException { JSONObject json = new JSONObject(); if (details != null) { json.put("name", details.getName()); json.put("firstTime", details.getFirstTime()); json.put("lastTime", details.getLastTime()); json.put("count", details.getCount()); } return json; } private static JSONObject utmDetailsToJSON(UTMDetail details) throws JSONException { JSONObject json = new JSONObject(); if (details != null) { json.put("campaign", details.getCampaign()); json.put("source", details.getSource()); json.put("medium", details.getMedium()); } return json; } private static JSONObject eventHistoryToJSON(Map<String, EventDetail> history) throws JSONException { JSONObject json = new JSONObject(); if (history != null) { for (Object key : history.keySet()) { json.put(key.toString(), eventDetailsToJSON(history.get((String) key))); } } return json; } }