package org.commcare.dalvik.activities;
import java.io.IOException;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Vector;
import org.commcare.android.database.SqlStorage;
import org.commcare.android.database.user.models.GeocodeCacheModel;
import org.commcare.android.models.Entity;
import org.commcare.android.models.NodeEntityFactory;
import org.commcare.android.util.CommCareInstanceInitializer;
import org.commcare.dalvik.R;
import org.commcare.dalvik.application.CommCareApplication;
import org.commcare.dalvik.geo.EntityOverlay;
import org.commcare.dalvik.geo.EntityOverlayItemFactory;
import org.commcare.suite.model.Detail;
import org.commcare.suite.model.Entry;
import org.commcare.suite.model.SessionDatum;
import org.commcare.util.CommCareSession;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.data.GeoPointData;
import org.javarosa.core.model.data.UncastData;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.services.storage.StorageFullException;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.OverlayItem;
/**
* @author ctsims
*
*/
public class EntityMapActivity extends MapActivity {
MapView map;
MyLocationOverlay mMyLocationOverlay;
Geocoder mGeoCoder;
LocationManager mLocationManager;
CommCareSession session;
Entry prototype;
EntityOverlay mEntityOverlay;
Vector<Entity<TreeReference>> entities;
/* (non-Javadoc)
* @see com.google.android.maps.MapActivity#onCreate(android.os.Bundle)
*/
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
String licenseKey = getLicenseKey();
map = new MapView(this, licenseKey);
this.setContentView(map);
mGeoCoder = new Geocoder(this);
// Get the location manager
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Define the criteria how to select the locatioin provider -> use
// default
Criteria criteria = new Criteria();
String provider = mLocationManager.getBestProvider(criteria, false);
Location location = mLocationManager.getLastKnownLocation(provider);
session = CommCareApplication._().getCurrentSession();
Vector<Entry> entries = session.getEntriesForCommand(session.getCommand());
prototype = entries.elementAt(0);
SessionDatum selectDatum = session.getNeededDatum();
Detail detail = session.getDetail(selectDatum.getShortDetail());
NodeEntityFactory factory = new NodeEntityFactory(detail, this.getEC());
Vector<TreeReference> references = getEC().expandReference(selectDatum.getNodeset());
entities = new Vector<Entity<TreeReference>>();
for(TreeReference ref : references) {
entities.add(factory.getEntity(ref));
}
System.out.println("Entities generated");
map.displayZoomControls(true);
mMyLocationOverlay = new MyLocationOverlay(this, map);
mMyLocationOverlay.runOnFirstFix(new Runnable() { public void run() {
map.getController().animateTo(mMyLocationOverlay.getMyLocation());
}});
double[] boundHints = new double[4];
// Initialize the location fields
if (location != null) {
double lat = location.getLatitude();
double lng = location.getLongitude();
//lLat
boundHints[0] = lat -1;
//lLng
boundHints[1] = lng -1;
//uLat
boundHints[2] = lat + 1;
//rLon
boundHints[3] = lng + 1;
} else {
//no location
}
Drawable defaultMarker = this.getResources().getDrawable(R.drawable.marker);
mEntityOverlay = new EntityOverlay(this, defaultMarker, map) {
/*
* (non-Javadoc)
* @see org.commcare.dalvik.geo.EntityOverlay#selected(org.javarosa.core.model.instance.TreeReference)
*/
@Override
protected void selected(TreeReference ref) {
Intent i = new Intent(EntityMapActivity.this.getIntent());
CommCareApplication._().serializeToIntent(i, EntityDetailActivity.CONTEXT_REFERENCE, ref);
setResult(RESULT_OK, i);
EntityMapActivity.this.finish();
}
};
System.out.println("Loading addresses...");
int legit = 0;
int bogus = 0;
EntityOverlayItemFactory overlayFactory = new EntityOverlayItemFactory(detail, defaultMarker);
SqlStorage<GeocodeCacheModel> geoCache = CommCareApplication._().getUserStorage(GeocodeCacheModel.STORAGE_KEY, GeocodeCacheModel.class);
for(Entity<TreeReference> e : entities) {
for(int i = 0 ; i < detail.getHeaderForms().length; ++i ){
if("address".equals(detail.getTemplateForms()[i])) {
String val = e.getFieldString(i).trim();
if(val != null && val != "") {
GeoPoint gp = null;
try {
GeoPointData data = new GeoPointData().cast(new UncastData(val));
if(data != null) {
int lat = (int) (data.getValue()[0] * 1E6);
int lng = (int) (data.getValue()[1] * 1E6);
gp = new GeoPoint(lat, lng);
}
} catch(Exception ex) {
//We might not have a geopoint at all. Don't even trip
}
boolean cached = false;
try {
GeocodeCacheModel record = geoCache.getRecordForValue(GeocodeCacheModel.META_LOCATION, val);
cached = true;
if(record.dataExists()){
gp = record.getGeoPoint();
}
} catch(NoSuchElementException nsee) {
//no record!
}
//If we don't have a geopoint, let's try to find our address
if(!cached && boundHints != null) {
try {
List<Address> addresses = mGeoCoder.getFromLocationName(val, 3, boundHints[0], boundHints[1], boundHints[2], boundHints[3]);
for(Address a : addresses) {
if(a.hasLatitude() && a.hasLongitude()) {
int lat = (int) (a.getLatitude() * 1E6);
int lng = (int) (a.getLongitude() * 1E6);
gp = new GeoPoint(lat, lng);
try {
geoCache.write(new GeocodeCacheModel(val, lat, lng));
} catch (StorageFullException e1) {
//this is the worst exception ever.
}
legit++;
break;
}
}
//We didn't find an address, make a miss record
if(gp == null) {
try {
geoCache.write(GeocodeCacheModel.NoHitRecord(val));
} catch (StorageFullException e1) {
//this is the worst exception ever.
}
}
} catch (IOException e1) {
e1.printStackTrace();
//Yo. What? I guess bad connection?
}
}
//Ok, so now we have an address or not. If we _do_ have one, let's have some fun
if(gp != null) {
OverlayItem overlayItem = overlayFactory.generateOverlay(gp, e);
mEntityOverlay.addOverlay(overlayItem, e.getElement());
}
else {
bogus++;
}
}
}
}
}
System.out.println("Loaded. " + legit +" addresses discovered, " + bogus + " could not be located");
if(legit != 0 && mEntityOverlay.getCenter() != null) {
map.getController().animateTo(mEntityOverlay.getCenter());
} else if(location != null) {
int lat = (int) (location.getLatitude() * 1E6);
int lng = (int) (location.getLongitude() * 1E6);
GeoPoint point = new GeoPoint(lat, lng);
map.getController().animateTo(point);
}
map.getOverlays().add(mMyLocationOverlay);
//The overlay crashes out if you try to draw it and it's empty,
//so only add it if we found something.
if(mEntityOverlay.size() > 0) {
map.getOverlays().add(mEntityOverlay);
}
map.getController().setZoom(18);
map.setClickable(true);
map.setEnabled(true);
System.out.println("Done loading");
}
private String getLicenseKey() {
//If there's a defined debug key in the local environment, use that.
int debugId = this.getResources().getIdentifier("maps_api_key_debug","string", this.getPackageName());
if(debugId == 0) {
debugId = R.string.maps_api_key;
}
return this.getString(debugId);
}
EvaluationContext entityContext;
private EvaluationContext getEC() {
if(entityContext == null) {
entityContext = session.getEvaluationContext(getInstanceInit());
}
return entityContext;
}
private CommCareInstanceInitializer getInstanceInit() {
return new CommCareInstanceInitializer(session);
}
/* (non-Javadoc)
* @see android.app.Activity#onStop()
*/
@Override
protected void onStop() {
super.onStop();
mMyLocationOverlay.disableCompass();
mMyLocationOverlay.disableMyLocation();
}
/* (non-Javadoc)
* @see com.google.android.maps.MapActivity#onResume()
*/
@Override
protected void onResume() {
super.onResume();
mMyLocationOverlay.enableMyLocation();
mMyLocationOverlay.enableCompass();
}
protected boolean isRouteDisplayed() {
return false;
}
}