Commit 67dc0227dc220eac264ae5b381bf94be6857ae00

Authored by 赵康
0 parents
Exists in master

first commit

Showing 145 changed files with 5193 additions and 0 deletions Side-by-side Diff

... ... @@ -0,0 +1,4 @@
  1 +/TraceSDK/bin
  2 +/TraceSDK/gen
  3 +/TraceSDKSample/bin
  4 +/TraceSDKSample/gen
0 5 \ No newline at end of file
... ... @@ -0,0 +1,9 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<classpath>
  3 + <classpathentry kind="src" path="src"/>
  4 + <classpathentry kind="src" path="gen"/>
  5 + <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
  6 + <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
  7 + <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
  8 + <classpathentry kind="output" path="bin/classes"/>
  9 +</classpath>
... ... @@ -0,0 +1,14 @@
  1 +#Fat Jar Configuration File
  2 +#Wed Sep 03 15:36:15 CST 2014
  3 +onejar.license.required=true
  4 +manifest.classpath=
  5 +manifest.removesigners=true
  6 +onejar.checkbox=false
  7 +jarname=TraceSDK.jar
  8 +manifest.mergeall=true
  9 +manifest.mainclass=
  10 +manifest.file=<createnew>
  11 +jarname.isextern=false
  12 +onejar.expand=
  13 +excludes=<po|TraceSDK>~com/~mobithink/~tracksdk/;<jar|android-support-v4.jar>
  14 +includes=
... ... @@ -0,0 +1,33 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<projectDescription>
  3 + <name>TraceSDK</name>
  4 + <comment></comment>
  5 + <projects>
  6 + </projects>
  7 + <buildSpec>
  8 + <buildCommand>
  9 + <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
  10 + <arguments>
  11 + </arguments>
  12 + </buildCommand>
  13 + <buildCommand>
  14 + <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
  15 + <arguments>
  16 + </arguments>
  17 + </buildCommand>
  18 + <buildCommand>
  19 + <name>org.eclipse.jdt.core.javabuilder</name>
  20 + <arguments>
  21 + </arguments>
  22 + </buildCommand>
  23 + <buildCommand>
  24 + <name>com.android.ide.eclipse.adt.ApkBuilder</name>
  25 + <arguments>
  26 + </arguments>
  27 + </buildCommand>
  28 + </buildSpec>
  29 + <natures>
  30 + <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
  31 + <nature>org.eclipse.jdt.core.javanature</nature>
  32 + </natures>
  33 +</projectDescription>
TraceSDK/AndroidManifest.xml
... ... @@ -0,0 +1,17 @@
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2 + package="com.mobithink.tracksdk"
  3 + android:versionCode="1"
  4 + android:versionName="1.0" >
  5 +
  6 + <uses-sdk
  7 + android:minSdkVersion="8"
  8 + android:targetSdkVersion="19" />
  9 +
  10 + <application
  11 + android:allowBackup="true"
  12 + android:icon="@drawable/ic_launcher"
  13 + android:label="@string/app_name"
  14 + android:theme="@style/AppTheme" >
  15 + </application>
  16 +
  17 +</manifest>
TraceSDK/TraceSDK.jar
No preview for this file type
TraceSDK/bin/AndroidManifest.xml
... ... @@ -0,0 +1,17 @@
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2 + package="com.mobithink.tracksdk"
  3 + android:versionCode="1"
  4 + android:versionName="1.0" >
  5 +
  6 + <uses-sdk
  7 + android:minSdkVersion="8"
  8 + android:targetSdkVersion="19" />
  9 +
  10 + <application
  11 + android:allowBackup="true"
  12 + android:icon="@drawable/ic_launcher"
  13 + android:label="@string/app_name"
  14 + android:theme="@style/AppTheme" >
  15 + </application>
  16 +
  17 +</manifest>
... ... @@ -0,0 +1,4 @@
  1 +int drawable ic_launcher 0x7f020000
  2 +int string app_name 0x7f030000
  3 +int style AppBaseTheme 0x7f040000
  4 +int style AppTheme 0x7f040001
TraceSDK/bin/classes/com/mobithink/tracesdk/Conf$App.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/Conf$Env.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/Conf.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/TraceAgent$1.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/TraceAgent$2.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/TraceAgent.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/event/Event.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/event/EventDBUtil$DBHelper.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/event/EventDBUtil.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSON.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSONArray.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSONException.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSONObject$1.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSONObject.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSONStringer$Scope.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSONStringer.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/json/JSONTokener.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/AbstractClient.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/DefaultRESTClient.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/EntityReader$GzipDecompressingEntity.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/EntityReader.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/EntityReaderImplJSON.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/EntityReaderImplString.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/HttpExecutor$HTTP_METHOD.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/HttpExecutor.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/HttpHeaders.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/IClient.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/MediaType.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/MetadataMap$KeyComparator.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/MetadataMap.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/MultivaluedMap.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/RESTClient.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/RequestEntity.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/RequestEntityImplJSON.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/RequestEntityImplString.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/Response.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/ResponseImpl.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/UriBuilder.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/rest/client/Utils.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/utils/LogUtil.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracesdk/utils/Util.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracksdk/BuildConfig.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracksdk/R$attr.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracksdk/R$drawable.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracksdk/R$string.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracksdk/R$style.class
No preview for this file type
TraceSDK/bin/classes/com/mobithink/tracksdk/R.class
No preview for this file type
TraceSDK/bin/jarlist.cache
... ... @@ -0,0 +1,3 @@
  1 +# cache for current jar dependency. DO NOT EDIT.
  2 +# format is <lastModified> <length> <SHA-1> <path>
  3 +# Encoding is UTF-8
TraceSDK/bin/res/crunch/drawable-hdpi/ic_launcher.png

8.98 KB

TraceSDK/bin/res/crunch/drawable-mdpi/ic_launcher.png

4.94 KB

TraceSDK/bin/res/crunch/drawable-xhdpi/ic_launcher.png

13.7 KB

TraceSDK/bin/tracesdk.jar
No preview for this file type
TraceSDK/gen/com/mobithink/tracksdk/BuildConfig.java
... ... @@ -0,0 +1,6 @@
  1 +/** Automatically generated file. DO NOT MODIFY */
  2 +package com.mobithink.tracksdk;
  3 +
  4 +public final class BuildConfig {
  5 + public final static boolean DEBUG = true;
  6 +}
0 7 \ No newline at end of file
TraceSDK/gen/com/mobithink/tracksdk/R.java
... ... @@ -0,0 +1,47 @@
  1 +/* AUTO-GENERATED FILE. DO NOT MODIFY.
  2 + *
  3 + * This class was automatically generated by the
  4 + * aapt tool from the resource data it found. It
  5 + * should not be modified by hand.
  6 + */
  7 +
  8 +package com.mobithink.tracksdk;
  9 +
  10 +public final class R {
  11 + public static final class attr {
  12 + }
  13 + public static final class drawable {
  14 + public static int ic_launcher=0x7f020000;
  15 + }
  16 + public static final class string {
  17 + public static int app_name=0x7f030000;
  18 + }
  19 + public static final class style {
  20 + /**
  21 + Base application theme, dependent on API level. This theme is replaced
  22 + by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
  23 +
  24 +
  25 + Theme customizations available in newer API levels can go in
  26 + res/values-vXX/styles.xml, while customizations related to
  27 + backward-compatibility can go here.
  28 +
  29 +
  30 + Base application theme for API 11+. This theme completely replaces
  31 + AppBaseTheme from res/values/styles.xml on API 11+ devices.
  32 +
  33 + API 11 theme customizations can go here.
  34 +
  35 + Base application theme for API 14+. This theme completely replaces
  36 + AppBaseTheme from BOTH res/values/styles.xml and
  37 + res/values-v11/styles.xml on API 14+ devices.
  38 +
  39 + API 14 theme customizations can go here.
  40 + */
  41 + public static int AppBaseTheme=0x7f040000;
  42 + /** Application theme.
  43 + All customizations that are NOT specific to a particular API-level can go here.
  44 + */
  45 + public static int AppTheme=0x7f040001;
  46 + }
  47 +}
TraceSDK/libs/android-support-v4.jar
No preview for this file type
TraceSDK/proguard-project.txt
... ... @@ -0,0 +1,20 @@
  1 +# To enable ProGuard in your project, edit project.properties
  2 +# to define the proguard.config property as described in that file.
  3 +#
  4 +# Add project specific ProGuard rules here.
  5 +# By default, the flags in this file are appended to flags specified
  6 +# in ${sdk.dir}/tools/proguard/proguard-android.txt
  7 +# You can edit the include path and order by changing the ProGuard
  8 +# include property in project.properties.
  9 +#
  10 +# For more details, see
  11 +# http://developer.android.com/guide/developing/tools/proguard.html
  12 +
  13 +# Add any project specific keep options here:
  14 +
  15 +# If your project uses WebView with JS, uncomment the following
  16 +# and specify the fully qualified class name to the JavaScript interface
  17 +# class:
  18 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  19 +# public *;
  20 +#}
TraceSDK/project.properties
... ... @@ -0,0 +1,15 @@
  1 +# This file is automatically generated by Android Tools.
  2 +# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
  3 +#
  4 +# This file must be checked in Version Control Systems.
  5 +#
  6 +# To customize properties used by the Ant build system edit
  7 +# "ant.properties", and override values to adapt the script to your
  8 +# project structure.
  9 +#
  10 +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
  11 +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
  12 +
  13 +# Project target.
  14 +target=android-19
  15 +android.library=true
TraceSDK/res/drawable-hdpi/ic_launcher.png

9.18 KB

TraceSDK/res/drawable-mdpi/ic_launcher.png

5.11 KB

TraceSDK/res/drawable-xhdpi/ic_launcher.png

14 KB

TraceSDK/res/values-v11/styles.xml
... ... @@ -0,0 +1,11 @@
  1 +<resources>
  2 +
  3 + <!--
  4 + Base application theme for API 11+. This theme completely replaces
  5 + AppBaseTheme from res/values/styles.xml on API 11+ devices.
  6 + -->
  7 + <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
  8 + <!-- API 11 theme customizations can go here. -->
  9 + </style>
  10 +
  11 +</resources>
TraceSDK/res/values-v14/styles.xml
... ... @@ -0,0 +1,12 @@
  1 +<resources>
  2 +
  3 + <!--
  4 + Base application theme for API 14+. This theme completely replaces
  5 + AppBaseTheme from BOTH res/values/styles.xml and
  6 + res/values-v11/styles.xml on API 14+ devices.
  7 + -->
  8 + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
  9 + <!-- API 14 theme customizations can go here. -->
  10 + </style>
  11 +
  12 +</resources>
TraceSDK/res/values/strings.xml
... ... @@ -0,0 +1,5 @@
  1 +<resources>
  2 +
  3 + <string name="app_name">TraceSDK</string>
  4 +
  5 +</resources>
TraceSDK/res/values/styles.xml
... ... @@ -0,0 +1,20 @@
  1 +<resources>
  2 +
  3 + <!--
  4 + Base application theme, dependent on API level. This theme is replaced
  5 + by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
  6 + -->
  7 + <style name="AppBaseTheme" parent="android:Theme.Light">
  8 + <!--
  9 + Theme customizations available in newer API levels can go in
  10 + res/values-vXX/styles.xml, while customizations related to
  11 + backward-compatibility can go here.
  12 + -->
  13 + </style>
  14 +
  15 + <!-- Application theme. -->
  16 + <style name="AppTheme" parent="AppBaseTheme">
  17 + <!-- All customizations that are NOT specific to a particular API-level can go here. -->
  18 + </style>
  19 +
  20 +</resources>
TraceSDK/src/com/mobithink/tracesdk/Conf.java
... ... @@ -0,0 +1,14 @@
  1 +package com.mobithink.tracesdk;
  2 +
  3 +public class Conf {
  4 +
  5 + public final class App {
  6 + public static final int VERSION = 1;
  7 +
  8 + public static final String API_SERVER = "http://dt.mtafftracking.com/";
  9 + }
  10 +
  11 + public static class Env {
  12 + public static boolean LOG_ON = true;
  13 + }
  14 +}
TraceSDK/src/com/mobithink/tracesdk/TraceAgent.java
... ... @@ -0,0 +1,131 @@
  1 +package com.mobithink.tracesdk;
  2 +
  3 +import java.util.List;
  4 +import java.util.Map;
  5 +
  6 +import org.apache.http.HttpStatus;
  7 +
  8 +import android.content.Context;
  9 +import android.text.TextUtils;
  10 +import android.util.Log;
  11 +
  12 +import com.mobithink.tracesdk.event.EventDBUtil;
  13 +import com.mobithink.tracesdk.event.Event;
  14 +import com.mobithink.tracesdk.json.JSONArray;
  15 +import com.mobithink.tracesdk.json.JSONException;
  16 +import com.mobithink.tracesdk.json.JSONObject;
  17 +import com.mobithink.tracesdk.rest.client.DefaultRESTClient;
  18 +import com.mobithink.tracesdk.rest.client.RequestEntityImplJSON;
  19 +import com.mobithink.tracesdk.rest.client.Response;
  20 +import com.mobithink.tracesdk.utils.LogUtil;
  21 +import com.mobithink.tracesdk.utils.Util;
  22 +
  23 +public class TraceAgent {
  24 +
  25 + private static final String TAG = "TraceAgent";
  26 +
  27 + private static long sessionPauseTime = 0L;
  28 +
  29 + private static String ofId;
  30 +
  31 + public static void active(final Context context, final String offerId) {
  32 + ofId = offerId;
  33 + new Thread() {
  34 +
  35 + @Override
  36 + public void run() {
  37 + DefaultRESTClient client = DefaultRESTClient.create();
  38 + client.path("conversion/").query("os", "android").query("version", android.os.Build.VERSION.RELEASE).query("manufacturer", android.os.Build.MANUFACTURER)
  39 + .query("model", android.os.Build.MODEL).query("build", android.os.Build.VERSION.INCREMENTAL).query("imei", Util.getIMEI(context))
  40 + .query("device_id", Util.getAndroidId(context)).query("mac_addr", Util.getMacAddr(context)).query("sdk_version", Conf.App.VERSION)
  41 + .query("offer_id", offerId).query("uniq_id", Util.getUniqueId(context)).get();
  42 + }
  43 + }.start();
  44 + }
  45 +
  46 + public static void setLogEnable(boolean enable) {
  47 + Conf.Env.LOG_ON = enable;
  48 + }
  49 +
  50 + public static void onResume(final Context Act) {
  51 + long now = System.currentTimeMillis();
  52 + if (now - sessionPauseTime > 10000)
  53 + new Thread() {
  54 +
  55 + @Override
  56 + public void run() {
  57 + List<Event> events = null;
  58 + int retry = 0;
  59 + do {
  60 + events = EventDBUtil.getInstance(Act).getEvents(300);
  61 + if (events == null || events.isEmpty())
  62 + break;
  63 + DefaultRESTClient client = DefaultRESTClient.create();
  64 + client.setRequestEntity(new RequestEntityImplJSON());
  65 + Response resp = client.path("event/index.php").query("os", "android").query("version", android.os.Build.VERSION.RELEASE)
  66 + .query("manufacturer", android.os.Build.MANUFACTURER).query("model", android.os.Build.MODEL).query("build", android.os.Build.VERSION.INCREMENTAL)
  67 + .query("imei", Util.getIMEI(Act)).query("device_id", Util.getAndroidId(Act)).query("mac_addr", Util.getMacAddr(Act))
  68 + .query("sdk_version", Conf.App.VERSION).query("offer_id", ofId).query("uniq_id", Util.getUniqueId(Act)).query("event", formatJson(events)).post();
  69 + Log.d(TAG, "resp code:" + resp.getHttpStatus());
  70 + if (resp != null && resp.getHttpStatus() == HttpStatus.SC_OK) {
  71 + EventDBUtil.getInstance(Act).clearEvent(events);
  72 + retry = 0;
  73 + } else
  74 + retry++;
  75 + } while (events != null && !events.isEmpty() && retry < 3);
  76 + }
  77 +
  78 + }.start();
  79 + }
  80 +
  81 + public static void onPause(Context act) {
  82 + sessionPauseTime = System.currentTimeMillis();
  83 + }
  84 +
  85 + public static void onEvent(Context context, String eventId) {
  86 + onEvent(context, eventId, "");
  87 + }
  88 +
  89 + public static void onEvent(Context context, String eventId, String label) {
  90 + Event event = new Event(eventId, label);
  91 + EventDBUtil dbUtil = EventDBUtil.getInstance(context.getApplicationContext());
  92 + boolean result = dbUtil.saveEvent(event);
  93 + LogUtil.i(TAG, "record event " + (result ? "success" : "error"));
  94 + }
  95 +
  96 + public static void onEvent(Context context, String eventId, Map<String, String> parameters) {
  97 + if (parameters != null) {
  98 + Event event = new Event(eventId, "");
  99 + for (Map.Entry<String, String> entry : parameters.entrySet()) {
  100 + // obj.put(entry.getKey(), entry.getValue());
  101 + event.addParam(entry.getKey(), entry.getValue());
  102 + }
  103 + EventDBUtil dbUtil = EventDBUtil.getInstance(context.getApplicationContext());
  104 + boolean result = dbUtil.saveEvent(event);
  105 + LogUtil.i(TAG, "record event " + (result ? "success" : "error"));
  106 + }
  107 + }
  108 +
  109 + private static JSONArray formatJson(List<Event> events) {
  110 + if (events != null && !events.isEmpty()) {
  111 + JSONArray array = new JSONArray();
  112 + for (Event event : events) {
  113 + JSONObject item = new JSONObject();
  114 + try {
  115 + item.put("type", event.getEventId());
  116 + item.put("time", event.getOccurtime());
  117 + item.put("timezone", event.getTimeZone());
  118 + JSONObject param = event.getParams();
  119 + if (!TextUtils.isEmpty(event.getLabel()))
  120 + param.put("def", event.getLabel());
  121 + item.put("data", param);
  122 + array.put(item);
  123 + } catch (JSONException e) {
  124 + e.printStackTrace();
  125 + }
  126 + }
  127 + return array;
  128 + }
  129 + return new JSONArray();
  130 + }
  131 +}
TraceSDK/src/com/mobithink/tracesdk/event/Event.java
... ... @@ -0,0 +1,137 @@
  1 +package com.mobithink.tracesdk.event;
  2 +
  3 +import java.util.ArrayList;
  4 +import java.util.Calendar;
  5 +import java.util.List;
  6 +
  7 +import android.content.ContentValues;
  8 +import android.database.Cursor;
  9 +import android.provider.BaseColumns;
  10 +
  11 +import com.mobithink.tracesdk.json.JSONException;
  12 +import com.mobithink.tracesdk.json.JSONObject;
  13 +
  14 +public class Event implements BaseColumns {
  15 +
  16 + public static final String TABLE_NAME = "mt_trace";
  17 +
  18 + private long rid;
  19 + private String eventId;
  20 + private JSONObject params;
  21 + private String label = "";
  22 + private long occurtime;
  23 + private String timeZone;
  24 + private int status;
  25 +
  26 + private Event() {
  27 + }
  28 +
  29 + public Event(String eventId, String label) {
  30 + this.eventId = eventId;
  31 + this.label = label;
  32 + occurtime = System.currentTimeMillis();
  33 + timeZone = Calendar.getInstance().getTimeZone().getID();
  34 + params = new JSONObject();
  35 + }
  36 +
  37 + public long getRid() {
  38 + return rid;
  39 + }
  40 +
  41 + public void setRid(long rid) {
  42 + this.rid = rid;
  43 + }
  44 +
  45 + public String getEventId() {
  46 + return eventId;
  47 + }
  48 +
  49 + public String getLabel() {
  50 + return label;
  51 + }
  52 +
  53 + public JSONObject getParams() {
  54 + return params;
  55 + }
  56 +
  57 + // public void setParams(JSONObject params) {
  58 + // this.params = params;
  59 + // }
  60 +
  61 + public String getTimeZone() {
  62 + return timeZone;
  63 + }
  64 +
  65 + public long getOccurtime() {
  66 + return occurtime;
  67 + }
  68 +
  69 + public int getStatus() {
  70 + return status;
  71 + }
  72 +
  73 + public void setStatus(int status) {
  74 + this.status = status;
  75 + }
  76 +
  77 + public void addParam(String key, String val) {
  78 + try {
  79 + params.put(key, val);
  80 + } catch (JSONException e) {
  81 + e.printStackTrace();
  82 + }
  83 + }
  84 +
  85 + public static String COLUMN_EVENT_ID = "event_id";
  86 + public static String COLUMN_LABEL = "label";
  87 + public static String COLUMN_PARAMS = "param_map";
  88 + public static String COLUMN_OCCURTIME = "occurtime";
  89 + public static String COLUMN_TIME_ZONE = "time_zone";
  90 + public static String COLUMN_STATUS = "status";
  91 +
  92 + public static final String[] COLUMNS = { _ID, COLUMN_EVENT_ID, COLUMN_LABEL, COLUMN_OCCURTIME, COLUMN_PARAMS, COLUMN_TIME_ZONE, COLUMN_STATUS };
  93 +
  94 + public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(" + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"//
  95 + + COLUMN_EVENT_ID + " TEXT,"//
  96 + + COLUMN_LABEL + " TEXT,"//
  97 + + COLUMN_OCCURTIME + " INTEGER,"//
  98 + + COLUMN_TIME_ZONE + " TEXT,"// 时区
  99 + + COLUMN_PARAMS + " TEXT,"//
  100 + + COLUMN_STATUS + " INTEGER"//
  101 + + ");";
  102 +
  103 + public ContentValues toContentValues() {
  104 + ContentValues values = new ContentValues();
  105 + values.put(COLUMN_EVENT_ID, eventId);
  106 + values.put(COLUMN_LABEL, label);
  107 + values.put(COLUMN_OCCURTIME, occurtime);
  108 + values.put(COLUMN_STATUS, status);
  109 + values.put(COLUMN_TIME_ZONE, timeZone);
  110 + // if (params != null)
  111 + values.put(COLUMN_PARAMS, params.toString());
  112 + return values;
  113 + }
  114 +
  115 + public static List<Event> parseCursor(Cursor cursor) {
  116 + if (cursor == null)
  117 + return null;
  118 + List<Event> ret = new ArrayList<Event>();
  119 + while (cursor.moveToNext()) {
  120 + Event event = new Event();
  121 + event.rid = cursor.getLong(cursor.getColumnIndexOrThrow(_ID));
  122 + event.eventId = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_EVENT_ID));
  123 + event.label = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_LABEL));
  124 + event.occurtime = cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_OCCURTIME));
  125 + event.timeZone = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_TIME_ZONE));
  126 + event.status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS));
  127 + String paramStr = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PARAMS));
  128 + try {
  129 + event.params = new JSONObject(paramStr);
  130 + } catch (JSONException e) {
  131 + e.printStackTrace();
  132 + }
  133 + ret.add(event);
  134 + }
  135 + return ret;
  136 + }
  137 +}
TraceSDK/src/com/mobithink/tracesdk/event/EventDBUtil.java
... ... @@ -0,0 +1,85 @@
  1 +package com.mobithink.tracesdk.event;
  2 +
  3 +import java.util.List;
  4 +
  5 +import android.content.Context;
  6 +import android.database.Cursor;
  7 +import android.database.sqlite.SQLiteDatabase;
  8 +import android.database.sqlite.SQLiteOpenHelper;
  9 +
  10 +public class EventDBUtil {
  11 +
  12 + private static final int version = 1;
  13 +
  14 + private static final String DB_NAME = "mt_trace.db";
  15 +
  16 + private static EventDBUtil instance = null;
  17 +
  18 + private DBHelper helper;
  19 +
  20 + private EventDBUtil(Context context) {
  21 + helper = new DBHelper(context);
  22 + }
  23 +
  24 + public static EventDBUtil getInstance(Context context) {
  25 + if (instance == null)
  26 + instance = new EventDBUtil(context);
  27 + return instance;
  28 + }
  29 +
  30 + public boolean saveEvent(Event event) {
  31 + SQLiteDatabase db = helper.getWritableDatabase();
  32 + long rowId = db.insert(Event.TABLE_NAME, null, event.toContentValues());
  33 + return rowId != -1;
  34 + }
  35 +
  36 + public boolean clearEvent(Event event) {
  37 + SQLiteDatabase db = helper.getWritableDatabase();
  38 + int count = db.delete(Event.TABLE_NAME, Event._ID + "=?", new String[] { event.getRid() + "" });
  39 + return count > 0;
  40 + }
  41 +
  42 + public void clearEvent(List<Event> events) {
  43 + SQLiteDatabase db = helper.getWritableDatabase();
  44 + for (Event event : events) {
  45 + try {
  46 + db.delete(Event.TABLE_NAME, Event._ID + "=?", new String[] { event.getRid() + "" });
  47 + } catch (Exception e) {
  48 + e.printStackTrace();
  49 + event.setStatus(-1);
  50 + db.update(Event.TABLE_NAME, event.toContentValues(), Event._ID + "=" + event.getRid(), null);
  51 + }
  52 + }
  53 + }
  54 +
  55 + public boolean clearEventAll() {
  56 + SQLiteDatabase db = helper.getWritableDatabase();
  57 + int count = db.delete(Event.TABLE_NAME, null, null);
  58 + return count > 0;
  59 + }
  60 +
  61 + public List<Event> getEvents(int limit) {
  62 + SQLiteDatabase db = helper.getWritableDatabase();
  63 + Cursor cursor = db.query(Event.TABLE_NAME, Event.COLUMNS, Event.COLUMN_STATUS + ">-1", null, null, null, null, limit + "");
  64 + List<Event> events = Event.parseCursor(cursor);
  65 + return events;
  66 + }
  67 +
  68 + class DBHelper extends SQLiteOpenHelper {
  69 +
  70 + public DBHelper(Context context) {
  71 + super(context, DB_NAME, null, version);
  72 + }
  73 +
  74 + @Override
  75 + public void onCreate(SQLiteDatabase db) {
  76 + db.execSQL(Event.CREATE_TABLE);
  77 + }
  78 +
  79 + @Override
  80 + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  81 +
  82 + }
  83 +
  84 + }
  85 +}
TraceSDK/src/com/mobithink/tracesdk/json/JSON.java
... ... @@ -0,0 +1,116 @@
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package com.mobithink.tracesdk.json;
  18 +
  19 +class JSON {
  20 + /**
  21 + * Returns the input if it is a JSON-permissible value; throws otherwise.
  22 + */
  23 + static double checkDouble(double d) throws JSONException {
  24 + if (Double.isInfinite(d) || Double.isNaN(d)) {
  25 + throw new JSONException("Forbidden numeric value: " + d);
  26 + }
  27 + return d;
  28 + }
  29 +
  30 + static Boolean toBoolean(Object value) {
  31 + if (value instanceof Boolean) {
  32 + return (Boolean) value;
  33 + } else if (value instanceof String) {
  34 + String stringValue = (String) value;
  35 + if ("true".equalsIgnoreCase(stringValue)) {
  36 + return true;
  37 + } else if ("false".equalsIgnoreCase(stringValue)) {
  38 + return false;
  39 + }
  40 + }
  41 + return null;
  42 + }
  43 +
  44 + static Double toDouble(Object value) {
  45 + if (value instanceof Double) {
  46 + return (Double) value;
  47 + } else if (value instanceof Number) {
  48 + return ((Number) value).doubleValue();
  49 + } else if (value instanceof String) {
  50 + try {
  51 + return Double.valueOf((String) value);
  52 + } catch (NumberFormatException ignored) {
  53 + }
  54 + }
  55 + return null;
  56 + }
  57 +
  58 + static Integer toInteger(Object value) {
  59 + if (value instanceof Integer) {
  60 + return (Integer) value;
  61 + } else if (value instanceof Number) {
  62 + return ((Number) value).intValue();
  63 + } else if (value instanceof String) {
  64 + try {
  65 + return (int) Double.parseDouble((String) value);
  66 + } catch (NumberFormatException ignored) {
  67 + }
  68 + }
  69 + return null;
  70 + }
  71 +
  72 + static Long toLong(Object value) {
  73 + if (value instanceof Long) {
  74 + return (Long) value;
  75 + } else if (value instanceof Number) {
  76 + return ((Number) value).longValue();
  77 + } else if (value instanceof String) {
  78 + try {
  79 + return (long) Double.parseDouble((String) value);
  80 + } catch (NumberFormatException ignored) {
  81 + }
  82 + }
  83 + return null;
  84 + }
  85 +
  86 + static String toString(Object value) {
  87 + if (value instanceof String) {
  88 + return (String) value;
  89 + } else if (value != null) {
  90 + return String.valueOf(value);
  91 + }
  92 + return null;
  93 + }
  94 +
  95 + public static JSONException typeMismatch(Object indexOrName, Object actual,
  96 + String requiredType) throws JSONException {
  97 + if (actual == null) {
  98 + throw new JSONException("Value at " + indexOrName + " is null.");
  99 + } else {
  100 + throw new JSONException("Value " + actual + " at " + indexOrName
  101 + + " of type " + actual.getClass().getName()
  102 + + " cannot be converted to " + requiredType);
  103 + }
  104 + }
  105 +
  106 + public static JSONException typeMismatch(Object actual, String requiredType)
  107 + throws JSONException {
  108 + if (actual == null) {
  109 + throw new JSONException("Value is null.");
  110 + } else {
  111 + throw new JSONException("Value " + actual
  112 + + " of type " + actual.getClass().getName()
  113 + + " cannot be converted to " + requiredType);
  114 + }
  115 + }
  116 +}
TraceSDK/src/com/mobithink/tracesdk/json/JSONArray.java
... ... @@ -0,0 +1,585 @@
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package com.mobithink.tracesdk.json;
  18 +
  19 +import java.util.ArrayList;
  20 +import java.util.Collection;
  21 +import java.util.List;
  22 +
  23 +// Note: this class was written without inspecting the non-free org.json sourcecode.
  24 +
  25 +/**
  26 + * A dense indexed sequence of values. Values may be any mix of
  27 + * {@link JSONObject JSONObjects}, other {@link JSONArray JSONArrays}, Strings,
  28 + * Booleans, Integers, Longs, Doubles, {@code null} or {@link JSONObject#NULL}.
  29 + * Values may not be {@link Double#isNaN() NaNs}, {@link Double#isInfinite()
  30 + * infinities}, or of any type not listed here.
  31 + *
  32 + * <p>{@code JSONArray} has the same type coercion behavior and
  33 + * optional/mandatory accessors as {@link JSONObject}. See that class'
  34 + * documentation for details.
  35 + *
  36 + * <p><strong>Warning:</strong> this class represents null in two incompatible
  37 + * ways: the standard Java {@code null} reference, and the sentinel value {@link
  38 + * JSONObject#NULL}. In particular, {@code get} fails if the requested index
  39 + * holds the null reference, but succeeds if it holds {@code JSONObject.NULL}.
  40 + *
  41 + * <p>Instances of this class are not thread safe. Although this class is
  42 + * nonfinal, it was not designed for inheritance and should not be subclassed.
  43 + * In particular, self-use by overridable methods is not specified. See
  44 + * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else
  45 + * prohibit it" for further information.
  46 + */
  47 +public class JSONArray {
  48 +
  49 + private final List<Object> values;
  50 +
  51 + /**
  52 + * Creates a {@code JSONArray} with no values.
  53 + */
  54 + public JSONArray() {
  55 + values = new ArrayList<Object>();
  56 + }
  57 +
  58 + /**
  59 + * Creates a new {@code JSONArray} by copying all values from the given
  60 + * collection.
  61 + *
  62 + * @param copyFrom a collection whose values are of supported types.
  63 + * Unsupported values are not permitted and will yield an array in an
  64 + * inconsistent state.
  65 + */
  66 + /* Accept a raw type for API compatibility */
  67 + public JSONArray(Collection copyFrom) {
  68 + this();
  69 + Collection<?> copyFromTyped = (Collection<?>) copyFrom;
  70 + values.addAll(copyFromTyped);
  71 + }
  72 +
  73 + /**
  74 + * Creates a new {@code JSONArray} with values from the next array in the
  75 + * tokener.
  76 + *
  77 + * @param readFrom a tokener whose nextValue() method will yield a
  78 + * {@code JSONArray}.
  79 + * @throws JSONException if the parse fails or doesn't yield a
  80 + * {@code JSONArray}.
  81 + */
  82 + public JSONArray(JSONTokener readFrom) throws JSONException {
  83 + /*
  84 + * Getting the parser to populate this could get tricky. Instead, just
  85 + * parse to temporary JSONArray and then steal the data from that.
  86 + */
  87 + Object object = readFrom.nextValue();
  88 + if (object instanceof JSONArray) {
  89 + values = ((JSONArray) object).values;
  90 + } else {
  91 + throw JSON.typeMismatch(object, "JSONArray");
  92 + }
  93 + }
  94 +
  95 + /**
  96 + * Creates a new {@code JSONArray} with values from the JSON string.
  97 + *
  98 + * @param json a JSON-encoded string containing an array.
  99 + * @throws JSONException if the parse fails or doesn't yield a {@code
  100 + * JSONArray}.
  101 + */
  102 + public JSONArray(String json) throws JSONException {
  103 + this(new JSONTokener(json));
  104 + }
  105 +
  106 + /**
  107 + * Returns the number of values in this array.
  108 + */
  109 + public int length() {
  110 + return values.size();
  111 + }
  112 +
  113 + /**
  114 + * Appends {@code value} to the end of this array.
  115 + *
  116 + * @return this array.
  117 + */
  118 + public JSONArray put(boolean value) {
  119 + values.add(value);
  120 + return this;
  121 + }
  122 +
  123 + /**
  124 + * Appends {@code value} to the end of this array.
  125 + *
  126 + * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
  127 + * {@link Double#isInfinite() infinities}.
  128 + * @return this array.
  129 + */
  130 + public JSONArray put(double value) throws JSONException {
  131 + values.add(JSON.checkDouble(value));
  132 + return this;
  133 + }
  134 +
  135 + /**
  136 + * Appends {@code value} to the end of this array.
  137 + *
  138 + * @return this array.
  139 + */
  140 + public JSONArray put(int value) {
  141 + values.add(value);
  142 + return this;
  143 + }
  144 +
  145 + /**
  146 + * Appends {@code value} to the end of this array.
  147 + *
  148 + * @return this array.
  149 + */
  150 + public JSONArray put(long value) {
  151 + values.add(value);
  152 + return this;
  153 + }
  154 +
  155 + /**
  156 + * Appends {@code value} to the end of this array.
  157 + *
  158 + * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
  159 + * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
  160 + * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
  161 + * infinities}. Unsupported values are not permitted and will cause the
  162 + * array to be in an inconsistent state.
  163 + * @return this array.
  164 + */
  165 + public JSONArray put(Object value) {
  166 + values.add(value);
  167 + return this;
  168 + }
  169 +
  170 + /**
  171 + * Sets the value at {@code index} to {@code value}, null padding this array
  172 + * to the required length if necessary. If a value already exists at {@code
  173 + * index}, it will be replaced.
  174 + *
  175 + * @return this array.
  176 + */
  177 + public JSONArray put(int index, boolean value) throws JSONException {
  178 + return put(index, (Boolean) value);
  179 + }
  180 +
  181 + /**
  182 + * Sets the value at {@code index} to {@code value}, null padding this array
  183 + * to the required length if necessary. If a value already exists at {@code
  184 + * index}, it will be replaced.
  185 + *
  186 + * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
  187 + * {@link Double#isInfinite() infinities}.
  188 + * @return this array.
  189 + */
  190 + public JSONArray put(int index, double value) throws JSONException {
  191 + return put(index, (Double) value);
  192 + }
  193 +
  194 + /**
  195 + * Sets the value at {@code index} to {@code value}, null padding this array
  196 + * to the required length if necessary. If a value already exists at {@code
  197 + * index}, it will be replaced.
  198 + *
  199 + * @return this array.
  200 + */
  201 + public JSONArray put(int index, int value) throws JSONException {
  202 + return put(index, (Integer) value);
  203 + }
  204 +
  205 + /**
  206 + * Sets the value at {@code index} to {@code value}, null padding this array
  207 + * to the required length if necessary. If a value already exists at {@code
  208 + * index}, it will be replaced.
  209 + *
  210 + * @return this array.
  211 + */
  212 + public JSONArray put(int index, long value) throws JSONException {
  213 + return put(index, (Long) value);
  214 + }
  215 +
  216 + /**
  217 + * Sets the value at {@code index} to {@code value}, null padding this array
  218 + * to the required length if necessary. If a value already exists at {@code
  219 + * index}, it will be replaced.
  220 + *
  221 + * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
  222 + * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
  223 + * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
  224 + * infinities}.
  225 + * @return this array.
  226 + */
  227 + public JSONArray put(int index, Object value) throws JSONException {
  228 + if (value instanceof Number) {
  229 + // deviate from the original by checking all Numbers, not just floats & doubles
  230 + JSON.checkDouble(((Number) value).doubleValue());
  231 + }
  232 + while (values.size() <= index) {
  233 + values.add(null);
  234 + }
  235 + values.set(index, value);
  236 + return this;
  237 + }
  238 +
  239 + /**
  240 + * Returns true if this array has no value at {@code index}, or if its value
  241 + * is the {@code null} reference or {@link JSONObject#NULL}.
  242 + */
  243 + public boolean isNull(int index) {
  244 + Object value = opt(index);
  245 + return value == null || value == JSONObject.NULL;
  246 + }
  247 +
  248 + /**
  249 + * Returns the value at {@code index}.
  250 + *
  251 + * @throws JSONException if this array has no value at {@code index}, or if
  252 + * that value is the {@code null} reference. This method returns
  253 + * normally if the value is {@code JSONObject#NULL}.
  254 + */
  255 + public Object get(int index) throws JSONException {
  256 + try {
  257 + Object value = values.get(index);
  258 + if (value == null) {
  259 + throw new JSONException("Value at " + index + " is null.");
  260 + }
  261 + return value;
  262 + } catch (IndexOutOfBoundsException e) {
  263 + throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")");
  264 + }
  265 + }
  266 +
  267 + /**
  268 + * Returns the value at {@code index}, or null if the array has no value
  269 + * at {@code index}.
  270 + */
  271 + public Object opt(int index) {
  272 + if (index < 0 || index >= values.size()) {
  273 + return null;
  274 + }
  275 + return values.get(index);
  276 + }
  277 +
  278 + /**
  279 + * Returns the value at {@code index} if it exists and is a boolean or can
  280 + * be coerced to a boolean.
  281 + *
  282 + * @throws JSONException if the value at {@code index} doesn't exist or
  283 + * cannot be coerced to a boolean.
  284 + */
  285 + public boolean getBoolean(int index) throws JSONException {
  286 + Object object = get(index);
  287 + Boolean result = JSON.toBoolean(object);
  288 + if (result == null) {
  289 + throw JSON.typeMismatch(index, object, "boolean");
  290 + }
  291 + return result;
  292 + }
  293 +
  294 + /**
  295 + * Returns the value at {@code index} if it exists and is a boolean or can
  296 + * be coerced to a boolean. Returns false otherwise.
  297 + */
  298 + public boolean optBoolean(int index) {
  299 + return optBoolean(index, false);
  300 + }
  301 +
  302 + /**
  303 + * Returns the value at {@code index} if it exists and is a boolean or can
  304 + * be coerced to a boolean. Returns {@code fallback} otherwise.
  305 + */
  306 + public boolean optBoolean(int index, boolean fallback) {
  307 + Object object = opt(index);
  308 + Boolean result = JSON.toBoolean(object);
  309 + return result != null ? result : fallback;
  310 + }
  311 +
  312 + /**
  313 + * Returns the value at {@code index} if it exists and is a double or can
  314 + * be coerced to a double.
  315 + *
  316 + * @throws JSONException if the value at {@code index} doesn't exist or
  317 + * cannot be coerced to a double.
  318 + */
  319 + public double getDouble(int index) throws JSONException {
  320 + Object object = get(index);
  321 + Double result = JSON.toDouble(object);
  322 + if (result == null) {
  323 + throw JSON.typeMismatch(index, object, "double");
  324 + }
  325 + return result;
  326 + }
  327 +
  328 + /**
  329 + * Returns the value at {@code index} if it exists and is a double or can
  330 + * be coerced to a double. Returns {@code NaN} otherwise.
  331 + */
  332 + public double optDouble(int index) {
  333 + return optDouble(index, Double.NaN);
  334 + }
  335 +
  336 + /**
  337 + * Returns the value at {@code index} if it exists and is a double or can
  338 + * be coerced to a double. Returns {@code fallback} otherwise.
  339 + */
  340 + public double optDouble(int index, double fallback) {
  341 + Object object = opt(index);
  342 + Double result = JSON.toDouble(object);
  343 + return result != null ? result : fallback;
  344 + }
  345 +
  346 + /**
  347 + * Returns the value at {@code index} if it exists and is an int or
  348 + * can be coerced to an int.
  349 + *
  350 + * @throws JSONException if the value at {@code index} doesn't exist or
  351 + * cannot be coerced to a int.
  352 + */
  353 + public int getInt(int index) throws JSONException {
  354 + Object object = get(index);
  355 + Integer result = JSON.toInteger(object);
  356 + if (result == null) {
  357 + throw JSON.typeMismatch(index, object, "int");
  358 + }
  359 + return result;
  360 + }
  361 +
  362 + /**
  363 + * Returns the value at {@code index} if it exists and is an int or
  364 + * can be coerced to an int. Returns 0 otherwise.
  365 + */
  366 + public int optInt(int index) {
  367 + return optInt(index, 0);
  368 + }
  369 +
  370 + /**
  371 + * Returns the value at {@code index} if it exists and is an int or
  372 + * can be coerced to an int. Returns {@code fallback} otherwise.
  373 + */
  374 + public int optInt(int index, int fallback) {
  375 + Object object = opt(index);
  376 + Integer result = JSON.toInteger(object);
  377 + return result != null ? result : fallback;
  378 + }
  379 +
  380 + /**
  381 + * Returns the value at {@code index} if it exists and is a long or
  382 + * can be coerced to a long.
  383 + *
  384 + * @throws JSONException if the value at {@code index} doesn't exist or
  385 + * cannot be coerced to a long.
  386 + */
  387 + public long getLong(int index) throws JSONException {
  388 + Object object = get(index);
  389 + Long result = JSON.toLong(object);
  390 + if (result == null) {
  391 + throw JSON.typeMismatch(index, object, "long");
  392 + }
  393 + return result;
  394 + }
  395 +
  396 + /**
  397 + * Returns the value at {@code index} if it exists and is a long or
  398 + * can be coerced to a long. Returns 0 otherwise.
  399 + */
  400 + public long optLong(int index) {
  401 + return optLong(index, 0L);
  402 + }
  403 +
  404 + /**
  405 + * Returns the value at {@code index} if it exists and is a long or
  406 + * can be coerced to a long. Returns {@code fallback} otherwise.
  407 + */
  408 + public long optLong(int index, long fallback) {
  409 + Object object = opt(index);
  410 + Long result = JSON.toLong(object);
  411 + return result != null ? result : fallback;
  412 + }
  413 +
  414 + /**
  415 + * Returns the value at {@code index} if it exists, coercing it if
  416 + * necessary.
  417 + *
  418 + * @throws JSONException if no such value exists.
  419 + */
  420 + public String getString(int index) throws JSONException {
  421 + Object object = get(index);
  422 + String result = JSON.toString(object);
  423 + if (result == null) {
  424 + throw JSON.typeMismatch(index, object, "String");
  425 + }
  426 + return result;
  427 + }
  428 +
  429 + /**
  430 + * Returns the value at {@code index} if it exists, coercing it if
  431 + * necessary. Returns the empty string if no such value exists.
  432 + */
  433 + public String optString(int index) {
  434 + return optString(index, "");
  435 + }
  436 +
  437 + /**
  438 + * Returns the value at {@code index} if it exists, coercing it if
  439 + * necessary. Returns {@code fallback} if no such value exists.
  440 + */
  441 + public String optString(int index, String fallback) {
  442 + Object object = opt(index);
  443 + String result = JSON.toString(object);
  444 + return result != null ? result : fallback;
  445 + }
  446 +
  447 + /**
  448 + * Returns the value at {@code index} if it exists and is a {@code
  449 + * JSONArray}.
  450 + *
  451 + * @throws JSONException if the value doesn't exist or is not a {@code
  452 + * JSONArray}.
  453 + */
  454 + public JSONArray getJSONArray(int index) throws JSONException {
  455 + Object object = get(index);
  456 + if (object instanceof JSONArray) {
  457 + return (JSONArray) object;
  458 + } else {
  459 + throw JSON.typeMismatch(index, object, "JSONArray");
  460 + }
  461 + }
  462 +
  463 + /**
  464 + * Returns the value at {@code index} if it exists and is a {@code
  465 + * JSONArray}. Returns null otherwise.
  466 + */
  467 + public JSONArray optJSONArray(int index) {
  468 + Object object = opt(index);
  469 + return object instanceof JSONArray ? (JSONArray) object : null;
  470 + }
  471 +
  472 + /**
  473 + * Returns the value at {@code index} if it exists and is a {@code
  474 + * JSONObject}.
  475 + *
  476 + * @throws JSONException if the value doesn't exist or is not a {@code
  477 + * JSONObject}.
  478 + */
  479 + public JSONObject getJSONObject(int index) throws JSONException {
  480 + Object object = get(index);
  481 + if (object instanceof JSONObject) {
  482 + return (JSONObject) object;
  483 + } else {
  484 + throw JSON.typeMismatch(index, object, "JSONObject");
  485 + }
  486 + }
  487 +
  488 + /**
  489 + * Returns the value at {@code index} if it exists and is a {@code
  490 + * JSONObject}. Returns null otherwise.
  491 + */
  492 + public JSONObject optJSONObject(int index) {
  493 + Object object = opt(index);
  494 + return object instanceof JSONObject ? (JSONObject) object : null;
  495 + }
  496 +
  497 + /**
  498 + * Returns a new object whose values are the values in this array, and whose
  499 + * names are the values in {@code names}. Names and values are paired up by
  500 + * index from 0 through to the shorter array's length. Names that are not
  501 + * strings will be coerced to strings. This method returns null if either
  502 + * array is empty.
  503 + */
  504 + public JSONObject toJSONObject(JSONArray names) throws JSONException {
  505 + JSONObject result = new JSONObject();
  506 + int length = Math.min(names.length(), values.size());
  507 + if (length == 0) {
  508 + return null;
  509 + }
  510 + for (int i = 0; i < length; i++) {
  511 + String name = JSON.toString(names.opt(i));
  512 + result.put(name, opt(i));
  513 + }
  514 + return result;
  515 + }
  516 +
  517 + /**
  518 + * Returns a new string by alternating this array's values with {@code
  519 + * separator}. This array's string values are quoted and have their special
  520 + * characters escaped. For example, the array containing the strings '12"
  521 + * pizza', 'taco' and 'soda' joined on '+' returns this:
  522 + * <pre>"12\" pizza"+"taco"+"soda"</pre>
  523 + */
  524 + public String join(String separator) throws JSONException {
  525 + JSONStringer stringer = new JSONStringer();
  526 + stringer.open(JSONStringer.Scope.NULL, "");
  527 + for (int i = 0, size = values.size(); i < size; i++) {
  528 + if (i > 0) {
  529 + stringer.out.append(separator);
  530 + }
  531 + stringer.value(values.get(i));
  532 + }
  533 + stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
  534 + return stringer.out.toString();
  535 + }
  536 +
  537 + /**
  538 + * Encodes this array as a compact JSON string, such as:
  539 + * <pre>[94043,90210]</pre>
  540 + */
  541 + @Override public String toString() {
  542 + try {
  543 + JSONStringer stringer = new JSONStringer();
  544 + writeTo(stringer);
  545 + return stringer.toString();
  546 + } catch (JSONException e) {
  547 + return null;
  548 + }
  549 + }
  550 +
  551 + /**
  552 + * Encodes this array as a human readable JSON string for debugging, such
  553 + * as:
  554 + * <pre>
  555 + * [
  556 + * 94043,
  557 + * 90210
  558 + * ]</pre>
  559 + *
  560 + * @param indentSpaces the number of spaces to indent for each level of
  561 + * nesting.
  562 + */
  563 + public String toString(int indentSpaces) throws JSONException {
  564 + JSONStringer stringer = new JSONStringer(indentSpaces);
  565 + writeTo(stringer);
  566 + return stringer.toString();
  567 + }
  568 +
  569 + void writeTo(JSONStringer stringer) throws JSONException {
  570 + stringer.array();
  571 + for (Object value : values) {
  572 + stringer.value(value);
  573 + }
  574 + stringer.endArray();
  575 + }
  576 +
  577 + @Override public boolean equals(Object o) {
  578 + return o instanceof JSONArray && ((JSONArray) o).values.equals(values);
  579 + }
  580 +
  581 + @Override public int hashCode() {
  582 + // diverge from the original, which doesn't implement hashCode
  583 + return values.hashCode();
  584 + }
  585 +}
TraceSDK/src/com/mobithink/tracesdk/json/JSONException.java
... ... @@ -0,0 +1,49 @@
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package com.mobithink.tracesdk.json;
  18 +
  19 +// Note: this class was written without inspecting the non-free org.json sourcecode.
  20 +
  21 +/**
  22 + * Thrown to indicate a problem with the JSON API. Such problems include:
  23 + * <ul>
  24 + * <li>Attempts to parse or construct malformed documents
  25 + * <li>Use of null as a name
  26 + * <li>Use of numeric types not available to JSON, such as {@link
  27 + * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
  28 + * <li>Lookups using an out of range index or nonexistent name
  29 + * <li>Type mismatches on lookups
  30 + * </ul>
  31 + *
  32 + * <p>Although this is a checked exception, it is rarely recoverable. Most
  33 + * callers should simply wrap this exception in an unchecked exception and
  34 + * rethrow:
  35 + * <pre> public JSONArray toJSONObject() {
  36 + * try {
  37 + * JSONObject result = new JSONObject();
  38 + * ...
  39 + * } catch (JSONException e) {
  40 + * throw new RuntimeException(e);
  41 + * }
  42 + * }</pre>
  43 + */
  44 +public class JSONException extends Exception {
  45 +
  46 + public JSONException(String s) {
  47 + super(s);
  48 + }
  49 +}
TraceSDK/src/com/mobithink/tracesdk/json/JSONObject.java
... ... @@ -0,0 +1,720 @@
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package com.mobithink.tracesdk.json;
  18 +
  19 +import java.util.ArrayList;
  20 +import java.util.HashMap;
  21 +import java.util.Iterator;
  22 +import java.util.Map;
  23 +
  24 +// Note: this class was written without inspecting the non-free org.json sourcecode.
  25 +
  26 +/**
  27 + * A modifiable set of name/value mappings. Names are unique, non-null strings.
  28 + * Values may be any mix of {@link JSONObject JSONObjects}, {@link JSONArray
  29 + * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}.
  30 + * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link
  31 + * Double#isInfinite() infinities}, or of any type not listed here.
  32 + *
  33 + * <p>This class can coerce values to another type when requested.
  34 + * <ul>
  35 + * <li>When the requested type is a boolean, strings will be coerced using a
  36 + * case-insensitive comparison to "true" and "false".
  37 + * <li>When the requested type is a double, other {@link Number} types will
  38 + * be coerced using {@link Number#doubleValue() doubleValue}. Strings
  39 + * that can be coerced using {@link Double#valueOf(String)} will be.
  40 + * <li>When the requested type is an int, other {@link Number} types will
  41 + * be coerced using {@link Number#intValue() intValue}. Strings
  42 + * that can be coerced using {@link Double#valueOf(String)} will be,
  43 + * and then cast to int.
  44 + * <li>When the requested type is a long, other {@link Number} types will
  45 + * be coerced using {@link Number#longValue() longValue}. Strings
  46 + * that can be coerced using {@link Double#valueOf(String)} will be,
  47 + * and then cast to long. This two-step conversion is lossy for very
  48 + * large values. For example, the string "9223372036854775806" yields the
  49 + * long 9223372036854775807.
  50 + * <li>When the requested type is a String, other non-null values will be
  51 + * coerced using {@link String#valueOf(Object)}. Although null cannot be
  52 + * coerced, the sentinel value {@link JSONObject#NULL} is coerced to the
  53 + * string "null".
  54 + * </ul>
  55 + *
  56 + * <p>This class can look up both mandatory and optional values:
  57 + * <ul>
  58 + * <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This
  59 + * fails with a {@code JSONException} if the requested name has no value
  60 + * or if the value cannot be coerced to the requested type.
  61 + * <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This
  62 + * returns a system- or user-supplied default if the requested name has no
  63 + * value or if the value cannot be coerced to the requested type.
  64 + * </ul>
  65 + *
  66 + * <p><strong>Warning:</strong> this class represents null in two incompatible
  67 + * ways: the standard Java {@code null} reference, and the sentinel value {@link
  68 + * JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the
  69 + * named entry from the object but {@code put(name, JSONObject.NULL)} stores an
  70 + * entry whose value is {@code JSONObject.NULL}.
  71 + *
  72 + * <p>Instances of this class are not thread safe. Although this class is
  73 + * nonfinal, it was not designed for inheritance and should not be subclassed.
  74 + * In particular, self-use by overrideable methods is not specified. See
  75 + * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else
  76 + * prohibit it" for further information.
  77 + */
  78 +public class JSONObject {
  79 +
  80 + private static final Double NEGATIVE_ZERO = -0d;
  81 +
  82 + /**
  83 + * A sentinel value used to explicitly define a name with no value. Unlike
  84 + * {@code null}, names with this value:
  85 + * <ul>
  86 + * <li>show up in the {@link #names} array
  87 + * <li>show up in the {@link #keys} iterator
  88 + * <li>return {@code true} for {@link #has(String)}
  89 + * <li>do not throw on {@link #get(String)}
  90 + * <li>are included in the encoded JSON string.
  91 + * </ul>
  92 + *
  93 + * <p>This value violates the general contract of {@link Object#equals} by
  94 + * returning true when compared to {@code null}. Its {@link #toString}
  95 + * method returns "null".
  96 + */
  97 + public static final Object NULL = new Object() {
  98 + @Override public boolean equals(Object o) {
  99 + return o == this || o == null; // API specifies this broken equals implementation
  100 + }
  101 + @Override public String toString() {
  102 + return "null";
  103 + }
  104 + };
  105 +
  106 + private final Map<String, Object> nameValuePairs;
  107 +
  108 + /**
  109 + * Creates a {@code JSONObject} with no name/value mappings.
  110 + */
  111 + public JSONObject() {
  112 + nameValuePairs = new HashMap<String, Object>();
  113 + }
  114 +
  115 + /**
  116 + * Creates a new {@code JSONObject} by copying all name/value mappings from
  117 + * the given map.
  118 + *
  119 + * @param copyFrom a map whose keys are of type {@link String} and whose
  120 + * values are of supported types.
  121 + * @throws NullPointerException if any of the map's keys are null.
  122 + */
  123 + /* (accept a raw type for API compatibility) */
  124 + public JSONObject(Map copyFrom) {
  125 + this();
  126 + Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
  127 + for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
  128 + /*
  129 + * Deviate from the original by checking that keys are non-null and
  130 + * of the proper type. (We still defer validating the values).
  131 + */
  132 + String key = (String) entry.getKey();
  133 + if (key == null) {
  134 + throw new NullPointerException();
  135 + }
  136 + nameValuePairs.put(key, entry.getValue());
  137 + }
  138 + }
  139 +
  140 + /**
  141 + * Creates a new {@code JSONObject} with name/value mappings from the next
  142 + * object in the tokener.
  143 + *
  144 + * @param readFrom a tokener whose nextValue() method will yield a
  145 + * {@code JSONObject}.
  146 + * @throws JSONException if the parse fails or doesn't yield a
  147 + * {@code JSONObject}.
  148 + */
  149 + public JSONObject(JSONTokener readFrom) throws JSONException {
  150 + /*
  151 + * Getting the parser to populate this could get tricky. Instead, just
  152 + * parse to temporary JSONObject and then steal the data from that.
  153 + */
  154 + Object object = readFrom.nextValue();
  155 + if (object instanceof JSONObject) {
  156 + this.nameValuePairs = ((JSONObject) object).nameValuePairs;
  157 + } else {
  158 + throw JSON.typeMismatch(object, "JSONObject");
  159 + }
  160 + }
  161 +
  162 + /**
  163 + * Creates a new {@code JSONObject} with name/value mappings from the JSON
  164 + * string.
  165 + *
  166 + * @param json a JSON-encoded string containing an object.
  167 + * @throws JSONException if the parse fails or doesn't yield a {@code
  168 + * JSONObject}.
  169 + */
  170 + public JSONObject(String json) throws JSONException {
  171 + this(new JSONTokener(json));
  172 + }
  173 +
  174 + /**
  175 + * Creates a new {@code JSONObject} by copying mappings for the listed names
  176 + * from the given object. Names that aren't present in {@code copyFrom} will
  177 + * be skipped.
  178 + */
  179 + public JSONObject(JSONObject copyFrom, String[] names) throws JSONException {
  180 + this();
  181 + for (String name : names) {
  182 + Object value = copyFrom.opt(name);
  183 + if (value != null) {
  184 + nameValuePairs.put(name, value);
  185 + }
  186 + }
  187 + }
  188 +
  189 + /**
  190 + * Returns the number of name/value mappings in this object.
  191 + */
  192 + public int length() {
  193 + return nameValuePairs.size();
  194 + }
  195 +
  196 + /**
  197 + * Maps {@code name} to {@code value}, clobbering any existing name/value
  198 + * mapping with the same name.
  199 + *
  200 + * @return this object.
  201 + */
  202 + public JSONObject put(String name, boolean value) throws JSONException {
  203 + nameValuePairs.put(checkName(name), value);
  204 + return this;
  205 + }
  206 +
  207 + /**
  208 + * Maps {@code name} to {@code value}, clobbering any existing name/value
  209 + * mapping with the same name.
  210 + *
  211 + * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
  212 + * {@link Double#isInfinite() infinities}.
  213 + * @return this object.
  214 + */
  215 + public JSONObject put(String name, double value) throws JSONException {
  216 + nameValuePairs.put(checkName(name), JSON.checkDouble(value));
  217 + return this;
  218 + }
  219 +
  220 + /**
  221 + * Maps {@code name} to {@code value}, clobbering any existing name/value
  222 + * mapping with the same name.
  223 + *
  224 + * @return this object.
  225 + */
  226 + public JSONObject put(String name, int value) throws JSONException {
  227 + nameValuePairs.put(checkName(name), value);
  228 + return this;
  229 + }
  230 +
  231 + /**
  232 + * Maps {@code name} to {@code value}, clobbering any existing name/value
  233 + * mapping with the same name.
  234 + *
  235 + * @return this object.
  236 + */
  237 + public JSONObject put(String name, long value) throws JSONException {
  238 + nameValuePairs.put(checkName(name), value);
  239 + return this;
  240 + }
  241 +
  242 + /**
  243 + * Maps {@code name} to {@code value}, clobbering any existing name/value
  244 + * mapping with the same name. If the value is {@code null}, any existing
  245 + * mapping for {@code name} is removed.
  246 + *
  247 + * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
  248 + * Integer, Long, Double, {@link #NULL}, or {@code null}. May not be
  249 + * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
  250 + * infinities}.
  251 + * @return this object.
  252 + */
  253 + public JSONObject put(String name, Object value) throws JSONException {
  254 + if (value == null) {
  255 + nameValuePairs.remove(name);
  256 + return this;
  257 + }
  258 + if (value instanceof Number) {
  259 + // deviate from the original by checking all Numbers, not just floats & doubles
  260 + JSON.checkDouble(((Number) value).doubleValue());
  261 + }
  262 + nameValuePairs.put(checkName(name), value);
  263 + return this;
  264 + }
  265 +
  266 + /**
  267 + * Equivalent to {@code put(name, value)} when both parameters are non-null;
  268 + * does nothing otherwise.
  269 + */
  270 + public JSONObject putOpt(String name, Object value) throws JSONException {
  271 + if (name == null || value == null) {
  272 + return this;
  273 + }
  274 + return put(name, value);
  275 + }
  276 +
  277 + /**
  278 + * Appends {@code value} to the array already mapped to {@code name}. If
  279 + * this object has no mapping for {@code name}, this inserts a new mapping.
  280 + * If the mapping exists but its value is not an array, the existing
  281 + * and new values are inserted in order into a new array which is itself
  282 + * mapped to {@code name}. In aggregate, this allows values to be added to a
  283 + * mapping one at a time.
  284 + *
  285 + * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
  286 + * Integer, Long, Double, {@link #NULL} or null. May not be {@link
  287 + * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
  288 + */
  289 + public JSONObject accumulate(String name, Object value) throws JSONException {
  290 + Object current = nameValuePairs.get(checkName(name));
  291 + if (current == null) {
  292 + return put(name, value);
  293 + }
  294 +
  295 + // check in accumulate, since array.put(Object) doesn't do any checking
  296 + if (value instanceof Number) {
  297 + JSON.checkDouble(((Number) value).doubleValue());
  298 + }
  299 +
  300 + if (current instanceof JSONArray) {
  301 + JSONArray array = (JSONArray) current;
  302 + array.put(value);
  303 + } else {
  304 + JSONArray array = new JSONArray();
  305 + array.put(current);
  306 + array.put(value);
  307 + nameValuePairs.put(name, array);
  308 + }
  309 + return this;
  310 + }
  311 +
  312 + String checkName(String name) throws JSONException {
  313 + if (name == null) {
  314 + throw new JSONException("Names must be non-null");
  315 + }
  316 + return name;
  317 + }
  318 +
  319 + /**
  320 + * Removes the named mapping if it exists; does nothing otherwise.
  321 + *
  322 + * @return the value previously mapped by {@code name}, or null if there was
  323 + * no such mapping.
  324 + */
  325 + public Object remove(String name) {
  326 + return nameValuePairs.remove(name);
  327 + }
  328 +
  329 + /**
  330 + * Returns true if this object has no mapping for {@code name} or if it has
  331 + * a mapping whose value is {@link #NULL}.
  332 + */
  333 + public boolean isNull(String name) {
  334 + Object value = nameValuePairs.get(name);
  335 + return value == null || value == NULL;
  336 + }
  337 +
  338 + /**
  339 + * Returns true if this object has a mapping for {@code name}. The mapping
  340 + * may be {@link #NULL}.
  341 + */
  342 + public boolean has(String name) {
  343 + return nameValuePairs.containsKey(name);
  344 + }
  345 +
  346 + /**
  347 + * Returns the value mapped by {@code name}.
  348 + *
  349 + * @throws JSONException if no such mapping exists.
  350 + */
  351 + public Object get(String name) throws JSONException {
  352 + Object result = nameValuePairs.get(name);
  353 + if (result == null) {
  354 + throw new JSONException("No value for " + name);
  355 + }
  356 + return result;
  357 + }
  358 +
  359 + /**
  360 + * Returns the value mapped by {@code name}, or null if no such mapping
  361 + * exists.
  362 + */
  363 + public Object opt(String name) {
  364 + return nameValuePairs.get(name);
  365 + }
  366 +
  367 + /**
  368 + * Returns the value mapped by {@code name} if it exists and is a boolean or
  369 + * can be coerced to a boolean.
  370 + *
  371 + * @throws JSONException if the mapping doesn't exist or cannot be coerced
  372 + * to a boolean.
  373 + */
  374 + public boolean getBoolean(String name) throws JSONException {
  375 + Object object = get(name);
  376 + Boolean result = JSON.toBoolean(object);
  377 + if (result == null) {
  378 + throw JSON.typeMismatch(name, object, "boolean");
  379 + }
  380 + return result;
  381 + }
  382 +
  383 + /**
  384 + * Returns the value mapped by {@code name} if it exists and is a boolean or
  385 + * can be coerced to a boolean. Returns false otherwise.
  386 + */
  387 + public boolean optBoolean(String name) {
  388 + return optBoolean(name, false);
  389 + }
  390 +
  391 + /**
  392 + * Returns the value mapped by {@code name} if it exists and is a boolean or
  393 + * can be coerced to a boolean. Returns {@code fallback} otherwise.
  394 + */
  395 + public boolean optBoolean(String name, boolean fallback) {
  396 + Object object = opt(name);
  397 + Boolean result = JSON.toBoolean(object);
  398 + return result != null ? result : fallback;
  399 + }
  400 +
  401 + /**
  402 + * Returns the value mapped by {@code name} if it exists and is a double or
  403 + * can be coerced to a double.
  404 + *
  405 + * @throws JSONException if the mapping doesn't exist or cannot be coerced
  406 + * to a double.
  407 + */
  408 + public double getDouble(String name) throws JSONException {
  409 + Object object = get(name);
  410 + Double result = JSON.toDouble(object);
  411 + if (result == null) {
  412 + throw JSON.typeMismatch(name, object, "double");
  413 + }
  414 + return result;
  415 + }
  416 +
  417 + /**
  418 + * Returns the value mapped by {@code name} if it exists and is a double or
  419 + * can be coerced to a double. Returns {@code NaN} otherwise.
  420 + */
  421 + public double optDouble(String name) {
  422 + return optDouble(name, Double.NaN);
  423 + }
  424 +
  425 + /**
  426 + * Returns the value mapped by {@code name} if it exists and is a double or
  427 + * can be coerced to a double. Returns {@code fallback} otherwise.
  428 + */
  429 + public double optDouble(String name, double fallback) {
  430 + Object object = opt(name);
  431 + Double result = JSON.toDouble(object);
  432 + return result != null ? result : fallback;
  433 + }
  434 +
  435 + /**
  436 + * Returns the value mapped by {@code name} if it exists and is an int or
  437 + * can be coerced to an int.
  438 + *
  439 + * @throws JSONException if the mapping doesn't exist or cannot be coerced
  440 + * to an int.
  441 + */
  442 + public int getInt(String name) throws JSONException {
  443 + Object object = get(name);
  444 + Integer result = JSON.toInteger(object);
  445 + if (result == null) {
  446 + throw JSON.typeMismatch(name, object, "int");
  447 + }
  448 + return result;
  449 + }
  450 +
  451 + /**
  452 + * Returns the value mapped by {@code name} if it exists and is an int or
  453 + * can be coerced to an int. Returns 0 otherwise.
  454 + */
  455 + public int optInt(String name) {
  456 + return optInt(name, 0);
  457 + }
  458 +
  459 + /**
  460 + * Returns the value mapped by {@code name} if it exists and is an int or
  461 + * can be coerced to an int. Returns {@code fallback} otherwise.
  462 + */
  463 + public int optInt(String name, int fallback) {
  464 + Object object = opt(name);
  465 + Integer result = JSON.toInteger(object);
  466 + return result != null ? result : fallback;
  467 + }
  468 +
  469 + /**
  470 + * Returns the value mapped by {@code name} if it exists and is a long or
  471 + * can be coerced to a long.
  472 + *
  473 + * @throws JSONException if the mapping doesn't exist or cannot be coerced
  474 + * to a long.
  475 + */
  476 + public long getLong(String name) throws JSONException {
  477 + Object object = get(name);
  478 + Long result = JSON.toLong(object);
  479 + if (result == null) {
  480 + throw JSON.typeMismatch(name, object, "long");
  481 + }
  482 + return result;
  483 + }
  484 +
  485 + /**
  486 + * Returns the value mapped by {@code name} if it exists and is a long or
  487 + * can be coerced to a long. Returns 0 otherwise.
  488 + */
  489 + public long optLong(String name) {
  490 + return optLong(name, 0L);
  491 + }
  492 +
  493 + /**
  494 + * Returns the value mapped by {@code name} if it exists and is a long or
  495 + * can be coerced to a long. Returns {@code fallback} otherwise.
  496 + */
  497 + public long optLong(String name, long fallback) {
  498 + Object object = opt(name);
  499 + Long result = JSON.toLong(object);
  500 + return result != null ? result : fallback;
  501 + }
  502 +
  503 + /**
  504 + * Returns the value mapped by {@code name} if it exists, coercing it if
  505 + * necessary.
  506 + *
  507 + * @throws JSONException if no such mapping exists.
  508 + */
  509 + public String getString(String name) throws JSONException {
  510 + Object object = get(name);
  511 + String result = JSON.toString(object);
  512 + if (result == null) {
  513 + throw JSON.typeMismatch(name, object, "String");
  514 + }
  515 + return result;
  516 + }
  517 +
  518 + /**
  519 + * Returns the value mapped by {@code name} if it exists, coercing it if
  520 + * necessary. Returns the empty string if no such mapping exists.
  521 + */
  522 + public String optString(String name) {
  523 + return optString(name, "");
  524 + }
  525 +
  526 + /**
  527 + * Returns the value mapped by {@code name} if it exists, coercing it if
  528 + * necessary. Returns {@code fallback} if no such mapping exists.
  529 + */
  530 + public String optString(String name, String fallback) {
  531 + Object object = opt(name);
  532 + String result = JSON.toString(object);
  533 + return result != null ? result : fallback;
  534 + }
  535 +
  536 + /**
  537 + * Returns the value mapped by {@code name} if it exists and is a {@code
  538 + * JSONArray}.
  539 + *
  540 + * @throws JSONException if the mapping doesn't exist or is not a {@code
  541 + * JSONArray}.
  542 + */
  543 + public JSONArray getJSONArray(String name) throws JSONException {
  544 + Object object = get(name);
  545 + if (object instanceof JSONArray) {
  546 + return (JSONArray) object;
  547 + } else {
  548 + throw JSON.typeMismatch(name, object, "JSONArray");
  549 + }
  550 + }
  551 +
  552 + /**
  553 + * Returns the value mapped by {@code name} if it exists and is a {@code
  554 + * JSONArray}. Returns null otherwise.
  555 + */
  556 + public JSONArray optJSONArray(String name) {
  557 + Object object = opt(name);
  558 + return object instanceof JSONArray ? (JSONArray) object : null;
  559 + }
  560 +
  561 + /**
  562 + * Returns the value mapped by {@code name} if it exists and is a {@code
  563 + * JSONObject}.
  564 + *
  565 + * @throws JSONException if the mapping doesn't exist or is not a {@code
  566 + * JSONObject}.
  567 + */
  568 + public JSONObject getJSONObject(String name) throws JSONException {
  569 + Object object = get(name);
  570 + if (object instanceof JSONObject) {
  571 + return (JSONObject) object;
  572 + } else {
  573 + throw JSON.typeMismatch(name, object, "JSONObject");
  574 + }
  575 + }
  576 +
  577 + /**
  578 + * Returns the value mapped by {@code name} if it exists and is a {@code
  579 + * JSONObject}. Returns null otherwise.
  580 + */
  581 + public JSONObject optJSONObject(String name) {
  582 + Object object = opt(name);
  583 + return object instanceof JSONObject ? (JSONObject) object : null;
  584 + }
  585 +
  586 + /**
  587 + * Returns an array with the values corresponding to {@code names}. The
  588 + * array contains null for names that aren't mapped. This method returns
  589 + * null if {@code names} is either null or empty.
  590 + */
  591 + public JSONArray toJSONArray(JSONArray names) throws JSONException {
  592 + JSONArray result = new JSONArray();
  593 + if (names == null) {
  594 + return null;
  595 + }
  596 + int length = names.length();
  597 + if (length == 0) {
  598 + return null;
  599 + }
  600 + for (int i = 0; i < length; i++) {
  601 + String name = JSON.toString(names.opt(i));
  602 + result.put(opt(name));
  603 + }
  604 + return result;
  605 + }
  606 +
  607 + /**
  608 + * Returns an iterator of the {@code String} names in this object. The
  609 + * returned iterator supports {@link Iterator#remove() remove}, which will
  610 + * remove the corresponding mapping from this object. If this object is
  611 + * modified after the iterator is returned, the iterator's behavior is
  612 + * undefined. The order of the keys is undefined.
  613 + */
  614 + /* Return a raw type for API compatibility */
  615 + public Iterator keys() {
  616 + return nameValuePairs.keySet().iterator();
  617 + }
  618 +
  619 + /**
  620 + * Returns an array containing the string names in this object. This method
  621 + * returns null if this object contains no mappings.
  622 + */
  623 + public JSONArray names() {
  624 + return nameValuePairs.isEmpty()
  625 + ? null
  626 + : new JSONArray(new ArrayList<String>(nameValuePairs.keySet()));
  627 + }
  628 +
  629 + /**
  630 + * Encodes this object as a compact JSON string, such as:
  631 + * <pre>{"query":"Pizza","locations":[94043,90210]}</pre>
  632 + */
  633 + @Override public String toString() {
  634 + try {
  635 + JSONStringer stringer = new JSONStringer();
  636 + writeTo(stringer);
  637 + return stringer.toString();
  638 + } catch (JSONException e) {
  639 + return null;
  640 + }
  641 + }
  642 +
  643 + /**
  644 + * Encodes this object as a human readable JSON string for debugging, such
  645 + * as:
  646 + * <pre>
  647 + * {
  648 + * "query": "Pizza",
  649 + * "locations": [
  650 + * 94043,
  651 + * 90210
  652 + * ]
  653 + * }</pre>
  654 + *
  655 + * @param indentSpaces the number of spaces to indent for each level of
  656 + * nesting.
  657 + */
  658 + public String toString(int indentSpaces) throws JSONException {
  659 + JSONStringer stringer = new JSONStringer(indentSpaces);
  660 + writeTo(stringer);
  661 + return stringer.toString();
  662 + }
  663 +
  664 + void writeTo(JSONStringer stringer) throws JSONException {
  665 + stringer.object();
  666 + for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) {
  667 + stringer.key(entry.getKey()).value(entry.getValue());
  668 + }
  669 + stringer.endObject();
  670 + }
  671 +
  672 + /**
  673 + * Encodes the number as a JSON string.
  674 + *
  675 + * @param number a finite value. May not be {@link Double#isNaN() NaNs} or
  676 + * {@link Double#isInfinite() infinities}.
  677 + */
  678 + public static String numberToString(Number number) throws JSONException {
  679 + if (number == null) {
  680 + throw new JSONException("Number must be non-null");
  681 + }
  682 +
  683 + double doubleValue = number.doubleValue();
  684 + JSON.checkDouble(doubleValue);
  685 +
  686 + // the original returns "-0" instead of "-0.0" for negative zero
  687 + if (number.equals(NEGATIVE_ZERO)) {
  688 + return "-0";
  689 + }
  690 +
  691 + long longValue = number.longValue();
  692 + if (doubleValue == (double) longValue) {
  693 + return Long.toString(longValue);
  694 + }
  695 +
  696 + return number.toString();
  697 + }
  698 +
  699 + /**
  700 + * Encodes {@code data} as a JSON string. This applies quotes and any
  701 + * necessary character escaping.
  702 + *
  703 + * @param data the string to encode. Null will be interpreted as an empty
  704 + * string.
  705 + */
  706 + public static String quote(String data) {
  707 + if (data == null) {
  708 + return "\"\"";
  709 + }
  710 + try {
  711 + JSONStringer stringer = new JSONStringer();
  712 + stringer.open(JSONStringer.Scope.NULL, "");
  713 + stringer.value(data);
  714 + stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
  715 + return stringer.toString();
  716 + } catch (JSONException e) {
  717 + throw new AssertionError();
  718 + }
  719 + }
  720 +}
TraceSDK/src/com/mobithink/tracesdk/json/JSONStringer.java
... ... @@ -0,0 +1,432 @@
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package com.mobithink.tracesdk.json;
  18 +
  19 +import java.util.ArrayList;
  20 +import java.util.Arrays;
  21 +import java.util.List;
  22 +
  23 +// Note: this class was written without inspecting the non-free org.json sourcecode.
  24 +
  25 +/**
  26 + * Implements {@link JSONObject#toString} and {@link JSONArray#toString}. Most
  27 + * application developers should use those methods directly and disregard this
  28 + * API. For example:<pre>
  29 + * JSONObject object = ...
  30 + * String json = object.toString();</pre>
  31 + *
  32 + * <p>Stringers only encode well-formed JSON strings. In particular:
  33 + * <ul>
  34 + * <li>The stringer must have exactly one top-level array or object.
  35 + * <li>Lexical scopes must be balanced: every call to {@link #array} must
  36 + * have a matching call to {@link #endArray} and every call to {@link
  37 + * #object} must have a matching call to {@link #endObject}.
  38 + * <li>Arrays may not contain keys (property names).
  39 + * <li>Objects must alternate keys (property names) and values.
  40 + * <li>Values are inserted with either literal {@link #value(Object) value}
  41 + * calls, or by nesting arrays or objects.
  42 + * </ul>
  43 + * Calls that would result in a malformed JSON string will fail with a
  44 + * {@link JSONException}.
  45 + *
  46 + * <p>This class provides no facility for pretty-printing (ie. indenting)
  47 + * output. To encode indented output, use {@link JSONObject#toString(int)} or
  48 + * {@link JSONArray#toString(int)}.
  49 + *
  50 + * <p>Some implementations of the API support at most 20 levels of nesting.
  51 + * Attempts to create more than 20 levels of nesting may fail with a {@link
  52 + * JSONException}.
  53 + *
  54 + * <p>Each stringer may be used to encode a single top level value. Instances of
  55 + * this class are not thread safe. Although this class is nonfinal, it was not
  56 + * designed for inheritance and should not be subclassed. In particular,
  57 + * self-use by overrideable methods is not specified. See <i>Effective Java</i>
  58 + * Item 17, "Design and Document or inheritance or else prohibit it" for further
  59 + * information.
  60 + */
  61 +public class JSONStringer {
  62 +
  63 + /** The output data, containing at most one top-level array or object. */
  64 + final StringBuilder out = new StringBuilder();
  65 +
  66 + /**
  67 + * Lexical scoping elements within this stringer, necessary to insert the
  68 + * appropriate separator characters (ie. commas and colons) and to detect
  69 + * nesting errors.
  70 + */
  71 + enum Scope {
  72 +
  73 + /**
  74 + * An array with no elements requires no separators or newlines before
  75 + * it is closed.
  76 + */
  77 + EMPTY_ARRAY,
  78 +
  79 + /**
  80 + * A array with at least one value requires a comma and newline before
  81 + * the next element.
  82 + */
  83 + NONEMPTY_ARRAY,
  84 +
  85 + /**
  86 + * An object with no keys or values requires no separators or newlines
  87 + * before it is closed.
  88 + */
  89 + EMPTY_OBJECT,
  90 +
  91 + /**
  92 + * An object whose most recent element is a key. The next element must
  93 + * be a value.
  94 + */
  95 + DANGLING_KEY,
  96 +
  97 + /**
  98 + * An object with at least one name/value pair requires a comma and
  99 + * newline before the next element.
  100 + */
  101 + NONEMPTY_OBJECT,
  102 +
  103 + /**
  104 + * A special bracketless array needed by JSONStringer.join() and
  105 + * JSONObject.quote() only. Not used for JSON encoding.
  106 + */
  107 + NULL,
  108 + }
  109 +
  110 + /**
  111 + * Unlike the original implementation, this stack isn't limited to 20
  112 + * levels of nesting.
  113 + */
  114 + private final List<Scope> stack = new ArrayList<Scope>();
  115 +
  116 + /**
  117 + * A string containing a full set of spaces for a single level of
  118 + * indentation, or null for no pretty printing.
  119 + */
  120 + private final String indent;
  121 +
  122 + public JSONStringer() {
  123 + indent = null;
  124 + }
  125 +
  126 + JSONStringer(int indentSpaces) {
  127 + char[] indentChars = new char[indentSpaces];
  128 + Arrays.fill(indentChars, ' ');
  129 + indent = new String(indentChars);
  130 + }
  131 +
  132 + /**
  133 + * Begins encoding a new array. Each call to this method must be paired with
  134 + * a call to {@link #endArray}.
  135 + *
  136 + * @return this stringer.
  137 + */
  138 + public JSONStringer array() throws JSONException {
  139 + return open(Scope.EMPTY_ARRAY, "[");
  140 + }
  141 +
  142 + /**
  143 + * Ends encoding the current array.
  144 + *
  145 + * @return this stringer.
  146 + */
  147 + public JSONStringer endArray() throws JSONException {
  148 + return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]");
  149 + }
  150 +
  151 + /**
  152 + * Begins encoding a new object. Each call to this method must be paired
  153 + * with a call to {@link #endObject}.
  154 + *
  155 + * @return this stringer.
  156 + */
  157 + public JSONStringer object() throws JSONException {
  158 + return open(Scope.EMPTY_OBJECT, "{");
  159 + }
  160 +
  161 + /**
  162 + * Ends encoding the current object.
  163 + *
  164 + * @return this stringer.
  165 + */
  166 + public JSONStringer endObject() throws JSONException {
  167 + return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}");
  168 + }
  169 +
  170 + /**
  171 + * Enters a new scope by appending any necessary whitespace and the given
  172 + * bracket.
  173 + */
  174 + JSONStringer open(Scope empty, String openBracket) throws JSONException {
  175 + if (stack.isEmpty() && out.length() > 0) {
  176 + throw new JSONException("Nesting problem: multiple top-level roots");
  177 + }
  178 + beforeValue();
  179 + stack.add(empty);
  180 + out.append(openBracket);
  181 + return this;
  182 + }
  183 +
  184 + /**
  185 + * Closes the current scope by appending any necessary whitespace and the
  186 + * given bracket.
  187 + */
  188 + JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException {
  189 + Scope context = peek();
  190 + if (context != nonempty && context != empty) {
  191 + throw new JSONException("Nesting problem");
  192 + }
  193 +
  194 + stack.remove(stack.size() - 1);
  195 + if (context == nonempty) {
  196 + newline();
  197 + }
  198 + out.append(closeBracket);
  199 + return this;
  200 + }
  201 +
  202 + /**
  203 + * Returns the value on the top of the stack.
  204 + */
  205 + private Scope peek() throws JSONException {
  206 + if (stack.isEmpty()) {
  207 + throw new JSONException("Nesting problem");
  208 + }
  209 + return stack.get(stack.size() - 1);
  210 + }
  211 +
  212 + /**
  213 + * Replace the value on the top of the stack with the given value.
  214 + */
  215 + private void replaceTop(Scope topOfStack) {
  216 + stack.set(stack.size() - 1, topOfStack);
  217 + }
  218 +
  219 + /**
  220 + * Encodes {@code value}.
  221 + *
  222 + * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
  223 + * Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs}
  224 + * or {@link Double#isInfinite() infinities}.
  225 + * @return this stringer.
  226 + */
  227 + public JSONStringer value(Object value) throws JSONException {
  228 + if (stack.isEmpty()) {
  229 + throw new JSONException("Nesting problem");
  230 + }
  231 +
  232 + if (value instanceof JSONArray) {
  233 + ((JSONArray) value).writeTo(this);
  234 + return this;
  235 +
  236 + } else if (value instanceof JSONObject) {
  237 + ((JSONObject) value).writeTo(this);
  238 + return this;
  239 + }
  240 +
  241 + beforeValue();
  242 +
  243 + if (value == null
  244 + || value instanceof Boolean
  245 + || value == JSONObject.NULL) {
  246 + out.append(value);
  247 +
  248 + } else if (value instanceof Number) {
  249 + out.append(JSONObject.numberToString((Number) value));
  250 +
  251 + } else {
  252 + string(value.toString());
  253 + }
  254 +
  255 + return this;
  256 + }
  257 +
  258 + /**
  259 + * Encodes {@code value} to this stringer.
  260 + *
  261 + * @return this stringer.
  262 + */
  263 + public JSONStringer value(boolean value) throws JSONException {
  264 + if (stack.isEmpty()) {
  265 + throw new JSONException("Nesting problem");
  266 + }
  267 + beforeValue();
  268 + out.append(value);
  269 + return this;
  270 + }
  271 +
  272 + /**
  273 + * Encodes {@code value} to this stringer.
  274 + *
  275 + * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
  276 + * {@link Double#isInfinite() infinities}.
  277 + * @return this stringer.
  278 + */
  279 + public JSONStringer value(double value) throws JSONException {
  280 + if (stack.isEmpty()) {
  281 + throw new JSONException("Nesting problem");
  282 + }
  283 + beforeValue();
  284 + out.append(JSONObject.numberToString(value));
  285 + return this;
  286 + }
  287 +
  288 + /**
  289 + * Encodes {@code value} to this stringer.
  290 + *
  291 + * @return this stringer.
  292 + */
  293 + public JSONStringer value(long value) throws JSONException {
  294 + if (stack.isEmpty()) {
  295 + throw new JSONException("Nesting problem");
  296 + }
  297 + beforeValue();
  298 + out.append(value);
  299 + return this;
  300 + }
  301 +
  302 + private void string(String value) {
  303 + out.append("\"");
  304 + for (int i = 0, length = value.length(); i < length; i++) {
  305 + char c = value.charAt(i);
  306 +
  307 + /*
  308 + * From RFC 4627, "All Unicode characters may be placed within the
  309 + * quotation marks except for the characters that must be escaped:
  310 + * quotation mark, reverse solidus, and the control characters
  311 + * (U+0000 through U+001F)."
  312 + */
  313 + switch (c) {
  314 + case '"':
  315 + case '\\':
  316 + case '/':
  317 + out.append('\\').append(c);
  318 + break;
  319 +
  320 + case '\t':
  321 + out.append("\\t");
  322 + break;
  323 +
  324 + case '\b':
  325 + out.append("\\b");
  326 + break;
  327 +
  328 + case '\n':
  329 + out.append("\\n");
  330 + break;
  331 +
  332 + case '\r':
  333 + out.append("\\r");
  334 + break;
  335 +
  336 + case '\f':
  337 + out.append("\\f");
  338 + break;
  339 +
  340 + default:
  341 + if (c <= 0x1F) {
  342 + out.append(String.format("\\u%04x", (int) c));
  343 + } else {
  344 + out.append(c);
  345 + }
  346 + break;
  347 + }
  348 +
  349 + }
  350 + out.append("\"");
  351 + }
  352 +
  353 + private void newline() {
  354 + if (indent == null) {
  355 + return;
  356 + }
  357 +
  358 + out.append("\n");
  359 + for (int i = 0; i < stack.size(); i++) {
  360 + out.append(indent);
  361 + }
  362 + }
  363 +
  364 + /**
  365 + * Encodes the key (property name) to this stringer.
  366 + *
  367 + * @param name the name of the forthcoming value. May not be null.
  368 + * @return this stringer.
  369 + */
  370 + public JSONStringer key(String name) throws JSONException {
  371 + if (name == null) {
  372 + throw new JSONException("Names must be non-null");
  373 + }
  374 + beforeKey();
  375 + string(name);
  376 + return this;
  377 + }
  378 +
  379 + /**
  380 + * Inserts any necessary separators and whitespace before a name. Also
  381 + * adjusts the stack to expect the key's value.
  382 + */
  383 + private void beforeKey() throws JSONException {
  384 + Scope context = peek();
  385 + if (context == Scope.NONEMPTY_OBJECT) { // first in object
  386 + out.append(',');
  387 + } else if (context != Scope.EMPTY_OBJECT) { // not in an object!
  388 + throw new JSONException("Nesting problem");
  389 + }
  390 + newline();
  391 + replaceTop(Scope.DANGLING_KEY);
  392 + }
  393 +
  394 + /**
  395 + * Inserts any necessary separators and whitespace before a literal value,
  396 + * inline array, or inline object. Also adjusts the stack to expect either a
  397 + * closing bracket or another element.
  398 + */
  399 + private void beforeValue() throws JSONException {
  400 + if (stack.isEmpty()) {
  401 + return;
  402 + }
  403 +
  404 + Scope context = peek();
  405 + if (context == Scope.EMPTY_ARRAY) { // first in array
  406 + replaceTop(Scope.NONEMPTY_ARRAY);
  407 + newline();
  408 + } else if (context == Scope.NONEMPTY_ARRAY) { // another in array
  409 + out.append(',');
  410 + newline();
  411 + } else if (context == Scope.DANGLING_KEY) { // value for key
  412 + out.append(indent == null ? ":" : ": ");
  413 + replaceTop(Scope.NONEMPTY_OBJECT);
  414 + } else if (context != Scope.NULL) {
  415 + throw new JSONException("Nesting problem");
  416 + }
  417 + }
  418 +
  419 + /**
  420 + * Returns the encoded JSON string.
  421 + *
  422 + * <p>If invoked with unterminated arrays or unclosed objects, this method's
  423 + * return value is undefined.
  424 + *
  425 + * <p><strong>Warning:</strong> although it contradicts the general contract
  426 + * of {@link Object#toString}, this method returns null if the stringer
  427 + * contains no data.
  428 + */
  429 + @Override public String toString() {
  430 + return out.length() == 0 ? null : out.toString();
  431 + }
  432 +}
TraceSDK/src/com/mobithink/tracesdk/json/JSONTokener.java
... ... @@ -0,0 +1,611 @@
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package com.mobithink.tracesdk.json;
  18 +
  19 +// Note: this class was written without inspecting the non-free org.json sourcecode.
  20 +
  21 +/**
  22 + * Parses a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
  23 + * encoded string into the corresponding object. Most clients of
  24 + * this class will use only need the {@link #JSONTokener(String) constructor}
  25 + * and {@link #nextValue} method. Example usage: <pre>
  26 + * String json = "{"
  27 + * + " \"query\": \"Pizza\", "
  28 + * + " \"locations\": [ 94043, 90210 ] "
  29 + * + "}";
  30 + *
  31 + * JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
  32 + * String query = object.getString("query");
  33 + * JSONArray locations = object.getJSONArray("locations");</pre>
  34 + *
  35 + * <p>For best interoperability and performance use JSON that complies with
  36 + * RFC 4627, such as that generated by {@link JSONStringer}. For legacy reasons
  37 + * this parser is lenient, so a successful parse does not indicate that the
  38 + * input string was valid JSON. All of the following syntax errors will be
  39 + * ignored:
  40 + * <ul>
  41 + * <li>End of line comments starting with {@code //} or {@code #} and ending
  42 + * with a newline character.
  43 + * <li>C-style comments starting with {@code /*} and ending with
  44 + * {@code *}{@code /}. Such comments may not be nested.
  45 + * <li>Strings that are unquoted or {@code 'single quoted'}.
  46 + * <li>Hexadecimal integers prefixed with {@code 0x} or {@code 0X}.
  47 + * <li>Octal integers prefixed with {@code 0}.
  48 + * <li>Array elements separated by {@code ;}.
  49 + * <li>Unnecessary array separators. These are interpreted as if null was the
  50 + * omitted value.
  51 + * <li>Key-value pairs separated by {@code =} or {@code =>}.
  52 + * <li>Key-value pairs separated by {@code ;}.
  53 + * </ul>
  54 + *
  55 + * <p>Each tokener may be used to parse a single JSON string. Instances of this
  56 + * class are not thread safe. Although this class is nonfinal, it was not
  57 + * designed for inheritance and should not be subclassed. In particular,
  58 + * self-use by overrideable methods is not specified. See <i>Effective Java</i>
  59 + * Item 17, "Design and Document or inheritance or else prohibit it" for further
  60 + * information.
  61 + */
  62 +public class JSONTokener {
  63 +
  64 + /** The input JSON. */
  65 + private final String in;
  66 +
  67 + /**
  68 + * The index of the next character to be returned by {@link #next}. When
  69 + * the input is exhausted, this equals the input's length.
  70 + */
  71 + private int pos;
  72 +
  73 + /**
  74 + * @param in JSON encoded string. Null is not permitted and will yield a
  75 + * tokener that throws {@code NullPointerExceptions} when methods are
  76 + * called.
  77 + */
  78 + public JSONTokener(String in) {
  79 + // consume an optional byte order mark (BOM) if it exists
  80 + if (in != null && in.startsWith("\ufeff")) {
  81 + in = in.substring(1);
  82 + }
  83 + this.in = in;
  84 + }
  85 +
  86 + /**
  87 + * Returns the next value from the input.
  88 + *
  89 + * @return a {@link JSONObject}, {@link JSONArray}, String, Boolean,
  90 + * Integer, Long, Double or {@link JSONObject#NULL}.
  91 + * @throws JSONException if the input is malformed.
  92 + */
  93 + public Object nextValue() throws JSONException {
  94 + int c = nextCleanInternal();
  95 + switch (c) {
  96 + case -1:
  97 + throw syntaxError("End of input");
  98 +
  99 + case '{':
  100 + return readObject();
  101 +
  102 + case '[':
  103 + return readArray();
  104 +
  105 + case '\'':
  106 + case '"':
  107 + return nextString((char) c);
  108 +
  109 + default:
  110 + pos--;
  111 + return readLiteral();
  112 + }
  113 + }
  114 +
  115 + private int nextCleanInternal() throws JSONException {
  116 + while (pos < in.length()) {
  117 + int c = in.charAt(pos++);
  118 + switch (c) {
  119 + case '\t':
  120 + case ' ':
  121 + case '\n':
  122 + case '\r':
  123 + continue;
  124 +
  125 + case '/':
  126 + if (pos == in.length()) {
  127 + return c;
  128 + }
  129 +
  130 + char peek = in.charAt(pos);
  131 + switch (peek) {
  132 + case '*':
  133 + // skip a /* c-style comment */
  134 + pos++;
  135 + int commentEnd = in.indexOf("*/", pos);
  136 + if (commentEnd == -1) {
  137 + throw syntaxError("Unterminated comment");
  138 + }
  139 + pos = commentEnd + 2;
  140 + continue;
  141 +
  142 + case '/':
  143 + // skip a // end-of-line comment
  144 + pos++;
  145 + skipToEndOfLine();
  146 + continue;
  147 +
  148 + default:
  149 + return c;
  150 + }
  151 +
  152 + case '#':
  153 + /*
  154 + * Skip a # hash end-of-line comment. The JSON RFC doesn't
  155 + * specify this behavior, but it's required to parse
  156 + * existing documents. See http://b/2571423.
  157 + */
  158 + skipToEndOfLine();
  159 + continue;
  160 +
  161 + default:
  162 + return c;
  163 + }
  164 + }
  165 +
  166 + return -1;
  167 + }
  168 +
  169 + /**
  170 + * Advances the position until after the next newline character. If the line
  171 + * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
  172 + * caller.
  173 + */
  174 + private void skipToEndOfLine() {
  175 + for (; pos < in.length(); pos++) {
  176 + char c = in.charAt(pos);
  177 + if (c == '\r' || c == '\n') {
  178 + pos++;
  179 + break;
  180 + }
  181 + }
  182 + }
  183 +
  184 + /**
  185 + * Returns the string up to but not including {@code quote}, unescaping any
  186 + * character escape sequences encountered along the way. The opening quote
  187 + * should have already been read. This consumes the closing quote, but does
  188 + * not include it in the returned string.
  189 + *
  190 + * @param quote either ' or ".
  191 + * @throws NumberFormatException if any unicode escape sequences are
  192 + * malformed.
  193 + */
  194 + public String nextString(char quote) throws JSONException {
  195 + /*
  196 + * For strings that are free of escape sequences, we can just extract
  197 + * the result as a substring of the input. But if we encounter an escape
  198 + * sequence, we need to use a StringBuilder to compose the result.
  199 + */
  200 + StringBuilder builder = null;
  201 +
  202 + /* the index of the first character not yet appended to the builder. */
  203 + int start = pos;
  204 +
  205 + while (pos < in.length()) {
  206 + int c = in.charAt(pos++);
  207 + if (c == quote) {
  208 + if (builder == null) {
  209 + // a new string avoids leaking memory
  210 + return new String(in.substring(start, pos - 1));
  211 + } else {
  212 + builder.append(in, start, pos - 1);
  213 + return builder.toString();
  214 + }
  215 + }
  216 +
  217 + if (c == '\\') {
  218 + if (pos == in.length()) {
  219 + throw syntaxError("Unterminated escape sequence");
  220 + }
  221 + if (builder == null) {
  222 + builder = new StringBuilder();
  223 + }
  224 + builder.append(in, start, pos - 1);
  225 + builder.append(readEscapeCharacter());
  226 + start = pos;
  227 + }
  228 + }
  229 +
  230 + throw syntaxError("Unterminated string");
  231 + }
  232 +
  233 + /**
  234 + * Unescapes the character identified by the character or characters that
  235 + * immediately follow a backslash. The backslash '\' should have already
  236 + * been read. This supports both unicode escapes "u000A" and two-character
  237 + * escapes "\n".
  238 + *
  239 + * @throws NumberFormatException if any unicode escape sequences are
  240 + * malformed.
  241 + */
  242 + private char readEscapeCharacter() throws JSONException {
  243 + char escaped = in.charAt(pos++);
  244 + switch (escaped) {
  245 + case 'u':
  246 + if (pos + 4 > in.length()) {
  247 + throw syntaxError("Unterminated escape sequence");
  248 + }
  249 + String hex = in.substring(pos, pos + 4);
  250 + pos += 4;
  251 + return (char) Integer.parseInt(hex, 16);
  252 +
  253 + case 't':
  254 + return '\t';
  255 +
  256 + case 'b':
  257 + return '\b';
  258 +
  259 + case 'n':
  260 + return '\n';
  261 +
  262 + case 'r':
  263 + return '\r';
  264 +
  265 + case 'f':
  266 + return '\f';
  267 +
  268 + case '\'':
  269 + case '"':
  270 + case '\\':
  271 + default:
  272 + return escaped;
  273 + }
  274 + }
  275 +
  276 + /**
  277 + * Reads a null, boolean, numeric or unquoted string literal value. Numeric
  278 + * values will be returned as an Integer, Long, or Double, in that order of
  279 + * preference.
  280 + */
  281 + private Object readLiteral() throws JSONException {
  282 + String literal = nextToInternal("{}[]/\\:,=;# \t\f");
  283 +
  284 + if (literal.length() == 0) {
  285 + throw syntaxError("Expected literal value");
  286 + } else if ("null".equalsIgnoreCase(literal)) {
  287 + return JSONObject.NULL;
  288 + } else if ("true".equalsIgnoreCase(literal)) {
  289 + return Boolean.TRUE;
  290 + } else if ("false".equalsIgnoreCase(literal)) {
  291 + return Boolean.FALSE;
  292 + }
  293 +
  294 + /* try to parse as an integral type... */
  295 + if (literal.indexOf('.') == -1) {
  296 + int base = 10;
  297 + String number = literal;
  298 + if (number.startsWith("0x") || number.startsWith("0X")) {
  299 + number = number.substring(2);
  300 + base = 16;
  301 + } else if (number.startsWith("0") && number.length() > 1) {
  302 + number = number.substring(1);
  303 + base = 8;
  304 + }
  305 + try {
  306 + long longValue = Long.parseLong(number, base);
  307 + if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) {
  308 + return (int) longValue;
  309 + } else {
  310 + return longValue;
  311 + }
  312 + } catch (NumberFormatException e) {
  313 + /*
  314 + * This only happens for integral numbers greater than
  315 + * Long.MAX_VALUE, numbers in exponential form (5e-10) and
  316 + * unquoted strings. Fall through to try floating point.
  317 + */
  318 + }
  319 + }
  320 +
  321 + /* ...next try to parse as a floating point... */
  322 + try {
  323 + return Double.valueOf(literal);
  324 + } catch (NumberFormatException ignored) {
  325 + }
  326 +
  327 + /* ... finally give up. We have an unquoted string */
  328 + return new String(literal); // a new string avoids leaking memory
  329 + }
  330 +
  331 + /**
  332 + * Returns the string up to but not including any of the given characters or
  333 + * a newline character. This does not consume the excluded character.
  334 + */
  335 + private String nextToInternal(String excluded) {
  336 + int start = pos;
  337 + for (; pos < in.length(); pos++) {
  338 + char c = in.charAt(pos);
  339 + if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) {
  340 + return in.substring(start, pos);
  341 + }
  342 + }
  343 + return in.substring(start);
  344 + }
  345 +
  346 + /**
  347 + * Reads a sequence of key/value pairs and the trailing closing brace '}' of
  348 + * an object. The opening brace '{' should have already been read.
  349 + */
  350 + private JSONObject readObject() throws JSONException {
  351 + JSONObject result = new JSONObject();
  352 +
  353 + /* Peek to see if this is the empty object. */
  354 + int first = nextCleanInternal();
  355 + if (first == '}') {
  356 + return result;
  357 + } else if (first != -1) {
  358 + pos--;
  359 + }
  360 +
  361 + while (true) {
  362 + Object name = nextValue();
  363 + if (!(name instanceof String)) {
  364 + if (name == null) {
  365 + throw syntaxError("Names cannot be null");
  366 + } else {
  367 + throw syntaxError("Names must be strings, but " + name
  368 + + " is of type " + name.getClass().getName());
  369 + }
  370 + }
  371 +
  372 + /*
  373 + * Expect the name/value separator to be either a colon ':', an
  374 + * equals sign '=', or an arrow "=>". The last two are bogus but we
  375 + * include them because that's what the original implementation did.
  376 + */
  377 + int separator = nextCleanInternal();
  378 + if (separator != ':' && separator != '=') {
  379 + throw syntaxError("Expected ':' after " + name);
  380 + }
  381 + if (pos < in.length() && in.charAt(pos) == '>') {
  382 + pos++;
  383 + }
  384 +
  385 + result.put((String) name, nextValue());
  386 +
  387 + switch (nextCleanInternal()) {
  388 + case '}':
  389 + return result;
  390 + case ';':
  391 + case ',':
  392 + continue;
  393 + default:
  394 + throw syntaxError("Unterminated object");
  395 + }
  396 + }
  397 + }
  398 +
  399 + /**
  400 + * Reads a sequence of values and the trailing closing brace ']' of an
  401 + * array. The opening brace '[' should have already been read. Note that
  402 + * "[]" yields an empty array, but "[,]" returns a two-element array
  403 + * equivalent to "[null,null]".
  404 + */
  405 + private JSONArray readArray() throws JSONException {
  406 + JSONArray result = new JSONArray();
  407 +
  408 + /* to cover input that ends with ",]". */
  409 + boolean hasTrailingSeparator = false;
  410 +
  411 + while (true) {
  412 + switch (nextCleanInternal()) {
  413 + case -1:
  414 + throw syntaxError("Unterminated array");
  415 + case ']':
  416 + if (hasTrailingSeparator) {
  417 + result.put(null);
  418 + }
  419 + return result;
  420 + case ',':
  421 + case ';':
  422 + /* A separator without a value first means "null". */
  423 + result.put(null);
  424 + hasTrailingSeparator = true;
  425 + continue;
  426 + default:
  427 + pos--;
  428 + }
  429 +
  430 + result.put(nextValue());
  431 +
  432 + switch (nextCleanInternal()) {
  433 + case ']':
  434 + return result;
  435 + case ',':
  436 + case ';':
  437 + hasTrailingSeparator = true;
  438 + continue;
  439 + default:
  440 + throw syntaxError("Unterminated array");
  441 + }
  442 + }
  443 + }
  444 +
  445 + /**
  446 + * Returns an exception containing the given message plus the current
  447 + * position and the entire input string.
  448 + */
  449 + public JSONException syntaxError(String message) {
  450 + return new JSONException(message + this);
  451 + }
  452 +
  453 + /**
  454 + * Returns the current position and the entire input string.
  455 + */
  456 + @Override public String toString() {
  457 + // consistent with the original implementation
  458 + return " at character " + pos + " of " + in;
  459 + }
  460 +
  461 + /*
  462 + * Legacy APIs.
  463 + *
  464 + * None of the methods below are on the critical path of parsing JSON
  465 + * documents. They exist only because they were exposed by the original
  466 + * implementation and may be used by some clients.
  467 + */
  468 +
  469 + /**
  470 + * Returns true until the input has been exhausted.
  471 + */
  472 + public boolean more() {
  473 + return pos < in.length();
  474 + }
  475 +
  476 + /**
  477 + * Returns the next available character, or the null character '\0' if all
  478 + * input has been exhausted. The return value of this method is ambiguous
  479 + * for JSON strings that contain the character '\0'.
  480 + */
  481 + public char next() {
  482 + return pos < in.length() ? in.charAt(pos++) : '\0';
  483 + }
  484 +
  485 + /**
  486 + * Returns the next available character if it equals {@code c}. Otherwise an
  487 + * exception is thrown.
  488 + */
  489 + public char next(char c) throws JSONException {
  490 + char result = next();
  491 + if (result != c) {
  492 + throw syntaxError("Expected " + c + " but was " + result);
  493 + }
  494 + return result;
  495 + }
  496 +
  497 + /**
  498 + * Returns the next character that is not whitespace and does not belong to
  499 + * a comment. If the input is exhausted before such a character can be
  500 + * found, the null character '\0' is returned. The return value of this
  501 + * method is ambiguous for JSON strings that contain the character '\0'.
  502 + */
  503 + public char nextClean() throws JSONException {
  504 + int nextCleanInt = nextCleanInternal();
  505 + return nextCleanInt == -1 ? '\0' : (char) nextCleanInt;
  506 + }
  507 +
  508 + /**
  509 + * Returns the next {@code length} characters of the input.
  510 + *
  511 + * <p>The returned string shares its backing character array with this
  512 + * tokener's input string. If a reference to the returned string may be held
  513 + * indefinitely, you should use {@code new String(result)} to copy it first
  514 + * to avoid memory leaks.
  515 + *
  516 + * @throws JSONException if the remaining input is not long enough to
  517 + * satisfy this request.
  518 + */
  519 + public String next(int length) throws JSONException {
  520 + if (pos + length > in.length()) {
  521 + throw syntaxError(length + " is out of bounds");
  522 + }
  523 + String result = in.substring(pos, pos + length);
  524 + pos += length;
  525 + return result;
  526 + }
  527 +
  528 + /**
  529 + * Returns the {@link String#trim trimmed} string holding the characters up
  530 + * to but not including the first of:
  531 + * <ul>
  532 + * <li>any character in {@code excluded}
  533 + * <li>a newline character '\n'
  534 + * <li>a carriage return '\r'
  535 + * </ul>
  536 + *
  537 + * <p>The returned string shares its backing character array with this
  538 + * tokener's input string. If a reference to the returned string may be held
  539 + * indefinitely, you should use {@code new String(result)} to copy it first
  540 + * to avoid memory leaks.
  541 + *
  542 + * @return a possibly-empty string
  543 + */
  544 + public String nextTo(String excluded) {
  545 + if (excluded == null) {
  546 + throw new NullPointerException();
  547 + }
  548 + return nextToInternal(excluded).trim();
  549 + }
  550 +
  551 + /**
  552 + * Equivalent to {@code nextTo(String.valueOf(excluded))}.
  553 + */
  554 + public String nextTo(char excluded) {
  555 + return nextToInternal(String.valueOf(excluded)).trim();
  556 + }
  557 +
  558 + /**
  559 + * Advances past all input up to and including the next occurrence of
  560 + * {@code thru}. If the remaining input doesn't contain {@code thru}, the
  561 + * input is exhausted.
  562 + */
  563 + public void skipPast(String thru) {
  564 + int thruStart = in.indexOf(thru, pos);
  565 + pos = thruStart == -1 ? in.length() : (thruStart + thru.length());
  566 + }
  567 +
  568 + /**
  569 + * Advances past all input up to but not including the next occurrence of
  570 + * {@code to}. If the remaining input doesn't contain {@code to}, the input
  571 + * is unchanged.
  572 + */
  573 + public char skipTo(char to) {
  574 + int index = in.indexOf(to, pos);
  575 + if (index != -1) {
  576 + pos = index;
  577 + return to;
  578 + } else {
  579 + return '\0';
  580 + }
  581 + }
  582 +
  583 + /**
  584 + * Unreads the most recent character of input. If no input characters have
  585 + * been read, the input is unchanged.
  586 + */
  587 + public void back() {
  588 + if (--pos == -1) {
  589 + pos = 0;
  590 + }
  591 + }
  592 +
  593 + /**
  594 + * Returns the integer [0..15] value for the given hex character, or -1
  595 + * for non-hex input.
  596 + *
  597 + * @param hex a character in the ranges [0-9], [A-F] or [a-f]. Any other
  598 + * character will yield a -1 result.
  599 + */
  600 + public static int dehexchar(char hex) {
  601 + if (hex >= '0' && hex <= '9') {
  602 + return hex - '0';
  603 + } else if (hex >= 'A' && hex <= 'F') {
  604 + return hex - 'A' + 10;
  605 + } else if (hex >= 'a' && hex <= 'f') {
  606 + return hex - 'a' + 10;
  607 + } else {
  608 + return -1;
  609 + }
  610 + }
  611 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/AbstractClient.java
... ... @@ -0,0 +1,140 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.net.URI;
  4 +import java.text.SimpleDateFormat;
  5 +import java.util.Date;
  6 +
  7 +import org.apache.http.cookie.Cookie;
  8 +
  9 +/**
  10 + * Common implementation of IClient.
  11 + *
  12 + */
  13 +public abstract class AbstractClient implements IClient {
  14 +
  15 + UriBuilder uriBuilder;
  16 +
  17 + MultivaluedMap<String, String> headers = new MetadataMap<String, String>();
  18 +
  19 + protected AbstractClient(URI baseUri) {
  20 + this.uriBuilder = new UriBuilder(baseUri);
  21 + }
  22 +
  23 + @Override
  24 + public IClient type(MediaType ct) {
  25 + return type(ct.getTypeName());
  26 + }
  27 +
  28 + @Override
  29 + public IClient type(String type) {
  30 + headers.putSingle(HttpHeaders.CONTENT_TYPE, type);
  31 + return this;
  32 + }
  33 +
  34 + @Override
  35 + public IClient accept(MediaType... types) {
  36 + for (MediaType type : types) {
  37 + headers.addNoDuplicateValue(HttpHeaders.ACCEPT, type.getTypeName());
  38 + }
  39 + return this;
  40 + }
  41 +
  42 + @Override
  43 + public IClient accept(String... types) {
  44 + for (String type : types) {
  45 + headers.addNoDuplicateValue(HttpHeaders.ACCEPT, type);
  46 + }
  47 + return this;
  48 + }
  49 +
  50 + @Override
  51 + public IClient language(String language) {
  52 + headers.putSingle(HttpHeaders.CONTENT_LANGUAGE, language);
  53 + return this;
  54 + }
  55 +
  56 + @Override
  57 + public IClient acceptLanguage(String... languages) {
  58 + for (String s : languages) {
  59 + headers.addNoDuplicateValue(HttpHeaders.ACCEPT_LANGUAGE, s);
  60 + }
  61 + return this;
  62 + }
  63 +
  64 + @Override
  65 + public IClient encoding(String encoding) {
  66 + headers.putSingle(HttpHeaders.CONTENT_ENCODING, encoding);
  67 + return this;
  68 + }
  69 +
  70 + @Override
  71 + public IClient acceptEncoding(String... encodings) {
  72 + for (String s : encodings) {
  73 + headers.addNoDuplicateValue(HttpHeaders.ACCEPT_ENCODING, s);
  74 + }
  75 + return this;
  76 + }
  77 +
  78 + @Override
  79 + public IClient match(String tag, boolean ifNot) {
  80 + String hName = ifNot ? HttpHeaders.IF_NONE_MATCH : HttpHeaders.IF_MATCH;
  81 + headers.putSingle(hName, tag);
  82 + return this;
  83 + }
  84 +
  85 + @Override
  86 + public IClient modified(Date date, boolean ifNot) {
  87 + SimpleDateFormat dateFormat = Utils.getHttpDateFormat();
  88 + String hName = ifNot ? HttpHeaders.IF_UNMODIFIED_SINCE : HttpHeaders.IF_MODIFIED_SINCE;
  89 + headers.putSingle(hName, dateFormat.format(date));
  90 + return this;
  91 + }
  92 +
  93 + @Override
  94 + public IClient cookie(Cookie cookie) {
  95 + headers.addNoDuplicateValue(HttpHeaders.COOKIE, cookie.toString());
  96 + return this;
  97 + }
  98 +
  99 + @Override
  100 + public IClient header(String name, Object... values) {
  101 + if (values == null) {
  102 + throw new IllegalArgumentException();
  103 + }
  104 + for (Object o : values) {
  105 + headers.addNoDuplicateValue(name, o.toString());
  106 + }
  107 + return this;
  108 + }
  109 +
  110 + @Override
  111 + public IClient headers(MultivaluedMap<String, String> map) {
  112 + headers.putAll(map);
  113 + return this;
  114 + }
  115 +
  116 + @Override
  117 + public IClient reset() {
  118 + headers.clear();
  119 + return this;
  120 + }
  121 +
  122 + @Override
  123 + public MultivaluedMap<String, String> getHeaders() {
  124 + return headers;
  125 + }
  126 +
  127 + @Override
  128 + public URI getBaseURI() {
  129 + return uriBuilder.getBaseUri();
  130 + }
  131 +
  132 + @Override
  133 + public URI getCurrentURI() {
  134 + return uriBuilder.build();
  135 + }
  136 +
  137 + @Override
  138 + public abstract Response getResponse();
  139 +
  140 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/DefaultRESTClient.java
... ... @@ -0,0 +1,28 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import com.mobithink.tracesdk.Conf;
  4 +
  5 +public class DefaultRESTClient extends RESTClient {
  6 +
  7 + // private static final String BASE_URI = "http://mtafftracking.com/";
  8 +
  9 + protected DefaultRESTClient(String baseUri) {
  10 + super(baseUri);
  11 + }
  12 +
  13 + public static DefaultRESTClient create() {
  14 + return DefaultRESTClient.create(Conf.App.API_SERVER);
  15 + }
  16 +
  17 + public static DefaultRESTClient create(String baseUri) {
  18 + DefaultRESTClient client = new DefaultRESTClient(baseUri);
  19 + DefaultRESTClient.DEBUG = true;
  20 + client.setRequestEntity(new RequestEntityImplString());
  21 + client.setEntityReader(new EntityReaderImplString());
  22 + client.type(MediaType.APPLICATION_FORM_URLENCODED);
  23 + client.accept(MediaType.TEXT_JSON);
  24 + client.acceptEncoding(EntityReader.ENCODING_GZIP);
  25 + return client;
  26 + }
  27 +
  28 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/EntityReader.java
... ... @@ -0,0 +1,44 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.io.IOException;
  4 +import java.io.InputStream;
  5 +import java.util.zip.GZIPInputStream;
  6 +
  7 +import org.apache.http.HttpEntity;
  8 +import org.apache.http.entity.HttpEntityWrapper;
  9 +
  10 +/**
  11 + * Read entity content to the target type.
  12 + */
  13 +public abstract class EntityReader<T> {
  14 +
  15 + public static final String ENCODING_GZIP = "gzip";
  16 +
  17 + public abstract T read(HttpEntity entity);
  18 +
  19 + protected String debug;
  20 +
  21 + public String getDebug() {
  22 + return this.debug;
  23 + }
  24 +
  25 + public static class GzipDecompressingEntity extends HttpEntityWrapper {
  26 +
  27 + public GzipDecompressingEntity(final HttpEntity entity) {
  28 + super(entity);
  29 + }
  30 +
  31 + @Override
  32 + public InputStream getContent() throws IOException, IllegalStateException {
  33 + // the wrapped entity's getContent() decides about repeatability
  34 + InputStream wrappedin = wrappedEntity.getContent();
  35 + return new GZIPInputStream(wrappedin);
  36 + }
  37 +
  38 + @Override
  39 + public long getContentLength() {
  40 + // length of ungzipped content is not known
  41 + return -1;
  42 + }
  43 + }
  44 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/EntityReaderImplJSON.java
... ... @@ -0,0 +1,42 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.io.IOException;
  4 +
  5 +import org.apache.http.Header;
  6 +import org.apache.http.HttpEntity;
  7 +import org.apache.http.ParseException;
  8 +import org.apache.http.protocol.HTTP;
  9 +import org.apache.http.util.EntityUtils;
  10 +
  11 +import com.mobithink.tracesdk.json.JSONException;
  12 +import com.mobithink.tracesdk.json.JSONObject;
  13 +
  14 +public class EntityReaderImplJSON extends EntityReader<JSONObject> {
  15 +
  16 + @Override
  17 + public JSONObject read(HttpEntity entity) {
  18 + Header encodingHead = entity.getContentEncoding();
  19 + if (encodingHead != null && encodingHead.getValue() != null && encodingHead.getValue().contains(ENCODING_GZIP)) {
  20 + entity = new GzipDecompressingEntity(entity);
  21 + }
  22 + try {
  23 + String result = EntityUtils.toString(entity, HTTP.UTF_8);
  24 + if (RESTClient.DEBUG) {
  25 + this.debug = result;
  26 + }
  27 + if (result != null && result.length() >= 3) {
  28 + return new JSONObject(result);
  29 + } else {
  30 + return null;
  31 + }
  32 + } catch (ParseException e) {
  33 + e.printStackTrace();
  34 + } catch (JSONException e) {
  35 + e.printStackTrace();
  36 + } catch (IOException e) {
  37 + e.printStackTrace();
  38 + }
  39 +
  40 + return null;
  41 + }
  42 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/EntityReaderImplString.java
... ... @@ -0,0 +1,33 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.io.IOException;
  4 +
  5 +import org.apache.http.Header;
  6 +import org.apache.http.HttpEntity;
  7 +import org.apache.http.ParseException;
  8 +import org.apache.http.protocol.HTTP;
  9 +import org.apache.http.util.EntityUtils;
  10 +
  11 +public class EntityReaderImplString extends EntityReader<String> {
  12 +
  13 + @Override
  14 + public String read(HttpEntity entity) {
  15 + Header encodingHead = entity.getContentEncoding();
  16 + if (encodingHead != null && encodingHead.getValue() != null && encodingHead.getValue().contains(ENCODING_GZIP)) {
  17 + entity = new GzipDecompressingEntity(entity);
  18 + }
  19 + try {
  20 + String result = EntityUtils.toString(entity, HTTP.UTF_8);
  21 + if (RESTClient.DEBUG) {
  22 + this.debug = result;
  23 + }
  24 + return result;
  25 + } catch (ParseException e) {
  26 + e.printStackTrace();
  27 + } catch (IOException e) {
  28 + e.printStackTrace();
  29 + }
  30 +
  31 + return null;
  32 + }
  33 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/HttpExecutor.java
... ... @@ -0,0 +1,175 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.io.IOException;
  4 +import java.util.List;
  5 +import java.util.Map;
  6 +
  7 +import org.apache.http.Header;
  8 +import org.apache.http.HttpEntity;
  9 +import org.apache.http.HttpResponse;
  10 +import org.apache.http.client.ClientProtocolException;
  11 +import org.apache.http.client.methods.HttpDelete;
  12 +import org.apache.http.client.methods.HttpGet;
  13 +import org.apache.http.client.methods.HttpOptions;
  14 +import org.apache.http.client.methods.HttpPost;
  15 +import org.apache.http.client.methods.HttpPut;
  16 +import org.apache.http.client.methods.HttpUriRequest;
  17 +import org.apache.http.conn.ClientConnectionManager;
  18 +import org.apache.http.impl.client.DefaultHttpClient;
  19 +import org.apache.http.params.BasicHttpParams;
  20 +import org.apache.http.params.HttpConnectionParams;
  21 +import org.apache.http.params.HttpParams;
  22 +
  23 +import android.util.Log;
  24 +
  25 +public class HttpExecutor {
  26 +
  27 + public static enum HTTP_METHOD {
  28 + GET, POST, PUT, DELETE, OPTIONS
  29 + };
  30 +
  31 + public static final int TIMEOUT_CONNECTION = 15000;
  32 + public static final int TIMEOUT_SOCKET = 10000;
  33 +
  34 + private DefaultHttpClient client;
  35 +
  36 + /**
  37 + * Do send the http request.
  38 + *
  39 + * @param uriBuilder
  40 + * The UriBuilder
  41 + * @param method
  42 + * HTTP method
  43 + * @param headers
  44 + * Multiple values map
  45 + * @param requestEntity
  46 + * Use on POST or PUT request
  47 + * @param entityReader
  48 + * Read the entity return
  49 + * @return The Response instance
  50 + */
  51 + public Response execute(UriBuilder uriBuilder, HTTP_METHOD method, MultivaluedMap<String, String> headers, RequestEntity requestEntity, EntityReader<?> entityReader) {
  52 + client = getHttpClient();
  53 + HttpUriRequest request = getHttpRequest(uriBuilder, method, headers, requestEntity);
  54 + ResponseImpl response = new ResponseImpl();
  55 + try {
  56 + HttpResponse httpResponse = client.execute(request);
  57 + for (Header header : httpResponse.getAllHeaders()) {
  58 + response.setHeader(header.getName(), header.getValue());
  59 + }
  60 + HttpEntity entity = httpResponse.getEntity();
  61 + if (entity != null) {
  62 + response.setEntity(entityReader.read(entity));
  63 + response.setDebug(entityReader.getDebug());
  64 + }
  65 + int httpStatus = httpResponse.getStatusLine().getStatusCode();
  66 + response.setHttpStatus(httpStatus);
  67 +
  68 + } catch (ClientProtocolException e) {
  69 + e.printStackTrace();
  70 + response.setErrorCode(Response.ERROR_CLIENT_NETWORK);
  71 + } catch (IOException e) {
  72 + e.printStackTrace();
  73 + response.setErrorCode(Response.ERROR_CLIENT_IO);
  74 + } catch (Exception e) {
  75 + e.printStackTrace();
  76 + response.setErrorCode(Response.ERROR_CLIENT_UNKNOWN);
  77 + } finally {
  78 + try {
  79 + client.getConnectionManager().shutdown();
  80 + } catch (Exception e) {
  81 + e.printStackTrace();
  82 + }
  83 + }
  84 + return response;
  85 + }
  86 +
  87 + public DefaultHttpClient getHttpClient() {
  88 + HttpParams httpParams = new BasicHttpParams();
  89 + HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_CONNECTION);
  90 + HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_SOCKET);
  91 + return new DefaultHttpClient(httpParams);
  92 + }
  93 +
  94 + public HttpUriRequest getHttpRequest(UriBuilder uriBuilder, HTTP_METHOD method, MultivaluedMap<String, String> headers, RequestEntity entityGenerator) {
  95 + switch (method) {
  96 + case GET:
  97 + return getRequest_GET(uriBuilder, headers);
  98 + case POST:
  99 + return getRequest_POST(uriBuilder, headers, entityGenerator);
  100 + case PUT:
  101 + return getRequest_PUT(uriBuilder, headers, entityGenerator);
  102 + case DELETE:
  103 + return getRequest_DELETE(uriBuilder, headers);
  104 + case OPTIONS:
  105 + return getRequest_OPTIONS(uriBuilder, headers);
  106 + }
  107 + return null;
  108 + }
  109 +
  110 + public HttpUriRequest getRequest_GET(UriBuilder uriBuilder, MultivaluedMap<String, String> headers) {
  111 + HttpGet get = new HttpGet(uriBuilder.buildIncludeParams());
  112 + addToHeader(get, headers);
  113 + return get;
  114 + }
  115 +
  116 + public HttpUriRequest getRequest_POST(UriBuilder uriBuilder, MultivaluedMap<String, String> headers, RequestEntity requestEntity) {
  117 + HttpPost post = new HttpPost(uriBuilder.build());
  118 + addToHeader(post, headers);
  119 + if (requestEntity != null) {
  120 + HttpEntity entity = requestEntity.get(uriBuilder, headers);
  121 + if (entity != null) {
  122 + post.setEntity(entity);
  123 + }
  124 + }
  125 + return post;
  126 + }
  127 +
  128 + public HttpUriRequest getRequest_PUT(UriBuilder uriBuilder, MultivaluedMap<String, String> headers, RequestEntity entityGenerator) {
  129 + HttpPut put = new HttpPut(uriBuilder.build());
  130 + addToHeader(put, headers);
  131 + if (entityGenerator != null) {
  132 + HttpEntity entity = entityGenerator.get(uriBuilder, headers);
  133 + if (entity != null) {
  134 + put.setEntity(entity);
  135 + }
  136 + }
  137 + return put;
  138 + }
  139 +
  140 + public HttpUriRequest getRequest_DELETE(UriBuilder uriBuilder, MultivaluedMap<String, String> headers) {
  141 + HttpDelete delete = new HttpDelete(uriBuilder.buildIncludeParams());
  142 + addToHeader(delete, headers);
  143 + return delete;
  144 + }
  145 +
  146 + public HttpUriRequest getRequest_OPTIONS(UriBuilder uriBuilder, MultivaluedMap<String, String> headers) {
  147 + HttpOptions opt = new HttpOptions(uriBuilder.buildIncludeParams());
  148 + addToHeader(opt, headers);
  149 + return opt;
  150 + }
  151 +
  152 + private void addToHeader(HttpUriRequest request, MultivaluedMap<String, String> headers) {
  153 + if (headers != null && headers.size() > 0) {
  154 + for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
  155 + String k = entry.getKey();
  156 + for (String v : entry.getValue()) {
  157 + request.addHeader(k, v);
  158 + }
  159 + }
  160 + }
  161 + }
  162 +
  163 + public void close() {
  164 + if (client != null) {
  165 + try {
  166 + ClientConnectionManager manager = client.getConnectionManager();
  167 + if (manager != null) {
  168 + manager.shutdown();
  169 + }
  170 + } catch (Exception e) {
  171 + e.printStackTrace();
  172 + }
  173 + }
  174 + }
  175 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/HttpHeaders.java
... ... @@ -0,0 +1,50 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.util.List;
  4 +import java.util.Locale;
  5 +import java.util.Map;
  6 +
  7 +import org.apache.http.cookie.Cookie;
  8 +
  9 +public abstract interface HttpHeaders {
  10 + public static final String ACCEPT = "Accept";
  11 + public static final String ACCEPT_CHARSET = "Accept-Charset";
  12 + public static final String ACCEPT_ENCODING = "Accept-Encoding";
  13 + public static final String ACCEPT_LANGUAGE = "Accept-Language";
  14 + public static final String AUTHORIZATION = "Authorization";
  15 + public static final String CACHE_CONTROL = "Cache-Control";
  16 + public static final String CONTENT_ENCODING = "Content-Encoding";
  17 + public static final String CONTENT_LANGUAGE = "Content-Language";
  18 + public static final String CONTENT_LENGTH = "Content-Length";
  19 + public static final String CONTENT_LOCATION = "Content-Location";
  20 + public static final String CONTENT_TYPE = "Content-Type";
  21 + public static final String DATE = "Date";
  22 + public static final String ETAG = "ETag";
  23 + public static final String EXPIRES = "Expires";
  24 + public static final String HOST = "Host";
  25 + public static final String IF_MATCH = "If-Match";
  26 + public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
  27 + public static final String IF_NONE_MATCH = "If-None-Match";
  28 + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
  29 + public static final String LAST_MODIFIED = "Last-Modified";
  30 + public static final String LOCATION = "Location";
  31 + public static final String USER_AGENT = "User-Agent";
  32 + public static final String VARY = "Vary";
  33 + public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
  34 + public static final String COOKIE = "Cookie";
  35 + public static final String SET_COOKIE = "Set-Cookie";
  36 +
  37 + public abstract List<String> getRequestHeader(String paramString);
  38 +
  39 + public abstract Map<String, String> getRequestHeaders();
  40 +
  41 + public abstract List<MediaType> getAcceptableMediaTypes();
  42 +
  43 + public abstract List<Locale> getAcceptableLanguages();
  44 +
  45 + public abstract MediaType getMediaType();
  46 +
  47 + public abstract Locale getLanguage();
  48 +
  49 + public abstract Map<String, Cookie> getCookies();
  50 +}
0 51 \ No newline at end of file
TraceSDK/src/com/mobithink/tracesdk/rest/client/IClient.java
... ... @@ -0,0 +1,155 @@
  1 +/**
  2 + * Licensed to the Apache Software Foundation (ASF) under one
  3 + * or more contributor license agreements. See the NOTICE file
  4 + * distributed with this work for additional information
  5 + * regarding copyright ownership. The ASF licenses this file
  6 + * to you under the Apache License, Version 2.0 (the
  7 + * "License"); you may not use this file except in compliance
  8 + * with the License. You may obtain a copy of the License at
  9 + *
  10 + * http://www.apache.org/licenses/LICENSE-2.0
  11 + *
  12 + * Unless required by applicable law or agreed to in writing,
  13 + * software distributed under the License is distributed on an
  14 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15 + * KIND, either express or implied. See the License for the
  16 + * specific language governing permissions and limitations
  17 + * under the License.
  18 + */
  19 +package com.mobithink.tracesdk.rest.client;
  20 +
  21 +import java.net.URI;
  22 +import java.util.Date;
  23 +
  24 +import org.apache.http.cookie.Cookie;
  25 +
  26 +/**
  27 + * Represents common proxy and http-centric client capabilities
  28 + *
  29 + */
  30 +public interface IClient {
  31 +
  32 + /**
  33 + * sets HTTP Content-Type header
  34 + * @param MediaType representing Content-Type value
  35 + * @return the updated Client
  36 + */
  37 + IClient type(MediaType ct);
  38 +
  39 + /**
  40 + * sets HTTP Content-Type header
  41 + * @param type Content-Type value
  42 + * @return the updated Client
  43 + */
  44 + IClient type(String type);
  45 +
  46 + /**
  47 + * sets HTTP Accept header
  48 + * @param types list of MediaTypes representing Accept header values
  49 + * @return the updated Client
  50 + */
  51 + IClient accept(MediaType... types);
  52 +
  53 + /**
  54 + * sets HTTP Accept header
  55 + * @param types list of Accept header values
  56 + * @return the updated Client
  57 + */
  58 + IClient accept(String... types);
  59 +
  60 + /**
  61 + * sets HTTP Content-Language header
  62 + * @param language Content-Language header value
  63 + * @return the updated Client
  64 + */
  65 + IClient language(String language);
  66 +
  67 + /**
  68 + * sets HTTP Accept-Language header
  69 + * @param languages list of Accept-Language header values
  70 + * @return the updated Client
  71 + */
  72 + IClient acceptLanguage(String ...languages);
  73 +
  74 + /**
  75 + * sets HTTP Content-Encoding header
  76 + * @param encoding Content-Encoding header value
  77 + * @return the updated Client
  78 + */
  79 + IClient encoding(String encoding);
  80 +
  81 + /**
  82 + * sets HTTP Accept-Encoding header
  83 + * @param encodings list of Accept-Encoding header value
  84 + * @return the updated Client
  85 + */
  86 + IClient acceptEncoding(String ...encodings);
  87 +
  88 + /**
  89 + * sets HTTP If-Match or If-None-Match header
  90 + * @param tag ETag value
  91 + * @param ifNot if true then If-None-Match is set, If-Match otherwise
  92 + * @return the updated Client
  93 + */
  94 + IClient match(String tag, boolean ifNot);
  95 +
  96 + /**
  97 + * sets HTTP If-Modified-Since or If-Unmodified-Since header
  98 + * @param date Date value, will be formated as "EEE, dd MMM yyyy HH:mm:ss zzz"
  99 + * @param ifNot if true then If-Unmodified-Since is set, If-Modified-Since otherwise
  100 + * @return the updated Client
  101 + */
  102 + IClient modified(Date date, boolean ifNot);
  103 +
  104 + /**
  105 + * sets HTTP Cookie header
  106 + * @param cookie Cookie value
  107 + * @return the updated Client
  108 + */
  109 + IClient cookie(Cookie cookie);
  110 +
  111 + /**
  112 + * Sets arbitrary HTTP Header
  113 + * @param name header name
  114 + * @param values list of header values
  115 + * @return the updated Client
  116 + */
  117 + IClient header(String name, Object... values);
  118 +
  119 + /**
  120 + * Sets HTTP Headers
  121 + * @param map headers
  122 + * @return the updated Client
  123 + */
  124 + IClient headers(MultivaluedMap<String, String> map);
  125 +
  126 + /**
  127 + * Resets the headers and response state if any
  128 + * @return the updated Client
  129 + */
  130 + IClient reset();
  131 +
  132 + /**
  133 + * Gets the copy of request headers
  134 + * @return request headers
  135 + */
  136 + MultivaluedMap<String, String> getHeaders();
  137 +
  138 + /**
  139 + * Gets the base URI this Client has been intialized with
  140 + * @return base URI
  141 + */
  142 + URI getBaseURI();
  143 +
  144 + /**
  145 + * Gets the current URI this Client is working with
  146 + * @return current URI
  147 + */
  148 + URI getCurrentURI();
  149 +
  150 + /**
  151 + * Gets the response state if any
  152 + * @return Response response
  153 + */
  154 + Response getResponse();
  155 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/MediaType.java
... ... @@ -0,0 +1,28 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +public class MediaType {
  4 +
  5 + public static final MediaType TEXT_JSON = new MediaType("text/json");
  6 + public static final MediaType TEXT_XML = new MediaType("text/xml");
  7 + public static final MediaType TEXT_HTML = new MediaType("text/html");
  8 + public static final MediaType APPLICATION_FORM_URLENCODED = new MediaType("application/x-www-form-urlencoded");
  9 + public static final MediaType APPLICATION_PROTOBUF = new MediaType("application/protobuf");
  10 +
  11 + private String type;
  12 +
  13 + protected MediaType(String type) {
  14 + this.type = type;
  15 + }
  16 +
  17 + public String getTypeName() {
  18 + return this.type;
  19 + }
  20 +
  21 + public String toString() {
  22 + return this.type;
  23 + }
  24 +
  25 + public boolean equals(String typeName) {
  26 + return this.type.equalsIgnoreCase(typeName);
  27 + }
  28 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/MetadataMap.java
... ... @@ -0,0 +1,270 @@
  1 +/**
  2 + * Licensed to the Apache Software Foundation (ASF) under one
  3 + * or more contributor license agreements. See the NOTICE file
  4 + * distributed with this work for additional information
  5 + * regarding copyright ownership. The ASF licenses this file
  6 + * to you under the Apache License, Version 2.0 (the
  7 + * "License"); you may not use this file except in compliance
  8 + * with the License. You may obtain a copy of the License at
  9 + *
  10 + * http://www.apache.org/licenses/LICENSE-2.0
  11 + *
  12 + * Unless required by applicable law or agreed to in writing,
  13 + * software distributed under the License is distributed on an
  14 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15 + * KIND, either express or implied. See the License for the
  16 + * specific language governing permissions and limitations
  17 + * under the License.
  18 + */
  19 +
  20 +package com.mobithink.tracesdk.rest.client;
  21 +
  22 +import java.util.ArrayList;
  23 +import java.util.Arrays;
  24 +import java.util.Collection;
  25 +import java.util.Collections;
  26 +import java.util.Comparator;
  27 +import java.util.LinkedHashMap;
  28 +import java.util.List;
  29 +import java.util.Map;
  30 +import java.util.Set;
  31 +import java.util.TreeSet;
  32 +
  33 +public class MetadataMap<K, V> implements MultivaluedMap<K, V> {
  34 +
  35 + private boolean caseInsensitive;
  36 + private Map<K, List<V>> m;
  37 +
  38 + public MetadataMap() {
  39 + this.m = new LinkedHashMap<K, List<V>>();
  40 + }
  41 +
  42 + public MetadataMap(int size) {
  43 + this.m = new LinkedHashMap<K, List<V>>(size);
  44 + }
  45 +
  46 + public MetadataMap(Map<K, List<V>> store) {
  47 + this(store, false, false);
  48 + }
  49 +
  50 + public MetadataMap(boolean readOnly, boolean caseInsensitive) {
  51 + this(null, readOnly, caseInsensitive);
  52 + }
  53 +
  54 + public MetadataMap(Map<K, List<V>> store, boolean readOnly, boolean caseInsensitive) {
  55 +
  56 + this(store, true, readOnly, caseInsensitive);
  57 +
  58 + }
  59 +
  60 + // TODO: Review the use of this constructor,
  61 + // refactor the code, copyStore and readOnly are duplicates
  62 + public MetadataMap(Map<K, List<V>> store, boolean copyStore, boolean readOnly, boolean caseInsensitive) {
  63 +
  64 + if (copyStore) {
  65 + this.m = new LinkedHashMap<K, List<V>>();
  66 + if (store != null) {
  67 + for (Map.Entry<K, List<V>> entry : store.entrySet()) {
  68 + List<V> values = new ArrayList<V>(entry.getValue());
  69 + m.put(entry.getKey(), readOnly ? Collections.unmodifiableList(values) : values);
  70 + }
  71 + }
  72 + if (readOnly) {
  73 + this.m = Collections.unmodifiableMap(m);
  74 + }
  75 + } else {
  76 + this.m = store;
  77 + }
  78 + this.caseInsensitive = caseInsensitive;
  79 +
  80 + }
  81 +
  82 + public void add(K key, V value) {
  83 + addValue(key, value, true);
  84 + }
  85 +
  86 + private void addValue(K key, V value, boolean last) {
  87 + List<V> data = getList(key);
  88 + if (last) {
  89 + data.add(value);
  90 + } else {
  91 + data.add(0, value);
  92 + }
  93 + }
  94 +
  95 + private List<V> getList(K key) {
  96 + List<V> data = this.get(key);
  97 + if (data == null) {
  98 + data = new ArrayList<V>();
  99 + m.put(key, data);
  100 + }
  101 + return data;
  102 + }
  103 +
  104 + public V getFirst(K key) {
  105 + List<V> data = this.get(key);
  106 + return data == null ? null : data.get(0);
  107 + }
  108 +
  109 + public void putSingle(K key, V value) {
  110 + List<V> data = new ArrayList<V>();
  111 + data.add(value);
  112 + this.put(key, data);
  113 + }
  114 +
  115 + public void clear() {
  116 + m.clear();
  117 + }
  118 +
  119 + public boolean containsKey(Object key) {
  120 + if (!caseInsensitive) {
  121 + return m.containsKey(key);
  122 + }
  123 + return getMatchingKey(key) != null;
  124 + }
  125 +
  126 + public boolean containsValue(Object value) {
  127 + return m.containsValue(value);
  128 + }
  129 +
  130 + public Set<Entry<K, List<V>>> entrySet() {
  131 + return m.entrySet();
  132 + }
  133 +
  134 + public List<V> get(Object key) {
  135 + if (!caseInsensitive) {
  136 + return m.get(key);
  137 + }
  138 + K realKey = getMatchingKey(key);
  139 + return realKey == null ? null : m.get(realKey);
  140 + }
  141 +
  142 + private K getMatchingKey(Object key) {
  143 + for (K entry : m.keySet()) {
  144 + if (entry.toString().equalsIgnoreCase(key.toString())) {
  145 + return entry;
  146 + }
  147 + }
  148 + return null;
  149 + }
  150 +
  151 + public boolean isEmpty() {
  152 + return m.isEmpty();
  153 + }
  154 +
  155 + public Set<K> keySet() {
  156 + if (!caseInsensitive) {
  157 + return m.keySet();
  158 + } else {
  159 + Set<K> set = new TreeSet<K>(new KeyComparator<K>());
  160 + set.addAll(m.keySet());
  161 + return set;
  162 + }
  163 + }
  164 +
  165 + public List<V> put(K key, List<V> value) {
  166 + K realKey = !caseInsensitive ? key : getMatchingKey(key);
  167 + return m.put(realKey == null ? key : realKey, value);
  168 + }
  169 +
  170 + public void putAll(Map<? extends K, ? extends List<V>> map) {
  171 + if (!caseInsensitive) {
  172 + m.putAll(map);
  173 + } else {
  174 + for (Map.Entry<? extends K, ? extends List<V>> entry : map.entrySet()) {
  175 + this.put(entry.getKey(), entry.getValue());
  176 + }
  177 + }
  178 + }
  179 +
  180 + public List<V> remove(Object key) {
  181 + if (caseInsensitive) {
  182 + K realKey = getMatchingKey(key);
  183 + return m.remove(realKey == null ? key : realKey);
  184 + } else {
  185 + return m.remove(key);
  186 + }
  187 + }
  188 +
  189 + public int size() {
  190 + return m.size();
  191 + }
  192 +
  193 + public Collection<List<V>> values() {
  194 + return m.values();
  195 + }
  196 +
  197 + @Override
  198 + public int hashCode() {
  199 + return m.hashCode();
  200 + }
  201 +
  202 + @Override
  203 + public boolean equals(Object o) {
  204 + return m.equals(o);
  205 + }
  206 +
  207 + public String toString() {
  208 + return m.toString();
  209 + }
  210 +
  211 + private static class KeyComparator<K> implements Comparator<K> {
  212 +
  213 + public int compare(K k1, K k2) {
  214 + String s1 = k1.toString();
  215 + String s2 = k2.toString();
  216 + return s1.compareToIgnoreCase(s2);
  217 + }
  218 +
  219 + }
  220 +
  221 + public void addAll(K key, V... newValues) {
  222 + this.addAllValues(key, Arrays.asList(newValues));
  223 + }
  224 +
  225 + public void addAll(K key, List<V> newValues) {
  226 + this.addAllValues(key, newValues);
  227 + }
  228 +
  229 + private void addAllValues(K key, List<V> newValues) {
  230 + if (newValues == null) {
  231 + throw new NullPointerException("List is empty");
  232 + }
  233 + if (newValues.isEmpty()) {
  234 + return;
  235 + }
  236 + getList(key).addAll(newValues);
  237 + }
  238 +
  239 + public void addFirst(K key, V value) {
  240 + addValue(key, value, false);
  241 + }
  242 +
  243 + public boolean equalsIgnoreValueOrder(MultivaluedMap<K, V> map) {
  244 + Set<K> mapKeys = map.keySet();
  245 + if (mapKeys.size() != m.keySet().size()) {
  246 + return false;
  247 + }
  248 +
  249 + for (K key : mapKeys) {
  250 + List<V> localValues = this.get(key);
  251 + List<V> mapValues = map.get(key);
  252 + if (localValues == null || localValues.size() != mapValues.size() || !localValues.containsAll(mapValues)) {
  253 + return false;
  254 + }
  255 + }
  256 + return true;
  257 + }
  258 +
  259 + @Override
  260 + public void addNoDuplicateValue(K key, V value) {
  261 + if (!isDuplicate(key, value)) {
  262 + add(key, value);
  263 + }
  264 + }
  265 +
  266 + private boolean isDuplicate(K key, V value) {
  267 + List<V> values = get(key);
  268 + return values != null && values.contains(value) ? true : false;
  269 + }
  270 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/MultivaluedMap.java
... ... @@ -0,0 +1,15 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.util.List;
  4 +import java.util.Map;
  5 +
  6 +public abstract interface MultivaluedMap<K, V> extends Map<K, List<V>> {
  7 +
  8 + public abstract void putSingle(K paramK, V paramV);
  9 +
  10 + public abstract void add(K paramK, V paramV);
  11 +
  12 + public abstract void addNoDuplicateValue(K paramK, V paramV);
  13 +
  14 + public abstract V getFirst(K paramK);
  15 +}
0 16 \ No newline at end of file
TraceSDK/src/com/mobithink/tracesdk/rest/client/RESTClient.java
... ... @@ -0,0 +1,180 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.net.URI;
  4 +import java.util.Date;
  5 +
  6 +import org.apache.http.cookie.Cookie;
  7 +
  8 +import com.mobithink.tracesdk.rest.client.HttpExecutor.HTTP_METHOD;
  9 +
  10 +public class RESTClient extends AbstractClient {
  11 +
  12 + public static boolean DEBUG = false;
  13 +
  14 + RequestEntity requestEntity = new RequestEntityImplString();
  15 +
  16 + EntityReader<?> entityReader = new EntityReaderImplJSON();
  17 +
  18 + HttpExecutor httpExecutor;
  19 +
  20 + Response response;
  21 +
  22 + public static RESTClient create(String baseUri) {
  23 + return new RESTClient(baseUri);
  24 + }
  25 +
  26 + public static RESTClient create(URI baseUri) {
  27 + return new RESTClient(baseUri);
  28 + }
  29 +
  30 + protected RESTClient(String baseUri) {
  31 + super(URI.create(baseUri));
  32 + }
  33 +
  34 + protected RESTClient(URI baseUri) {
  35 + super(baseUri);
  36 + }
  37 +
  38 + public RESTClient path(String path) {
  39 + uriBuilder.addPath(path);
  40 + return this;
  41 + }
  42 +
  43 + public RESTClient query(String key, Object value) {
  44 + uriBuilder.addQuery(key, value);
  45 + return this;
  46 + }
  47 +
  48 + public Response get() {
  49 + handle(HTTP_METHOD.GET);
  50 + return getResponse();
  51 + }
  52 +
  53 + public Response post() {
  54 + handle(HTTP_METHOD.POST);
  55 + return getResponse();
  56 + }
  57 +
  58 + public Response put() {
  59 + handle(HTTP_METHOD.PUT);
  60 + return getResponse();
  61 + }
  62 +
  63 + public Response delete() {
  64 + handle(HTTP_METHOD.DELETE);
  65 + return getResponse();
  66 + }
  67 +
  68 + /**
  69 + * Do handle request
  70 + *
  71 + * @param method
  72 + */
  73 + private void handle(HTTP_METHOD method) {
  74 + httpExecutor = new HttpExecutor();
  75 + this.response = httpExecutor.execute(uriBuilder, method, headers, requestEntity, entityReader);
  76 + }
  77 +
  78 + @Override
  79 + public Response getResponse() {
  80 + return this.response;
  81 + }
  82 +
  83 + @Override
  84 + public RESTClient type(MediaType ct) {
  85 + return (RESTClient) super.type(ct);
  86 + }
  87 +
  88 + @Override
  89 + public RESTClient type(String type) {
  90 + return (RESTClient) super.type(type);
  91 + }
  92 +
  93 + @Override
  94 + public RESTClient accept(MediaType... types) {
  95 + return (RESTClient) super.accept(types);
  96 + }
  97 +
  98 + @Override
  99 + public RESTClient accept(String... types) {
  100 + return (RESTClient) super.accept(types);
  101 + }
  102 +
  103 + @Override
  104 + public RESTClient language(String language) {
  105 + return (RESTClient) super.language(language);
  106 + }
  107 +
  108 + @Override
  109 + public RESTClient acceptLanguage(String... languages) {
  110 + return (RESTClient) super.acceptLanguage(languages);
  111 + }
  112 +
  113 + @Override
  114 + public RESTClient encoding(String encoding) {
  115 + return (RESTClient) super.encoding(encoding);
  116 + }
  117 +
  118 + @Override
  119 + public RESTClient acceptEncoding(String... encodings) {
  120 + return (RESTClient) super.acceptEncoding(encodings);
  121 + }
  122 +
  123 + @Override
  124 + public RESTClient match(String tag, boolean ifNot) {
  125 + return (RESTClient) super.match(tag, ifNot);
  126 + }
  127 +
  128 + @Override
  129 + public RESTClient modified(Date date, boolean ifNot) {
  130 + return (RESTClient) super.modified(date, ifNot);
  131 + }
  132 +
  133 + @Override
  134 + public RESTClient cookie(Cookie cookie) {
  135 + return (RESTClient) super.cookie(cookie);
  136 + }
  137 +
  138 + @Override
  139 + public RESTClient header(String name, Object... values) {
  140 + return (RESTClient) super.header(name, values);
  141 + }
  142 +
  143 + @Override
  144 + public RESTClient headers(MultivaluedMap<String, String> map) {
  145 + return (RESTClient) super.headers(map);
  146 + }
  147 +
  148 + @Override
  149 + public RESTClient reset() {
  150 + return (RESTClient) super.reset();
  151 + }
  152 +
  153 + /**
  154 + * set a RequestEntity that the request will format params.
  155 + *
  156 + * @param requestEntity
  157 + * @return
  158 + */
  159 + public RESTClient setRequestEntity(RequestEntity requestEntity) {
  160 + this.requestEntity = requestEntity;
  161 + return this;
  162 + }
  163 +
  164 + /**
  165 + * Set an EntityReader which to read the response entity and parse it to the target format.
  166 + *
  167 + * @param reader
  168 + * @return
  169 + */
  170 + public RESTClient setEntityReader(EntityReader<?> reader) {
  171 + this.entityReader = reader;
  172 + return this;
  173 + }
  174 +
  175 + public void close() {
  176 + if (httpExecutor != null) {
  177 + httpExecutor.close();
  178 + }
  179 + }
  180 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/RequestEntity.java
... ... @@ -0,0 +1,21 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.io.UnsupportedEncodingException;
  4 +
  5 +import org.apache.http.HttpEntity;
  6 +import org.apache.http.entity.StringEntity;
  7 +import org.apache.http.protocol.HTTP;
  8 +
  9 +public abstract class RequestEntity {
  10 +
  11 + public abstract HttpEntity get(UriBuilder uriBuilder, MultivaluedMap<String, String> headers);
  12 +
  13 + protected StringEntity getStringEntity(String params) {
  14 + try {
  15 + return new StringEntity(params, HTTP.UTF_8);
  16 + } catch (UnsupportedEncodingException e) {
  17 + e.printStackTrace();
  18 + }
  19 + return null;
  20 + }
  21 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/RequestEntityImplJSON.java
... ... @@ -0,0 +1,16 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import org.apache.http.HttpEntity;
  4 +
  5 +import android.util.Log;
  6 +
  7 +import com.mobithink.tracesdk.json.JSONObject;
  8 +
  9 +public class RequestEntityImplJSON extends RequestEntity {
  10 +
  11 + @Override
  12 + public HttpEntity get(UriBuilder uriBuilder, MultivaluedMap<String, String> headers) {
  13 + String entityStr = new JSONObject(uriBuilder.getQuery()).toString();
  14 + return getStringEntity(entityStr);
  15 + }
  16 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/RequestEntityImplString.java
... ... @@ -0,0 +1,16 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import org.apache.http.HttpEntity;
  4 +
  5 +import android.util.Log;
  6 +
  7 +public class RequestEntityImplString extends RequestEntity {
  8 +
  9 + @Override
  10 + public HttpEntity get(UriBuilder uriBuilder, MultivaluedMap<String, String> headers) {
  11 + String params = uriBuilder.assambleParams();
  12 + Log.d("RequestEntityImplString", "params str:"+params);
  13 + return getStringEntity(params);
  14 + }
  15 +
  16 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/Response.java
... ... @@ -0,0 +1,43 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +public abstract class Response {
  4 +
  5 + public static final int ERROR_CLIENT_NETWORK = -100;
  6 + public static final int ERROR_CLIENT_IO = -200;
  7 + public static final int ERROR_CLIENT_UNKNOWN = -300;
  8 +
  9 + public abstract Object getEntity();
  10 +
  11 + public abstract int getHttpStatus();
  12 +
  13 + public abstract int getErrorCode();
  14 +
  15 + public abstract MultivaluedMap<String, Object> getHeaderMetadata();
  16 +
  17 + // ///////////////////////////////////////////////
  18 +
  19 + public abstract String getContentType();
  20 +
  21 + public abstract String getContentEncoding();
  22 +
  23 + public abstract String getContentLanguage();
  24 +
  25 + public abstract String getLocation();
  26 +
  27 + public abstract String getContentLocation();
  28 +
  29 + public abstract String getETag();
  30 +
  31 + public abstract String getLastModified();
  32 +
  33 + public abstract String getCacheControl();
  34 +
  35 + public abstract String getExpires();
  36 +
  37 + public abstract long getExpiresTime();
  38 +
  39 + public abstract String getCookie();
  40 +
  41 + public abstract String getDebug();
  42 +
  43 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/ResponseImpl.java
... ... @@ -0,0 +1,150 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.text.ParseException;
  4 +import java.text.SimpleDateFormat;
  5 +import java.util.Date;
  6 +
  7 +public final class ResponseImpl extends Response {
  8 +
  9 + private int httpStatus;
  10 + private int errorCode;
  11 + private Object entity;
  12 + private MultivaluedMap<String, Object> metadata = new MetadataMap<String, Object>();
  13 +
  14 + private String debug;
  15 +
  16 + public void setDebug(String debugStr) {
  17 + this.debug = debugStr;
  18 + }
  19 +
  20 + public String getDebug() {
  21 + return this.debug;
  22 + }
  23 +
  24 + @Override
  25 + public Object getEntity() {
  26 + return this.entity;
  27 + }
  28 +
  29 + @Override
  30 + public int getHttpStatus() {
  31 + return this.httpStatus;
  32 + }
  33 +
  34 + @Override
  35 + public MultivaluedMap<String, Object> getHeaderMetadata() {
  36 + return this.metadata;
  37 + }
  38 +
  39 + @Override
  40 + public int getErrorCode() {
  41 + return this.errorCode;
  42 + }
  43 +
  44 + protected void setHttpStatus(int s) {
  45 + this.httpStatus = s;
  46 + }
  47 +
  48 + public void setErrorCode(int code) {
  49 + this.errorCode = code;
  50 + }
  51 +
  52 + protected void setEntity(Object paramObject) {
  53 + this.entity = paramObject;
  54 + }
  55 +
  56 + @Override
  57 + public String getContentType() {
  58 + return getHeaderValue(HttpHeaders.CONTENT_TYPE).toString();
  59 + }
  60 +
  61 + @Override
  62 + public String getContentEncoding() {
  63 + return getHeaderValue(HttpHeaders.CONTENT_ENCODING).toString();
  64 + }
  65 +
  66 + @Override
  67 + public String getContentLanguage() {
  68 + return getHeaderValue(HttpHeaders.CONTENT_LANGUAGE).toString();
  69 + }
  70 +
  71 + @Override
  72 + public String getLocation() {
  73 + return getHeaderValue(HttpHeaders.LOCATION).toString();
  74 + }
  75 +
  76 + @Override
  77 + public String getContentLocation() {
  78 + return getHeaderValue(HttpHeaders.CONTENT_LOCATION).toString();
  79 + }
  80 +
  81 + @Override
  82 + public String getETag() {
  83 + return getHeaderValue(HttpHeaders.ETAG).toString();
  84 + }
  85 +
  86 + @Override
  87 + public String getLastModified() {
  88 + return getHeaderValue(HttpHeaders.LAST_MODIFIED).toString();
  89 + }
  90 +
  91 + @Override
  92 + public String getCacheControl() {
  93 + return getHeaderValue(HttpHeaders.CACHE_CONTROL).toString();
  94 + }
  95 +
  96 + @Override
  97 + public String getExpires() {
  98 + return getHeaderValue(HttpHeaders.EXPIRES).toString();
  99 + }
  100 +
  101 + @Override
  102 + public long getExpiresTime() {
  103 + String ex = getHeaderValue("Cache-Expires").toString();
  104 + if (ex == null || ex.length() == 0) {
  105 + return 0;
  106 + }
  107 + try {
  108 + return Long.valueOf(ex) * 1000;
  109 + } catch (Exception e) {
  110 + return 0;
  111 + }
  112 + }
  113 +
  114 + @Override
  115 + public String getCookie() {
  116 + return getHeaderValue(HttpHeaders.SET_COOKIE).toString();
  117 + }
  118 +
  119 + public void setHeader(String name, String value) {
  120 + if (value == null) {
  121 + metadata.remove(name);
  122 + } else {
  123 + metadata.putSingle(name, value.toString());
  124 + }
  125 + }
  126 +
  127 + private Object getHeaderValue(String name) {
  128 + Object value = metadata.getFirst(name);
  129 + if (value == null) {
  130 + return "";
  131 + }
  132 + return value;
  133 + }
  134 +
  135 + public String toHttpDate(Date date) {
  136 + SimpleDateFormat format = Utils.getHttpDateFormat();
  137 + return format.format(date);
  138 + }
  139 +
  140 + public Date toDate(String dateOfGMT) {
  141 + SimpleDateFormat format = Utils.getHttpDateFormat();
  142 + try {
  143 + return format.parse(dateOfGMT);
  144 + } catch (ParseException e) {
  145 + e.printStackTrace();
  146 + return null;
  147 + }
  148 + }
  149 +
  150 +}
0 151 \ No newline at end of file
TraceSDK/src/com/mobithink/tracesdk/rest/client/UriBuilder.java
... ... @@ -0,0 +1,89 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.io.UnsupportedEncodingException;
  4 +import java.net.URI;
  5 +import java.net.URLEncoder;
  6 +import java.util.ArrayList;
  7 +import java.util.Iterator;
  8 +import java.util.LinkedHashMap;
  9 +import java.util.List;
  10 +import java.util.Map.Entry;
  11 +
  12 +import android.util.Log;
  13 +
  14 +public class UriBuilder {
  15 +
  16 + private URI baseUri;
  17 + private List<String> paths = new ArrayList<String>();
  18 + private LinkedHashMap<String, Object> query = new LinkedHashMap<String, Object>();
  19 +
  20 + public UriBuilder(URI uri) {
  21 + this.baseUri = uri;
  22 + }
  23 +
  24 + public void addPath(String path) {
  25 + paths.add(path);
  26 + }
  27 +
  28 + public void addQuery(String key, Object value) {
  29 + query.put(key, value);
  30 + }
  31 +
  32 + public URI getBaseUri() {
  33 + return baseUri;
  34 + }
  35 +
  36 + public URI build() {
  37 + return URI.create(baseUri.toString() + assamblePath());
  38 + }
  39 +
  40 + public URI buildIncludeParams() {
  41 + return URI.create(baseUri.toString() + assamblePathAndParams());
  42 + }
  43 +
  44 + private String assamblePathAndParams() {
  45 + StringBuilder strBuilder = new StringBuilder(assamblePath());
  46 + String params = assambleParams();
  47 + if (params != null && params.length() > 0) {
  48 + strBuilder.append("?").append(params);
  49 + }
  50 + return strBuilder.toString();
  51 + }
  52 +
  53 + private String assamblePath() {
  54 + if (paths != null && paths.size() > 0) {
  55 + StringBuilder strBuilder = new StringBuilder();
  56 + Iterator<String> iter = paths.iterator();
  57 + while (iter.hasNext()) {
  58 + strBuilder.append("/").append(iter.next());
  59 + }
  60 + return strBuilder.toString();
  61 + }
  62 + return "";
  63 + }
  64 +
  65 + public String assambleParams() {
  66 + if (query != null && query.size() > 0) {
  67 + StringBuilder strBuilder = new StringBuilder();
  68 + int i = 0;
  69 + for (Entry<String, Object> entry : query.entrySet()) {
  70 + if (i > 0) {
  71 + strBuilder.append("&");
  72 + }
  73 + try {
  74 + Object v = entry.getValue();
  75 + strBuilder.append(entry.getKey()).append("=").append(URLEncoder.encode(v == null ? "" : v.toString(), "utf-8"));
  76 + } catch (UnsupportedEncodingException e) {
  77 + e.printStackTrace();
  78 + }
  79 + i++;
  80 + }
  81 + return strBuilder.toString();
  82 + }
  83 + return "";
  84 + }
  85 +
  86 + public LinkedHashMap<String, Object> getQuery() {
  87 + return this.query;
  88 + }
  89 +}
TraceSDK/src/com/mobithink/tracesdk/rest/client/Utils.java
... ... @@ -0,0 +1,44 @@
  1 +package com.mobithink.tracesdk.rest.client;
  2 +
  3 +import java.text.ParseException;
  4 +import java.text.SimpleDateFormat;
  5 +import java.util.Date;
  6 +import java.util.Locale;
  7 +import java.util.TimeZone;
  8 +
  9 +public class Utils {
  10 +
  11 + public static SimpleDateFormat getHttpDateFormat() {
  12 + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
  13 + TimeZone tZone = TimeZone.getTimeZone("GMT");
  14 + dateFormat.setTimeZone(tZone);
  15 + return dateFormat;
  16 + }
  17 +
  18 + public static long GMT2LocaleTime(long time_GMT) {
  19 + try {
  20 + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
  21 + String timeStr_GMT = dateFormat.format(new Date(time_GMT));
  22 + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
  23 + Date date = dateFormat.parse(timeStr_GMT);
  24 +
  25 + dateFormat.setTimeZone(TimeZone.getDefault());
  26 + String timeStr_locale = dateFormat.format(date);
  27 + return dateFormat.parse(timeStr_locale).getTime();
  28 + } catch (ParseException e) {
  29 + e.printStackTrace();
  30 + }
  31 +
  32 + return 0;
  33 + }
  34 +
  35 + public static long getTime(String timeStr) {
  36 + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
  37 + try {
  38 + return dateFormat.parse(timeStr).getTime();
  39 + } catch (ParseException e) {
  40 + e.printStackTrace();
  41 + }
  42 + return 0;
  43 + }
  44 +}
TraceSDK/src/com/mobithink/tracesdk/utils/LogUtil.java
... ... @@ -0,0 +1,28 @@
  1 +package com.mobithink.tracesdk.utils;
  2 +
  3 +import com.mobithink.tracesdk.Conf;
  4 +
  5 +import android.util.Log;
  6 +
  7 +public class LogUtil {
  8 +
  9 + public static void d(String tag, String msg) {
  10 + if (Conf.Env.LOG_ON)
  11 + Log.d(tag, msg);
  12 + }
  13 +
  14 + public static void i(String tag, String msg) {
  15 + if (Conf.Env.LOG_ON)
  16 + Log.i(tag, msg);
  17 + }
  18 +
  19 + public static void w(String tag, String msg) {
  20 + if (Conf.Env.LOG_ON)
  21 + Log.w(tag, msg);
  22 + }
  23 +
  24 + public static void e(String tag, String msg) {
  25 + if (Conf.Env.LOG_ON)
  26 + Log.e(tag, msg);
  27 + }
  28 +}
TraceSDK/src/com/mobithink/tracesdk/utils/Util.java
... ... @@ -0,0 +1,98 @@
  1 +package com.mobithink.tracesdk.utils;
  2 +
  3 +import java.security.MessageDigest;
  4 +import java.security.NoSuchAlgorithmException;
  5 +import java.util.UUID;
  6 +
  7 +import android.bluetooth.BluetoothAdapter;
  8 +import android.content.Context;
  9 +import android.net.wifi.WifiInfo;
  10 +import android.net.wifi.WifiManager;
  11 +import android.provider.Settings;
  12 +import android.provider.Settings.Secure;
  13 +import android.telephony.TelephonyManager;
  14 +import android.text.TextUtils;
  15 +
  16 +public class Util {
  17 +
  18 + public static String getIMEI(Context context) {
  19 + TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  20 + String imei = tm.getDeviceId();
  21 + return imei;
  22 + }
  23 +
  24 + public static String getMacAddr(Context context) {
  25 + WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  26 + if (wm.getWifiState() == WifiManager.WIFI_STATE_ENABLED)
  27 + return wm.getConnectionInfo().getMacAddress();
  28 + else
  29 + return "";
  30 + }
  31 +
  32 + public static String getAndroidId(Context context) {
  33 + // fetch androidId
  34 + String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
  35 + return androidId;
  36 + }
  37 +
  38 + public static String getUniqueId(Context context) {
  39 + String did = Settings.System.getString(context.getContentResolver(), "deviceId");
  40 + if (did == null || did.length() == 0) {
  41 + try {
  42 + // fetch imei
  43 + TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  44 + String imei = tm.getDeviceId();
  45 + String simSN = tm.getSimSerialNumber();
  46 + // fetch bluetooth mac addr
  47 + BluetoothAdapter m_BluetoothAdapter = null; // Local Bluetooth
  48 + // adapter
  49 + m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  50 + String BTMAC = m_BluetoothAdapter.getAddress();
  51 + // fetch wifi mac
  52 + WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  53 + WifiInfo wifiInf = wifiMan.getConnectionInfo();
  54 +
  55 + String macAddr = wifiInf.getMacAddress();
  56 + if (macAddr != null) {
  57 + macAddr = macAddr.replace(":", "");
  58 + }
  59 + // fetch androidId
  60 + String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
  61 +
  62 + String figureStr = imei + simSN + BTMAC + macAddr + androidId;
  63 + if (!TextUtils.isEmpty(figureStr))
  64 + did = md5(figureStr);
  65 + else
  66 + did = UUID.randomUUID().toString();
  67 + Settings.System.putString(context.getContentResolver(), "deviceId", did);
  68 + } catch (Exception ex) {
  69 + ex.printStackTrace();
  70 + }
  71 + }
  72 + return did;
  73 + }
  74 +
  75 + public static String md5(String input) {
  76 + MessageDigest m = null;
  77 + try {
  78 + m = MessageDigest.getInstance("MD5");
  79 + } catch (NoSuchAlgorithmException e) {
  80 + e.printStackTrace();
  81 + }
  82 + m.update(input.getBytes(), 0, input.length());
  83 + byte p_md5Data[] = m.digest();
  84 +
  85 + String mOutput = new String();
  86 + for (int i = 0; i < p_md5Data.length; i++) {
  87 + int b = (0xFF & p_md5Data[i]);
  88 + // if it is a single digit, make sure it have 0 in front (proper
  89 + // padding)
  90 + if (b <= 0xF)
  91 + mOutput += "0";
  92 + // add number to string
  93 + mOutput += Integer.toHexString(b);
  94 + }
  95 + // hex string to uppercase
  96 + return mOutput.toUpperCase();
  97 + }
  98 +}
TraceSDKSample/.classpath
... ... @@ -0,0 +1,9 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<classpath>
  3 + <classpathentry kind="src" path="src"/>
  4 + <classpathentry kind="src" path="gen"/>
  5 + <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
  6 + <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
  7 + <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
  8 + <classpathentry kind="output" path="bin/classes"/>
  9 +</classpath>
TraceSDKSample/.project
... ... @@ -0,0 +1,33 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<projectDescription>
  3 + <name>TraceSDKSample</name>
  4 + <comment></comment>
  5 + <projects>
  6 + </projects>
  7 + <buildSpec>
  8 + <buildCommand>
  9 + <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
  10 + <arguments>
  11 + </arguments>
  12 + </buildCommand>
  13 + <buildCommand>
  14 + <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
  15 + <arguments>
  16 + </arguments>
  17 + </buildCommand>
  18 + <buildCommand>
  19 + <name>org.eclipse.jdt.core.javabuilder</name>
  20 + <arguments>
  21 + </arguments>
  22 + </buildCommand>
  23 + <buildCommand>
  24 + <name>com.android.ide.eclipse.adt.ApkBuilder</name>
  25 + <arguments>
  26 + </arguments>
  27 + </buildCommand>
  28 + </buildSpec>
  29 + <natures>
  30 + <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
  31 + <nature>org.eclipse.jdt.core.javanature</nature>
  32 + </natures>
  33 +</projectDescription>
TraceSDKSample/AndroidManifest.xml
... ... @@ -0,0 +1,30 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3 + package="com.mobithink.tracesdk.sample"
  4 + android:versionCode="1"
  5 + android:versionName="1.0" >
  6 +
  7 + <uses-sdk
  8 + android:minSdkVersion="8"
  9 + android:targetSdkVersion="10" />
  10 + <uses-permission android:name="android.permission.INTERNET"/>
  11 + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  12 + <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
  13 +
  14 + <application
  15 + android:allowBackup="true"
  16 + android:icon="@drawable/ic_launcher"
  17 + android:label="@string/app_name"
  18 + android:theme="@style/AppTheme" >
  19 + <activity
  20 + android:name="com.mobithink.tracesdk.sample.MainActivity"
  21 + android:label="@string/app_name" >
  22 + <intent-filter>
  23 + <action android:name="android.intent.action.MAIN" />
  24 +
  25 + <category android:name="android.intent.category.LAUNCHER" />
  26 + </intent-filter>
  27 + </activity>
  28 + </application>
  29 +
  30 +</manifest>
TraceSDKSample/bin/AndroidManifest.xml
... ... @@ -0,0 +1,30 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3 + package="com.mobithink.tracesdk.sample"
  4 + android:versionCode="1"
  5 + android:versionName="1.0" >
  6 +
  7 + <uses-sdk
  8 + android:minSdkVersion="8"
  9 + android:targetSdkVersion="10" />
  10 + <uses-permission android:name="android.permission.INTERNET"/>
  11 + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  12 + <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
  13 +
  14 + <application
  15 + android:allowBackup="true"
  16 + android:icon="@drawable/ic_launcher"
  17 + android:label="@string/app_name"
  18 + android:theme="@style/AppTheme" >
  19 + <activity
  20 + android:name="com.mobithink.tracesdk.sample.MainActivity"
  21 + android:label="@string/app_name" >
  22 + <intent-filter>
  23 + <action android:name="android.intent.action.MAIN" />
  24 +
  25 + <category android:name="android.intent.category.LAUNCHER" />
  26 + </intent-filter>
  27 + </activity>
  28 + </application>
  29 +
  30 +</manifest>
TraceSDKSample/bin/R.txt
... ... @@ -0,0 +1,14 @@
  1 +int dimen activity_horizontal_margin 0x7f060000
  2 +int dimen activity_vertical_margin 0x7f060001
  3 +int drawable ic_launcher 0x7f020000
  4 +int id container 0x7f070000
  5 +int id purchase 0x7f070003
  6 +int id send 0x7f070001
  7 +int id upgrade 0x7f070002
  8 +int layout activity_main 0x7f030000
  9 +int layout fragment_main 0x7f030001
  10 +int string action_settings 0x7f040002
  11 +int string app_name 0x7f040000
  12 +int string hello_world 0x7f040001
  13 +int style AppBaseTheme 0x7f050000
  14 +int style AppTheme 0x7f050001
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/BuildConfig.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/MainActivity$1.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/MainActivity.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R$attr.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R$dimen.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R$drawable.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R$id.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R$layout.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R$string.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R$style.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracesdk/sample/R.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracksdk/R$drawable.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracksdk/R$string.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracksdk/R$style.class
No preview for this file type
TraceSDKSample/bin/classes/com/mobithink/tracksdk/R.class
No preview for this file type
TraceSDKSample/bin/dexedLibs/android-support-v4-c5dd981f46e5a43e2371ed794bb1e533.jar
No preview for this file type
TraceSDKSample/bin/dexedLibs/tracesdk-1785c0d93fefc13a56ba69391ae6943c.jar
No preview for this file type
TraceSDKSample/bin/jarlist.cache
... ... @@ -0,0 +1,5 @@
  1 +# cache for current jar dependency. DO NOT EDIT.
  2 +# format is <lastModified> <length> <SHA-1> <path>
  3 +# Encoding is UTF-8
  4 +1408593124631 648327 ded9acc6a9792b8f1afc470f0c9cd36d178914cd E:\Project\TraceSDKSample\libs\android-support-v4.jar
  5 +1408592397716 648327 ded9acc6a9792b8f1afc470f0c9cd36d178914cd E:\Project\TraceSDK\libs\android-support-v4.jar
TraceSDKSample/bin/res/crunch/drawable-hdpi/ic_launcher.png

8.98 KB

TraceSDKSample/bin/res/crunch/drawable-mdpi/ic_launcher.png

4.94 KB

TraceSDKSample/bin/res/crunch/drawable-xhdpi/ic_launcher.png

13.7 KB

TraceSDKSample/gen/com/mobithink/tracesdk/sample/BuildConfig.java
... ... @@ -0,0 +1,6 @@
  1 +/** Automatically generated file. DO NOT MODIFY */
  2 +package com.mobithink.tracesdk.sample;
  3 +
  4 +public final class BuildConfig {
  5 + public final static boolean DEBUG = true;
  6 +}
0 7 \ No newline at end of file
TraceSDKSample/gen/com/mobithink/tracesdk/sample/R.java
... ... @@ -0,0 +1,81 @@
  1 +/* AUTO-GENERATED FILE. DO NOT MODIFY.
  2 + *
  3 + * This class was automatically generated by the
  4 + * aapt tool from the resource data it found. It
  5 + * should not be modified by hand.
  6 + */
  7 +
  8 +package com.mobithink.tracesdk.sample;
  9 +
  10 +public final class R {
  11 + public static final class attr {
  12 + }
  13 + public static final class dimen {
  14 + /** Default screen margins, per the Android Design guidelines.
  15 +
  16 + Example customization of dimensions originally defined in res/values/dimens.xml
  17 + (such as screen margins) for screens with more than 820dp of available width. This
  18 + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively).
  19 +
  20 + */
  21 + public static final int activity_horizontal_margin=0x7f060000;
  22 + public static final int activity_vertical_margin=0x7f060001;
  23 + }
  24 + public static final class drawable {
  25 + public static final int ic_launcher=0x7f020000;
  26 + }
  27 + public static final class id {
  28 + public static final int container=0x7f070000;
  29 + public static final int purchase=0x7f070003;
  30 + public static final int send=0x7f070001;
  31 + public static final int upgrade=0x7f070002;
  32 + }
  33 + public static final class layout {
  34 + public static final int activity_main=0x7f030000;
  35 + public static final int fragment_main=0x7f030001;
  36 + }
  37 + public static final class string {
  38 + public static final int action_settings=0x7f040002;
  39 + public static final int app_name=0x7f040000;
  40 + public static final int hello_world=0x7f040001;
  41 + }
  42 + public static final class style {
  43 + /**
  44 + Base application theme, dependent on API level. This theme is replaced
  45 + by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
  46 +
  47 +
  48 + Theme customizations available in newer API levels can go in
  49 + res/values-vXX/styles.xml, while customizations related to
  50 + backward-compatibility can go here.
  51 +
  52 +
  53 + Base application theme for API 11+. This theme completely replaces
  54 + AppBaseTheme from res/values/styles.xml on API 11+ devices.
  55 +
  56 + API 11 theme customizations can go here.
  57 +
  58 + Base application theme for API 14+. This theme completely replaces
  59 + AppBaseTheme from BOTH res/values/styles.xml and
  60 + res/values-v11/styles.xml on API 14+ devices.
  61 +
  62 + API 14 theme customizations can go here.
  63 +
  64 + Base application theme, dependent on API level. This theme is replaced
  65 + by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
  66 +
  67 +
  68 + Theme customizations available in newer API levels can go in
  69 + res/values-vXX/styles.xml, while customizations related to
  70 + backward-compatibility can go here.
  71 +
  72 + */
  73 + public static final int AppBaseTheme=0x7f050000;
  74 + /** Application theme.
  75 + All customizations that are NOT specific to a particular API-level can go here.
  76 + Application theme.
  77 + All customizations that are NOT specific to a particular API-level can go here.
  78 + */
  79 + public static final int AppTheme=0x7f050001;
  80 + }
  81 +}
TraceSDKSample/gen/com/mobithink/tracksdk/R.java
... ... @@ -0,0 +1,20 @@
  1 +/* AUTO-GENERATED FILE. DO NOT MODIFY.
  2 + *
  3 + * This class was automatically generated by the
  4 + * aapt tool from the resource data it found. It
  5 + * should not be modified by hand.
  6 + */
  7 +package com.mobithink.tracksdk;
  8 +
  9 +public final class R {
  10 + public static final class drawable {
  11 + public static final int ic_launcher = 0x7f020000;
  12 + }
  13 + public static final class string {
  14 + public static final int app_name = 0x7f040000;
  15 + }
  16 + public static final class style {
  17 + public static final int AppBaseTheme = 0x7f050000;
  18 + public static final int AppTheme = 0x7f050001;
  19 + }
  20 +}
TraceSDKSample/libs/android-support-v4.jar
No preview for this file type
TraceSDKSample/proguard-project.txt
... ... @@ -0,0 +1,20 @@
  1 +# To enable ProGuard in your project, edit project.properties
  2 +# to define the proguard.config property as described in that file.
  3 +#
  4 +# Add project specific ProGuard rules here.
  5 +# By default, the flags in this file are appended to flags specified
  6 +# in ${sdk.dir}/tools/proguard/proguard-android.txt
  7 +# You can edit the include path and order by changing the ProGuard
  8 +# include property in project.properties.
  9 +#
  10 +# For more details, see
  11 +# http://developer.android.com/guide/developing/tools/proguard.html
  12 +
  13 +# Add any project specific keep options here:
  14 +
  15 +# If your project uses WebView with JS, uncomment the following
  16 +# and specify the fully qualified class name to the JavaScript interface
  17 +# class:
  18 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  19 +# public *;
  20 +#}
TraceSDKSample/project.properties
... ... @@ -0,0 +1,15 @@
  1 +# This file is automatically generated by Android Tools.
  2 +# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
  3 +#
  4 +# This file must be checked in Version Control Systems.
  5 +#
  6 +# To customize properties used by the Ant build system edit
  7 +# "ant.properties", and override values to adapt the script to your
  8 +# project structure.
  9 +#
  10 +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
  11 +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
  12 +
  13 +# Project target.
  14 +target=android-19
  15 +android.library.reference.1=../TraceSDK
TraceSDKSample/res/drawable-hdpi/ic_launcher.png

9.18 KB

TraceSDKSample/res/drawable-mdpi/ic_launcher.png

5.11 KB

TraceSDKSample/res/drawable-xhdpi/ic_launcher.png

14 KB

TraceSDKSample/res/layout/activity_main.xml
... ... @@ -0,0 +1,26 @@
  1 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2 + android:id="@+id/container"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + android:gravity="center"
  6 + android:orientation="vertical" >
  7 +
  8 + <Button
  9 + android:id="@+id/send"
  10 + android:layout_width="wrap_content"
  11 + android:layout_height="wrap_content"
  12 + android:text="Mark an event" />
  13 +
  14 + <Button
  15 + android:id="@+id/upgrade"
  16 + android:layout_width="wrap_content"
  17 + android:layout_height="wrap_content"
  18 + android:text="Level upgrade" />
  19 +
  20 + <Button
  21 + android:id="@+id/purchase"
  22 + android:layout_width="wrap_content"
  23 + android:layout_height="wrap_content"
  24 + android:text="Purchase" />
  25 +
  26 +</LinearLayout>
0 27 \ No newline at end of file
TraceSDKSample/res/layout/fragment_main.xml
... ... @@ -0,0 +1,16 @@
  1 +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:tools="http://schemas.android.com/tools"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + android:paddingBottom="@dimen/activity_vertical_margin"
  6 + android:paddingLeft="@dimen/activity_horizontal_margin"
  7 + android:paddingRight="@dimen/activity_horizontal_margin"
  8 + android:paddingTop="@dimen/activity_vertical_margin"
  9 + tools:context="com.mobithink.tracesdk.sample.MainActivity$PlaceholderFragment" >
  10 +
  11 + <TextView
  12 + android:layout_width="wrap_content"
  13 + android:layout_height="wrap_content"
  14 + android:text="@string/hello_world" />
  15 +
  16 +</RelativeLayout>
TraceSDKSample/res/values-w820dp/dimens.xml
... ... @@ -0,0 +1,10 @@
  1 +<resources>
  2 +
  3 + <!--
  4 + Example customization of dimensions originally defined in res/values/dimens.xml
  5 + (such as screen margins) for screens with more than 820dp of available width. This
  6 + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively).
  7 + -->
  8 + <dimen name="activity_horizontal_margin">64dp</dimen>
  9 +
  10 +</resources>
TraceSDKSample/res/values/dimens.xml
... ... @@ -0,0 +1,7 @@
  1 +<resources>
  2 +
  3 + <!-- Default screen margins, per the Android Design guidelines. -->
  4 + <dimen name="activity_horizontal_margin">16dp</dimen>
  5 + <dimen name="activity_vertical_margin">16dp</dimen>
  6 +
  7 +</resources>
TraceSDKSample/res/values/strings.xml
... ... @@ -0,0 +1,8 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 +
  4 + <string name="app_name">TraceSDKSample</string>
  5 + <string name="hello_world">Hello world!</string>
  6 + <string name="action_settings">Settings</string>
  7 +
  8 +</resources>
TraceSDKSample/res/values/styles.xml
... ... @@ -0,0 +1,20 @@
  1 +<resources>
  2 +
  3 + <!--
  4 + Base application theme, dependent on API level. This theme is replaced
  5 + by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
  6 + -->
  7 + <style name="AppBaseTheme" >
  8 + <!--
  9 + Theme customizations available in newer API levels can go in
  10 + res/values-vXX/styles.xml, while customizations related to
  11 + backward-compatibility can go here.
  12 + -->
  13 + </style>
  14 +
  15 + <!-- Application theme. -->
  16 + <style name="AppTheme" parent="AppBaseTheme">
  17 + <!-- All customizations that are NOT specific to a particular API-level can go here. -->
  18 + </style>
  19 +
  20 +</resources>
TraceSDKSample/src/com/mobithink/tracesdk/sample/MainActivity.java
... ... @@ -0,0 +1,61 @@
  1 +package com.mobithink.tracesdk.sample;
  2 +
  3 +import java.util.HashMap;
  4 +import java.util.Map;
  5 +
  6 +import com.mobithink.tracesdk.TraceAgent;
  7 +
  8 +import android.os.Bundle;
  9 +import android.support.v4.app.FragmentActivity;
  10 +import android.view.View;
  11 +import android.view.View.OnClickListener;
  12 +import android.widget.Button;
  13 +
  14 +public class MainActivity extends FragmentActivity {
  15 +
  16 + @Override
  17 + protected void onCreate(Bundle savedInstanceState) {
  18 + super.onCreate(savedInstanceState);
  19 + setContentView(R.layout.activity_main);
  20 + TraceAgent.active(MainActivity.this, "5486");
  21 + Button btnEvent = (Button) findViewById(R.id.send);
  22 + Button btnUp = (Button) findViewById(R.id.upgrade);
  23 + Button btnPurchase = (Button) findViewById(R.id.purchase);
  24 + btnEvent.setOnClickListener(listener);
  25 + btnUp.setOnClickListener(listener);
  26 + btnPurchase.setOnClickListener(listener);
  27 + }
  28 +
  29 + @Override
  30 + protected void onPause() {
  31 + TraceAgent.onPause(this);
  32 + super.onPause();
  33 + }
  34 +
  35 + @Override
  36 + protected void onResume() {
  37 + TraceAgent.onResume(this);
  38 + super.onResume();
  39 + }
  40 +
  41 + OnClickListener listener = new OnClickListener() {
  42 +
  43 + @Override
  44 + public void onClick(View v) {
  45 + switch (v.getId()) {
  46 + case R.id.send:
  47 + TraceAgent.onEvent(getApplicationContext(), "one test event");
  48 + break;
  49 + case R.id.purchase:
  50 + Map<String, String> params = new HashMap<String, String>();
  51 + params.put("amount", "10.5");
  52 + params.put("currency", "USD");
  53 + TraceAgent.onEvent(getApplicationContext(), "purchase", params);
  54 + break;
  55 + case R.id.upgrade:
  56 + TraceAgent.onEvent(getApplicationContext(), "upgrade", "11");
  57 + break;
  58 + }
  59 + }
  60 + };
  61 +}