{"action":"created","comment":{"url":"https://api.github.com/repos/dimagi/commcare-odk/pulls/comments/50742540","id":50742540,"diff_hunk":"@@ -1,288 +1,186 @@\n package org.commcare.dalvik.activities;\r\n \r\n-import android.Manifest;\r\n-import android.content.Context;\r\n+import android.annotation.TargetApi;\r\n import android.content.Intent;\r\n+import android.Manifest;\r\n import android.content.pm.PackageManager;\r\n-import android.graphics.drawable.Drawable;\r\n-import android.location.Address;\r\n-import android.location.Criteria;\r\n-import android.location.Geocoder;\r\n-import android.location.Location;\r\n-import android.location.LocationManager;\r\n import android.os.Bundle;\r\n import android.support.v4.content.ContextCompat;\r\n import android.util.Log;\r\n-\r\n-import com.google.android.maps.GeoPoint;\r\n-import com.google.android.maps.MapActivity;\r\n-import com.google.android.maps.MapView;\r\n-import com.google.android.maps.MyLocationOverlay;\r\n-import com.google.android.maps.OverlayItem;\r\n-\r\n-import org.commcare.android.database.SqlStorage;\r\n-import org.commcare.android.database.user.models.GeocodeCacheModel;\r\n+import android.util.Pair;\r\n+\r\n+import com.google.android.gms.maps.CameraUpdateFactory;\r\n+import com.google.android.gms.maps.GoogleMap;\r\n+import com.google.android.gms.maps.MapFragment;\r\n+import com.google.android.gms.maps.OnMapReadyCallback;\r\n+import com.google.android.gms.maps.model.LatLng;\r\n+import com.google.android.gms.maps.model.LatLngBounds;\r\n+import com.google.android.gms.maps.model.Marker;\r\n+import com.google.android.gms.maps.model.MarkerOptions;\r\n+\r\n+import org.commcare.android.framework.CommCareActivity;\r\n import org.commcare.android.models.Entity;\r\n import org.commcare.android.models.NodeEntityFactory;\r\n import org.commcare.android.util.AndroidInstanceInitializer;\r\n import org.commcare.android.util.SerializationUtil;\r\n-import org.commcare.dalvik.BuildConfig;\r\n import org.commcare.dalvik.R;\r\n import org.commcare.dalvik.application.CommCareApplication;\r\n-import org.commcare.dalvik.geo.EntityOverlay;\r\n-import org.commcare.dalvik.geo.EntityOverlayItemFactory;\r\n import org.commcare.session.CommCareSession;\r\n import org.commcare.suite.model.Detail;\r\n-import org.commcare.suite.model.Entry;\r\n import org.commcare.suite.model.SessionDatum;\r\n import org.javarosa.core.model.condition.EvaluationContext;\r\n import org.javarosa.core.model.data.GeoPointData;\r\n import org.javarosa.core.model.data.UncastData;\r\n import org.javarosa.core.model.instance.TreeReference;\r\n-import org.javarosa.core.services.storage.StorageFullException;\r\n \r\n-import java.io.IOException;\r\n-import java.util.List;\r\n-import java.util.NoSuchElementException;\r\n+import java.util.HashMap;\r\n import java.util.Vector;\r\n \r\n /**\r\n  * @author ctsims\r\n  */\r\n-public class EntityMapActivity extends MapActivity {\r\n+@TargetApi(11)\r\n+public class EntityMapActivity extends CommCareActivity implements OnMapReadyCallback,\r\n+        GoogleMap.OnInfoWindowClickListener {\r\n     private static final String TAG = EntityMapActivity.class.getSimpleName();\r\n-    private final static int LOCATION_PERMISSIONS_REQUEST = 1;\r\n+    private static final int MAP_PADDING = 50;  // Number of pixels to pad bounding region of markers\r\n \r\n-    private MapView map;\r\n-    private MyLocationOverlay mMyLocationOverlay;\r\n-    private Geocoder mGeoCoder;\r\n-    private LocationManager mLocationManager;\r\n-    \r\n+    private EvaluationContext entityEvaluationContext;\r\n     private CommCareSession session;\r\n+    Vector<Entity<TreeReference>> entities;\r\n+\r\n+    Vector<Pair<Entity<TreeReference>, LatLng>> entityLocations;\r\n+    HashMap<Marker, TreeReference> markerReferences = new HashMap<>();\r\n+\r\n+    GoogleMap mMap;\r\n \r\n-    private EntityOverlay mEntityOverlay;\r\n-    private Vector<Entity<TreeReference>> entities;\r\n-    \r\n     @Override\r\n-    protected void onCreate(Bundle icicle) {\r\n-        super.onCreate(icicle);\r\n-        String licenseKey = getLicenseKey();\r\n-        map = new MapView(this, licenseKey);\r\n-        \r\n-        this.setContentView(map);\r\n-        \r\n-        mGeoCoder = new Geocoder(this);\r\n+    protected void onCreate(Bundle savedInstanceState) {\r\n+        super.onCreate(savedInstanceState);\r\n+        setContentView(R.layout.entity_map_view);\r\n \r\n-        session = CommCareApplication._().getCurrentSession();\r\n-        Vector<Entry> entries = session.getEntriesForCommand(session.getCommand());\r\n+        MapFragment mapFragment = (MapFragment) getFragmentManager()\r\n+                .findFragmentById(R.id.map);\r\n+        mapFragment.getMapAsync(this);\r\n \r\n+        session = CommCareApplication._().getCurrentSession();\r\n         SessionDatum selectDatum = session.getNeededDatum();\r\n         Detail detail = session.getDetail(selectDatum.getShortDetail());\r\n-        NodeEntityFactory factory = new NodeEntityFactory(detail, this.getEC());\r\n-        \r\n-        Vector<TreeReference> references = getEC().expandReference(selectDatum.getNodeset());\r\n-        \r\n+        NodeEntityFactory factory = new NodeEntityFactory(detail, this.getEvaluationContext());\r\n+\r\n+        Vector<TreeReference> references = getEvaluationContext().expandReference(\r\n+                selectDatum.getNodeset());\r\n         entities = new Vector<>();\r\n         for(TreeReference ref : references) {\r\n             entities.add(factory.getEntity(ref));\r\n         }\r\n \r\n-        Log.d(TAG, \"Entities generated\");\r\n-        \r\n-        map.displayZoomControls(true);\r\n-        mMyLocationOverlay = new MyLocationOverlay(this, map);\r\n-        mMyLocationOverlay.runOnFirstFix(new Runnable() { public void run() {\r\n-            map.getController().animateTo(mMyLocationOverlay.getMyLocation());\r\n-        }});\r\n-        \r\n-        Drawable defaultMarker = this.getResources().getDrawable(R.drawable.marker);\r\n-        mEntityOverlay = new EntityOverlay(defaultMarker, map) {\r\n-\r\n-            @Override\r\n-            protected void selected(TreeReference ref) {\r\n-                Intent i = new Intent(EntityMapActivity.this.getIntent());\r\n-                SerializationUtil.serializeToIntent(i, EntityDetailActivity.CONTEXT_REFERENCE, ref);\r\n-                \r\n-                setResult(RESULT_OK, i);\r\n-\r\n-                EntityMapActivity.this.finish();                \r\n-            }\r\n-        };\r\n-        Log.d(TAG, \"Loading addresses...\");\r\n-        \r\n-        int legit = 0;\r\n-        int bogus = 0;\r\n-        \r\n-        EntityOverlayItemFactory overlayFactory = new EntityOverlayItemFactory(detail, defaultMarker);\r\n-        \r\n-        SqlStorage<GeocodeCacheModel> geoCache = CommCareApplication._().getUserStorage(GeocodeCacheModel.STORAGE_KEY, GeocodeCacheModel.class);\r\n-\r\n-        double[] boundHints = new double[4];\r\n-        Location location = getLocation();\r\n-        setLocationBounds(location, boundHints);\r\n-\r\n-        for(Entity<TreeReference> e : entities) {\r\n+        int bogusAddresses = 0;\r\n+        entityLocations = new Vector<>();\r\n+        for(Entity<TreeReference> entity : entities) {\r\n             for(int i = 0 ; i < detail.getHeaderForms().length; ++i ){\r\n                 if(\"address\".equals(detail.getTemplateForms()[i])) {\r\n-                    String val = e.getFieldString(i).trim();\r\n-                    if(val != null && !\"\".equals(val)) {\r\n-                        GeoPoint gp = null;\r\n-                        try {\r\n-                            GeoPointData data = new GeoPointData().cast(new UncastData(val));\r\n-                            if(data != null) {\r\n-                                int lat = (int) (data.getValue()[0] * 1E6);\r\n-                                int lng = (int) (data.getValue()[1] * 1E6);\r\n-                                gp = new GeoPoint(lat, lng);\r\n-                            }\r\n-                        } catch(Exception ex) {\r\n-                            //We might not have a geopoint at all. Don't even trip\r\n-                        }\r\n-                        \r\n-                        boolean cached = false;\r\n-                        try {\r\n-                            GeocodeCacheModel record = geoCache.getRecordForValue(GeocodeCacheModel.META_LOCATION, val);\r\n-                            cached = true;\r\n-                            if(record.dataExists()){\r\n-                                gp = record.getGeoPoint();\r\n-                            }\r\n-                        } catch(NoSuchElementException nsee) {\r\n-                            //no record!\r\n-                        }\r\n-                        \r\n-                        //If we don't have a geopoint, let's try to find our address\r\n-                        if (!cached && location != null) {\r\n-                            try {\r\n-                                List<Address> addresses = mGeoCoder.getFromLocationName(val, 3, boundHints[0], boundHints[1], boundHints[2], boundHints[3]);\r\n-                                for(Address a : addresses) {\r\n-                                    if(a.hasLatitude() && a.hasLongitude()) {\r\n-                                        int lat = (int) (a.getLatitude() * 1E6);\r\n-                                        int lng = (int) (a.getLongitude() * 1E6);\r\n-                                        gp = new GeoPoint(lat, lng);\r\n-                                        \r\n-                                        geoCache.write(new GeocodeCacheModel(val, lat, lng));\r\n-                                        legit++;\r\n-                                        break;\r\n-                                    }\r\n-                                }\r\n-                                \r\n-                                //We didn't find an address, make a miss record\r\n-                                if(gp == null) {\r\n-                                    geoCache.write(GeocodeCacheModel.NoHitRecord(val));\r\n-                                }\r\n-                            } catch (StorageFullException | IOException e1) {\r\n-                                e1.printStackTrace();\r\n-                            }\r\n-                        }\r\n-                        \r\n-                        //Ok, so now we have an address or not. If we _do_ have one, let's have some fun\r\n-                        \r\n-                        if(gp != null) {\r\n-                            OverlayItem overlayItem = overlayFactory.generateOverlay(gp, e);\r\n-                            mEntityOverlay.addOverlay(overlayItem, e.getElement());\r\n-                        }\r\n-                        else { \r\n-                            bogus++;\r\n+                    String address = entity.getFieldString(i).trim();\r\n+                    if(address != null && !\"\".equals(address)) {\r\n+                        LatLng location = getLatLngFromAddress(address);\r\n+                        if (location == null) {\r\n+                            bogusAddresses++;\r\n+                        } else {\r\n+                            entityLocations.add(new Pair<>(entity, location));\r\n                         }\r\n                     }\r\n                 }\r\n             }\r\n         }\r\n-        \r\n-        Log.d(TAG, \"Loaded. \" + legit +\" addresses discovered, \" + bogus + \" could not be located\");\r\n-\r\n-        if (legit != 0 && mEntityOverlay.getCenter() != null) {\r\n-            map.getController().animateTo(mEntityOverlay.getCenter());\r\n-        } else if(location != null) {\r\n-            int lat = (int) (location.getLatitude() * 1E6);\r\n-            int lng = (int) (location.getLongitude() * 1E6);\r\n-            GeoPoint point = new GeoPoint(lat, lng);\r\n-            map.getController().animateTo(point);\r\n-        }\r\n-        \r\n-        map.getOverlays().add(mMyLocationOverlay);\r\n-        //The overlay crashes out if you try to draw it and it's empty,\r\n-        //so only add it if we found something.\r\n-        if(mEntityOverlay.size() > 0) {\r\n-            map.getOverlays().add(mEntityOverlay);\r\n-        }\r\n-        map.getController().setZoom(18);\r\n-        map.setClickable(true);\r\n-        map.setEnabled(true);\r\n-        Log.d(TAG, \"Done loading\");\r\n+        Log.d(TAG, \"Loaded. \" + entityLocations.size() +\" addresses discovered, \" + bogusAddresses + \" could not be located\");\r\n     }\r\n-    \r\n-    private String getLicenseKey() {\r\n-        //If there's a defined debug key in the local environment, use that.\r\n-        int debugId = this.getResources().getIdentifier(\"maps_api_key_debug\",\"string\", this.getPackageName());\r\n-        if(debugId == 0) { \r\n-            return BuildConfig.MAPS_API_KEY;\r\n+\r\n+    private LatLng getLatLngFromAddress(String address) {\r\n+        LatLng location = null;\r\n+        try {\r\n+            GeoPointData data = new GeoPointData().cast(new UncastData(address));\r\n+            if(data != null) {\r\n+                location = new LatLng(data.getLatitude(), data.getLongitude());\r\n+            }\r\n+        } catch(Exception ex) {\r\n         }\r\n-        return this.getString(debugId);\r\n-    }\r\n \r\n-    private EvaluationContext entityContext;\r\n+        // Geocaching code removed\r\n \r\n-    private EvaluationContext getEC() {\r\n-        if(entityContext == null) {\r\n-            entityContext = session.getEvaluationContext(getInstanceInit());\r\n-        }\r\n-        return entityContext;\r\n-    }\r\n-    \r\n-    private AndroidInstanceInitializer getInstanceInit() {\r\n-        return new AndroidInstanceInitializer(session);\r\n+        return location;\r\n     }\r\n \r\n     @Override\r\n-    protected void onStop() {\r\n-        super.onStop();\r\n-        mMyLocationOverlay.disableCompass(); \r\n-        mMyLocationOverlay.disableMyLocation();\r\n+    public void onMapReady(final GoogleMap map) {\r\n+        mMap = map;\r\n+\r\n+        // Find bounding region of markers\r\n+        LatLngBounds.Builder builder = new LatLngBounds.Builder();\r\n+        for (Pair<Entity<TreeReference>, LatLng> entityLocation: entityLocations) {\r\n+            Marker marker = map.addMarker(new MarkerOptions()\r\n+                    .position(entityLocation.second)\r\n+                    .title(entityLocation.first.getFieldString(0))\r\n+                    .snippet(entityLocation.first.getFieldString(1)));\r\n+            markerReferences.put(marker, entityLocation.first.getElement());\r\n+            builder.include(entityLocation.second);\r\n+        }\r\n+        final LatLngBounds bounds = builder.build();\r\n+\r\n+        // Move camera to be include all markers\r\n+        // TODO: does this work for 0 or 1 marker?\r\n+        map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {\r\n+            @Override\r\n+            public void onMapLoaded() {\r\n+                map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, MAP_PADDING));\r\n+            }\r\n+        });\r\n+\r\n+        map.setOnInfoWindowClickListener(this);\r\n+\r\n+        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED\r\n+                || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {\r\n+            map.setMyLocationEnabled(true);\r\n+        }\r\n     }\r\n \r\n     @Override\r\n     protected void onResume() {\r\n         super.onResume();\r\n-        mMyLocationOverlay.enableMyLocation();\r\n-        mMyLocationOverlay.enableCompass();\r\n-    }\r\n \r\n-    protected boolean isRouteDisplayed() {\r\n-        return false;\r\n+        if (mMap != null && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED\r\n+                || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {\r\n+            mMap.setMyLocationEnabled(true);\r\n+        }\r\n     }\r\n \r\n-    private Location getLocation() {\r\n-        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_DENIED ||\r\n-                ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {\r\n-            // TODO PLM: warn user and ask for permissions if the user has disabled them\r\n-            return null;\r\n-        }\r\n+    @Override\r\n+    protected void onPause() {\r\n+        super.onPause();\r\n \r\n-        // Get the location manager\r\n-        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\r\n-        // Define the criteria how to select the locatioin provider -> use\r\n-        // default\r\n-        Criteria criteria = new Criteria();\r\n-        String provider = mLocationManager.getBestProvider(criteria, false);\r\n-        return mLocationManager.getLastKnownLocation(provider);\r\n+        if (mMap != null && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED\r\n+                || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {\r\n+            mMap.setMyLocationEnabled(false);\r\n+        }\r\n     }\r\n \r\n-    private void setLocationBounds(Location location, double[] boundHints) {\r\n-        // Initialize the location fields\r\n-        if (location != null) {\r\n-            double lat = location.getLatitude();\r\n-            double lng = location.getLongitude();\r\n-\r\n-            //lLat\r\n-            boundHints[0] = lat -1;\r\n-            //lLng\r\n-            boundHints[1] = lng -1;\r\n+    @Override\r\n+    public void onInfoWindowClick(Marker marker) {\r\n+        Intent i = new Intent(EntityMapActivity.this.getIntent());\r\n+        TreeReference ref = markerReferences.get(marker);\r\n+        SerializationUtil.serializeToIntent(i, EntityDetailActivity.CONTEXT_REFERENCE, ref);\r\n \r\n-            //uLat\r\n-            boundHints[2] = lat + 1;\r\n+        setResult(RESULT_OK, i);\r\n+        EntityMapActivity.this.finish();\r\n+    }\r\n \r\n-            //rLon\r\n-            boundHints[3] = lng + 1;\r\n+    private EvaluationContext getEvaluationContext() {\r\n+        if(entityEvaluationContext == null) {\r\n+            entityEvaluationContext = session.getEvaluationContext(getInstanceInit());\r\n         }\r\n+        return entityEvaluationContext;\r\n+    }\r\n+\r\n+    private AndroidInstanceInitializer getInstanceInit() {\r","path":"app/src/org/commcare/dalvik/activities/EntityMapActivity.java","position":401,"original_position":401,"commit_id":"b986afb495280c501633534a637719e7f1ea60d0","original_commit_id":"b986afb495280c501633534a637719e7f1ea60d0","user":{"login":"phillipm","id":94817,"avatar_url":"https://avatars.githubusercontent.com/u/94817?v=3","gravatar_id":"","url":"https://api.github.com/users/phillipm","html_url":"https://github.com/phillipm","followers_url":"https://api.github.com/users/phillipm/followers","following_url":"https://api.github.com/users/phillipm/following{/other_user}","gists_url":"https://api.github.com/users/phillipm/gists{/gist_id}","starred_url":"https://api.github.com/users/phillipm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phillipm/subscriptions","organizations_url":"https://api.github.com/users/phillipm/orgs","repos_url":"https://api.github.com/users/phillipm/repos","events_url":"https://api.github.com/users/phillipm/events{/privacy}","received_events_url":"https://api.github.com/users/phillipm/received_events","type":"User","site_admin":false},"body":"Also not really needed, removing this will enable you to make `session` a local variable instead of a class field","created_at":"2016-01-25T19:26:23Z","updated_at":"2016-01-25T19:26:23Z","html_url":"https://github.com/dimagi/commcare-odk/pull/1012#discussion_r50742540","pull_request_url":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012","_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-odk/pulls/comments/50742540"},"html":{"href":"https://github.com/dimagi/commcare-odk/pull/1012#discussion_r50742540"},"pull_request":{"href":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012"}}},"pull_request":{"url":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012","id":57109924,"html_url":"https://github.com/dimagi/commcare-odk/pull/1012","diff_url":"https://github.com/dimagi/commcare-odk/pull/1012.diff","patch_url":"https://github.com/dimagi/commcare-odk/pull/1012.patch","issue_url":"https://api.github.com/repos/dimagi/commcare-odk/issues/1012","number":1012,"state":"open","locked":false,"title":"Update map api","user":{"login":"forest-tong","id":6332211,"avatar_url":"https://avatars.githubusercontent.com/u/6332211?v=3","gravatar_id":"","url":"https://api.github.com/users/forest-tong","html_url":"https://github.com/forest-tong","followers_url":"https://api.github.com/users/forest-tong/followers","following_url":"https://api.github.com/users/forest-tong/following{/other_user}","gists_url":"https://api.github.com/users/forest-tong/gists{/gist_id}","starred_url":"https://api.github.com/users/forest-tong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forest-tong/subscriptions","organizations_url":"https://api.github.com/users/forest-tong/orgs","repos_url":"https://api.github.com/users/forest-tong/repos","events_url":"https://api.github.com/users/forest-tong/events{/privacy}","received_events_url":"https://api.github.com/users/forest-tong/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-01-25T18:24:55Z","updated_at":"2016-01-25T19:26:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2cbaa286c41c14a98369b65dba31b734985d7605","assignee":null,"milestone":{"url":"https://api.github.com/repos/dimagi/commcare-odk/milestones/7","html_url":"https://github.com/dimagi/commcare-odk/milestones/commcare_2.27","labels_url":"https://api.github.com/repos/dimagi/commcare-odk/milestones/7/labels","id":1523294,"number":7,"title":"commcare_2.27","description":null,"creator":{"login":"phillipm","id":94817,"avatar_url":"https://avatars.githubusercontent.com/u/94817?v=3","gravatar_id":"","url":"https://api.github.com/users/phillipm","html_url":"https://github.com/phillipm","followers_url":"https://api.github.com/users/phillipm/followers","following_url":"https://api.github.com/users/phillipm/following{/other_user}","gists_url":"https://api.github.com/users/phillipm/gists{/gist_id}","starred_url":"https://api.github.com/users/phillipm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phillipm/subscriptions","organizations_url":"https://api.github.com/users/phillipm/orgs","repos_url":"https://api.github.com/users/phillipm/repos","events_url":"https://api.github.com/users/phillipm/events{/privacy}","received_events_url":"https://api.github.com/users/phillipm/received_events","type":"User","site_admin":false},"open_issues":6,"closed_issues":2,"state":"open","created_at":"2016-01-20T15:41:42Z","updated_at":"2016-01-25T18:24:55Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012/commits","review_comments_url":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012/comments","review_comment_url":"https://api.github.com/repos/dimagi/commcare-odk/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dimagi/commcare-odk/issues/1012/comments","statuses_url":"https://api.github.com/repos/dimagi/commcare-odk/statuses/b986afb495280c501633534a637719e7f1ea60d0","head":{"label":"dimagi:update-map-api","ref":"update-map-api","sha":"b986afb495280c501633534a637719e7f1ea60d0","user":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"repo":{"id":20939877,"name":"commcare-odk","full_name":"dimagi/commcare-odk","owner":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dimagi/commcare-odk","description":"The best open source form management tool in town","fork":false,"url":"https://api.github.com/repos/dimagi/commcare-odk","forks_url":"https://api.github.com/repos/dimagi/commcare-odk/forks","keys_url":"https://api.github.com/repos/dimagi/commcare-odk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dimagi/commcare-odk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dimagi/commcare-odk/teams","hooks_url":"https://api.github.com/repos/dimagi/commcare-odk/hooks","issue_events_url":"https://api.github.com/repos/dimagi/commcare-odk/issues/events{/number}","events_url":"https://api.github.com/repos/dimagi/commcare-odk/events","assignees_url":"https://api.github.com/repos/dimagi/commcare-odk/assignees{/user}","branches_url":"https://api.github.com/repos/dimagi/commcare-odk/branches{/branch}","tags_url":"https://api.github.com/repos/dimagi/commcare-odk/tags","blobs_url":"https://api.github.com/repos/dimagi/commcare-odk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dimagi/commcare-odk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dimagi/commcare-odk/git/refs{/sha}","trees_url":"https://api.github.com/repos/dimagi/commcare-odk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dimagi/commcare-odk/statuses/{sha}","languages_url":"https://api.github.com/repos/dimagi/commcare-odk/languages","stargazers_url":"https://api.github.com/repos/dimagi/commcare-odk/stargazers","contributors_url":"https://api.github.com/repos/dimagi/commcare-odk/contributors","subscribers_url":"https://api.github.com/repos/dimagi/commcare-odk/subscribers","subscription_url":"https://api.github.com/repos/dimagi/commcare-odk/subscription","commits_url":"https://api.github.com/repos/dimagi/commcare-odk/commits{/sha}","git_commits_url":"https://api.github.com/repos/dimagi/commcare-odk/git/commits{/sha}","comments_url":"https://api.github.com/repos/dimagi/commcare-odk/comments{/number}","issue_comment_url":"https://api.github.com/repos/dimagi/commcare-odk/issues/comments{/number}","contents_url":"https://api.github.com/repos/dimagi/commcare-odk/contents/{+path}","compare_url":"https://api.github.com/repos/dimagi/commcare-odk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dimagi/commcare-odk/merges","archive_url":"https://api.github.com/repos/dimagi/commcare-odk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dimagi/commcare-odk/downloads","issues_url":"https://api.github.com/repos/dimagi/commcare-odk/issues{/number}","pulls_url":"https://api.github.com/repos/dimagi/commcare-odk/pulls{/number}","milestones_url":"https://api.github.com/repos/dimagi/commcare-odk/milestones{/number}","notifications_url":"https://api.github.com/repos/dimagi/commcare-odk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dimagi/commcare-odk/labels{/name}","releases_url":"https://api.github.com/repos/dimagi/commcare-odk/releases{/id}","deployments_url":"https://api.github.com/repos/dimagi/commcare-odk/deployments","created_at":"2014-06-17T21:17:26Z","updated_at":"2016-01-25T13:36:24Z","pushed_at":"2016-01-25T18:24:55Z","git_url":"git://github.com/dimagi/commcare-odk.git","ssh_url":"git@github.com:dimagi/commcare-odk.git","clone_url":"https://github.com/dimagi/commcare-odk.git","svn_url":"https://github.com/dimagi/commcare-odk","homepage":"https://play.google.com/store/apps/details?id=org.commcare.dalvik&hl=en","size":64988,"stargazers_count":6,"watchers_count":6,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":9,"mirror_url":null,"open_issues_count":7,"forks":9,"open_issues":7,"watchers":6,"default_branch":"master"}},"base":{"label":"dimagi:master","ref":"master","sha":"33b033f327206e136263b2f117d24ad34d751630","user":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"repo":{"id":20939877,"name":"commcare-odk","full_name":"dimagi/commcare-odk","owner":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dimagi/commcare-odk","description":"The best open source form management tool in town","fork":false,"url":"https://api.github.com/repos/dimagi/commcare-odk","forks_url":"https://api.github.com/repos/dimagi/commcare-odk/forks","keys_url":"https://api.github.com/repos/dimagi/commcare-odk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dimagi/commcare-odk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dimagi/commcare-odk/teams","hooks_url":"https://api.github.com/repos/dimagi/commcare-odk/hooks","issue_events_url":"https://api.github.com/repos/dimagi/commcare-odk/issues/events{/number}","events_url":"https://api.github.com/repos/dimagi/commcare-odk/events","assignees_url":"https://api.github.com/repos/dimagi/commcare-odk/assignees{/user}","branches_url":"https://api.github.com/repos/dimagi/commcare-odk/branches{/branch}","tags_url":"https://api.github.com/repos/dimagi/commcare-odk/tags","blobs_url":"https://api.github.com/repos/dimagi/commcare-odk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dimagi/commcare-odk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dimagi/commcare-odk/git/refs{/sha}","trees_url":"https://api.github.com/repos/dimagi/commcare-odk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dimagi/commcare-odk/statuses/{sha}","languages_url":"https://api.github.com/repos/dimagi/commcare-odk/languages","stargazers_url":"https://api.github.com/repos/dimagi/commcare-odk/stargazers","contributors_url":"https://api.github.com/repos/dimagi/commcare-odk/contributors","subscribers_url":"https://api.github.com/repos/dimagi/commcare-odk/subscribers","subscription_url":"https://api.github.com/repos/dimagi/commcare-odk/subscription","commits_url":"https://api.github.com/repos/dimagi/commcare-odk/commits{/sha}","git_commits_url":"https://api.github.com/repos/dimagi/commcare-odk/git/commits{/sha}","comments_url":"https://api.github.com/repos/dimagi/commcare-odk/comments{/number}","issue_comment_url":"https://api.github.com/repos/dimagi/commcare-odk/issues/comments{/number}","contents_url":"https://api.github.com/repos/dimagi/commcare-odk/contents/{+path}","compare_url":"https://api.github.com/repos/dimagi/commcare-odk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dimagi/commcare-odk/merges","archive_url":"https://api.github.com/repos/dimagi/commcare-odk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dimagi/commcare-odk/downloads","issues_url":"https://api.github.com/repos/dimagi/commcare-odk/issues{/number}","pulls_url":"https://api.github.com/repos/dimagi/commcare-odk/pulls{/number}","milestones_url":"https://api.github.com/repos/dimagi/commcare-odk/milestones{/number}","notifications_url":"https://api.github.com/repos/dimagi/commcare-odk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dimagi/commcare-odk/labels{/name}","releases_url":"https://api.github.com/repos/dimagi/commcare-odk/releases{/id}","deployments_url":"https://api.github.com/repos/dimagi/commcare-odk/deployments","created_at":"2014-06-17T21:17:26Z","updated_at":"2016-01-25T13:36:24Z","pushed_at":"2016-01-25T18:24:55Z","git_url":"git://github.com/dimagi/commcare-odk.git","ssh_url":"git@github.com:dimagi/commcare-odk.git","clone_url":"https://github.com/dimagi/commcare-odk.git","svn_url":"https://github.com/dimagi/commcare-odk","homepage":"https://play.google.com/store/apps/details?id=org.commcare.dalvik&hl=en","size":64988,"stargazers_count":6,"watchers_count":6,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":9,"mirror_url":null,"open_issues_count":7,"forks":9,"open_issues":7,"watchers":6,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012"},"html":{"href":"https://github.com/dimagi/commcare-odk/pull/1012"},"issue":{"href":"https://api.github.com/repos/dimagi/commcare-odk/issues/1012"},"comments":{"href":"https://api.github.com/repos/dimagi/commcare-odk/issues/1012/comments"},"review_comments":{"href":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012/comments"},"review_comment":{"href":"https://api.github.com/repos/dimagi/commcare-odk/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dimagi/commcare-odk/pulls/1012/commits"},"statuses":{"href":"https://api.github.com/repos/dimagi/commcare-odk/statuses/b986afb495280c501633534a637719e7f1ea60d0"}}}}