/* * Copyright 2012 The Stanford MobiSocial Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mobisocial.metrics; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import mobisocial.musubi.App; import mobisocial.musubi.ui.MusubiBaseActivity; import mobisocial.socialkit.User; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; /** * Record user events, supporting anonymous and non-anonymous reporting. */ public class UsageMetrics { public static final String CHIRP_REPORTING_ENDPOINT = null;//"https://chirp.yourserver.com"; public static final String CHIRP_VERSIONING_ENDPOINT = null;//"https://chirp.yoursever.com/version/latest.json"; public enum ReportingLevel { ANONYMOUS, NON_ANONYMOUS, DISABLED }; static ReportingThread mReportingThread; private final Context mContext; private final User mUser; private ReportingLevel mLevel = ReportingLevel.ANONYMOUS; /** * Creates a UserMetrics for tracking wholly anonymous statistics. */ public UsageMetrics(Context context) { mContext = context.getApplicationContext(); mUser = null; init(); } public void setReportingLevel(ReportingLevel level) { mLevel = level; } /** * Creates a UserMetrics with stats linked against the given Musubi user. */ public UsageMetrics(Context context, User user) { mContext = context.getApplicationContext(); mUser = user; mLevel = ReportingLevel.NON_ANONYMOUS; init(); } private void init() { if (mReportingThread == null) { mReportingThread = new ReportingThread(); mReportingThread.start(); synchronized (mReportingThread) { while (!mReportingThread.isStarted) { try { mReportingThread.wait(); } catch (InterruptedException e) {} } } } } private static UsageMetrics sUsageMetrics; public static UsageMetrics getUsageMetrics(Context c) { synchronized(UsageMetrics.class) { if (sUsageMetrics == null) { if (MusubiBaseActivity.isDeveloperModeEnabled(c)) { sUsageMetrics = new UsageMetrics(c, App.getMusubi(c).userForLocalDevice(null)); } else { sUsageMetrics = new UsageMetrics(c); } } return sUsageMetrics; } } public void report(Throwable exception) { if (mLevel == ReportingLevel.DISABLED || UsageMetrics.CHIRP_REPORTING_ENDPOINT == null) { return; } Message msg = mReportingThread.mHandler.obtainMessage(ReportingHandler.HTTP_REQUEST); HttpPost post = new HttpPost(UsageMetrics.CHIRP_REPORTING_ENDPOINT); List<NameValuePair> data = new ArrayList<NameValuePair>(); JSONObject json = MusubiExceptionHandler.jsonForException(mContext, exception, true); data.add(new BasicNameValuePair("json", json.toString())); try { post.setEntity(new UrlEncodedFormEntity(data, HTTP.UTF_8)); msg.obj = post; mReportingThread.mHandler.sendMessage(msg); } catch (IOException e) { } } public void report(String feature) { if (mLevel == ReportingLevel.DISABLED || UsageMetrics.CHIRP_REPORTING_ENDPOINT == null) { return; } Message msg = mReportingThread.mHandler.obtainMessage(ReportingHandler.HTTP_REQUEST); HttpPost post = new HttpPost(UsageMetrics.CHIRP_REPORTING_ENDPOINT); List<NameValuePair> data = new ArrayList<NameValuePair>(); JSONObject json = jsonForReport(feature); try { json.put("feature", feature); } catch (JSONException e) {} data.add(new BasicNameValuePair("json", json.toString())); try { post.setEntity(new UrlEncodedFormEntity(data, HTTP.UTF_8)); msg.obj = post; mReportingThread.mHandler.sendMessage(msg); } catch (IOException e) { } } public void report(String feature, String value) { if (mLevel == ReportingLevel.DISABLED || UsageMetrics.CHIRP_REPORTING_ENDPOINT == null) { return; } Message msg = mReportingThread.mHandler.obtainMessage(ReportingHandler.HTTP_REQUEST); HttpPost post = new HttpPost(UsageMetrics.CHIRP_REPORTING_ENDPOINT); List<NameValuePair> data = new ArrayList<NameValuePair>(); JSONObject json = jsonForReport(feature); try { json.put("feature", feature); json.put("value", value); } catch (JSONException e) {} data.add(new BasicNameValuePair("json", json.toString())); try { post.setEntity(new UrlEncodedFormEntity(data, HTTP.UTF_8)); msg.obj = post; mReportingThread.mHandler.sendMessage(msg); } catch (IOException e) { } } private JSONObject jsonForReport(String feature) { JSONObject json = new JSONObject(); try { json.put("type", "feature"); json.put("timestamp", Long.toString(new Date().getTime())); json.put("feature", feature); if (mUser != null && mLevel == ReportingLevel.NON_ANONYMOUS) { json.put("user", mUser.getId()); } } catch (JSONException e) {} return json; } class ReportingThread extends Thread { public Handler mHandler; public boolean isStarted = false; public void run() { Looper.prepare(); mHandler = new ReportingHandler(); isStarted = true; synchronized (this) { notify(); } Looper.loop(); } } class ReportingHandler extends Handler { static final int HTTP_REQUEST = 1; public void handleMessage(android.os.Message msg) { switch (msg.what) { case HTTP_REQUEST: HttpClient http = MusubiExceptionHandler.getHttpClient(mContext); HttpUriRequest request = (HttpUriRequest) msg.obj; try { http.execute(request); } catch (IOException e) { } break; } } } }