diff --git a/README.md b/README.md
index 991513195..fd7c05be2 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,38 @@ We use ***very brief and informal*** design documents with descriptions of the p
If you have skipped this step and have gone ahead and made your changes already, feel free to open a pull request, but don't be too surprised if we ask you to go back and document it in a design document. Remember that the main goal of doing this is ***to gather as much feedback, as early as possible***. We will also possibly ask you to put an instance with your changes on [appspot](http://appspot.com), and provide a modified Companion app (if that applies) so that reviewers can play with the changes before looking at the source.
+### Forking or cloning
+Consider ***forking*** the project if you want to make changes to the sources. If you simply want to run it locally, you can simply ***clone*** it.
+
+#### Forking
+If you decide to fork, follow the [instructions](https://help.github.com/articles/fork-a-repo) given by github. After that you can clone your own copy of the sources with:
+
+ $ git clone https://github.com/YOUR_USER_NAME/punya.git
+
+Make sure you change *YOUR_USER_NAME* to your user name.
+
+Configuring a remote pointing to this repository is also a good idea if you are forking:
+
+ $ cd punya
+ $ git remote add upstream https://github.com/mit-dig/punya.git
+
+Finally, you will also have to make sure that you are ignoring files that need ignoring:
+
+ $ cp sample-.gitignore .gitignore
+
+### Checkout dependencies
+App Inventor uses Blockly, the web-based visual programming editor from Google, as a core part of its editor. Blockly core is made available to App Inventor as a git submodule. The first time after forking or cloning the repository, you will need to perform the following commands:
+
+ $ git submodule update --init
+
+For developers who will be working on Blockly within the context of App Inventor, the preferred checkout procedure is to perform a `git submodule init`, edit the `.git/config` file to use the read/write SSH URL for [MIT CML's Blockly fork](https://github.com/mit-cml/blockly) instead of the public read-only HTTPS URL assumed by default (to support pushing changes). After changing `.git/config`, a `git submodule update` will pull the repository.
+
+If you need to switch back to a branch that does contains the Blockly and Closure Library sources in the tree, you will need to run the command:
+
+ $ git submodule deinit --all
+
+to clear out the submodules ___before switching branches___. When switching back, you will need to repeat the initialization and update procedure above.
+
## Setup instructions (Vagrant)
The easiest way to get a development environment up and running is to use the provided Vagrantfile. Install [Vagrant](https://vagrantup.com) and open a terminal in the root directory of this repository. Run the following commands
@@ -81,38 +113,6 @@ If you are on an RPM-based distribution(Fedora), use:
Note 2: Certain Java 8 features, such as lambda expressions, are not supported on Android, so please don't use them in your changes to the source code.
-### Forking or cloning
-Consider ***forking*** the project if you want to make changes to the sources. If you simply want to run it locally, you can simply ***clone*** it.
-
-#### Forking
-If you decide to fork, follow the [instructions](https://help.github.com/articles/fork-a-repo) given by github. After that you can clone your own copy of the sources with:
-
- $ git clone https://github.com/YOUR_USER_NAME/punya.git
-
-Make sure you change *YOUR_USER_NAME* to your user name.
-
-Configuring a remote pointing to this repository is also a good idea if you are forking:
-
- $ cd punya
- $ git remote add upstream https://github.com/mit-dig/punya.git
-
-Finally, you will also have to make sure that you are ignoring files that need ignoring:
-
- $ cp sample-.gitignore .gitignore
-
-### Checkout dependencies
-App Inventor uses Blockly, the web-based visual programming editor from Google, as a core part of its editor. Blockly core is made available to App Inventor as a git submodule. The first time after forking or cloning the repository, you will need to perform the following commands:
-
- $ git submodule update --init
-
-For developers who will be working on Blockly within the context of App Inventor, the preferred checkout procedure is to perform a `git submodule init`, edit the `.git/config` file to use the read/write SSH URL for [MIT CML's Blockly fork](https://github.com/mit-cml/blockly) instead of the public read-only HTTPS URL assumed by default (to support pushing changes). After changing `.git/config`, a `git submodule update` will pull the repository.
-
-If you need to switch back to a branch that does contains the Blockly and Closure Library sources in the tree, you will need to run the command:
-
- $ git submodule deinit --all
-
-to clear out the submodules ___before switching branches___. When switching back, you will need to repeat the initialization and update procedure above.
-
### Troubleshooting common installation issues
Run this command to run a self-diagnosis of your environment. This command tries to figure out common installation issues and offers you a solution to fix them yourself. Make sure this passes all the checks before you proceed further.
@@ -156,19 +156,19 @@ The build server can be run from the terminal by typing:
Note that you will only need to run the build server if you are going to build an app as an apk. You can do all the layout and programming without having the build server running, but you will need it to download the apk.
-### Accessing your local server
+## Accessing your local server
You should now be up and running; you can test this by pointing your browser to:
http://localhost:8888
Before entering or scanning the QR code in the Companion, check the box labeled "Use Legacy Connection".
-### Running tests
+## Running tests
The automated tests depend on [Phantomjs](http://phantomjs.org/). Make sure you install it and add it to your path. After that, you can run all tests by typing the following in a terminal window:
$ ant tests
-### Building Release Code
+## Building Release Code
Release builds with optimizations turned on for the web components of the system can be done by passing `-Drelease=true` to `ant`, e.g.:
@@ -182,7 +182,7 @@ The release configuration sets the following additional options:
- App Engine YaClient module is compiled without `` to create per-language/browser builds
- App Engine YaClient module is compiled with optimization tuned to 9 and with 8 threads
-### Hot-reloading GWT code with 'Super Dev Mode'
+## Hot-reloading GWT code with 'Super Dev Mode'
1. Run `ant devmode`
2. [Run the main server](#running-the-main-server).
3. Open http://localhost:9876 (*GWT CodeServer*) and drag the two bookmarklets (*Dev Mode On & Off*) to the bookmarks bar.
diff --git a/appinventor/appengine/build.xml b/appinventor/appengine/build.xml
index 0e6ea6382..da5508925 100644
--- a/appinventor/appengine/build.xml
+++ b/appinventor/appengine/build.xml
@@ -591,6 +591,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -638,30 +641,8 @@
description="Run development mode">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
diff --git a/appinventor/appengine/src/com/google/appinventor/client/utils/html5dnd.js b/appinventor/appengine/src/com/google/appinventor/client/utils/html5dnd.js
index 6f658398b..f963392f0 100644
--- a/appinventor/appengine/src/com/google/appinventor/client/utils/html5dnd.js
+++ b/appinventor/appengine/src/com/google/appinventor/client/utils/html5dnd.js
@@ -25,7 +25,7 @@ top.HTML5DragDrop_reportError = function(errorCode) {};
top.HTML5DragDrop_confirmOverwriteKey = function(callback) {};
top.HTML5DragDrop_confirmOverwriteAsset = function(proejctId, name, callback) {};
top.HTML5DragDrop_checkProjectNameForCollision = function(name) {};
-top.HTML5DragDrop_shouldShowDropTarget = function(target) {};s
+top.HTML5DragDrop_shouldShowDropTarget = function(target) {};
var dropdiv = document.createElement('div');
dropdiv.className = 'dropdiv';
diff --git a/appinventor/appengine/src/com/google/appinventor/shared/rpc/semweb/SemWebConstants.properties b/appinventor/appengine/src/com/google/appinventor/shared/rpc/semweb/SemWebConstants.properties
index 0c1f62123..2a85c14fc 100644
--- a/appinventor/appengine/src/com/google/appinventor/shared/rpc/semweb/SemWebConstants.properties
+++ b/appinventor/appengine/src/com/google/appinventor/shared/rpc/semweb/SemWebConstants.properties
@@ -4,4 +4,4 @@ defaultEndpoint = http://dbpedia.org/sparql
#not found: http://orion.tw.rpi.edu/~pattoe/moac.ttl
#syntax errors: https://www.w3.org/2001/sw/RDFCore/Schema/200212bwm/rdfs-namespace.xml
-ontologies = http://xmlns.com/foaf/0.1/,http://hxl.humanitarianresponse.info/ns/hxl.ttl,https://download.bio2rdf.org/files/current/drugbank/drugbank.schema.owl,http://purl.org/dc/terms/,https://schema.org/version/latest/schemaorg-current-http.ttl,https://projects.cs.dal.ca/niche/sleepapnea.owl,https://www.w3.org/2000/01/rdf-schema#
+ontologies = http://xmlns.com/foaf/0.1/,http://hxl.humanitarianresponse.info/ns/hxl.ttl,https://download.bio2rdf.org/files/current/drugbank/drugbank.schema.owl,http://purl.org/dc/terms/,https://schema.org/version/latest/schemaorg-current-http.ttl,https://projects.cs.dal.ca/niche/sleepapnea.owl
diff --git a/appinventor/components/build.xml b/appinventor/components/build.xml
index 08787f969..aa706165c 100755
--- a/appinventor/components/build.xml
+++ b/appinventor/components/build.xml
@@ -198,6 +198,8 @@
+
+
diff --git a/appinventor/components/src/com/google/appinventor/components/common/YaVersion.java b/appinventor/components/src/com/google/appinventor/components/common/YaVersion.java
index ca18d9017..5fe8f9b3a 100644
--- a/appinventor/components/src/com/google/appinventor/components/common/YaVersion.java
+++ b/appinventor/components/src/com/google/appinventor/components/common/YaVersion.java
@@ -1549,6 +1549,8 @@ private YaVersion() {
public static final int LIGHTSENSOR_COMPONENT_VERSION = 1;
public static final int THERMOMETER_COMPONENT_VERSION = 1;
+ public static final int GPLACES_COMPONENT_VERSION = 1;
+
// Companion Versions and Update Information
// The PREFERRED_COMPANION is displayed to the end-user if
diff --git a/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
new file mode 100644
index 000000000..b777d903e
--- /dev/null
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
@@ -0,0 +1,216 @@
+package com.google.appinventor.components.runtime;
+
+import com.google.appinventor.components.annotations.*;
+import com.google.appinventor.components.common.ComponentCategory;
+import com.google.appinventor.components.common.PropertyTypeConstants;
+import com.google.appinventor.components.common.YaVersion;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.google.appinventor.components.runtime.util.YailDictionary;
+import com.google.appinventor.components.runtime.util.YailList;
+import com.google.gson.JsonElement;
+import java.time.LocalTime;
+import java.util.Arrays;
+import com.google.maps.GeoApiContext;
+import com.google.maps.NearbySearchRequest;
+import com.google.maps.PendingResult.Callback;
+import com.google.maps.PlacesApi;
+import com.google.maps.errors.ApiException;
+import com.google.maps.model.LatLng;
+import com.google.maps.model.PlaceType;
+import com.google.maps.model.PlacesSearchResponse;
+import com.google.maps.model.PlacesSearchResult;
+import com.google.maps.model.OpeningHours.Period;
+
+import edu.mit.media.funf.json.IJsonObject;
+import edu.mit.media.funf.probe.builtin.ProbeKeys;
+
+@DesignerComponent(version = YaVersion.GPLACES_COMPONENT_VERSION,
+ description = "A component that accesses the Google Places API given the user's current location, " +
+ "and returns a set of nearby places (see also OverpassPlacesService). " +
+ "It relies on the LocationProbeSensor and thus offers similar options, " +
+ "i.e., periodically get the user's location at a configurable time-interval, scanning period, " +
+ "and good-enough-accuracy. Subsequently, the component will get places nearby the user's location. " +
+ "(After the scanning period, the component will return the found location with the highest accuracy; " +
+ "ending early when finding a location with good-enough-accuracy.)
" +
+ "Additionally, one can specify a minimum-location-change property that will call the Places API" +
+ " only in case the user's location has changed significantly.
" +
+ "One can specify the radius (in meters) around the user for which to return places.
",
+ category = ComponentCategory.CONNECTIVITY, nonVisible = true, iconName = "images/locationProbe.png", showOnPalette = true)
+@SimpleObject
+@UsesLibraries(libraries = "google-maps-services-0.18.1.jar,funf.jar")
+public class GooglePlacesService extends PlacesWebService implements Callback {
+
+ private GeoApiContext context;
+
+ /**
+ * Creates a new GooglePlacesService component.
+ *
+ * @param container the container that this component will be placed in
+ */
+ protected GooglePlacesService(ComponentContainer container) {
+ super(container);
+
+ apiKeyRequired = true;
+ }
+
+ @Override
+ protected boolean checkInput() {
+ if (super.checkInput()) {
+ if (placeType != null) {
+ try {
+ PlaceType.valueOf(placeType);
+ } catch (IllegalArgumentException e) {
+ ServiceError("Unknown type of place: " + placeType + ". See "
+ + "https://developers.google.com/maps/documentation/places/web-service/supported_types "
+ + "for a list of supported types.");
+ return false;
+ }
+ }
+ return true;
+
+ } else
+ return false;
+ }
+
+ /**
+ * Returns the type of nearby places that should be returned. See
+ * https://developers.google.com/maps/documentation/places/web-service/supported_types
+ * for the list of supported types.
+ *
+ * @return placeType
+ */
+ @SimpleProperty(description = "type of nearby places that should be returned. " +
+ "See https://developers.google.com/maps/documentation/places/web-service/supported_types " +
+ "for the list of supported types", category = PropertyCategory.BEHAVIOR)
+ public String PlaceType() {
+ return placeType;
+ }
+
+ /**
+ * Specifies the type of nearby places that should be returned.
+ *
+ * @param placeType
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING)
+ @SimpleProperty
+ public void PlaceType(String placeType) {
+ this.placeType = placeType.toUpperCase();
+ }
+
+ /**
+ * This method is called when receiving new location data, as per the
+ * configuration of the location probe sensor. A subclass will do something with
+ * this location (e.g., call a service for nearby places) and likely raise an
+ * event.
+ *
+ * @param completeProbeUri, location
+ */
+ @Override
+ public void onDataReceived(IJsonObject completeProbeUri, IJsonObject location) {
+ System.out.println("location: " + location);
+
+ double lat = location.get(ProbeKeys.LocationKeys.LATITUDE).getAsDouble();
+ double lon = location.get(ProbeKeys.LocationKeys.LONGITUDE).getAsDouble();
+
+ context = new GeoApiContext.Builder().apiKey(apiKey).build();
+ LatLng myLocation = new LatLng(lat, lon);
+
+ try {
+ NearbySearchRequest request = PlacesApi.nearbySearchQuery(context, myLocation).radius(nearbyRadius);
+ if (placeType != null)
+ request.type(PlaceType.valueOf(placeType));
+
+ // TODO does not get called
+// request.setCallback(this);
+
+ onResult(request.await());
+
+ } catch (IOException | InterruptedException | ApiException e) {
+ e.printStackTrace();
+ ServiceError(e.getMessage());
+ }
+ }
+
+ @Override
+ public void onResult(PlacesSearchResponse response) {
+ System.out.println("response? " + response);
+
+ List places = new ArrayList<>();
+ for (PlacesSearchResult result : response.results)
+ places.add(toDictionary(result));
+
+ NearbyPlacesReceived(YailList.makeList(places));
+
+ // TODO apparently need to wait 2 seconds
+ // https://developers.google.com/maps/documentation/javascript/places#PlaceSearchPaging
+
+// if (response.nextPageToken != null) {
+// try {
+// onResult(PlacesApi.nearbySearchNextPage(context, response.nextPageToken).await());
+//
+// } catch (IOException | InterruptedException | ApiException e) {
+// e.printStackTrace();
+// ServiceError(e.getMessage());
+// }
+// }
+
+ context.shutdown();
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+// StringWriter sw = new StringWriter();
+// PrintWriter pw = new PrintWriter(sw);
+// e.printStackTrace(pw);
+// ServiceError(sw.toString());
+
+ e.printStackTrace();
+ ServiceError(e.getMessage());
+ }
+
+ private YailDictionary toDictionary(PlacesSearchResult result) {
+ System.out.println(result);
+
+ YailDictionary place = new YailDictionary();
+
+ LatLng latLng = result.geometry.location;
+ place.put("location", YailList.makeList(new Double[] { latLng.lat, latLng.lng }));
+
+ place.put("types", YailList.makeList(result.types));
+
+ place.put("permanentlyClosed", result.permanentlyClosed);
+
+ if (result.openingHours != null) {
+
+ if (result.openingHours.openNow)
+ place.put("openNow", result.openingHours.openNow);
+
+ if (result.openingHours.periods != null) {
+ YailList hours = YailList.makeEmptyList();
+ place.put("hours", hours);
+
+ for (Period orPeriod : result.openingHours.periods) {
+ YailList open = YailList.makeList(new Object[] { orPeriod.open.day.ordinal(), orPeriod.open.time });
+ YailList close = YailList
+ .makeList(new Object[] { orPeriod.close.day.ordinal(), orPeriod.close.time });
+
+ YailList period = YailList.makeList(new Object[] { open, close });
+ hours.add(period);
+ }
+ }
+ }
+ System.out.println(place + "\n");
+
+ return place;
+ }
+
+ @Override
+ public void onDataCompleted(IJsonObject iJsonObject, JsonElement jsonElement) {
+ }
+}
diff --git a/appinventor/components/src/com/google/appinventor/components/runtime/LocationAwareWebService.java b/appinventor/components/src/com/google/appinventor/components/runtime/LocationAwareWebService.java
new file mode 100644
index 000000000..17763f893
--- /dev/null
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/LocationAwareWebService.java
@@ -0,0 +1,146 @@
+package com.google.appinventor.components.runtime;
+
+import android.util.Log;
+import com.google.appinventor.components.annotations.*;
+import com.google.appinventor.components.common.PropertyTypeConstants;
+import com.google.gson.JsonElement;
+import edu.mit.media.funf.json.IJsonObject;
+import edu.mit.media.funf.probe.Probe;
+import edu.mit.media.funf.probe.builtin.ProbeKeys;
+
+/**
+ * This class is meant to act as a superclass to concrete location-aware, web service components.
+ *
+ * @author william.van.woensel@gmail.com
+ */
+@SimpleObject
+@UsesPermissions(permissionNames = "android.permission.ACCESS_FINE_LOCATION")
+public abstract class LocationAwareWebService extends WebService implements Probe.DataListener {
+
+ private final String TAG = "PlacesWebService";
+
+ protected LocationProbeSensor locationProbeSensor;
+
+ protected int minimumLocationChange = 0; // 0 meters
+
+ /**
+ * Creates a new LocationAwareWebService component.
+ *
+ * @param container the container that this component will be placed in
+ */
+ protected LocationAwareWebService(ComponentContainer container) {
+ super(container);
+
+ this.locationProbeSensor = new LocationProbeSensor(container);
+ // override the data listener with this component
+ // when run-once or scheduled location fixes are received,
+ // the methods of this listener will be called
+ locationProbeSensor.overrideListener(this);
+ }
+
+ /**
+ * Returns the default interval (in seconds) between actions, where an action includes a location probe plus service call.
+ *
+ * @return defaultInterval
+ */
+ @SimpleProperty(description = "The default interval (in seconds) between actions, where an action includes a location probe plus service call",
+ category = PropertyCategory.BEHAVIOR)
+ public int DefaultInterval(){return locationProbeSensor.DefaultInterval();}
+
+ /**
+ * Specifies the default interval (in seconds) between actions, where an action includes a location probe plus service call.
+ *
+ * @param defaultInterval
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "180")
+ @SimpleProperty
+ public void DefaultInterval(int defaultInterval) { locationProbeSensor.DefaultInterval(defaultInterval); }
+
+ /**
+ * Returns the default duration (in seconds) of each location probe scan
+ *
+ * @return defaultDuration
+ */
+ @SimpleProperty(description = "The default duration (in seconds) of each location probe scan",
+ category = PropertyCategory.BEHAVIOR)
+ public int DefaultDuration(){ return locationProbeSensor.DefaultDuration();}
+
+ /**
+ * Specifies the default duration (in seconds) of each location probe scan.
+ *
+ * @param defaultDuration
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "10")
+ @SimpleProperty
+ public void DefaultDuration(int defaultDuration) { locationProbeSensor.DefaultDuration(defaultDuration); }
+
+ /**
+ * Returns the good-enough-accuracy of the location data (0-100).
+ * If the location accuracy lies below this threshold, then the online service will not be called.
+ *
+ * @return goodEnoughAccuracy
+ */
+ @SimpleProperty(description = "The good-enough-accuracy of the location data (0-100). " +
+ "If the location accuracy lies below this threshold, then the online service will not be called.",
+ category = PropertyCategory.BEHAVIOR)
+ public int GoodEnoughAccuracy() {
+ return locationProbeSensor.GoodEnoughAccuracy();
+ }
+
+ /**
+ * Sets the good-enough-accuracy of the location data (0-100).
+ *
+ * @param goodEnoughAccuracy
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "80")
+ @SimpleProperty
+ public void GoodEnoughAccuracy(int goodEnoughAccuracy) { locationProbeSensor.GoodEnoughAccuracy(goodEnoughAccuracy); }
+
+ /**
+ * Returns whether the location probe will use GPS or not.
+ */
+ @SimpleProperty(description = "Whether the location probe will use GPS or not", category = PropertyCategory.BEHAVIOR)
+ public boolean UseGPS() {
+ return locationProbeSensor.UseGPS();
+ }
+
+ /**
+ * Specifies whether the location probe will use GPS or not.
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "True")
+ @SimpleProperty
+ public void UseGPS(boolean useGPS) { locationProbeSensor.UseGPS(useGPS); }
+
+ /**
+ * Returns whether the location probe will use the network or not.
+ */
+ @SimpleProperty(description = "whether the location probe will use the network or not",
+ category = PropertyCategory.BEHAVIOR)
+ public boolean UseNetwork() { return locationProbeSensor.UseNetwork(); }
+
+ /**
+ * Specifies whether the location probe will use the network or not.
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "True")
+ @SimpleProperty
+ public void UseNetwork(boolean useNetwork) { locationProbeSensor.UseNetwork(useNetwork); }
+
+ /**
+ * Returns the minimal difference in location (in meters) compared to the prior location, before the service is called.
+ * This avoids calling the online service for location-specific data when the user's location has not really changed much.
+ *
+ * @return minimumLocationChange
+ */
+ @SimpleProperty(description = "The minimal difference in location (in meters) compared to the prior location, before the service is called. " +
+ "This avoids calling the online service for location-specific data when the user's location has not really changed much.",
+ category = PropertyCategory.BEHAVIOR)
+ public int MinimumLocationChange() { return minimumLocationChange; }
+
+ /**
+ * Specifies the minimal difference in location (in meters) compared to the prior location, before the service is called.
+ *
+ * @param minimumLocationChange
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "0")
+ public void MinimumLocationChange(int minimumLocationChange) { this.minimumLocationChange = minimumLocationChange; }
+}
\ No newline at end of file
diff --git a/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java b/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java
index 7188a84d9..520b65a07 100755
--- a/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java
@@ -28,10 +28,6 @@
import edu.mit.media.funf.probe.builtin.SensorProbe;
import edu.mit.media.funf.probe.builtin.SimpleLocationProbe;
-
-
-
-
/**
* Record GPS location periodically
*
@@ -68,10 +64,10 @@ public class LocationProbeSensor extends ProbeBase{
private SimpleLocationProbe probe;
-
+
//default settings for schedule
- private final int SCHEDULE_INTERVAL = 1800; //read location information every 10 minutes
- private final int SCHEDULE_DURATION = 60; //scan for 60 seconds everytime
+ private final int SCHEDULE_INTERVAL = 180; //read location information every 3 minutes
+ private final int SCHEDULE_DURATION = 10; //scan for 10 seconds everytime
private final int GOOD_ENOUGHT_ACCURACY = 80;
private boolean useGPS = true;
private boolean useNetwork = true;
@@ -82,7 +78,6 @@ public class LocationProbeSensor extends ProbeBase{
public LocationProbeSensor(ComponentContainer container) {
super(container);
- // TODO Auto-generated constructor stub
// Set up listeners
form.registerForOnDestroy(this);
@@ -98,7 +93,6 @@ public LocationProbeSensor(ComponentContainer container) {
interval = SCHEDULE_INTERVAL;
duration = SCHEDULE_DURATION;
goodEnoughAccurary = GOOD_ENOUGHT_ACCURACY;
-
}
final Handler myHandler = new Handler() {
@@ -117,15 +111,9 @@ public void handleMessage(Message msg) {
Log.i(TAG, " before call LocationInfoReceived();");
LocationInfoReceived(timestamp, mLatitude, mLongitude, mAccuracy, mProvider);
Log.i(TAG, " after call LocationInfoReceived();");
-
}
-
-
};
-
-
-
/**
* Indicates that the Location info has been received.
*/
@@ -144,12 +132,9 @@ public void run() {
mAccuracy, mProvider);
}
});
-
- }
-
+ }
}
-
/**
* Indicates that the updating Location info has completed.
*/
@@ -164,13 +149,9 @@ public void run() {
"LocationUpdateComplete");
}
});
- }
-
-
+ }
}
-
-
private DataListener listener = new DataListener() {
@Override
public void onDataCompleted(IJsonObject completeProbeUri,
@@ -193,7 +174,6 @@ public void onDataReceived(IJsonObject completeProbeUri,
//save data to DB is enabledSaveToDB is true
if(enabledSaveToDB){
-
saveToDB(completeProbeUri, data);
}
@@ -202,14 +182,12 @@ public void onDataReceived(IJsonObject completeProbeUri,
msg.obj = data;
myHandler.sendMessage(msg);
-
-
}
-
};
-
-
-
+
+ public void overrideListener(DataListener listener) {
+ this.listener = listener;
+ }
/**
* Indicates whether the sensor should "run once" to listen for location information
@@ -234,7 +212,6 @@ public void Enabled(boolean enabled) {
probe.unregisterListener(listener);
Log.i(TAG, "unregister location run-once listener");
}
-
}
/*
@@ -256,7 +233,7 @@ private JsonObject createNewConfig(boolean useGPS, boolean useNetwork, int goodE
* GPS or Network fix
* @param newVal
*/
- @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "Fsocalse")
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "False")
@SimpleProperty(description = "Set whether the location info will use the last known location without" +
" acquring a new location either through GPC or Network fix")
public void UseCache(boolean newVal){
@@ -279,9 +256,7 @@ public boolean UseCache(){
public void GoodEnoughAccuracy(int newVal) {
if(goodEnoughAccurary != newVal){
this.goodEnoughAccurary = newVal;
-
}
-
}
/**
@@ -300,9 +275,7 @@ public int GoodEnoughAccuracy() {
public void UseGPS(boolean newVal) {
if(useGPS != newVal){
this.useGPS = newVal;
-
}
-
}
@@ -323,9 +296,7 @@ public boolean UseGPS() {
public void UseNetwork(boolean newVal) {
if(useNetwork != newVal){
this.useNetwork = newVal;
-
}
-
}
@@ -337,12 +308,8 @@ public boolean UseNetwork() {
return useNetwork;
}
-
-
@Override
public void registerDataRequest(int interval, int duration) {
- // TODO Auto-generated method stub
-
Log.i(TAG, "Registering location data requests.");
JsonElement dataRequest = null;
@@ -355,22 +322,15 @@ public void registerDataRequest(int interval, int duration) {
Log.i(TAG, "Location Data request: " + dataRequest.toString());
mBoundFunfManager.requestData(listener, dataRequest);
-
}
-
-
@Override
public void unregisterDataRequest() {
- // TODO Auto-generated method stub
-
Log.i(TAG, "Unregistering location data requests.");
//mBoundFunfManager.stopForeground(true);
mBoundFunfManager.unrequestAllData2(listener);
Log.i(TAG, "After Unregistering location data requests.");
-
-
}
// /**
@@ -422,21 +382,21 @@ public void unregisterDataRequest() {
* Returns the default interval between each scan for this probe
*/
@SimpleProperty(description = "The default interval (in seconds) between each scan for this probe")
- public float DefaultInterval(){
-
- return SCHEDULE_INTERVAL;
- }
+ public int DefaultInterval(){return interval;}
+
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "180")
+ @SimpleProperty(category = PropertyCategory.BEHAVIOR)
+ public void DefaultInterval(int interval) { this.interval = interval; }
+
/*
* Returns the default duration of each scan for this probe
*/
@SimpleProperty(description = "The default duration (in seconds) of each scan for this probe")
- public float DefaultDuration(){
-
- return SCHEDULE_DURATION;
- }
-
+ public int DefaultDuration(){ return duration;}
-
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "10")
+ @SimpleProperty(category = PropertyCategory.BEHAVIOR)
+ public void DefaultDuration(int duration) { this.duration = duration; }
}
\ No newline at end of file
diff --git a/appinventor/components/src/com/google/appinventor/components/runtime/PlacesWebService.java b/appinventor/components/src/com/google/appinventor/components/runtime/PlacesWebService.java
new file mode 100644
index 000000000..d1178576e
--- /dev/null
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/PlacesWebService.java
@@ -0,0 +1,100 @@
+package com.google.appinventor.components.runtime;
+
+import com.google.appinventor.components.annotations.*;
+import com.google.appinventor.components.common.PropertyTypeConstants;
+import com.google.appinventor.components.runtime.util.YailList;
+
+import java.time.LocalTime;
+import java.util.Arrays;
+import java.util.List;
+
+@SimpleObject
+public abstract class PlacesWebService extends LocationAwareWebService {
+
+ protected boolean enabledNearbyPlaces = false; // run once
+ protected boolean enabledScheduleNearbyPlaces = false; // run periodically
+
+ protected int nearbyRadius = 100; // 100 meters
+ protected String placeType = null;
+
+ /**
+ * Creates a new PlacesWebService component.
+ *
+ * @param container the container that this component will be placed in
+ */
+ protected PlacesWebService(ComponentContainer container) {
+ super(container);
+ }
+
+ @SimpleProperty(description = "Whether the component was run once to get location data and call the web service for nearby places",
+ category = PropertyCategory.BEHAVIOR)
+ public boolean EnabledNearbyPlaces() { return enabledNearbyPlaces; }
+
+ @SimpleProperty(description = "Whether the component is enabled to periodically listen for location data, " +
+ "and call the web service for nearby places",
+ category = PropertyCategory.BEHAVIOR)
+ public boolean EnabledScheduleNearbyPlaces() { return enabledScheduleNearbyPlaces; }
+
+ @SimpleProperty(description = "The default interval (in seconds) between actions, where an action includes a location probe plus service call",
+ category = PropertyCategory.BEHAVIOR)
+ public int ScheduleNearbyPlacesInterval() { return locationProbeSensor.DefaultInterval(); }
+
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "180")
+ @SimpleProperty
+ public void ScheduleNearbyPlacesInterval(int interval) { locationProbeSensor.DefaultInterval(interval); }
+
+ @SimpleFunction(description = "Enable the component to run once to get location data, call the web service for nearby places, " +
+ "and raise the corresponding events")
+ public void EnableNearbyPlaces(boolean enableNearbyPlaces) {
+ if (checkInput()) {
+ this.enabledNearbyPlaces = enableNearbyPlaces;
+ locationProbeSensor.Enabled(enableNearbyPlaces);
+ }
+ }
+
+ @SimpleFunction(description = "Enable the component to periodically listen for location data, " +
+ "call the web service for nearby places, and raise the corresponding events")
+ public void EnableScheduleNearbyPlaces(boolean enableScheduleNearbyPlaces) {
+ if (checkInput()) {
+ this.enabledScheduleNearbyPlaces = enableScheduleNearbyPlaces;
+ locationProbeSensor.EnabledSchedule(enableScheduleNearbyPlaces);
+ }
+ }
+
+ /**
+ * Returns the radius around the user’s current location (meters) for which nearby places should be returned.
+ *
+ * @return nearbyRadius
+ */
+ @SimpleProperty(description = "The radius around the user’s current location (meters) for which nearby places should be returned",
+ category = PropertyCategory.BEHAVIOR)
+ public int NearbyRadius() { return nearbyRadius; }
+
+ /**
+ * Specifies the radius around the user’s current location (meters) for which nearby places should be returned.
+ *
+ * @param nearbyRadius
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "100")
+ public void NearbyRadius(int nearbyRadius) { this.nearbyRadius = nearbyRadius; }
+
+ @SimpleEvent(description = "Event indicating that nearby places have been received")
+ public void NearbyPlacesReceived(final YailList nearbyPlaces) {
+ final Component component = this;
+ if (enabledNearbyPlaces || enabledScheduleNearbyPlaces) {
+ // TODO unclear whether this is needed (done in LocationProbeSensor, not Web)
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ EventDispatcher.dispatchEvent(component, "NearbyPlacesReceived", nearbyPlaces);
+ }
+ });
+ }
+ }
+
+ // We want service-specific documentation so have subclasses implement these
+ // methods. Also, subclasses may want to pre-process these types.
+
+ public abstract String PlaceType();
+
+ public abstract void PlaceType(String placeType);
+}
diff --git a/appinventor/components/src/com/google/appinventor/components/runtime/WebService.java b/appinventor/components/src/com/google/appinventor/components/runtime/WebService.java
new file mode 100644
index 000000000..987003308
--- /dev/null
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/WebService.java
@@ -0,0 +1,91 @@
+package com.google.appinventor.components.runtime;
+
+import android.app.Activity;
+import com.google.appinventor.components.annotations.*;
+import com.google.appinventor.components.common.PropertyTypeConstants;
+
+/**
+ * This class is meant to act as a superclass to concrete web service components.
+ *
+ * @author william.van.woensel@gmail.com
+ */
+@SimpleObject
+@UsesPermissions(permissionNames = "android.permission.INTERNET")
+ // "android.permission.WRITE_EXTERNAL_STORAGE," +
+ // "android.permission.READ_EXTERNAL_STORAGE")
+
+public class WebService extends AndroidNonvisibleComponent implements Component {
+
+ protected final Activity activity;
+ protected boolean apiKeyRequired = false;
+ protected String apiKey;
+
+ /**
+ * Creates a new WebService component.
+ *
+ * @param container the container that this component will be placed in
+ */
+ protected WebService(ComponentContainer container) {
+ super(container.$form());
+
+ this.activity = container.$context();
+ }
+
+ protected boolean checkInput() {
+ if (apiKeyRequired && apiKey == null) {
+ ServiceError("API key required for this service but none given. See ApiKey property.");
+ return false;
+ } else
+ return true;
+ }
+
+ /**
+ * The API key for the web service, if any.
+ *
+ * @return the API key
+ */
+ @SimpleProperty(description = "The API key for this web service, if any.", category = PropertyCategory.BEHAVIOR)
+ public String ApiKey() { return apiKey; }
+
+ /**
+ * Specifies the API key.
+ *
+ * @param apiKey
+ */
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING,
+ defaultValue = "")
+ @SimpleProperty
+ public void ApiKey(String apiKey) { this.apiKey = apiKey; }
+
+ /**
+ * Event indicating an error during the service call.
+ *
+ * @param error
+ */
+ @SimpleEvent(description = "Event indicating an error during the service call")
+ public void ServiceError(final String error) {
+ final Component component = this;
+ // TODO unclear whether this is needed (done in LocationProbeSensor, not Web)
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ EventDispatcher.dispatchEvent(component, "ServiceError", error);
+ }
+ });
+ }
+
+ /**
+ * Event indicating that the service call has finished.
+ *
+ * @param data
+ */
+ @SimpleEvent(description = "Event indicating that the service call has finished")
+ public void ServiceDataReceived(final String data) {
+ final Component component = this;
+ // TODO unclear whether this is needed (done in LocationProbeSensor, not Web)
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ EventDispatcher.dispatchEvent(component, "ServiceDataReceived", data);
+ }
+ });
+ }
+}
diff --git a/appinventor/docs/html/reference/components/connectivity.html b/appinventor/docs/html/reference/components/connectivity.html
index a2c7eb661..dee039e9b 100644
--- a/appinventor/docs/html/reference/components/connectivity.html
+++ b/appinventor/docs/html/reference/components/connectivity.html
@@ -134,6 +134,7 @@ Connectivity
ActivityStarter
BluetoothClient
BluetoothServer
+ GooglePlacesService
LdpCoapClient
Serial
Web
@@ -475,6 +476,60 @@ Methods
Stop accepting an incoming connection.
+GooglePlacesService
+
+Component for GooglePlacesService
+
+Properties
+
+
+ - ApiKey
+ - Specifies the API key.
+ - DefaultDuration
+ - Specifies the default duration (in seconds) of each location probe scan.
+ - DefaultInterval
+ - Specifies the default interval (in seconds) between actions, where an action includes a location probe plus service call.
+ - EnabledNearbyPlaces
+ - Whether the component was run once to get location data and call the web service for nearby places
+ - EnabledScheduleNearbyPlaces
+ - Whether the component is enabled to periodically listen for location data, and call the web service for nearby places
+ - GoodEnoughAccuracy
+ - Sets the good-enough-accuracy of the location data (0-100).
+ - MinimumLocationChange
+ - Returns the minimal difference in location (in meters) compared to the prior location, before the service is called.
+ This avoids calling the online service for location-specific data when the user’s location has not really changed much.
+ - NearbyRadius
+ - Returns the radius around the user’s current location (meters) for which nearby places should be returned.
+ - PlaceType
+ - Specifies the type of nearby places that should be returned.
+ - ScheduleNearbyPlacesInterval
+ - The default interval (in seconds) between actions, where an action includes a location probe plus service call
+ - UseGPS
+ - Specifies whether the location probe will use GPS or not.
+ - UseNetwork
+ - Specifies whether the location probe will use the network or not.
+
+
+Events
+
+
+ - NearbyPlacesReceived(nearbyPlaces)
+ - Event indicating that nearby places have been received
+ - ServiceDataReceived(data)
+ - Event indicating that the service call has finished.
+ - ServiceError(error)
+ - Event indicating an error during the service call.
+
+
+Methods
+
+
+ - EnableNearbyPlaces(enableNearbyPlaces)
+ - Enable the component to run once to get location data, call the web service for nearby places, and raise the corresponding events
+ - EnableScheduleNearbyPlaces(enableScheduleNearbyPlaces)
+ - Enable the component to periodically listen for location data, call the web service for nearby places, and raise the corresponding events
+
+
LdpCoapClient

diff --git a/appinventor/docs/html/reference/components/sensors.html b/appinventor/docs/html/reference/components/sensors.html
index 61eb44924..f8245b4bb 100644
--- a/appinventor/docs/html/reference/components/sensors.html
+++ b/appinventor/docs/html/reference/components/sensors.html
@@ -792,9 +792,9 @@ LocationProbeSensor
Properties
- - DefaultDuration
+ - DefaultDuration
- The default duration (in seconds) of each scan for this probe
- - DefaultInterval
+ - DefaultInterval
- The default interval (in seconds) between each scan for this probe
- EnableSaveToDB
- Set to indicate whether the returned sensor data should be saved to db automatically
diff --git a/appinventor/docs/markdown/reference/components/connectivity.md b/appinventor/docs/markdown/reference/components/connectivity.md
index c35513b98..05cb6f3f9 100644
--- a/appinventor/docs/markdown/reference/components/connectivity.md
+++ b/appinventor/docs/markdown/reference/components/connectivity.md
@@ -11,6 +11,7 @@ Table of Contents:
* [ActivityStarter](#ActivityStarter)
* [BluetoothClient](#BluetoothClient)
* [BluetoothServer](#BluetoothServer)
+* [GooglePlacesService](#GooglePlacesService)
* [LdpCoapClient](#LdpCoapClient)
* [Serial](#Serial)
* [Web](#Web)
@@ -396,6 +397,76 @@ Use the `BluetoothServer` component to turn your device into a server that recei
{:id="BluetoothServer.StopAccepting" class="method"} StopAccepting()
: Stop accepting an incoming connection.
+## GooglePlacesService {#GooglePlacesService}
+
+Component for GooglePlacesService
+
+
+
+### Properties {#GooglePlacesService-Properties}
+
+{:.properties}
+
+{:id="GooglePlacesService.ApiKey" .text} *ApiKey*
+: Specifies the API key.
+
+{:id="GooglePlacesService.DefaultDuration" .number} *DefaultDuration*
+: Specifies the default duration (in seconds) of each location probe scan.
+
+{:id="GooglePlacesService.DefaultInterval" .number} *DefaultInterval*
+: Specifies the default interval (in seconds) between actions, where an action includes a location probe plus service call.
+
+{:id="GooglePlacesService.EnabledNearbyPlaces" .boolean .ro .bo} *EnabledNearbyPlaces*
+: Whether the component was run once to get location data and call the web service for nearby places
+
+{:id="GooglePlacesService.EnabledScheduleNearbyPlaces" .boolean .ro .bo} *EnabledScheduleNearbyPlaces*
+: Whether the component is enabled to periodically listen for location data, and call the web service for nearby places
+
+{:id="GooglePlacesService.GoodEnoughAccuracy" .number} *GoodEnoughAccuracy*
+: Sets the good-enough-accuracy of the location data (0-100).
+
+{:id="GooglePlacesService.MinimumLocationChange" .number .ro} *MinimumLocationChange*
+: Returns the minimal difference in location (in meters) compared to the prior location, before the service is called.
+ This avoids calling the online service for location-specific data when the user's location has not really changed much.
+
+{:id="GooglePlacesService.NearbyRadius" .number .ro} *NearbyRadius*
+: Returns the radius around the user’s current location (meters) for which nearby places should be returned.
+
+{:id="GooglePlacesService.PlaceType" .text} *PlaceType*
+: Specifies the type of nearby places that should be returned.
+
+{:id="GooglePlacesService.ScheduleNearbyPlacesInterval" .number} *ScheduleNearbyPlacesInterval*
+: The default interval (in seconds) between actions, where an action includes a location probe plus service call
+
+{:id="GooglePlacesService.UseGPS" .boolean} *UseGPS*
+: Specifies whether the location probe will use GPS or not.
+
+{:id="GooglePlacesService.UseNetwork" .boolean} *UseNetwork*
+: Specifies whether the location probe will use the network or not.
+
+### Events {#GooglePlacesService-Events}
+
+{:.events}
+
+{:id="GooglePlacesService.NearbyPlacesReceived"} NearbyPlacesReceived(*nearbyPlaces*{:.list})
+: Event indicating that nearby places have been received
+
+{:id="GooglePlacesService.ServiceDataReceived"} ServiceDataReceived(*data*{:.text})
+: Event indicating that the service call has finished.
+
+{:id="GooglePlacesService.ServiceError"} ServiceError(*error*{:.text})
+: Event indicating an error during the service call.
+
+### Methods {#GooglePlacesService-Methods}
+
+{:.methods}
+
+{:id="GooglePlacesService.EnableNearbyPlaces" class="method"} EnableNearbyPlaces(*enableNearbyPlaces*{:.boolean})
+: Enable the component to run once to get location data, call the web service for nearby places, and raise the corresponding events
+
+{:id="GooglePlacesService.EnableScheduleNearbyPlaces" class="method"} EnableScheduleNearbyPlaces(*enableScheduleNearbyPlaces*{:.boolean})
+: Enable the component to periodically listen for location data, call the web service for nearby places, and raise the corresponding events
+
## LdpCoapClient {#LdpCoapClient}

diff --git a/appinventor/docs/markdown/reference/components/sensors.md b/appinventor/docs/markdown/reference/components/sensors.md
index 96b4c4b20..277d9f0b6 100644
--- a/appinventor/docs/markdown/reference/components/sensors.md
+++ b/appinventor/docs/markdown/reference/components/sensors.md
@@ -810,10 +810,10 @@ Record GPS location periodically
{:.properties}
-{:id="LocationProbeSensor.DefaultDuration" .number .ro .bo} *DefaultDuration*
+{:id="LocationProbeSensor.DefaultDuration" .number} *DefaultDuration*
: The default duration (in seconds) of each scan for this probe
-{:id="LocationProbeSensor.DefaultInterval" .number .ro .bo} *DefaultInterval*
+{:id="LocationProbeSensor.DefaultInterval" .number} *DefaultInterval*
: The default interval (in seconds) between each scan for this probe
{:id="LocationProbeSensor.EnableSaveToDB" .boolean .wo .bo} *EnableSaveToDB*
diff --git a/appinventor/lib/google-maps-services/google-maps-services-0.18.1.jar b/appinventor/lib/google-maps-services/google-maps-services-0.18.1.jar
new file mode 100644
index 000000000..f2801a787
Binary files /dev/null and b/appinventor/lib/google-maps-services/google-maps-services-0.18.1.jar differ