From fa9eb27514c7e82de13ce903e4f22ee8679e7871 Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Wed, 12 May 2021 14:23:53 -0300
Subject: [PATCH 01/11] Update README.md
---
README.md | 64 +++++++++++++++++++++++++++----------------------------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/README.md b/README.md
index 991513195..1b396bc3c 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.
From ec2347319bf510ca92f1350850fb30d42b34636b Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Wed, 12 May 2021 14:34:11 -0300
Subject: [PATCH 02/11] Update README.md
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 1b396bc3c..fd7c05be2 100644
--- a/README.md
+++ b/README.md
@@ -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.
From 85b7bbc1569c461e4acd3a57d52c97ee3f64e1dd Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Fri, 14 May 2021 16:11:03 -0300
Subject: [PATCH 03/11] removed non-functioning ontologies (slows down
start_appinventor)
---
.../shared/rpc/semweb/SemWebConstants.properties | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
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 42ce98984..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
@@ -1,3 +1,7 @@
defaultEndpoint = http://dbpedia.org/sparql
#ontologies = http://orion.tw.rpi.edu/~pattoe/dbpedia_3.8.owl,http://xmlns.com/foaf/0.1/
-ontologies = http://xmlns.com/foaf/0.1/,http://hxl.humanitarianresponse.info/ns/hxl.ttl,http://orion.tw.rpi.edu/~pattoe/moac.ttl,https://download.bio2rdf.org/files/current/drugbank/drugbank.schema.owl,http://purl.org/dc/terms/,https://www.w3.org/2001/sw/RDFCore/Schema/200212bwm/rdfs-namespace.xml,https://schema.org/version/latest/schemaorg-current-http.ttl,https://projects.cs.dal.ca/niche/sleepapnea.owl
+
+#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
From 0d43fd5cc0be1d9ba2218fafe2aa0dcd1ffce106 Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Sat, 22 May 2021 12:23:14 -0300
Subject: [PATCH 04/11] added setters for LocationProbeSensor interval,
duration
---
.../runtime/LocationProbeSensor.java | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
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..a4b96bbfc 100755
--- a/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java
@@ -422,21 +422,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 float DefaultInterval(){return interval;}
+
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_FLOAT)
+ @SimpleProperty
+ public void DefaultInterval(float defaultInterval) { 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 float DefaultDuration(){ return duration;}
-
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_FLOAT)
+ @SimpleProperty
+ public void DefaultDuration(float defaultDuration) { this.duration = duration; }
}
\ No newline at end of file
From 45c277286e8a98def37424ada4c21f7ba2be0581 Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Sat, 22 May 2021 12:26:43 -0300
Subject: [PATCH 05/11] using saner defaults for this component
---
.../components/runtime/LocationProbeSensor.java | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
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 a4b96bbfc..4483e951e 100755
--- a/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/LocationProbeSensor.java
@@ -70,8 +70,8 @@ public class LocationProbeSensor extends ProbeBase{
//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;
@@ -256,7 +256,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){
@@ -422,21 +422,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 interval;}
+ public int DefaultInterval(){return interval;}
- @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_FLOAT)
+ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "180")
@SimpleProperty
- public void DefaultInterval(float defaultInterval) { this.interval = interval; }
+ public void DefaultInterval(int defaultInterval) { 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 duration;}
+ public int DefaultDuration(){ return duration;}
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_FLOAT)
@SimpleProperty
- public void DefaultDuration(float defaultDuration) { this.duration = duration; }
+ public void DefaultDuration(int defaultDuration) { this.duration = duration; }
}
\ No newline at end of file
From f0f050e0429b15a26269b2563b2bf7e2645e1999 Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Sat, 22 May 2021 17:41:51 -0300
Subject: [PATCH 06/11] added component classes
---
.../runtime/GooglePlacesService.java | 62 +++++
.../runtime/LocationAwareWebService.java | 216 ++++++++++++++++++
.../runtime/LocationProbeSensor.java | 64 +-----
.../components/runtime/PlacesWebService.java | 160 +++++++++++++
.../components/runtime/WebService.java | 91 ++++++++
5 files changed, 541 insertions(+), 52 deletions(-)
create mode 100644 appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
create mode 100644 appinventor/components/src/com/google/appinventor/components/runtime/LocationAwareWebService.java
create mode 100644 appinventor/components/src/com/google/appinventor/components/runtime/PlacesWebService.java
create mode 100644 appinventor/components/src/com/google/appinventor/components/runtime/WebService.java
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..aa2cd7fb1
--- /dev/null
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
@@ -0,0 +1,62 @@
+package com.google.appinventor.components.runtime;
+
+import com.google.appinventor.components.annotations.DesignerComponent;
+import com.google.appinventor.components.annotations.UsesLibraries;
+import com.google.appinventor.components.common.ComponentCategory;
+import com.google.appinventor.components.common.YaVersion;
+
+import java.time.LocalTime;
+import java.util.Arrays;
+import com.google.maps.GeoApiContext;
+import com.google.maps.PlacesApi;
+import com.google.maps.model.LatLng;
+import com.google.maps.model.PlacesSearchResponse;
+import com.google.maps.model.PlacesSearchResult;
+
+@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")
+@UsesLibraries(libraries = "google-maps-services-0.18.1.jar")
+public class GooglePlacesService extends PlacesWebService {
+
+ /**
+ * Creates a new GooglePlacesService component.
+ *
+ * @param container the container that this component will be placed in
+ */
+ protected GooglePlacesService(ComponentContainer container) {
+ super(container);
+
+ apiKeyRequired = true;
+ }
+
+ /**
+ * 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 locationData
+ */
+ @Override
+ protected void locationDataReceived(Location locationData) {
+ GeoApiContext context = new GeoApiContext.Builder().apiKey(apiKey).build();
+ LatLng myLocation = new LatLng(locationData.lat, locationData.lon);
+
+ try {
+ PlacesSearchResponse results = PlacesApi.nearbySearchQuery(context, myLocation).radius(nearbyRadius).await();
+ for (PlacesSearchResult result : results.results) {
+
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
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..718325ff9
--- /dev/null
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/LocationAwareWebService.java
@@ -0,0 +1,216 @@
+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")
+ 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")
+ 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.")
+ 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")
+ 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")
+ 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.")
+ 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; }
+
+ @Override
+ public void onDataReceived(IJsonObject completeProbeUri, IJsonObject data) {
+ Log.i(TAG, "receive data");
+
+ Location location = new Location(data.get(ProbeKeys.LocationKeys.LATITUDE).getAsDouble(),
+ data.get(ProbeKeys.LocationKeys.LONGITUDE).getAsDouble(),
+ data.get(ProbeKeys.LocationKeys.ACCURACY).getAsFloat(),
+ data.get("mProvider").getAsString(),
+ data.get(ProbeKeys.LocationKeys.TIMESTAMP).getAsLong());
+
+ Log.i(TAG, "location:" + location);
+
+ locationDataReceived(location);
+ }
+
+ @Override
+ public void onDataCompleted(IJsonObject iJsonObject, JsonElement jsonElement) {
+ }
+
+ /**
+ * 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 locationData
+ */
+ protected abstract void locationDataReceived(Location locationData);
+
+ protected class Location {
+
+ double lat;
+ double lon;
+ float accuracy;
+ String provider;
+ long timestamp;
+
+ public Location(double lat, double lon, float accuracy, String provider, long timestamp) {
+ this.lat = lat;
+ this.lon = lon;
+ this.accuracy = accuracy;
+ this.provider = provider;
+ this.timestamp = timestamp;
+ }
+
+ public double getLat() {
+ return lat;
+ }
+
+ public double getLon() {
+ return lon;
+ }
+
+ public float getAccuracy() {
+ return accuracy;
+ }
+
+ public String getProvider() {
+ return provider;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ @Override
+ public String toString() {
+ return "LocationData{" +
+ "lat=" + lat +
+ ", lon=" + lon +
+ ", accuracy=" + accuracy +
+ ", provider='" + provider + '\'' +
+ ", timestamp=" + timestamp +
+ '}';
+ }
+ }
+}
\ 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 4483e951e..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,7 +64,7 @@ public class LocationProbeSensor extends ProbeBase{
private SimpleLocationProbe probe;
-
+
//default settings for schedule
private final int SCHEDULE_INTERVAL = 180; //read location information every 3 minutes
private final int SCHEDULE_DURATION = 10; //scan for 10 seconds everytime
@@ -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");
}
-
}
/*
@@ -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.");
-
-
}
// /**
@@ -425,8 +385,8 @@ public void unregisterDataRequest() {
public int DefaultInterval(){return interval;}
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "180")
- @SimpleProperty
- public void DefaultInterval(int defaultInterval) { this.interval = interval; }
+ @SimpleProperty(category = PropertyCategory.BEHAVIOR)
+ public void DefaultInterval(int interval) { this.interval = interval; }
/*
@@ -435,8 +395,8 @@ public void unregisterDataRequest() {
@SimpleProperty(description = "The default duration (in seconds) of each scan for this probe")
public int DefaultDuration(){ return duration;}
- @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_FLOAT)
- @SimpleProperty
- public void DefaultDuration(int defaultDuration) { this.duration = 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..f3e536110
--- /dev/null
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/PlacesWebService.java
@@ -0,0 +1,160 @@
+package com.google.appinventor.components.runtime;
+
+import com.google.appinventor.components.annotations.DesignerProperty;
+import com.google.appinventor.components.annotations.SimpleEvent;
+import com.google.appinventor.components.annotations.SimpleFunction;
+import com.google.appinventor.components.annotations.SimpleProperty;
+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;
+
+public abstract class PlacesWebService extends LocationAwareWebService {
+
+ protected boolean enabledNearbyPlaces = false; // run once
+ protected boolean enabledScheduleNearbyPlaces = false; // run periodically
+
+ protected int nearbyRadius = 100; // 100 meters
+
+ /**
+ * 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")
+ 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")
+ public boolean EnabledScheduleNearbyPlaces() { return enabledScheduleNearbyPlaces; }
+
+ @SimpleProperty(description = "The default interval (in seconds) between actions, where an action includes a location probe plus service call")
+ 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 (checkRequirements()) {
+ 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 (checkRequirements()) {
+ 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")
+ 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);
+ }
+ });
+ }
+ }
+
+ protected class NearbyPlace {
+
+ private Location location;
+ private String name;
+ private List types;
+ private OpeningHours hours;
+
+ public NearbyPlace(Location location, String name, List types, OpeningHours hours) {
+ this.location = location;
+ this.name = name;
+ this.types = types;
+ this.hours = hours;
+ }
+
+ public Location getLocation() {
+ return location;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getTypes() {
+ return types;
+ }
+
+ public OpeningHours getHours() {
+ return hours;
+ }
+
+ // based on com.google.maps.model.OpeningHours
+ public class OpeningHours {
+
+ public static class Period {
+
+ public static class Time {
+
+ public int day;
+ public LocalTime time;
+
+ @Override
+ public String toString() {
+ return String.format("%s %s", day, time);
+ }
+ }
+
+ public Time open;
+ public Time close;
+
+ @Override
+ public String toString() {
+ return String.format("%s - %s", open, close);
+ }
+ }
+
+ public Period[] periods;
+ public Boolean permanentlyClosed;
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("[OpeningHours:");
+ if (permanentlyClosed != null && permanentlyClosed) {
+ sb.append(" permanentlyClosed");
+ }
+ sb.append(" ").append(Arrays.toString(periods));
+ return sb.toString();
+ }
+ }
+ }
+}
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..356d491a8
--- /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 checkRequirements() {
+ 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.")
+ 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);
+ }
+ });
+ }
+}
From 876e42e73232ed62f0209c0424652bd32ef8dfdc Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Mon, 24 May 2021 10:34:41 -0300
Subject: [PATCH 07/11] commit before checkout
---
appinventor/components/build.xml | 2 ++
.../components/common/YaVersion.java | 2 ++
.../runtime/GooglePlacesService.java | 2 +-
.../reference/components/connectivity.md | 25 +++++++++++++++++++
.../markdown/reference/components/sensors.md | 4 +--
5 files changed, 32 insertions(+), 3 deletions(-)
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
index aa2cd7fb1..130077464 100644
--- a/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
@@ -53,7 +53,7 @@ protected void locationDataReceived(Location locationData) {
try {
PlacesSearchResponse results = PlacesApi.nearbySearchQuery(context, myLocation).radius(nearbyRadius).await();
for (PlacesSearchResult result : results.results) {
-
+
}
} catch(Exception e) {
e.printStackTrace();
diff --git a/appinventor/docs/markdown/reference/components/connectivity.md b/appinventor/docs/markdown/reference/components/connectivity.md
index c35513b98..bd7f478af 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,30 @@ 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}
+None
+
+
+### Events {#GooglePlacesService-Events}
+
+{:.events}
+None
+
+
+### Methods {#GooglePlacesService-Methods}
+
+{:.methods}
+None
+
+
## 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*
From 52b6d77a895ba3b64d53c5a1ff2e4db5c768c1a1 Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Tue, 25 May 2021 10:29:38 -0300
Subject: [PATCH 08/11] added property categories
---
.../runtime/GooglePlacesService.java | 182 ++++++++++++++++--
.../runtime/LocationAwareWebService.java | 92 ++-------
.../components/runtime/PlacesWebService.java | 94 ++-------
.../components/runtime/WebService.java | 10 +-
.../reference/components/connectivity.md | 52 ++++-
5 files changed, 250 insertions(+), 180 deletions(-)
diff --git a/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
index 130077464..54efb8d5a 100644
--- a/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
@@ -1,17 +1,34 @@
package com.google.appinventor.components.runtime;
-import com.google.appinventor.components.annotations.DesignerComponent;
-import com.google.appinventor.components.annotations.UsesLibraries;
+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, " +
@@ -24,9 +41,12 @@
"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")
+ category = ComponentCategory.CONNECTIVITY, nonVisible = true, iconName = "images/locationProbe.png", showOnPalette = true)
+@SimpleObject
@UsesLibraries(libraries = "google-maps-services-0.18.1.jar")
-public class GooglePlacesService extends PlacesWebService {
+public class GooglePlacesService extends PlacesWebService implements Callback {
+
+ private GeoApiContext context;
/**
* Creates a new GooglePlacesService component.
@@ -39,24 +59,158 @@ protected GooglePlacesService(ComponentContainer 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;
+ }
+
/**
- * 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.
+ * Specifies the type of nearby places that should be returned.
*
- * @param locationData
+ * @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
- protected void locationDataReceived(Location locationData) {
- GeoApiContext context = new GeoApiContext.Builder().apiKey(apiKey).build();
- LatLng myLocation = new LatLng(locationData.lat, locationData.lon);
+ 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 {
- PlacesSearchResponse results = PlacesApi.nearbySearchQuery(context, myLocation).radius(nearbyRadius).await();
- for (PlacesSearchResult result : results.results) {
+ NearbySearchRequest request = PlacesApi.nearbySearchQuery(context, myLocation).radius(nearbyRadius);
+ if (placeType != null)
+ request.type(PlaceType.valueOf(placeType));
- }
- } catch(Exception e) {
+ // 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
index 718325ff9..17763f893 100644
--- a/appinventor/components/src/com/google/appinventor/components/runtime/LocationAwareWebService.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/LocationAwareWebService.java
@@ -43,7 +43,8 @@ protected LocationAwareWebService(ComponentContainer container) {
*
* @return defaultInterval
*/
- @SimpleProperty(description = "The default interval (in seconds) between actions, where an action includes a location probe plus service call")
+ @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();}
/**
@@ -60,7 +61,8 @@ protected LocationAwareWebService(ComponentContainer container) {
*
* @return defaultDuration
*/
- @SimpleProperty(description = "The default duration (in seconds) of each location probe scan")
+ @SimpleProperty(description = "The default duration (in seconds) of each location probe scan",
+ category = PropertyCategory.BEHAVIOR)
public int DefaultDuration(){ return locationProbeSensor.DefaultDuration();}
/**
@@ -79,7 +81,8 @@ protected LocationAwareWebService(ComponentContainer container) {
* @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.")
+ "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();
}
@@ -96,7 +99,7 @@ public int GoodEnoughAccuracy() {
/**
* Returns whether the location probe will use GPS or not.
*/
- @SimpleProperty(description = "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();
}
@@ -111,7 +114,8 @@ public boolean UseGPS() {
/**
* Returns whether the location probe will use the network or not.
*/
- @SimpleProperty(description = "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(); }
/**
@@ -128,7 +132,8 @@ public boolean UseGPS() {
* @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.")
+ "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; }
/**
@@ -138,79 +143,4 @@ public boolean UseGPS() {
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "0")
public void MinimumLocationChange(int minimumLocationChange) { this.minimumLocationChange = minimumLocationChange; }
-
- @Override
- public void onDataReceived(IJsonObject completeProbeUri, IJsonObject data) {
- Log.i(TAG, "receive data");
-
- Location location = new Location(data.get(ProbeKeys.LocationKeys.LATITUDE).getAsDouble(),
- data.get(ProbeKeys.LocationKeys.LONGITUDE).getAsDouble(),
- data.get(ProbeKeys.LocationKeys.ACCURACY).getAsFloat(),
- data.get("mProvider").getAsString(),
- data.get(ProbeKeys.LocationKeys.TIMESTAMP).getAsLong());
-
- Log.i(TAG, "location:" + location);
-
- locationDataReceived(location);
- }
-
- @Override
- public void onDataCompleted(IJsonObject iJsonObject, JsonElement jsonElement) {
- }
-
- /**
- * 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 locationData
- */
- protected abstract void locationDataReceived(Location locationData);
-
- protected class Location {
-
- double lat;
- double lon;
- float accuracy;
- String provider;
- long timestamp;
-
- public Location(double lat, double lon, float accuracy, String provider, long timestamp) {
- this.lat = lat;
- this.lon = lon;
- this.accuracy = accuracy;
- this.provider = provider;
- this.timestamp = timestamp;
- }
-
- public double getLat() {
- return lat;
- }
-
- public double getLon() {
- return lon;
- }
-
- public float getAccuracy() {
- return accuracy;
- }
-
- public String getProvider() {
- return provider;
- }
-
- public long getTimestamp() {
- return timestamp;
- }
-
- @Override
- public String toString() {
- return "LocationData{" +
- "lat=" + lat +
- ", lon=" + lon +
- ", accuracy=" + accuracy +
- ", provider='" + provider + '\'' +
- ", timestamp=" + timestamp +
- '}';
- }
- }
}
\ 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
index f3e536110..d1178576e 100644
--- a/appinventor/components/src/com/google/appinventor/components/runtime/PlacesWebService.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/PlacesWebService.java
@@ -1,9 +1,6 @@
package com.google.appinventor.components.runtime;
-import com.google.appinventor.components.annotations.DesignerProperty;
-import com.google.appinventor.components.annotations.SimpleEvent;
-import com.google.appinventor.components.annotations.SimpleFunction;
-import com.google.appinventor.components.annotations.SimpleProperty;
+import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.runtime.util.YailList;
@@ -11,12 +8,14 @@
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.
@@ -27,14 +26,17 @@ 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")
+ @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")
+ "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")
+ @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")
@@ -44,7 +46,7 @@ protected PlacesWebService(ComponentContainer container) {
@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 (checkRequirements()) {
+ if (checkInput()) {
this.enabledNearbyPlaces = enableNearbyPlaces;
locationProbeSensor.Enabled(enableNearbyPlaces);
}
@@ -53,7 +55,7 @@ public void EnableNearbyPlaces(boolean 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 (checkRequirements()) {
+ if (checkInput()) {
this.enabledScheduleNearbyPlaces = enableScheduleNearbyPlaces;
locationProbeSensor.EnabledSchedule(enableScheduleNearbyPlaces);
}
@@ -64,7 +66,8 @@ public void EnableScheduleNearbyPlaces(boolean enableScheduleNearbyPlaces) {
*
* @return nearbyRadius
*/
- @SimpleProperty(description = "The radius around the user’s current location (meters) for which nearby places should be returned")
+ @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; }
/**
@@ -88,73 +91,10 @@ public void run() {
}
}
- protected class NearbyPlace {
+ // We want service-specific documentation so have subclasses implement these
+ // methods. Also, subclasses may want to pre-process these types.
- private Location location;
- private String name;
- private List types;
- private OpeningHours hours;
+ public abstract String PlaceType();
- public NearbyPlace(Location location, String name, List types, OpeningHours hours) {
- this.location = location;
- this.name = name;
- this.types = types;
- this.hours = hours;
- }
-
- public Location getLocation() {
- return location;
- }
-
- public String getName() {
- return name;
- }
-
- public List getTypes() {
- return types;
- }
-
- public OpeningHours getHours() {
- return hours;
- }
-
- // based on com.google.maps.model.OpeningHours
- public class OpeningHours {
-
- public static class Period {
-
- public static class Time {
-
- public int day;
- public LocalTime time;
-
- @Override
- public String toString() {
- return String.format("%s %s", day, time);
- }
- }
-
- public Time open;
- public Time close;
-
- @Override
- public String toString() {
- return String.format("%s - %s", open, close);
- }
- }
-
- public Period[] periods;
- public Boolean permanentlyClosed;
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("[OpeningHours:");
- if (permanentlyClosed != null && permanentlyClosed) {
- sb.append(" permanentlyClosed");
- }
- sb.append(" ").append(Arrays.toString(periods));
- return sb.toString();
- }
- }
- }
+ 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
index 356d491a8..987003308 100644
--- a/appinventor/components/src/com/google/appinventor/components/runtime/WebService.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/WebService.java
@@ -10,9 +10,9 @@
* @author william.van.woensel@gmail.com
*/
@SimpleObject
-@UsesPermissions(permissionNames = "android.permission.INTERNET," +
- "android.permission.WRITE_EXTERNAL_STORAGE," +
- "android.permission.READ_EXTERNAL_STORAGE")
+@UsesPermissions(permissionNames = "android.permission.INTERNET")
+ // "android.permission.WRITE_EXTERNAL_STORAGE," +
+ // "android.permission.READ_EXTERNAL_STORAGE")
public class WebService extends AndroidNonvisibleComponent implements Component {
@@ -31,7 +31,7 @@ protected WebService(ComponentContainer container) {
this.activity = container.$context();
}
- protected boolean checkRequirements() {
+ protected boolean checkInput() {
if (apiKeyRequired && apiKey == null) {
ServiceError("API key required for this service but none given. See ApiKey property.");
return false;
@@ -44,7 +44,7 @@ protected boolean checkRequirements() {
*
* @return the API key
*/
- @SimpleProperty(description = "The API key for this web service, if any.")
+ @SimpleProperty(description = "The API key for this web service, if any.", category = PropertyCategory.BEHAVIOR)
public String ApiKey() { return apiKey; }
/**
diff --git a/appinventor/docs/markdown/reference/components/connectivity.md b/appinventor/docs/markdown/reference/components/connectivity.md
index bd7f478af..05cb6f3f9 100644
--- a/appinventor/docs/markdown/reference/components/connectivity.md
+++ b/appinventor/docs/markdown/reference/components/connectivity.md
@@ -406,20 +406,66 @@ Component for GooglePlacesService
### Properties {#GooglePlacesService-Properties}
{:.properties}
-None
+{: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}
-None
+{: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}
-None
+{: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}
From 092cc6003b323e24d270405a16ca744907357a63 Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Fri, 14 May 2021 16:11:03 -0300
Subject: [PATCH 09/11] removed non-functioning ontologies (slows down
start_appinventor)
---
.../appinventor/shared/rpc/semweb/SemWebConstants.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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
From 956e8fac87d1a398d99aade111bd9ab1931c85cc Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Thu, 7 Oct 2021 12:05:18 -0300
Subject: [PATCH 10/11] add funf dependency to gplaces-service
---
.../runtime/GooglePlacesService.java | 2 +-
.../reference/components/connectivity.html | 55 +++++++++++++++++++
.../html/reference/components/sensors.html | 4 +-
3 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
index 54efb8d5a..b777d903e 100644
--- a/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
+++ b/appinventor/components/src/com/google/appinventor/components/runtime/GooglePlacesService.java
@@ -43,7 +43,7 @@
"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")
+@UsesLibraries(libraries = "google-maps-services-0.18.1.jar,funf.jar")
public class GooglePlacesService extends PlacesWebService implements Callback {
private GeoApiContext context;
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
From 544030a080d2239b30ff93a4e8dbb12fb8e4fcc9 Mon Sep 17 00:00:00 2001
From: William Van Woensel
Date: Wed, 14 Dec 2022 07:28:21 -0500
Subject: [PATCH 11/11] added gms dep | applied devmode fix
---
appinventor/appengine/build.xml | 73 +++++++-----------
.../appinventor/client/utils/html5dnd.js | 2 +-
.../google-maps-services-0.18.1.jar | Bin 0 -> 226843 bytes
3 files changed, 28 insertions(+), 47 deletions(-)
create mode 100644 appinventor/lib/google-maps-services/google-maps-services-0.18.1.jar
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/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 0000000000000000000000000000000000000000..f2801a78774540d70fb763ca89f2dc81a0a039f5
GIT binary patch
literal 226843
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
z
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