/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.mozstumbler.client.serialize; import android.os.AsyncTask; import android.util.Log; import com.ekito.simpleKML.Serializer; import com.ekito.simpleKML.model.Coordinate; import com.ekito.simpleKML.model.Data; import com.ekito.simpleKML.model.Document; import com.ekito.simpleKML.model.ExtendedData; import com.ekito.simpleKML.model.Feature; import com.ekito.simpleKML.model.Folder; import com.ekito.simpleKML.model.Geometry; import com.ekito.simpleKML.model.Icon; import com.ekito.simpleKML.model.IconStyle; import com.ekito.simpleKML.model.Kml; import com.ekito.simpleKML.model.Placemark; import com.ekito.simpleKML.model.Point; import com.ekito.simpleKML.model.Style; import com.ekito.simpleKML.model.StyleSelector; import com.ekito.simpleKML.model.TimeStamp; import org.joda.time.DateTime; import org.mozilla.mozstumbler.client.mapview.ObservationPoint; import org.mozilla.mozstumbler.svclocator.services.log.LoggerUtil; import java.io.File; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; // add menu item for save/load observations // activity will have save and load buttons // save -> save with name obs-date-#obs.kml // load -> show list to pick from public class ObservationPointSerializer extends AsyncTask<Void, Void, Boolean> { public static final String KML_PROVIDER = "KML_FILE"; public static final String WIFIS = "Wi-Fis"; public static final String CELLS = "Cells"; public static final String TRACK_SEGMENT = "Track segment"; private static final String LOG_TAG = LoggerUtil.makeLogTag(ObservationPointSerializer.class); private static final String GPS_NAME = "GPS"; private static final String MLS_NAME = "MLS"; private static final String ICON_RED_CIRCLE = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle_highlight.png"; private static final String STYLE_NAME_RED_CIRCLE = "redcircle"; private static final String ICON_ARROW = "http://earth.google.com/images/kml-icons/track-directional/track-0.png"; private static final String STYLE_NAME_ARROW = "arrow"; private static final String COLOR_HAS_WIFI = "ff0000ff"; // red in AABBGGRR private static final String COLOR_HAS_CELLS = "ffff0000"; // blue private static final String COLOR_HAS_BOTH = "ff00ff00"; // green private static final String COLOR_HAS_NONE = "aa000000"; // transparent black final WeakReference<IListener> mObservationPointSerializerListener; private final LinkedList<ObservationPoint> mPointList; private File mFile; private Mode mMode; ObservationPointSerializer(IListener listener, Mode mode, File file, LinkedList<ObservationPoint> pointList) { mObservationPointSerializerListener = new WeakReference<IListener>(listener); mFile = file; mPointList = pointList; mMode = mode; } void addStyle(Document doc, String iconHref, String styleName, Float scale) { Icon icon = new Icon(); icon.setHref(iconHref); IconStyle iconStyle = new IconStyle(); iconStyle.setIcon(icon); if (scale != null) { iconStyle.setScale(scale); } Style style = new Style(); style.setId(styleName); style.setIconStyle(iconStyle); List<StyleSelector> styleSelectorList = doc.getStyleSelector(); if (doc.getStyleSelector() == null) { styleSelectorList = new LinkedList<StyleSelector>(); doc.setStyleSelector(styleSelectorList); } styleSelectorList.add(style); } Folder createFolder(String name, List<Feature> features) { Folder folder = new Folder(); folder.setName(name); folder.setFeatureList(features); return folder; } void setHeadingAndColor(Placemark placemark, double heading, String color) { placemark.setStyleUrl("#" + STYLE_NAME_ARROW); IconStyle iconStyle = new IconStyle(); iconStyle.setHeading((float) heading); if (color != null) { iconStyle.setColor(color); } Style style = new Style(); style.setIconStyle(iconStyle); List<StyleSelector> styleSelector = new LinkedList<StyleSelector>(); styleSelector.add(style); placemark.setStyleSelector(styleSelector); } synchronized boolean writeOut(File outFile) { List<Feature> gpsFeatures = new LinkedList<Feature>(); List<Feature> mlsFeatures = new LinkedList<Feature>(); int idCounter = 0; for (ObservationPoint observationPoint : mPointList) { if (observationPoint.pointGPS.getProvider().equals(KML_PROVIDER)) { // This was previously read in, don't write out again continue; } idCounter++; Point point = new Point(); point.setId("p" + idCounter); // used to match with MLS point point.setCoordinates(observationPoint.getGPSCoordinate()); Placemark placemark = new Placemark(); placemark.setName(GPS_NAME); DateTime dateTime = new DateTime(observationPoint.pointGPS.getTime()); TimeStamp time = new TimeStamp(); time.setWhen(dateTime.toString() /* Date auto formats to RFC 3339 */); placemark.setTimePrimitive(time); String color = COLOR_HAS_NONE; if (observationPoint.mWifiCount > 0 && observationPoint.mCellCount > 0) { color = COLOR_HAS_BOTH; } else if (observationPoint.mWifiCount > 0) { color = COLOR_HAS_WIFI; } else if (observationPoint.mCellCount > 0) { color = COLOR_HAS_CELLS; } setHeadingAndColor(placemark, observationPoint.pointGPS.getBearing(), color); List<Data> dataList = new LinkedList<Data>(); Data data = new Data(); data.setName(WIFIS); data.setValue(String.valueOf(observationPoint.mWifiCount)); dataList.add(data); data = new Data(); data.setName(CELLS); data.setValue(String.valueOf(observationPoint.mCellCount)); dataList.add(data); data = new Data(); data.setName(TRACK_SEGMENT); data.setValue(String.valueOf(observationPoint.mTrackSegment)); dataList.add(data); ExtendedData extendedData = new ExtendedData(); extendedData.setDataList(dataList); placemark.setExtendedData(extendedData); List<Geometry> geometryList = new LinkedList<Geometry>(); geometryList.add(point); placemark.setGeometryList(geometryList); gpsFeatures.add(placemark); if (observationPoint.pointMLS != null) { placemark = new Placemark(); placemark.setStyleUrl("#" + STYLE_NAME_RED_CIRCLE); placemark.setName(MLS_NAME); placemark.setTimePrimitive(time); point = new Point(); point.setId("p" + idCounter); // used to match with gps point point.setCoordinates(observationPoint.getMLSCoordinate()); geometryList = new LinkedList<Geometry>(); geometryList.add(point); placemark.setGeometryList(geometryList); mlsFeatures.add(placemark); } } Document doc = new Document(); addStyle(doc, ICON_RED_CIRCLE, STYLE_NAME_RED_CIRCLE, null); addStyle(doc, ICON_ARROW, STYLE_NAME_ARROW, 1.2f); List<Feature> docFeatures = new LinkedList<Feature>(); docFeatures.add(createFolder(GPS_NAME, gpsFeatures)); docFeatures.add(createFolder(MLS_NAME, mlsFeatures)); doc.setFeatureList(docFeatures); Kml kml = new Kml(); kml.setFeature(doc); Serializer kmlSerializer = new Serializer(); try { kmlSerializer.write(kml, outFile); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage()); return false; } Log.d(LOG_TAG, "write done"); return true; } private boolean isGpsPointRead(String name) { return name.equals(GPS_NAME); } synchronized boolean readIn(File file) { Serializer kmlSerializer = new Serializer(); Kml kml; try { kml = kmlSerializer.read(file); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage()); return false; } Feature feature = kml.getFeature(); if (!(feature instanceof Document)) { Log.e(LOG_TAG, "expected document"); return false; } Document doc = (Document) feature; List<Feature> featureList = doc.getFeatureList(); if (featureList == null) { Log.e(LOG_TAG, "expected doc features"); return false; } HashMap<String, ObservationPoint> gpsList = new HashMap<String, ObservationPoint>(); HashMap<String, Coordinate> mlsList = new HashMap<String, Coordinate>(); for (Feature topFeatures : featureList) { if (!(topFeatures instanceof Folder)) { continue; } List<Feature> subFeatures = ((Folder) topFeatures).getFeatureList(); if (subFeatures == null) { continue; } for (Feature f : subFeatures) { if (!(f instanceof Placemark)) { continue; } Placemark placemark = (Placemark) f; List<Geometry> geometryList = placemark.getGeometryList(); if (geometryList == null || geometryList.size() != 1) { continue; } Geometry geometry = geometryList.get(0); if (!(geometry instanceof Point)) { continue; } Point p = (Point) geometry; Coordinate coordinate = p.getCoordinates(); boolean isGps = isGpsPointRead(placemark.getName()); int wifis = 0; int cells = 0; ExtendedData extendedData = placemark.getExtendedData(); if (extendedData != null) { List<Data> data = extendedData.getDataList(); for (Data d : data) { if (d.getName().equals(WIFIS)) { wifis = Integer.parseInt(d.getValue()); } else if (d.getName().equals(CELLS)) { cells = Integer.parseInt(d.getValue()); } } } ObservationPoint observationPoint = new ObservationPoint(KML_PROVIDER, coordinate, wifis, cells); if (isGps) { mPointList.add(observationPoint); gpsList.put(placemark.getId(), observationPoint); } else { mlsList.put(placemark.getId(), coordinate); } } } for (Map.Entry<String, Coordinate> entry : mlsList.entrySet()) { ObservationPoint obs = gpsList.get(entry.getKey()); if (obs != null) { obs.setMLSCoordinate(entry.getValue()); } } return true; } @Override protected Boolean doInBackground(Void... params) { if (mMode == Mode.WRITE) { return writeOut(mFile); } else { return readIn(mFile); } } @Override protected void onPostExecute(Boolean result) { IListener listener = mObservationPointSerializerListener.get(); if (listener == null) { return; } if (!result) { listener.onError(); return; } if (mMode == Mode.WRITE) { listener.onWriteComplete(mFile); } else { listener.onReadComplete(mFile); } } public enum Mode {READ, WRITE} public interface IListener { void onWriteComplete(File file); void onReadComplete(File file); void onError(); } }