You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tasks/astrid/common-src/com/localytics/android/DatapointHelper.java

325 lines
12 KiB
Java

/**
* DatapointHelper.java
* Copyright (C) 2009 Char Software Inc., DBA Localytics
*
* This code is provided under the Localytics Modified BSD License.
* A copy of this license has been distributed in a file called LICENSE
* with this source code.
*
* Please visit www.localytics.com for more information.
*/
package com.localytics.android;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.provider.Settings.System;
import android.telephony.TelephonyManager;
import android.util.Log;
/**
* Provides a number of static functions to aid in the collection and formatting
* of datapoints.
* @author Localytics
*/
@SuppressWarnings("nls")
public final class DatapointHelper
{
// This class should never be instantiated
private DatapointHelper() { /**/ }
private static final String LOG_PREFIX = "(DatapointHelper) ";
private static final String DROID2_ANDROID_ID = "9774d56d682e549c";
// Each YAML entry either goes to the session controller, the event controller
// or the optin controller
public static final String CONTROLLER_SESSION = "- c: se\n";
public static final String CONTROLLER_EVENT = "- c: ev\n";
public static final String CONTROLLER_OPT = "- c: optin\n";
// Each entry is either a create action, or an update action
public static final String ACTION_CREATE = " a: c\n";
public static final String ACTION_UPDATE = " a: u\n";
public static final String ACTION_OPTIN = " a: optin\n";
// The target object for the data being set up.
public static final String OBJECT_SESSION_DP = " se:\n";
public static final String OBJECT_EVENT_DP = " ev:\n";
public static final String OBJECT_OPT = " optin:\n";
// Events can have attributes
public static final String EVENT_ATTRIBUTE = " attrs:\n";
/*******************************
* WEBSERVICE PARAMENTER NAMES *
* *****************************
*/
// Every object has a UUID
public static final String PARAM_UUID = "u";
// The identifier for this application, generated by the user on the webservice
public static final String PARAM_APP_UUID = "au";
// The version of this application, taken from the application's manifest.
public static final String PARAM_APP_VERSION = "av";
// A session's UUID as previously created.
public static final String PARAM_SESSION_UUID = "su";
// A hashed identifier unique to this device
public static final String PARAM_DEVICE_UUID = "du";
// android, iphone, blackberry, windowsmobile
public static final String PARAM_DEVICE_PLATFORM = "dp";
// maker of this device (currently not supported by Android)
public static final String PARAM_DEVICE_MAKE = "dma";
// model of the device
public static final String PARAM_DEVICE_MODEL = "dmo";
// version of the OS on this device
public static final String PARAM_OS_VERSION = "dov";
// country device is from (obtained by querying the SIM card)
public static final String PARAM_DEVICE_COUNTRY = "dc";
// country the current locale is set to
public static final String PARAM_LOCALE_COUNTRY = "dlc";
// country the language is set to
public static final String PARAM_LOCALE_LANGUAGE = "dll";
// Locale as a language_country string. (Not collected because this info
// is already provided by LOCALE_LANGUAGE and LOCALE_COUNTRY.
public static final String PARAM_LOCALE = "dl";
// Country the user is currently in (comes from Sim card)
public static final String PARAM_NETWORK_COUNTRY = "nc";
// Current carrier (comes from sim card)
public static final String PARAM_NETWORK_CARRIER = "nca";
// Current mobile network code (comes from sim card)
public static final String PARAM_NETWORK_MNC = "mnc";
// current mobile country code (comes from sim card)
public static final String PARAM_NETWORK_MCC = "mcc";
// type of data connection (wifi, umts, gprs, evdo, ...)
public static final String PARAM_DATA_CONNECTION = "dac";
// the version of this Localytics client library
public static final String PARAM_LIBRARY_VERSION = "lv";
// The source where the location came from
public static final String PARAM_LOCATION_SOURCE = "ls";
// the latitude returned by the location provider
public static final String PARAM_LOCATION_LAT = "lat";
// the longitude from the location provider
public static final String PARAM_LOCATION_LNG = "lng";
// the current time on the user's device
public static final String PARAM_CLIENT_TIME = "ct";
// sent at closing time, the current time on the users's device
public static final String PARAM_CLIENT_CLOSED_TIME = "ctc";
// The name an event that occured
public static final String PARAM_EVENT_NAME = "n";
// the optin value sent in if a user opts in or out.
public static final String PARAM_OPT_VALUE = "optin";
/**
* Returns the given key/value pair as a YAML string. This string is intended to be
* used to define values for the first level of data in the YAML file. This is
* different from the datapoints which belong another level in.
* @param paramName The name of the parameter
* @param paramValue The value of the parameter
* @param paramIndent The indent level of the parameter
* @return a YAML string which can be dumped to the YAML file
*/
public static String formatYAMLLine(String paramName,String paramValue, int paramIndent)
{
if (paramName.length() > LocalyticsSession.MAX_NAME_LENGTH)
{
Log.v(DatapointHelper.LOG_PREFIX, "Parameter name exceeds "
+ LocalyticsSession.MAX_NAME_LENGTH + " character limit. Truncating.");
paramName = paramName.substring(0, LocalyticsSession.MAX_NAME_LENGTH);
}
if (paramValue.length() > LocalyticsSession.MAX_NAME_LENGTH)
{
Log.v(DatapointHelper.LOG_PREFIX, "Parameter value exceeds "
+ LocalyticsSession.MAX_NAME_LENGTH + " character limit. Truncating.");
paramValue = paramValue.substring(0, LocalyticsSession.MAX_NAME_LENGTH);
}
// The params are stored in the second tier of the YAML data.
// so with spacing, the expected result is: " paramname:paramvalue\n"
StringBuffer formattedString = new StringBuffer();
for (int currentIndent = 0; currentIndent < paramIndent; currentIndent++)
{
formattedString.append(" ");
}
formattedString.append(escapeString(paramName));
formattedString.append(": ");
// Escape the string.
formattedString.append(escapeString(paramValue));
formattedString.append("\n");
return formattedString.toString();
}
/**
* Gets a 1-way hashed value of the device's unique ID. This value is encoded using a SHA-256
* one way hash and cannot be used to determine what device this data came from.
* @param appContext The context used to access the settings resolver
* @return An 1-way hashed identifier unique to this device or null if an ID, or the hashing
* algorithm is not available.
*/
public static String getGlobalDeviceId(final Context appContext)
{
String systemId = System.getString(appContext.getContentResolver(), System.ANDROID_ID);
if(systemId == null || systemId.toLowerCase().equals(DROID2_ANDROID_ID))
{
return null;
}
try
{
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(systemId.getBytes());
BigInteger hashedNumber = new BigInteger(1, digest);
return new String(hashedNumber.toString(16));
}
catch(NoSuchAlgorithmException e)
{
return null;
}
}
/**
* Determines the type of network this device is connected to.
* @param appContext the context used to access the device's WIFI
* @param telephonyManager The manager used to access telephony info
* @return The type of network, or unknown if the information is unavailable
*/
public static String getNetworkType(
final Context appContext,
TelephonyManager telephonyManager)
{
WifiManager wifiManager = (WifiManager)appContext.getSystemService(Context.WIFI_SERVICE);
// this will only work for apps which already have wifi permissions.
try
{
if(wifiManager.isWifiEnabled())
{
return "wifi";
}
}
catch (Exception e) { /**/ }
switch (telephonyManager.getNetworkType())
{
case TelephonyManager.NETWORK_TYPE_EDGE : return "edge";
case TelephonyManager.NETWORK_TYPE_GPRS : return "GPRS";
case TelephonyManager.NETWORK_TYPE_UMTS : return "UMTS";
case TelephonyManager.NETWORK_TYPE_UNKNOWN : return "unknown";
}
return "none";
}
/**
* Gets the pretty string for this application's version.
* @param appContext The context used to examine packages
* @return The application's version as a pretty string
*/
public static String getAppVersion(final Context appContext)
{
PackageManager pm = appContext.getPackageManager();
try
{
return pm.getPackageInfo(appContext.getPackageName(), 0).versionName;
}
catch (PackageManager.NameNotFoundException e)
{
return "unknown";
}
}
/**
* Gets the current time, along with local timezone, formatted as a DateTime for the webservice.
* @return a DateTime of the current local time and timezone.
*/
public static String getTimeAsDatetime()
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss-00:00");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(new Date());
}
/***************************
* Private Helper Functions *
***************************/
/**
* Escapes strings for YAML parser
* @param rawString The string we want to escape.
* @return An escaped string ready for YAML
*/
private static String escapeString(String rawString)
{
StringBuffer parseString = new StringBuffer("\"");
int startRead = 0; // Index to start reading at
int stopRead = 0; // Index characters get read from and where the substring ends
int bufferLength = rawString == null ? 0 : rawString.length();
if (rawString == null)
{
return "";
}
// Every time we come across a " or \, append what we have so far, append a \,
// then manage our indexes to continue where we left off.
while (stopRead < bufferLength)
{
if (rawString.charAt(stopRead) == '\"' || rawString.charAt(stopRead) == '\\')
{
parseString.append(rawString.substring(startRead, stopRead));
parseString.append('\\');
startRead = stopRead;
}
// Skip null characters.
else if (rawString.charAt(stopRead) == '\0')
{
parseString.append(rawString.substring(startRead, stopRead));
startRead = stopRead + 1;
}
stopRead++;
}
// Append whatever is left after parsing
parseString.append(rawString.substring(startRead, stopRead));
// and finish with a closing "
parseString.append('\"');
return parseString.toString();
}
}