diff options
Diffstat (limited to 'mobile/android/thirdparty/com/keepsafe/switchboard')
5 files changed, 0 insertions, 691 deletions
diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/AsyncConfigLoader.java b/mobile/android/thirdparty/com/keepsafe/switchboard/AsyncConfigLoader.java deleted file mode 100644 index 2cff4b4c3..000000000 --- a/mobile/android/thirdparty/com/keepsafe/switchboard/AsyncConfigLoader.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright 2012 KeepSafe Software 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.keepsafe.switchboard; - - -import android.content.Context; -import android.os.AsyncTask; - -/** - * An async loader to load user config in background thread based on internal generated UUID. - * - * Call <code>AsyncConfigLoader.execute()</code> to load SwitchBoard.loadConfig() with own ID. - * To use your custom UUID call <code>AsyncConfigLoader.execute(uuid)</code> with uuid being your unique user id - * as a String - * - * @author Philipp Berner - * - */ -public class AsyncConfigLoader extends AsyncTask<Void, Void, Void> { - - private Context context; - private String defaultServerUrl; - - /** - * Sets the params for async loading either SwitchBoard.updateConfigServerUrl() - * or SwitchBoard.loadConfig. - * Loads config with a custom UUID - * @param c Application context - * @param defaultServerUrl Default URL endpoint for Switchboard config. - */ - public AsyncConfigLoader(Context c, String defaultServerUrl) { - this.context = c; - this.defaultServerUrl = defaultServerUrl; - } - - @Override - protected Void doInBackground(Void... params) { - SwitchBoard.loadConfig(context, defaultServerUrl); - return null; - } -} diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/DeviceUuidFactory.java b/mobile/android/thirdparty/com/keepsafe/switchboard/DeviceUuidFactory.java deleted file mode 100644 index c4476d2cd..000000000 --- a/mobile/android/thirdparty/com/keepsafe/switchboard/DeviceUuidFactory.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright 2012 KeepSafe Software 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.keepsafe.switchboard; - -import java.util.UUID; - -import android.content.Context; -import android.content.SharedPreferences; - -/** - * Generates a UUID and stores is persistent as in the apps shared preferences. - * - * @author Philipp Berner - */ -public class DeviceUuidFactory { - protected static final String PREFS_FILE = "com.keepsafe.switchboard.uuid"; - protected static final String PREFS_DEVICE_ID = "device_id"; - - private static UUID uuid = null; - - public DeviceUuidFactory(Context context) { - if (uuid == null) { - synchronized (DeviceUuidFactory.class) { - if (uuid == null) { - final SharedPreferences prefs = context - .getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE); - final String id = prefs.getString(PREFS_DEVICE_ID, null); - - if (id != null) { - // Use the ids previously computed and stored in the prefs file - uuid = UUID.fromString(id); - } else { - uuid = UUID.randomUUID(); - - // Write the value out to the prefs file - prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString()).apply(); - } - } - } - } - } - - /** - * Returns a unique UUID for the current android device. As with all UUIDs, - * this unique ID is "very highly likely" to be unique across all Android - * devices. Much more so than ANDROID_ID is. - * - * The UUID is generated with <code>UUID.randomUUID()</code>. - * - * @return a UUID that may be used to uniquely identify your device for most - * purposes. - */ - public UUID getDeviceUuid() { - return uuid; - } - -}
\ No newline at end of file diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/Preferences.java b/mobile/android/thirdparty/com/keepsafe/switchboard/Preferences.java deleted file mode 100644 index f7f6f7cb7..000000000 --- a/mobile/android/thirdparty/com/keepsafe/switchboard/Preferences.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright 2012 KeepSafe Software 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.keepsafe.switchboard; - - -import android.content.Context; -import android.content.SharedPreferences; -import android.support.annotation.Nullable; - -/** - * Application preferences for SwitchBoard. - * @author Philipp Berner - * - */ -public class Preferences { - - private static final String switchBoardSettings = "com.keepsafe.switchboard.settings"; - - private static final String CONFIG_JSON = "config-json"; - private static final String OVERRIDE_PREFIX = "experiment.override."; - - - /** - * Gets the user config as a JSON string. - * @param c Context - * @return Config JSON - */ - @Nullable public static String getDynamicConfigJson(Context c) { - final SharedPreferences prefs = c.getApplicationContext().getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE); - return prefs.getString(CONFIG_JSON, null); - } - - /** - * Saves the user config as a JSON sting. - * @param c Context - * @param configJson Config JSON - */ - public static void setDynamicConfigJson(Context c, String configJson) { - final SharedPreferences.Editor editor = c.getApplicationContext(). - getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE).edit(); - editor.putString(CONFIG_JSON, configJson); - editor.apply(); - } - - /** - * Gets the override value for an experiment. - * - * @param c Context - * @param experimentName Experiment name - * @return Whether or not the experiment should be enabled, or null if there is no override. - */ - @Nullable public static Boolean getOverrideValue(Context c, String experimentName) { - final SharedPreferences prefs = c.getApplicationContext(). - getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE); - - final String key = OVERRIDE_PREFIX + experimentName; - if (prefs.contains(key)) { - // This will never fall back to the default value. - return prefs.getBoolean(key, false); - } - - // Default to returning null if no override was found. - return null; - } - - /** - * Saves an override value for an experiment. - * - * @param c Context - * @param experimentName Experiment name - * @param isEnabled Whether or not to enable the experiment - */ - public static void setOverrideValue(Context c, String experimentName, boolean isEnabled) { - final SharedPreferences.Editor editor = c.getApplicationContext(). - getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE).edit(); - editor.putBoolean(OVERRIDE_PREFIX + experimentName, isEnabled); - editor.apply(); - } - - /** - * Clears the override value for an experiment. - * - * @param c Context - * @param experimentName Experiment name - */ - public static void clearOverrideValue(Context c, String experimentName) { - final SharedPreferences.Editor editor = c.getApplicationContext(). - getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE).edit(); - editor.remove(OVERRIDE_PREFIX + experimentName); - editor.apply(); - } -} diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/Switch.java b/mobile/android/thirdparty/com/keepsafe/switchboard/Switch.java deleted file mode 100644 index 5307750bb..000000000 --- a/mobile/android/thirdparty/com/keepsafe/switchboard/Switch.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - Copyright 2012 KeepSafe Software 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.keepsafe.switchboard; - -import org.json.JSONObject; - -import android.content.Context; - -/** - * Single instance of an existing experiment for easier and cleaner code. - * - * @author Philipp Berner - * - */ -public class Switch { - - private Context context; - private String experimentName; - - /** - * Creates an instance of a single experiment to give more convenient access to its values. - * When the given experiment does not exist, it will give back default valued that can be found - * in <code>Switchboard</code>. Developer has to know that experiment exists when using it. - * @param c Application context - * @param experimentName Name of the experiment as defined on the server - */ - public Switch(Context c, String experimentName) { - this.context = c; - this.experimentName = experimentName; - } - - /** - * Returns true if the experiment is active for this particular user. - * @return Status of the experiment and false when experiment does not exist. - */ - public boolean isActive() { - return SwitchBoard.isInExperiment(context, experimentName); - } - - /** - * Returns true if the experiment has additional values. - * @return true when values exist - */ - public boolean hasValues() { - return SwitchBoard.hasExperimentValues(context, experimentName); - } - - /** - * Gives back all the experiment values in a JSONObject. This function checks if - * values exists. If no values exist, it returns null. - * @return Values in JSONObject or null if non - */ - public JSONObject getValues() { - if(hasValues()) - return SwitchBoard.getExperimentValuesFromJson(context, experimentName); - else - return null; - } -} diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java b/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java deleted file mode 100644 index e99144045..000000000 --- a/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - Copyright 2012 KeepSafe Software 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.keepsafe.switchboard; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.MissingResourceException; -import java.util.zip.CRC32; - -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONArray; - -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - - -/** - * SwitchBoard is the core class of the KeepSafe Switchboard mobile A/B testing framework. - * This class provides a bunch of static methods that can be used in your app to run A/B tests. - * - * The SwitchBoard supports production and staging environment. - * - * For usage <code>initDefaultServerUrls</code> for first time usage. Server URLs can be updates from - * a remote location with <code>initConfigServerUrl</code>. - * - * To run a experiment use <code>isInExperiment()</code>. The experiment name has to match the one you - * setup on the server. - * All functions are design to be safe for programming mistakes and network connection issues. If the - * experiment does not exists it will return false and pretend the user is not part of it. - * - * @author Philipp Berner - * - */ -public class SwitchBoard { - - private static final String TAG = "SwitchBoard"; - - /** Set if the application is run in debug mode. */ - public static boolean DEBUG = true; - - // Top-level experiment keys. - private static final String KEY_DATA = "data"; - private static final String KEY_NAME = "name"; - private static final String KEY_MATCH = "match"; - private static final String KEY_BUCKETS = "buckets"; - private static final String KEY_VALUES = "values"; - - // Match keys. - private static final String KEY_APP_ID = "appId"; - private static final String KEY_COUNTRY = "country"; - private static final String KEY_DEVICE = "device"; - private static final String KEY_LANG = "lang"; - private static final String KEY_MANUFACTURER = "manufacturer"; - private static final String KEY_VERSION = "version"; - - // Bucket keys. - private static final String KEY_MIN = "min"; - private static final String KEY_MAX = "max"; - - /** - * Loads a new config for a user. This method does network I/O, so it - * should not be called on the main thread. - * - * @param c ApplicationContext - * @param serverUrl Server URL endpoint. - */ - static void loadConfig(Context c, @NonNull String serverUrl) { - final URL url; - try { - url = new URL(serverUrl); - } catch (MalformedURLException e) { - Log.e(TAG, "Exception creating server URL", e); - return; - } - - final String result = readFromUrlGET(url); - if (DEBUG) Log.d(TAG, "Result: " + result); - if (result == null) { - return; - } - - // Cache result locally in shared preferences. - Preferences.setDynamicConfigJson(c, result); - } - - public static boolean isInBucket(Context c, int low, int high) { - final int userBucket = getUserBucket(c); - return (userBucket >= low) && (userBucket < high); - } - - /** - * Looks up in config if user is in certain experiment. Returns false as a default value when experiment - * does not exist. - * Experiment names are defined server side as Key in array for return values. - * @param experimentName Name of the experiment to lookup - * @return returns value for experiment or false if experiment does not exist. - */ - public static boolean isInExperiment(Context c, String experimentName) { - final Boolean override = Preferences.getOverrideValue(c, experimentName); - if (override != null) { - return override; - } - - final String config = Preferences.getDynamicConfigJson(c); - if (config == null) { - return false; - } - - try { - // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key - final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA); - JSONObject experiment = null; - - for (int i = 0; i < experiments.length(); i++) { - JSONObject entry = experiments.getJSONObject(i); - final String name = entry.getString(KEY_NAME); - if (name.equals(experimentName)) { - experiment = entry; - break; - } - } - - if (experiment == null) { - return false; - } - - if (!isMatch(c, experiment.optJSONObject(KEY_MATCH))) { - return false; - } - - final JSONObject buckets = experiment.getJSONObject(KEY_BUCKETS); - final boolean inExperiment = isInBucket(c, buckets.getInt(KEY_MIN), buckets.getInt(KEY_MAX)); - - if (DEBUG) { - Log.d(TAG, experimentName + " = " + inExperiment); - } - return inExperiment; - } catch (JSONException e) { - // If the experiment name is not found in the JSON, just return false. - // There is no need to log an error, since we don't really care if an - // inactive experiment is missing from the config. - return false; - } - } - - private static List<String> getExperimentNames(Context c) throws JSONException { - // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key - final List<String> returnList = new ArrayList<>(); - final String config = Preferences.getDynamicConfigJson(c); - final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA); - - for (int i = 0; i < experiments.length(); i++) { - JSONObject entry = experiments.getJSONObject(i); - returnList.add(entry.getString(KEY_NAME)); - } - return returnList; - } - - @Nullable - private static JSONObject getExperiment(Context c, String experimentName) throws JSONException { - // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key - final String config = Preferences.getDynamicConfigJson(c); - final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA); - JSONObject experiment = null; - - for (int i = 0; i < experiments.length(); i++) { - JSONObject entry = experiments.getJSONObject(i); - if (entry.getString(KEY_NAME).equals(experimentName)) { - experiment = entry; - break; - } - } - return experiment; - } - - private static boolean isMatch(Context c, @Nullable JSONObject matchKeys) { - // If no match keys are specified, default to enabling the experiment. - if (matchKeys == null) { - return true; - } - - if (matchKeys.has(KEY_APP_ID)) { - final String packageName = c.getPackageName(); - try { - if (!packageName.matches(matchKeys.getString(KEY_APP_ID))) { - return false; - } - } catch (JSONException e) { - Log.e(TAG, "Exception matching appId", e); - } - } - - if (matchKeys.has(KEY_COUNTRY)) { - try { - final String country = Locale.getDefault().getISO3Country(); - if (!country.matches(matchKeys.getString(KEY_COUNTRY))) { - return false; - } - } catch (MissingResourceException|JSONException e) { - Log.e(TAG, "Exception matching country", e); - } - } - - if (matchKeys.has(KEY_DEVICE)) { - final String device = Build.DEVICE; - try { - if (!device.matches(matchKeys.getString(KEY_DEVICE))) { - return false; - } - } catch (JSONException e) { - Log.e(TAG, "Exception matching device", e); - } - - } - if (matchKeys.has(KEY_LANG)) { - try { - final String lang = Locale.getDefault().getISO3Language(); - if (!lang.matches(matchKeys.getString(KEY_LANG))) { - return false; - } - } catch (MissingResourceException|JSONException e) { - Log.e(TAG, "Exception matching lang", e); - } - } - if (matchKeys.has(KEY_MANUFACTURER)) { - final String manufacturer = Build.MANUFACTURER; - try { - if (!manufacturer.matches(matchKeys.getString(KEY_MANUFACTURER))) { - return false; - } - } catch (JSONException e) { - Log.e(TAG, "Exception matching manufacturer", e); - } - } - - if (matchKeys.has(KEY_VERSION)) { - try { - final String version = c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName; - if (!version.matches(matchKeys.getString(KEY_VERSION))) { - return false; - } - } catch (NameNotFoundException|JSONException e) { - Log.e(TAG, "Exception matching version", e); - } - } - - // Default to return true if no matches failed. - return true; - } - - /** - * @return a list of all active experiments. - */ - public static List<String> getActiveExperiments(Context c) { - final List<String> returnList = new ArrayList<>(); - - final String config = Preferences.getDynamicConfigJson(c); - if (config == null) { - return returnList; - } - - try { - final JSONObject data = new JSONObject(config); - final List<String> experiments = getExperimentNames(c); - - for (int i = 0; i < experiments.size(); i++) { - final String name = experiments.get(i); - - // Check override value before reading saved JSON. - Boolean isActive = Preferences.getOverrideValue(c, name); - if (isActive == null) { - // TODO: This is inefficient because it will check all the match cases on all experiments. - isActive = isInExperiment(c, name); - } - if (isActive) { - returnList.add(name); - } - } - } catch (JSONException e) { - // Something went wrong! - } - - return returnList; - } - - /** - * Checks if a certain experiment has additional values. - * @param c ApplicationContext - * @param experimentName Name of the experiment - * @return true when experiment exists - */ - public static boolean hasExperimentValues(Context c, String experimentName) { - return getExperimentValuesFromJson(c, experimentName) != null; - } - - /** - * Returns the experiment value as a JSONObject. - * @param experimentName Name of the experiment - * @return Experiment value as String, null if experiment does not exist. - */ - @Nullable - public static JSONObject getExperimentValuesFromJson(Context c, String experimentName) { - final String config = Preferences.getDynamicConfigJson(c); - - if (config == null) { - return null; - } - - try { - final JSONObject experiment = getExperiment(c, experimentName); - if (experiment == null) { - return null; - } - return experiment.getJSONObject(KEY_VALUES); - } catch (JSONException e) { - Log.e(TAG, "Could not create JSON object from config string", e); - } - - return null; - } - - /** - * Returns a String containing the server response from a GET request - * @param url URL for GET request. - * @return Returns String from server or null when failed. - */ - @Nullable private static String readFromUrlGET(URL url) { - try { - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setUseCaches(false); - - InputStream is = connection.getInputStream(); - InputStreamReader inputStreamReader = new InputStreamReader(is); - BufferedReader bufferReader = new BufferedReader(inputStreamReader, 8192); - String line; - StringBuilder resultContent = new StringBuilder(); - while ((line = bufferReader.readLine()) != null) { - resultContent.append(line); - } - bufferReader.close(); - - return resultContent.toString(); - } catch (IOException e) { - e.printStackTrace(); - } - - return null; - } - - /** - * Return the bucket number of the user. There are 100 possible buckets. - */ - private static int getUserBucket(Context c) { - final DeviceUuidFactory df = new DeviceUuidFactory(c); - final String uuid = df.getDeviceUuid().toString(); - - CRC32 crc = new CRC32(); - crc.update(uuid.getBytes()); - long checksum = crc.getValue(); - return (int)(checksum % 100L); - } -} |