/* * This file is part of Sensorium. * * Sensorium is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Sensorium is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Sensorium. If not, see * <http://www.gnu.org/licenses/>. * * */ package at.univie.sensorium.logging; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Environment; import android.os.Handler; import android.util.Log; import com.google.gson.stream.JsonWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import at.univie.sensorium.SensorRegistry; import at.univie.sensorium.extinterfaces.HTTPSUploader; import at.univie.sensorium.preferences.Preferences; import at.univie.sensorium.privacy.Privacy; import at.univie.sensorium.sensors.AbstractSensor; import at.univie.sensorium.sensors.NestedSensorValue; import at.univie.sensorium.sensors.SensorChangeListener; import at.univie.sensorium.sensors.SensorValue; /** * Create one JSON output stream for every sensor Use * https://code.google.com/p/google-gson/ for compatibility with Android 2.x * * */ public class JSONLogger implements SensorChangeListener { private List<AbstractSensor> sensors; private Map<String, JsonWriter> jsonMap; private Map<String, FileWriter> writerMap; private List<File> files; File extDir; public JSONLogger() { } public void init(List<AbstractSensor> sensors) { this.sensors = sensors; init(); } private void init() { jsonMap = new HashMap<String, JsonWriter>(); writerMap = new HashMap<String, FileWriter>(); files = new LinkedList<File>(); // TODO: needs to check if there is external storage, else die (toast // message?) gracefully extDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/sensorium"); extDir.mkdirs(); for (AbstractSensor sensor : sensors) { sensor.addListener(this); } } private JsonWriter getWriterForName(String sensorname) { JsonWriter writer = jsonMap.get(sensorname); if (writer == null) { try { String filename = sensorname.substring(sensorname.lastIndexOf('.') + 1) + ".json"; File extFile = new File(extDir, filename); if (extFile.exists()) { // get the first free filename.number int i = 0; boolean done = false; while (!done) { File f = new File(extDir, filename + "." + String.valueOf(i++)); if (f.exists()) continue; extFile.renameTo(f); extFile = new File(extDir, filename); // reset extFile // to original // pointer done = true; } } FileWriter fw = new FileWriter(extFile); writer = new JsonWriter(fw); writer.beginArray(); jsonMap.put(sensorname, writer); writerMap.put(sensorname, fw); files.add(extFile); } catch (FileNotFoundException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); Log.d(SensorRegistry.TAG, sw.toString()); jsonMap.remove(sensorname); writerMap.remove(sensorname); } catch (IOException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); Log.d(SensorRegistry.TAG, sw.toString()); } } return writer; } private boolean externalMediaWriteable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } Log.i(SensorRegistry.TAG, "External media not writeable, not attempting to write"); return false; } private void writeObject(AbstractSensor sensor) { if(externalMediaWriteable()){ JsonWriter jw = getWriterForName(sensor.getClass().getName()); List<SensorValue> valuelist = sensor.getSensorValues(); if (jw != null) { try { jw.beginObject(); jw.name("privacy-level").value(sensor.getPrivacylevel().name()); for (SensorValue value : valuelist) { if(value.isNested()){ jw.name(value.getType().getName()); jw.beginArray(); List<NestedSensorValue> nested = (List<NestedSensorValue>) value.getValue(); for(NestedSensorValue nsv: nested){ List<SensorValue> values = nsv.getInnerSensorValues(); jw.beginObject(); for(SensorValue nestedvalue: values){ SensorValue privatized = Privacy.anonymize(nestedvalue, sensor.getPrivacylevel()); jw.name(privatized.getType().getName()).value(privatized.getValueRepresentation()); } jw.endObject(); } jw.endArray(); } else{ SensorValue privatized = Privacy.anonymize(value, sensor.getPrivacylevel()); jw.name(privatized.getType().getName()).value(privatized.getValueRepresentation()); } } jw.endObject(); writerMap.get(sensor.getClass().getName()).flush(); } catch (IOException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); Log.d(SensorRegistry.TAG, sw.toString()); } } else { Log.d(SensorRegistry.TAG, "Can't get write access to log file, skipping"); } } } @Override public void sensorUpdated(AbstractSensor sensor) { writeObject(sensor); } public void finalizeLog() { // String id = UUID.randomUUID().toString(); for (AbstractSensor s : SensorRegistry.getInstance().getSensors()) { s.removeListener(this); } for (JsonWriter js : jsonMap.values()) { try { // js.beginObject(); // js.name("id").value(id); // js.endObject(); js.endArray(); js.flush(); // added on 2013-04-09; does this help in fixing the missing ]? js.close(); // added on 2013-04-09; does this help in fixing the missing ]? } catch (IOException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); Log.d(SensorRegistry.TAG, sw.toString()); } } // JSONWriter.close() already flushes/closes underlying writer, // so explicity closing the FileWriter would always throw an exception } public void upload() { Preferences prefs = SensorRegistry.getInstance().getPreferences(); String uploadurl = prefs.getString(Preferences.UPLOAD_URL_PREF, ""); upload(uploadurl); } public void upload(String uploadurl) { finalizeLog(); // close the json objects Preferences prefs = SensorRegistry.getInstance().getPreferences(); String uploadusername = prefs.getString(Preferences.UPLOAD_USERNAME, ""); String uploadpassword = prefs.getString(Preferences.UPLOAD_PASSWORD, ""); new HTTPSUploader(uploadurl, uploadusername, uploadpassword).execute(files); init(); // restart the logging } public void autoupload(String url, int interval, boolean wifionly) { this.interval = interval; this.wifionly = wifionly; handler.postDelayed(runnable, Math.max((long) interval * 1000, 300000)); } public void cancelautoupload() { handler.removeCallbacks(runnable); // TODO: check if this really stops // the queue } private int interval; private boolean wifionly; private Handler handler = new Handler(); private Runnable runnable = new Runnable() { @Override public void run() { ConnectivityManager connManager = (ConnectivityManager) SensorRegistry.getInstance().getContext().getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (!wifionly || (wifionly && mWifi.isConnected())) { SensorRegistry.getInstance().getJSONLogger().upload(); } handler.postDelayed(this, Math.max((long) interval * 1000, 300000)); } }; }