package org.commcare.activities;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.util.Pair;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import org.commcare.CommCareApplication;
import org.commcare.cases.entity.Entity;
import org.commcare.cases.entity.NodeEntityFactory;
import org.commcare.dalvik.R;
import org.commcare.session.CommCareSession;
import org.commcare.suite.model.Detail;
import org.commcare.suite.model.EntityDatum;
import org.commcare.suite.model.SessionDatum;
import org.commcare.utils.AndroidInstanceInitializer;
import org.commcare.utils.SerializationUtil;
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 java.util.HashMap;
import java.util.Vector;
/**
* @author Forest Tong (ftong@dimagi.com)
*/
public class EntityMapActivity extends CommCareActivity implements OnMapReadyCallback,
GoogleMap.OnInfoWindowClickListener {
private static final String TAG = EntityMapActivity.class.getSimpleName();
private static final int MAP_PADDING = 50; // Number of pixels to pad bounding region of markers
private final CommCareSession session = CommCareApplication.instance().getCurrentSession();
private EntityDatum selectDatum;
private final Vector<Pair<Entity<TreeReference>, LatLng>> entityLocations = new Vector<>();
private final HashMap<Marker, TreeReference> markerReferences = new HashMap<>();
private GoogleMap mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.entity_map_view);
SupportMapFragment mapFragment = (SupportMapFragment)getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
SessionDatum datum = session.getNeededDatum();
if (datum instanceof EntityDatum) {
selectDatum = (EntityDatum)datum;
Detail detail = session.getDetail(selectDatum.getShortDetail());
addEntityLocations(detail);
Log.d(TAG, "Loaded. " + entityLocations.size() + " addresses discovered, " + (
detail.getHeaderForms().length - entityLocations.size()) + " could not be located");
}
}
/**
* Gets entity locations, and adds corresponding pairs to the vector entityLocations.
*/
private void addEntityLocations(Detail detail) {
for (Entity<TreeReference> entity : getEntities(detail)) {
for (int i = 0; i < detail.getHeaderForms().length; ++i) {
if ("address".equals(detail.getTemplateForms()[i])) {
String address = entity.getFieldString(i).trim();
if (!"".equals(address)) {
LatLng location = getLatLngFromAddress(address);
if (location != null) {
entityLocations.add(new Pair<>(entity, location));
}
}
}
}
}
}
private Vector<Entity<TreeReference>> getEntities(Detail detail) {
EvaluationContext evaluationContext = session.getEvaluationContext(
new AndroidInstanceInitializer(session));
evaluationContext.addFunctionHandler(EntitySelectActivity.getHereFunctionHandler());
NodeEntityFactory factory = new NodeEntityFactory(detail, evaluationContext);
Vector<TreeReference> references = evaluationContext.expandReference(
selectDatum.getNodeset());
Vector<Entity<TreeReference>> entities = new Vector<>();
for (TreeReference ref : references) {
entities.add(factory.getEntity(ref));
}
return entities;
}
private LatLng getLatLngFromAddress(@NonNull String address) {
LatLng location = null;
try {
GeoPointData data = new GeoPointData().cast(new UncastData(address));
if (data != null) {
location = new LatLng(data.getLatitude(), data.getLongitude());
}
} catch (IllegalArgumentException ignored) {
}
return location;
}
@Override
public void onMapReady(final GoogleMap map) {
mMap = map;
if (entityLocations.size() > 0) {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
// Add markers to map and find bounding region
for (Pair<Entity<TreeReference>, LatLng> entityLocation : entityLocations) {
Marker marker = mMap.addMarker(new MarkerOptions()
.position(entityLocation.second)
.title(entityLocation.first.getFieldString(0))
.snippet(entityLocation.first.getFieldString(1)));
markerReferences.put(marker, entityLocation.first.getElement());
builder.include(entityLocation.second);
}
final LatLngBounds bounds = builder.build();
// Move camera to be include all markers
mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, MAP_PADDING));
}
});
}
mMap.setOnInfoWindowClickListener(this);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mMap.setMyLocationEnabled(true);
}
}
@Override
protected void onResume() {
super.onResume();
if (mMap != null && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {
mMap.setMyLocationEnabled(true);
}
}
@Override
protected void onPause() {
super.onPause();
if (mMap != null) {
mMap.setOnMapLoadedCallback(null); // Avoid memory leak in callback
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mMap.setMyLocationEnabled(false);
}
}
}
@Override
public void onInfoWindowClick(Marker marker) {
Intent i = new Intent(getIntent());
TreeReference ref = markerReferences.get(marker);
SerializationUtil.serializeToIntent(i, EntityDetailActivity.CONTEXT_REFERENCE, ref);
setResult(RESULT_OK, i);
finish();
}
}