{ "id": "92919", "key": "TIMOB-9434", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "13271", "description": "Release 2.1.0", "name": "Release 2.1.0", "archived": false, "released": true, "releaseDate": "2012-06-29" }, { "id": "13404", "description": "Sprint 2012-12 Core", "name": "Sprint 2012-12 Core", "archived": true, "released": true, "releaseDate": "2012-06-17" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2012-07-12T00:43:29.000+0000", "created": "2012-06-06T18:51:11.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [ "core", "module_geolocation", "qe-testadded" ], "versions": [ { "id": "11331", "description": "", "name": "Release 1.8.0", "archived": true, "released": true, "releaseDate": "2011-10-31" } ], "issuelinks": [ { "id": "17745", "type": { "id": "10002", "name": "Duplicate", "inward": "is duplicated by", "outward": "duplicates" }, "inwardIssue": { "id": "85479", "key": "TIMOB-7408", "fields": { "summary": "Android: Geolocation - trueHeading returns undefined/null", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "Medium", "id": "3" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } }, { "id": "17724", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "outwardIssue": { "id": "85305", "key": "TIMOB-8672", "fields": { "summary": "Android: Compass True Heading - Magnetic declination seem to be added in the opposite direction", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "Medium", "id": "3" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } }, { "id": "17725", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "inwardIssue": { "id": "63709", "key": "TIMOB-3077", "fields": { "summary": "Android: compass true heading is sometimes returned as greater than 360 degrees", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "Medium", "id": "3" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } }, { "id": "24645", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "inwardIssue": { "id": "108056", "key": "DE-231", "fields": { "summary": "KitchenSink: Geolocation example is stupid", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "Low", "id": "4" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } } ], "assignee": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2013-01-17T15:59:30.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10202", "name": "Android", "description": "Android Platform" } ], "description": "When calling {{Ti.Geolocation.getCurrentHeading}}, the {{trueHeading}} property is defined in the callback object. However, when registering for heading updates, the trueHeading property is not defined in the event -- **unless you first call {{getCurrentHeading}}**.\r\n\r\nWhen we encode the event into a hash map, we do this:\r\n\r\n{code}\r\n if (geomagneticField != null) {\r\n float trueHeading = x - geomagneticField.getDeclination();\r\n if (trueHeading < 0) {\r\n trueHeading = (360 - trueHeading) % 360;\r\n }\r\n{code}\r\n\r\nHowever, {{geomagneticField}} is only initialized in getCurrentHeading.\r\n\r\n{code}\r\n String provider = tiLocation.locationManager.getBestProvider(criteria, true);\r\n if (provider != null) {\r\n Location location = tiLocation.locationManager.getLastKnownLocation(provider);\r\n if (location != null) {\r\n geomagneticField = new GeomagneticField((float)location.getLatitude(), (float)location. getLongitude(), (float)(location.getAltitude()), System.currentTimeMillis());\r\n }\r\n }\r\n{code}\r\nIf we were to initialize {{geomagneticField}} when a listener is added, it might fix the immediate problem, although this wouldn't address the case when no last known location is available. Even if the user registers for compass updates and location updates at the same time, a location may not be immediately available.\r\n \r\nAlso, I think this should be reevaluated in light of the new Android Geo code. Instead of calling {{getLastKnownLocation(provider)}}, it might be preferable to call {{tiLocation.getLastKnownLocation}}, which will check all providers.\r\n", "attachment": [], "flagged": false, "summary": "Android: compass event \"trueHeading\" not generated for recurring compass events", "creator": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "environment": null, "comment": { "comments": [ { "id": "197688", "author": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Think I have a fix for this issue, need to test it on device.", "updateAuthor": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-07T10:29:59.000+0000", "updated": "2012-06-07T10:29:59.000+0000" }, { "id": "197869", "author": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Fix didn't work on Nexus 1. Booting this bug back to the big boys.\r\n\r\nAfter discussing this with Josh, I think we really should be recalculating the declination periodically, but I'm not sure of the best approach to take.\r\n\r\n- Since we generate several heading events a second, generating a new GeomagneticField with every heading event is going to be too expensive.\r\n\r\n- Calculating the distance between the last fix and latest fix is also somewhat expensive.\r\n\r\n- Might be sufficient to just check the age of the last location fix, which would be a cheap comparison. Recalculating declination every few minutes should be plenty accurate enough, and fairly cheap.\r\n\r\nHere was my attempt:\r\n{code}\r\n--- a/android/modules/geolocation/src/java/ti/modules/titanium/geolocation/TiCompass.java\r\n+++ b/android/modules/geolocation/src/java/ti/modules/titanium/geolocation/TiCompass.java\r\n@@ -32,6 +32,8 @@ public class TiCompass\r\n {\r\n private static final String LCAT = \"TiCompass\";\r\n private static final boolean DBG = TiConfig.LOGD;\r\n+ // recalculate the magnetic declination if we've traveled the specified number of meters\r\n+ private static final int GEOMAGNETIC_FIELD_THRESHOLD = 10000;\r\n \r\n private GeolocationModule geolocationModule;\r\n private TiLocation tiLocation;\r\n@@ -40,6 +42,7 @@ public class TiCompass\r\n private long lastEventInUpdate;\r\n private float lastHeading = 0.0f;\r\n private GeomagneticField geomagneticField;\r\n+ private Location geomagneticFieldLocation;\r\n \r\n \r\n public TiCompass(GeolocationModule geolocationModule, TiLocation tiLocation)\r\n@@ -50,6 +53,7 @@ public class TiCompass\r\n \r\n public void registerListener()\r\n {\r\n+ updateDeclination();\r\n TiSensorHelper.registerListener(Sensor.TYPE_ORIENTATION, this, SensorManager.SENSOR_DELAY_UI);\r\n }\r\n \r\n@@ -88,6 +92,18 @@ public class TiCompass\r\n }\r\n }\r\n \r\n+ private void updateDeclination()\r\n+ {\r\n+ Location location = tiLocation.getLastKnownLocation();\r\n+ if (location != null) {\r\n+ if (geomagneticFieldLocation == null ||\r\n+ location.distanceTo(geomagneticFieldLocation) > GEOMAGNETIC_FIELD_THRESHOLD) {\r\n+ geomagneticField = new GeomagneticField((float)location.getLatitude(), (float)location.getLongitude(), (float)(location.getAltitude()), System.currentTimeMillis());\r\n+ geomagneticFieldLocation = location;\r\n+ }\r\n+ }\r\n+ }\r\n+\r\n private Object eventToHashMap(SensorEvent event, long timestamp)\r\n {\r\n float x = event.values[0];\r\n@@ -170,16 +186,7 @@ public class TiCompass\r\n }\r\n };\r\n \r\n- Criteria criteria = new Criteria();\r\n- \r\n- String provider = tiLocation.locationManager.getBestProvider(criteria, true);\r\n- if (provider != null) {\r\n- Location location = tiLocation.locationManager.getLastKnownLocation(provider);\r\n- if (location != null) {\r\n- geomagneticField = new GeomagneticField((float)location.getLatitude(), (float)location.getLongitude(), (float)(location.getAltitude()), System.currentTimeMillis());\r\n- }\r\n- }\r\n-\r\n+ updateDeclination();\r\n TiSensorHelper.registerListener(Sensor.TYPE_ORIENTATION, oneShotHeadingListener, SensorManager.SENSOR_DELAY_UI);\r\n }\r\n }\r\n{code}\r\n\r\nI'm guessing maybe the tiLocation.getLastKnownLocation() is not working if the user hasn't registered any location providers. \r\n\r\n\r\n", "updateAuthor": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-08T10:29:22.000+0000", "updated": "2012-06-08T10:29:22.000+0000" }, { "id": "197945", "author": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Weekend project.", "updateAuthor": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-08T18:42:02.000+0000", "updated": "2012-06-08T18:42:02.000+0000" }, { "id": "198171", "author": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "body": "#### Testing\r\n\r\nNote that this code must be tested on device with compass and GPS support. It can't be tested on emulator.\r\n\r\nUse the following app.js:\r\n\r\n{code}\r\nvar win1 = Titanium.UI.createWindow({\r\n\ttitle : 'Tab 1',\r\n\tbackgroundColor : '#fff',\r\n\tfullscreen : false,\r\n\texitOnClose : true\r\n});\r\nvar hasCompass = false;\r\nvar haveLocationFix = false;\r\nvar locationHandlerRegistered = false;\r\n\r\nvar label1 = Titanium.UI.createLabel({\r\n\tcolor : '#999',\r\n\ttext : 'Checking for Compass Support',\r\n\tfont : {\r\n\t\tfontSize : 20,\r\n\t\tfontFamily : 'Helvetica Neue'\r\n\t},\r\n\ttextAlign : 'center',\r\n\twidth : '80%',\r\n\theight : 160,\r\n\ttop : 20\r\n});\r\n\r\nwin1.add(label1);\r\nvar label2 = Ti.UI.createLabel({\r\n\ttext : 'Checking for location fix',\r\n\ttextAlign : 'center',\r\n\twidth : '80%',\r\n\ttop : 200\r\n});\r\nwin1.add(label2);\r\n\r\nvar getLocationButton = Ti.UI.createButton({\r\n\ttitle : \"Get Location Fix\",\r\n\tbottom : 0,\r\n\tleft : 0,\r\n\twidth : '50%'\r\n});\r\nvar getHeadingButton = Ti.UI.createButton({\r\n\ttitle : \"Get Heading\",\r\n\tbottom : 0,\r\n\tright : 0,\r\n\twidth : '50%'\r\n});\r\nwin1.add(getLocationButton);\r\nwin1.add(getHeadingButton);\r\nwin1.open();\r\n\r\nvar locationHandler = function(e) {\r\n\tTi.API.info('In locationHandler, event = ' + JSON.stringify(e));\r\n\tif (e.success) {\r\n\t\tif (haveLocationFix == false) {\r\n\t\t\thaveLocationFix = true;\r\n\t\t\tlabel2.text = 'Have location fix.';\r\n\t\t}\r\n\t\tif (locationHandlerRegistered) {\r\n\t\t\tTi.Geolocation.removeEventListener(\"location\", locationHandler);\r\n\t\t\tlocationHandlerRegistered = false;\r\n\t\t}\r\n\t} else {\r\n\t\tlabel2.text = 'No location fix.';\r\n\t}\r\n\r\n}\r\n// Check for location fix--on Android, this will NOT activate the radios, just\r\n// return the cached location fix, so we use it to figure out whether we have a current fix.\r\nTi.Geolocation.getCurrentPosition(locationHandler);\r\ngetLocationButton.addEventListener('click', function(e) {\r\n\tif (!locationHandlerRegistered) {\r\n\t\tlabel2.text = 'Trying to get location fix.',\r\n\t\t// Get one-shot location fix.\r\n\t\tTi.Geolocation.accuracy = Ti.Geolocation.ACCURACY_HIGH;\r\n\t\tTi.Geolocation.addEventListener(\"location\", locationHandler);\r\n\t\tlocationHandlerRegistered = true;\r\n\t}\r\n});\r\n\r\nvar headingHandler = function(e) {\r\n\tif (e.success === undefined || e.success) {\r\n\t\tlabel1.setText(\"Mag: \" + e.heading.magneticHeading + \"\\nTrue: \" + e.heading.trueHeading);\r\n\t}\r\n}\r\n\r\n// Without the fix, calling getCurrentHeading is required in order to get a trueHeading value.\r\ngetHeadingButton.addEventListener('click', function(e) {\r\n\tTi.Geolocation.getCurrentHeading(headingHandler);\r\n});\r\n\r\nTi.API.info(\"Got this far.\");\r\nif (Ti.Geolocation.hasCompass !== undefined) {\r\n\tTi.API.info(\"Not undefined.\");\r\n\tif (Ti.Geolocation.hasCompass.call) {\r\n\t\thasCompass = Ti.Geolocation.hasCompass();\r\n\t\tTi.API.info(\"Is method, hasCompass = \" + hasCompass);\r\n\t} else {\r\n\t\thasCompass = Ti.Geolocation.hasCompass;\r\n\t\tTi.API.info(\"Is property, hasCompass = \" + hasCompass);\r\n\t}\r\n}\r\nif (hasCompass) {\r\n\tTi.Geolocation.addEventListener(\"heading\", headingHandler);\r\n} else {\r\n\tlabel1.text = \"Compass not supported\";\r\n}\r\n{code}\r\n\r\nWhen testing _without_ the fix, note that the Mag value shows up (magnetic heading) but the True value (true heading) stays undefined, even if a location fix is available. (If no location fix is available, press the Get Location Fix button.)\r\n\r\nWhen you've satisfied yourself that true heading isn't showing up, press the Get Heading button, and note that the true heading shows up.\r\n\r\nWhen testing with the fix, start the application and note that if a location fix is available when the app starts, the true heading shows up immediately.\r\n\r\nIf there is no location fix, press the Get Location Fix button (but NOT the Get Heading button). After the location label updates to \"Have location fix\", wait for true heading to show up. This should show up in one minute (or less).\r\n", "updateAuthor": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-12T10:58:12.000+0000", "updated": "2012-06-12T10:58:12.000+0000" }, { "id": "198180", "author": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Addressed in PR: \r\nhttps://github.com/appcelerator/titanium_mobile/pull/2373", "updateAuthor": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-12T11:27:26.000+0000", "updated": "2012-06-12T11:27:26.000+0000" }, { "id": "198582", "author": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Updated test code:\r\n\r\n{code}\r\n\r\nvar win1 = Titanium.UI.createWindow({\r\n\ttitle : 'Tab 1',\r\n\tbackgroundColor : '#fff',\r\n\tfullscreen : false,\r\n\texitOnClose : true\r\n});\r\nvar hasCompass = false;\r\nvar haveLocationFix = false;\r\nvar locationHandlerRegistered = false;\r\nvar lastFix =-1;\r\n\r\nvar label1 = Titanium.UI.createLabel({\r\n\tcolor : '#999',\r\n\ttext : 'Checking for Compass Support',\r\n\tfont : {\r\n\t\tfontSize : 20,\r\n\t\tfontFamily : 'Helvetica Neue'\r\n\t},\r\n\ttextAlign : 'center',\r\n\twidth : '80%',\r\n\theight : 160,\r\n\ttop : 20\r\n});\r\n\r\nwin1.add(label1);\r\nvar label2 = Ti.UI.createLabel({\r\n\ttext : 'Checking for location fix',\r\n\ttextAlign : 'center',\r\n\twidth : '80%',\r\n\ttop : 200\r\n});\r\nwin1.add(label2);\r\n\r\nvar getLocationButton = Ti.UI.createButton({\r\n\ttitle : \"Get Location Fix\",\r\n\tbottom : 0,\r\n\tleft : 0,\r\n\twidth : '50%'\r\n});\r\nvar getHeadingButton = Ti.UI.createButton({\r\n\ttitle : \"Get Heading\",\r\n\tbottom : 0,\r\n\tright : 0,\r\n\twidth : '50%'\r\n});\r\nwin1.add(getLocationButton);\r\nwin1.add(getHeadingButton);\r\nwin1.open();\r\n\r\nvar locationHandler = function(e) {\r\n\tTi.API.info('In locationHandler, event = ' + JSON.stringify(e));\r\n\tif (e.success) {\r\n\t\tif (haveLocationFix == false) {\r\n\t\t\thaveLocationFix = true;\r\n\t\t}\r\n\t\tlastFix = (new Date().getTime() - e.coords.timestamp)/60000;\r\n\t\tlabel2.text = String.format('Have location fix, age=%.2f minutes.', lastFix);\r\n\t\tif (locationHandlerRegistered) {\r\n\t\t\tTi.Geolocation.removeEventListener(\"location\", locationHandler);\r\n\t\t\tlocationHandlerRegistered = false;\r\n\t\t}\r\n\t} else {\r\n\t\tlabel2.text = String.format('No location fix, last fix=%.2f minutes', lastfix);\r\n\t}\r\n\r\n}\r\n// Check for location fix--on Android, this will NOT activate the radios, just\r\n// return the cached location fix, so we use it to figure out whether we have a current fix.\r\nsetInterval(function() {\r\n\tTi.Geolocation.getCurrentPosition(locationHandler);\r\n}, 10000);\r\ngetLocationButton.addEventListener('click', function(e) {\r\n\tif (!locationHandlerRegistered) {\r\n\t\tlabel2.text = 'Trying to get location fix.',\r\n\t\t// Get one-shot location fix.\r\n\t\tTi.Geolocation.accuracy = Ti.Geolocation.ACCURACY_HIGH;\r\n\t\tTi.Geolocation.addEventListener(\"location\", locationHandler);\r\n\t\tlocationHandlerRegistered = true;\r\n\t}\r\n});\r\n\r\nvar headingHandler = function(e) {\r\n\tif (e.success === undefined || e.success) {\r\n\t\tlabel1.setText(\"Mag: \" + e.heading.magneticHeading + \"\\nTrue: \" + e.heading.trueHeading);\r\n\t}\r\n}\r\n// Without the fix, calling getCurrentHeading is required in order to get a trueHeading value.\r\ngetHeadingButton.addEventListener('click', function(e) {\r\n\tTi.Geolocation.getCurrentHeading(headingHandler);\r\n});\r\n\r\nTi.API.info(\"Got this far.\");\r\nif (Ti.Geolocation.hasCompass.call) {\r\n\thasCompass = Ti.Geolocation.hasCompass();\r\n} else {\r\n\thasCompass = Ti.Geolocation.hasCompass;\r\n}\r\n\r\nif (hasCompass) {\r\n\tTi.Geolocation.addEventListener(\"heading\", headingHandler);\r\n} else {\r\n\tlabel1.text = \"Compass not supported\";\r\n}\r\n{code}\r\n\r\nUpdated testing instructions:\r\n\r\n- test on a device hooked up to DDMS.\r\n\r\n- To test without location fixes, turn airplane mode on and leave the app to run for 10 minutes. Updates will continue to show trueHeading, but a warning message will be logged 1/minute showing that location is stale.\r\n\r\n- If you start the app when no location fixes are available, you'll see trueHeading is undefined, and a warning message will be logged 1/minute showing that no location fix is available.\r\n\r\n- To test restoring location fixes, turn airplane mode back off and wait for a few minutes. You probably won't even need to request a location explicitly. If the device doesn't show a fix after a few minutes, you can press the Get Location Fix button. (Within ~1 minute of getting a fix, the device should start showing a true heading value.)\r\n", "updateAuthor": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-14T16:16:32.000+0000", "updated": "2012-06-15T15:08:39.000+0000" }, { "id": "198780", "author": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "body": "After discussions with Opie, updated the fix so that we use the stored location, even if it's stale, but we log a message for stale location/no location. Test instructions above are updated.\r\n\r\nKey points for this issue are that we get trueHeading values on 'heading' events (assuming that a location is available).\r\n\r\nWithout the fix, you should see true heading == undefined. ", "updateAuthor": { "name": "aevans", "key": "aevans", "displayName": "Arthur Evans", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-15T15:13:20.000+0000", "updated": "2012-06-15T15:13:20.000+0000" }, { "id": "198991", "author": { "name": "emerriman", "key": "emerriman", "displayName": "Eric Merriman ", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Verified fixed with SDK 2.1.0.v20120618102300 using a droid 1", "updateAuthor": { "name": "emerriman", "key": "emerriman", "displayName": "Eric Merriman ", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-06-18T15:22:22.000+0000", "updated": "2012-06-18T15:22:22.000+0000" }, { "id": "202866", "author": { "name": "amittal", "key": "amittal", "displayName": "Anshu Mittal", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Reopening to update labels.", "updateAuthor": { "name": "amittal", "key": "amittal", "displayName": "Anshu Mittal", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-07-12T00:42:47.000+0000", "updated": "2012-07-12T00:42:47.000+0000" } ], "maxResults": 9, "total": 9, "startAt": 0 } } }